import java.util.Optional;
import org.sonar.ce.task.projectanalysis.analysis.Organization;
+import org.sonar.server.project.Project;
public interface QualityGateService {
Optional<QualityGate> findById(long id);
/**
- * Retrieve the {@link QualityGate} from the database with the specified uuid.
+ * Retrieve the {@link QualityGate} from the database using organization.
* @throws IllegalStateException if database is corrupted and default gate can't be found.
*/
QualityGate findDefaultQualityGate(Organization organizationDto);
+ /**
+ * Retrieve the {@link QualityGate} from the database associated with project.
+ */
+ Optional<QualityGate> findQualityGate(Project project);
+
}
import org.sonar.db.DbSession;
import org.sonar.db.qualitygate.QualityGateConditionDto;
import org.sonar.db.qualitygate.QualityGateDto;
+import org.sonar.server.project.Project;
import static org.sonar.core.util.stream.MoreCollectors.toList;
}
}
+ @Override
+ public Optional<QualityGate> findQualityGate(Project project) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ QualityGateDto qualityGateDto = dbClient.qualityGateDao().selectByProjectUuid(dbSession, project.getUuid());
+ if (qualityGateDto == null) {
+ return Optional.empty();
+ }
+ return Optional.of(toQualityGate(dbSession, qualityGateDto));
+ }
+ }
+
private QualityGate toQualityGate(DbSession dbSession, QualityGateDto qualityGateDto) {
Collection<QualityGateConditionDto> dtos = dbClient.gateConditionDao().selectForQualityGate(dbSession, qualityGateDto.getId());
import java.util.Optional;
import java.util.stream.Collectors;
-import org.sonar.api.config.Configuration;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
-import org.sonar.ce.task.projectanalysis.component.ConfigurationRepository;
import org.sonar.ce.task.projectanalysis.qualitygate.Condition;
import org.sonar.ce.task.projectanalysis.qualitygate.MutableQualityGateHolder;
import org.sonar.ce.task.projectanalysis.qualitygate.QualityGate;
import org.sonar.ce.task.projectanalysis.qualitygate.QualityGateService;
import org.sonar.ce.task.step.ComputationStep;
-
-import static org.apache.commons.lang.StringUtils.isBlank;
+import org.sonar.server.project.Project;
/**
* This step retrieves the QualityGate and stores it in
* {@link MutableQualityGateHolder}.
*/
public class LoadQualityGateStep implements ComputationStep {
- private static final String PROPERTY_PROJECT_QUALITY_GATE = "sonar.qualitygate";
- private final ConfigurationRepository configRepository;
private final QualityGateService qualityGateService;
private final MutableQualityGateHolder qualityGateHolder;
private final AnalysisMetadataHolder analysisMetadataHolder;
- public LoadQualityGateStep(ConfigurationRepository settingsRepository, QualityGateService qualityGateService, MutableQualityGateHolder qualityGateHolder,
- AnalysisMetadataHolder analysisMetadataHolder) {
- this.configRepository = settingsRepository;
+ public LoadQualityGateStep(QualityGateService qualityGateService, MutableQualityGateHolder qualityGateHolder,
+ AnalysisMetadataHolder analysisMetadataHolder) {
this.qualityGateService = qualityGateService;
this.qualityGateHolder = qualityGateHolder;
this.analysisMetadataHolder = analysisMetadataHolder;
}
private Optional<QualityGate> getProjectQualityGate() {
- Configuration config = configRepository.getConfiguration();
- String qualityGateSetting = config.get(PROPERTY_PROJECT_QUALITY_GATE).orElse(null);
-
- if (isBlank(qualityGateSetting)) {
- return Optional.empty();
- }
-
- try {
- long qualityGateId = Long.parseLong(qualityGateSetting);
- return qualityGateService.findById(qualityGateId);
- } catch (NumberFormatException e) {
- throw new IllegalStateException(
- String.format("Unsupported value (%s) in property %s", qualityGateSetting, PROPERTY_PROJECT_QUALITY_GATE), e);
- }
+ Project project = analysisMetadataHolder.getProject();
+ return qualityGateService.findQualityGate(project);
}
private QualityGate getOrganizationDefaultQualityGate() {
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
+import org.sonar.ce.task.projectanalysis.analysis.Organization;
import org.sonar.ce.task.projectanalysis.metric.Metric;
import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
import org.sonar.db.DbClient;
+import org.sonar.db.qualitygate.QGateWithOrgDto;
import org.sonar.db.qualitygate.QualityGateConditionDao;
import org.sonar.db.qualitygate.QualityGateConditionDto;
import org.sonar.db.qualitygate.QualityGateDao;
import org.sonar.db.qualitygate.QualityGateDto;
+import org.sonar.server.project.Project;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
assertThat(res.get().getConditions()).containsOnly(
new Condition(METRIC_2, CONDITION_2.getOperator(), CONDITION_2.getErrorThreshold()));
}
+
+ @Test(expected = IllegalStateException.class)
+ public void findDefaultQualityGate_by_organization_not_found() {
+ when(qualityGateDao.selectByOrganizationAndUuid(any(), any(), any())).thenReturn(null);
+
+ underTest.findDefaultQualityGate(mock(Organization.class));
+ }
+
+ @Test
+ public void findDefaultQualityGate_by_organization_found() {
+ QGateWithOrgDto qGateWithOrgDto = new QGateWithOrgDto();
+ qGateWithOrgDto.setId(QUALITY_GATE_DTO.getId());
+ qGateWithOrgDto.setName(QUALITY_GATE_DTO.getName());
+ when(qualityGateDao.selectByOrganizationAndUuid(any(), any(), any())).thenReturn(qGateWithOrgDto);
+ when(qualityGateConditionDao.selectForQualityGate(any(), eq(SOME_ID))).thenReturn(ImmutableList.of(CONDITION_1, CONDITION_2));
+ when(metricRepository.getOptionalById(METRIC_ID_1)).thenReturn(Optional.empty());
+ when(metricRepository.getOptionalById(METRIC_ID_2)).thenReturn(Optional.of(METRIC_2));
+
+ QualityGate result = underTest.findDefaultQualityGate(mock(Organization.class));
+
+ assertThat(result).isNotNull();
+ assertThat(result.getId()).isEqualTo(QUALITY_GATE_DTO.getId());
+ assertThat(result.getName()).isNotBlank();
+ assertThat(result.getName()).isEqualTo(QUALITY_GATE_DTO.getName());
+ }
+
+ @Test
+ public void findQualityGate_by_project_not_found() {
+ when(qualityGateDao.selectByProjectUuid(any(), any())).thenReturn(null);
+ Optional<QualityGate> result = underTest.findQualityGate(mock(Project.class));
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ public void findQualityGate_by_project_found() {
+ QGateWithOrgDto qGateWithOrgDto = new QGateWithOrgDto();
+ qGateWithOrgDto.setId(QUALITY_GATE_DTO.getId());
+ qGateWithOrgDto.setName(QUALITY_GATE_DTO.getName());
+ when(qualityGateDao.selectByProjectUuid(any(), any())).thenReturn(qGateWithOrgDto);
+ when(qualityGateConditionDao.selectForQualityGate(any(), eq(SOME_ID))).thenReturn(ImmutableList.of(CONDITION_1, CONDITION_2));
+ when(metricRepository.getOptionalById(METRIC_ID_1)).thenReturn(Optional.empty());
+ when(metricRepository.getOptionalById(METRIC_ID_2)).thenReturn(Optional.of(METRIC_2));
+
+ Optional<QualityGate> result = underTest.findQualityGate(mock(Project.class));
+
+ assertThat(result).isNotNull();
+ assertThat(result).isNotEmpty();
+
+ QualityGate resultData = result.get();
+ assertThat(resultData.getId()).isEqualTo(QUALITY_GATE_DTO.getId());
+ assertThat(resultData.getName()).isNotBlank();
+ assertThat(resultData.getName()).isEqualTo(QUALITY_GATE_DTO.getName());
+ }
}
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
-import org.sonar.api.config.internal.MapSettings;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
-import org.sonar.ce.task.projectanalysis.component.ConfigurationRepository;
+import org.sonar.ce.task.projectanalysis.analysis.Organization;
import org.sonar.ce.task.projectanalysis.metric.Metric;
import org.sonar.ce.task.projectanalysis.metric.MetricImpl;
import org.sonar.ce.task.projectanalysis.qualitygate.Condition;
import org.sonar.ce.task.projectanalysis.qualitygate.QualityGate;
import org.sonar.ce.task.projectanalysis.qualitygate.QualityGateService;
import org.sonar.ce.task.step.TestComputationStepContext;
+import org.sonar.server.project.Project;
import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
public MutableQualityGateHolderRule mutableQualityGateHolder = new MutableQualityGateHolderRule();
private AnalysisMetadataHolder analysisMetadataHolder = mock(AnalysisMetadataHolder.class);
- private ConfigurationRepository settingsRepository = mock(ConfigurationRepository.class);
private QualityGateService qualityGateService = mock(QualityGateService.class);
- private LoadQualityGateStep underTest = new LoadQualityGateStep(settingsRepository, qualityGateService, mutableQualityGateHolder, analysisMetadataHolder);
+ private LoadQualityGateStep underTest = new LoadQualityGateStep(qualityGateService, mutableQualityGateHolder, analysisMetadataHolder);
@Before
public void setUp() {
when(analysisMetadataHolder.isShortLivingBranch()).thenReturn(false);
+ when(analysisMetadataHolder.getOrganization()).thenReturn(mock(Organization.class));
}
@Test
public void filter_conditions_on_short_living_branch() {
- when(settingsRepository.getConfiguration()).thenReturn(new MapSettings().asConfig());
Metric newMetric = new MetricImpl(1, "new_key", "name", Metric.MetricType.INT);
Metric metric = new MetricImpl(2, "key", "name", Metric.MetricType.INT);
when(analysisMetadataHolder.isSLBorPR()).thenReturn(true);
QualityGate defaultGate = new QualityGate(1, "qg", Arrays.asList(variation, condition));
- when(qualityGateService.findDefaultQualityGate(any())).thenReturn(defaultGate);
+ when(qualityGateService.findDefaultQualityGate(any(Organization.class))).thenReturn(defaultGate);
underTest.execute(new TestComputationStepContext());
@Test
public void filter_conditions_on_pull_request() {
- when(settingsRepository.getConfiguration()).thenReturn(new MapSettings().asConfig());
-
Metric newMetric = new MetricImpl(1, "new_key", "name", Metric.MetricType.INT);
Metric metric = new MetricImpl(2, "key", "name", Metric.MetricType.INT);
Condition variation = new Condition(newMetric, Condition.Operator.GREATER_THAN.getDbValue(), "1.0");
when(analysisMetadataHolder.isSLBorPR()).thenReturn(true);
QualityGate defaultGate = new QualityGate(1, "qg", Arrays.asList(variation, condition));
- when(qualityGateService.findDefaultQualityGate(any())).thenReturn(defaultGate);
+ when(qualityGateService.findDefaultQualityGate(any(Organization.class))).thenReturn(defaultGate);
underTest.execute(new TestComputationStepContext());
@Test
public void execute_sets_default_QualityGate_when_project_has_no_settings() {
- when(settingsRepository.getConfiguration()).thenReturn(new MapSettings().asConfig());
QualityGate defaultGate = mock(QualityGate.class);
- when(qualityGateService.findDefaultQualityGate(any())).thenReturn(defaultGate);
+ when(qualityGateService.findDefaultQualityGate(any(Organization.class))).thenReturn(defaultGate);
underTest.execute(new TestComputationStepContext());
assertThat(mutableQualityGateHolder.getQualityGate().get()).isSameAs(defaultGate);
}
- @Test
- public void execute_sets_default_QualityGate_when_property_value_is_not_a_long() {
- expectedException.expect(IllegalStateException.class);
- expectedException.expectMessage("Unsupported value (10 sds) in property sonar.qualitygate");
-
- when(settingsRepository.getConfiguration()).thenReturn(new MapSettings().setProperty("sonar.qualitygate", "10 sds").asConfig());
-
- underTest.execute(new TestComputationStepContext());
- }
-
@Test
public void execute_sets_QualityGate_if_it_can_be_found_by_service() {
QualityGate qualityGate = new QualityGate(10, "name", emptyList());
- when(settingsRepository.getConfiguration()).thenReturn(new MapSettings().setProperty("sonar.qualitygate", 10).asConfig());
- when(qualityGateService.findById(10)).thenReturn(Optional.of(qualityGate));
+ when(analysisMetadataHolder.getProject()).thenReturn(mock(Project.class));
+ when(qualityGateService.findQualityGate(any(Project.class))).thenReturn(Optional.of(qualityGate));
underTest.execute(new TestComputationStepContext());
return mapper(dbSession).selectProjects(query);
}
- /**
- * @return quality gate id if a specific Quality Gate has been defined for the given component id. <br>
- * Returns <code>{@link Optional#empty()}</code> otherwise (ex: default quality gate applies)
- */
- public Optional<Long> selectQGateIdByComponentId(DbSession dbSession, long componentId) {
- String id = mapper(dbSession).selectQGateIdByComponentId(componentId);
-
- 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)
List<ProjectQgateAssociationDto> selectProjects(@Param("query") ProjectQgateAssociationQuery query);
- @CheckForNull
- String selectQGateIdByComponentId(long componentId);
-
@CheckForNull
String selectQGateUuidByComponentUuid(String componentUuid);
public static final String OUT = "deselected";
public static final Set<String> AVAILABLE_MEMBERSHIP = ImmutableSet.of(ANY, IN, OUT);
- private final String gateId;
+ private final String gateUuid;
private final String organizationUuid;
private final String membership;
private final int pageIndex;
private ProjectQgateAssociationQuery(Builder builder) {
- this.gateId = Long.toString(builder.qualityGate.getId());
+ this.gateUuid = builder.qualityGate.getUuid();
this.organizationUuid = builder.qualityGate.getOrganizationUuid();
this.membership = builder.membership;
this.projectSearch = builder.projectSearch;
this.pageIndex = builder.pageIndex;
}
- public String gateId() {
- return gateId;
+ public String gateUuid() {
+ return gateUuid;
}
public String organizationUuid() {
return mapper(session).selectById(id);
}
+ @CheckForNull
+ public QualityGateDto selectByUuid(DbSession session, String uuid) {
+ return mapper(session).selectByUuid(uuid);
+ }
+
@CheckForNull
public QGateWithOrgDto selectByOrganizationAndUuid(DbSession dbSession, OrganizationDto organization, String qualityGateUuid) {
return mapper(dbSession).selectByUuidAndOrganization(qualityGateUuid, organization.getUuid());
void ensureOneBuiltInQualityGate(String builtInQualityName);
+ QualityGateDto selectByUuid(String uuid);
+
QualityGateDto selectByProjectUuid(@Param("projectUuid") String projectUuid);
}
<mapper namespace="org.sonar.db.qualitygate.ProjectQgateAssociationMapper">
<select id="selectProjects" parameterType="map" resultType="ProjectQgateAssociation">
- SELECT proj.id as id, proj.kee as "key", proj.name as name, prop.text_value as gateId
+ SELECT proj.id as id, proj.kee as "key", proj.name as name, qg.id as gateId
FROM projects proj
- LEFT JOIN properties prop ON prop.resource_id=proj.id AND prop.prop_key='sonar.qualitygate' AND prop.text_value = #{query.gateId}
+ LEFT JOIN project_qgates prqg ON prqg.project_uuid=proj.uuid AND prqg.quality_gate_uuid = #{query.gateUuid, jdbcType=VARCHAR}
+ LEFT JOIN quality_gates qg ON qg.uuid = prqg.quality_gate_uuid
where
proj.qualifier = 'TRK'
and proj.enabled = ${_true}
and proj.organization_uuid=#{query.organizationUuid, jdbcType=VARCHAR}
<choose>
<when test="query.membership() == 'selected'">
- and prop.text_value IS NOT NULL
+ and qg.id IS NOT NULL
</when>
<when test="query.membership() == 'deselected'">
- and prop.text_value IS NULL
+ and qg.id IS NULL
</when>
</choose>
<if test="query.projectSearch() != null">
order by proj.name
</select>
- <select id="selectQGateIdByComponentId" parameterType="long" resultType="string">
- SELECT text_value
- FROM properties
- <where>
- AND resource_id=#{componentId}
- AND prop_key='sonar.qualitygate'
- </where>
- </select>
-
<select id="selectQGateUuidByComponentUuid" parameterType="String" resultType="string">
SELECT quality_gate_uuid
FROM project_qgates
where name=#{name, jdbcType=VARCHAR}
</select>
+ <select id="selectByUuid" parameterType="String" resultType="QualityGate">
+ select
+ <include refid="gateColumns"/>
+ from quality_gates
+ where uuid=#{uuid, jdbcType=VARCHAR}
+ </select>
+
<select id="selectByUuidAndOrganization" parameterType="Map" resultType="org.sonar.db.qualitygate.QGateWithOrgDto">
SELECT
<include refid="qateWithOrgColumns"/>
}
@Test
- public void select_qgate_id_is_absent() {
+ public void select_qgate_uuid_is_absent() {
ComponentDto project = db.components().insertPrivateProject();
- Optional<Long> result = underTest.selectQGateIdByComponentId(dbSession, project.getId());
+ Optional<String> result = underTest.selectQGateUuidByComponentUuid(dbSession, project.uuid());
assertThat(result.isPresent()).isFalse();
}
@Test
- public void select_qgate_id() {
+ public void select_qgate_uuid() {
OrganizationDto organization = db.organizations().insert();
QGateWithOrgDto qualityGate1 = db.qualityGates().insertQualityGate(organization);
QGateWithOrgDto qualityGate2 = db.qualityGates().insertQualityGate(organization);
db.qualityGates().associateProjectToQualityGate(project1, qualityGate1);
db.qualityGates().associateProjectToQualityGate(project2, qualityGate2);
- Optional<Long> result = underTest.selectQGateIdByComponentId(dbSession, project1.getId());
+ Optional<String> result = underTest.selectQGateUuidByComponentUuid(dbSession, project1.uuid());
- assertThat(result).contains(qualityGate1.getId());
+ assertThat(result).contains(qualityGate1.getUuid());
}
- @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();
}
@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());
+ public void testSelectByUuid() {
+ insertQualityGates();
+ assertThat(underTest.selectByUuid(dbSession, underTest.selectByName(dbSession, "Very strict").getUuid()).getName()).isEqualTo("Very strict");
+ assertThat(underTest.selectByUuid(dbSession, "not-existing-uuid")).isNull();
}
@Test
assertThat(underTest.selectDefault(dbSession, otherOrganization).getUuid()).isEqualTo(otherQualityGate.getUuid());
}
+ @Test
+ public void select_by_project_uuid() {
+ OrganizationDto organization = db.organizations().insert();
+
+ ComponentDto project = db.components().insertPrivateProject(organization);
+
+ QGateWithOrgDto qualityGate1 = db.qualityGates().insertQualityGate(organization);
+ QGateWithOrgDto qualityGate2 = db.qualityGates().insertQualityGate(organization);
+
+ OrganizationDto otherOrganization = db.organizations().insert();
+ QGateWithOrgDto qualityGate3 = db.qualityGates().insertQualityGate(otherOrganization);
+
+ db.qualityGates().associateProjectToQualityGate(project, qualityGate1);
+
+ assertThat(underTest.selectByProjectUuid(dbSession, project.uuid()).getUuid()).isEqualTo(qualityGate1.getUuid());
+ assertThat(underTest.selectByProjectUuid(dbSession, "not-existing-uuid")).isNull();
+ }
+
@Test
public void delete() {
OrganizationDto organization = db.organizations().insert();
import org.sonar.db.component.ComponentDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.organization.OrganizationDto;
-import org.sonar.db.property.PropertyDto;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.apache.commons.lang.RandomStringUtils.randomNumeric;
}
public void associateProjectToQualityGate(ComponentDto component, QualityGateDto qualityGate) {
- dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto()
- .setKey("sonar.qualitygate")
- .setResourceId(component.getId())
- .setValue(String.valueOf(qualityGate.getId())));
-
dbClient.projectQgateAssociationDao().insertProjectQGateAssociation(dbSession, component.uuid(), qualityGate.getUuid());
-
db.commit();
}
return condition;
}
- public Optional<String> selectQGateUuidByComponentUuid(String uuid) {
- return dbClient.projectQgateAssociationDao().selectQGateUuidByComponentUuid(dbSession, uuid);
+ public Optional<String> selectQGateUuidByComponentUuid(String componentUuid) {
+ return dbClient.projectQgateAssociationDao().selectQGateUuidByComponentUuid(dbSession, componentUuid);
}
}
registry
.add(3000, "Set Organizations#guarded column nullable", MakeOrganizationsGuardedNullable.class)
.add(3001, "Create ProjectQualityGates table", CreateProjectQualityGatesTable.class)
- .add(3002, "Make index on DEPRECATED_RULE_KEYS.RULE_ID non unique", MakeDeprecatedRuleKeysRuleIdIndexNonUnique.class);
+ .add(3002, "Make index on DEPRECATED_RULE_KEYS.RULE_ID non unique", MakeDeprecatedRuleKeysRuleIdIndexNonUnique.class)
+ .add(3003, "Populate ProjectQualityGate table from Properties table", PopulateProjectQualityGatesTable.class);
}
}
--- /dev/null
+/*
+ * 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 java.util.List;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.Upsert;
+
+public class PopulateProjectQualityGatesTable extends DataChange {
+
+ public PopulateProjectQualityGatesTable(Database db) {
+ super(db);
+ }
+
+ @Override
+ protected void execute(Context context) throws SQLException {
+ List<ProjectQualityGate> projectQualityGates = context.prepareSelect(
+ "select prj.uuid, qg.uuid from properties p \n" +
+ "join projects prj on p.resource_id = prj.id\n" +
+ "join quality_gates qg on qg.id = CAST(p.text_value AS int)\n" +
+ "where p.prop_key = 'sonar.qualitygate'\n" +
+ "and not exists(select pqg.project_uuid from project_qgates pqg where pqg.project_uuid = prj.uuid)")
+ .list(row -> {
+ String projectUuid = row.getString(1);
+ String qualityGateUuid = row.getString(2);
+ return new ProjectQualityGate(projectUuid, qualityGateUuid);
+ });
+
+ if (!projectQualityGates.isEmpty()) {
+ populateProjectQualityGates(context, projectQualityGates);
+ }
+ }
+
+ private static void populateProjectQualityGates(Context context, List<ProjectQualityGate> projectQualityGates) throws SQLException {
+ Upsert insertQuery = prepareInsertProjectQualityGateQuery(context);
+ for (ProjectQualityGate projectQualityGate : projectQualityGates) {
+ insertQuery
+ .setString(1, projectQualityGate.projectUuid)
+ .setString(2, projectQualityGate.qualityGateUuid)
+ .addBatch();
+ }
+ insertQuery
+ .execute()
+ .commit();
+ }
+
+ private static Upsert prepareInsertProjectQualityGateQuery(Context context) throws SQLException {
+ return context.prepareUpsert("insert into project_qgates(project_uuid, quality_gate_uuid) VALUES (?, ?)");
+ }
+
+ private static class ProjectQualityGate {
+ private final String projectUuid;
+ private final String qualityGateUuid;
+
+ ProjectQualityGate(String projectUuid, String qualityGateUuid) {
+ this.projectUuid = projectUuid;
+ this.qualityGateUuid = qualityGateUuid;
+ }
+ }
+
+}
@Test
public void verify_migration_count() {
- verifyMigrationCount(underTest, 3);
+ verifyMigrationCount(underTest, 4);
}
}
--- /dev/null
+/*
+ * 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 java.time.Instant;
+import java.util.List;
+import java.util.Random;
+import java.util.stream.Collectors;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+import static java.lang.String.valueOf;
+
+
+public class PopulateProjectQualityGatesTableTest {
+ private static final String PROJECTS_TABLE_NAME = "projects";
+ private static final String QUALITY_GATES_TABLE_NAME = "quality_gates";
+ private static final String PROPERTIES_TABLE_NAME = "properties";
+ private static final int NUMBER_OF_PROJECTS_TO_INSERT = 5;
+
+ private final Random random = new Random();
+
+ @Rule
+ public CoreDbTester dbTester = CoreDbTester.createForSchema(PopulateProjectQualityGatesTableTest.class, "schema.sql");
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private PopulateProjectQualityGatesTable underTest = new PopulateProjectQualityGatesTable(dbTester.database());
+
+ @Test
+ public void copy_quality_gates_properties_to_project_qgate_table() throws SQLException {
+ long firstQualityGateId = insertQualityGate("qg1");
+ long secondQualityGateId = insertQualityGate("qg2");
+
+ for (long i = 1; i <= NUMBER_OF_PROJECTS_TO_INSERT; i++) {
+ long projectId = insertComponent("p" + i);
+ long qualityGateId = random.nextBoolean() ? firstQualityGateId : secondQualityGateId;
+ insertQualityGateProperty(projectId, qualityGateId);
+ }
+
+ underTest.execute();
+
+ List<ProjectQualityGate> qualityGates = getQualityGates();
+ Assert.assertEquals(NUMBER_OF_PROJECTS_TO_INSERT, qualityGates.size());
+
+ //must not delete properties
+ int propertiesCount = dbTester.countRowsOfTable(PROPERTIES_TABLE_NAME);
+ Assert.assertEquals(5, propertiesCount);
+
+ //should not fail if executed twice
+ underTest.execute();
+ }
+
+ private long insertQualityGate(String qualityGateUuid) {
+ dbTester.executeInsert(
+ QUALITY_GATES_TABLE_NAME,
+ "UUID", qualityGateUuid,
+ "NAME", "name_" + qualityGateUuid,
+ "IS_BUILT_IN", valueOf(true)
+ );
+ return (long) dbTester.selectFirst("select id as \"ID\" from quality_gates where uuid='" + qualityGateUuid + "'").get("ID");
+ }
+
+ private long insertComponent(String uuid) {
+ dbTester.executeInsert(
+ PROJECTS_TABLE_NAME,
+ "ORGANIZATION_UUID", "org_" + uuid,
+ "SCOPE", "PRJ",
+ "QUALIFIER", "TRK",
+ "UUID", uuid,
+ "UUID_PATH", "path_" + uuid,
+ "ROOT_UUID", "root_" + uuid,
+ "PROJECT_UUID", uuid,
+ "PRIVATE", valueOf(false));
+ return (long) dbTester.selectFirst("select id as \"ID\" from projects where uuid='" + uuid + "'").get("ID");
+ }
+
+ private void insertQualityGateProperty(Long projectId, Long qualityGateId) {
+ dbTester.executeInsert(PROPERTIES_TABLE_NAME,
+ "prop_key", "sonar.qualitygate",
+ "resource_id", projectId,
+ "is_empty", false,
+ "text_value", Long.toString(qualityGateId),
+ "created_at", Instant.now().toEpochMilli());
+ }
+
+ private List<ProjectQualityGate> getQualityGates() {
+ return dbTester.select("select pqg.project_uuid, pqg.quality_gate_uuid from project_qgates pqg " +
+ "join projects p on pqg.project_uuid = p.uuid " +
+ "join quality_gates qg on pqg.quality_gate_uuid = qg.uuid")
+ .stream()
+ .map(row -> {
+ String projectUuid = String.valueOf(row.get("PROJECT_UUID"));
+ String qualityGateUuid = String.valueOf(row.get("QUALITY_GATE_UUID"));
+ return new ProjectQualityGate(projectUuid, qualityGateUuid);
+ })
+ .collect(Collectors.toList());
+ }
+
+ private static class ProjectQualityGate {
+ final String projectUuid;
+ final String qualityGateUuid;
+
+ private ProjectQualityGate(String projectUuid, String qualityGateUuid) {
+ this.projectUuid = projectUuid;
+ this.qualityGateUuid = qualityGateUuid;
+ }
+ }
+}
--- /dev/null
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
+ "KEE" VARCHAR(400),
+ "UUID" VARCHAR(50) NOT NULL,
+ "UUID_PATH" VARCHAR(1500) NOT NULL,
+ "ROOT_UUID" VARCHAR(50) NOT NULL,
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(1500),
+ "MAIN_BRANCH_PROJECT_UUID" VARCHAR(50),
+ "NAME" VARCHAR(2000),
+ "DESCRIPTION" VARCHAR(2000),
+ "PRIVATE" BOOLEAN NOT NULL,
+ "TAGS" VARCHAR(500),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_COMPONENT_UUID" VARCHAR(50),
+ "LONG_NAME" VARCHAR(2000),
+ "DEVELOPER_UUID" VARCHAR(50),
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT,
+ "B_CHANGED" BOOLEAN,
+ "B_COPY_COMPONENT_UUID" VARCHAR(50),
+ "B_DESCRIPTION" VARCHAR(2000),
+ "B_ENABLED" BOOLEAN,
+ "B_UUID_PATH" VARCHAR(1500),
+ "B_LANGUAGE" VARCHAR(20),
+ "B_LONG_NAME" VARCHAR(500),
+ "B_MODULE_UUID" VARCHAR(50),
+ "B_MODULE_UUID_PATH" VARCHAR(1500),
+ "B_NAME" VARCHAR(500),
+ "B_PATH" VARCHAR(2000),
+ "B_QUALIFIER" VARCHAR(10)
+);
+CREATE INDEX "PROJECTS_ORGANIZATION" ON "PROJECTS" ("ORGANIZATION_UUID");
+CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE");
+CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID");
+CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID");
+CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID");
+CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID");
+CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER");
+
+CREATE TABLE "QUALITY_GATES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(40) NOT NULL,
+ "NAME" VARCHAR(100) NOT NULL,
+ "IS_BUILT_IN" BOOLEAN NOT NULL,
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP,
+);
+CREATE UNIQUE INDEX "UNIQ_QUALITY_GATES_UUID" ON "QUALITY_GATES" ("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,
+ "RESOURCE_ID" INTEGER,
+ "USER_ID" INTEGER,
+ "IS_EMPTY" BOOLEAN NOT NULL,
+ "TEXT_VALUE" VARCHAR(4000),
+ "CLOB_VALUE" CLOB,
+ "CREATED_AT" BIGINT
+);
+CREATE INDEX "PROPERTIES_KEY" ON "PROPERTIES" ("PROP_KEY");
+
+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");
public class QualityGateFinder {
- public static final String SONAR_QUALITYGATE_PROPERTY = "sonar.qualitygate";
-
private final DbClient dbClient;
public QualityGateFinder(DbClient dbClient) {
* It will first try to get the quality gate explicitly defined on a project, if none it will try to return default quality gate of the organization
*/
public Optional<QualityGateData> getQualityGate(DbSession dbSession, OrganizationDto organization, ComponentDto component) {
- Optional<QualityGateData> res = dbClient.projectQgateAssociationDao().selectQGateIdByComponentId(dbSession, component.getId())
- .map(qualityGateId -> dbClient.qualityGateDao().selectById(dbSession, qualityGateId))
+ Optional<QualityGateData> res = dbClient.projectQgateAssociationDao().selectQGateUuidByComponentUuid(dbSession, component.uuid())
+ .map(qualityGateUuid -> dbClient.qualityGateDao().selectByUuid(dbSession, qualityGateUuid))
.map(qualityGateDto -> new QualityGateData(qualityGateDto, false));
if (res.isPresent()) {
return res;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.server.component.ComponentFinder;
-import static org.sonar.server.qualitygate.QualityGateFinder.SONAR_QUALITYGATE_PROPERTY;
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PROJECT_ID;
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PROJECT_KEY;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
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();
}
import org.sonar.server.qualitygate.QualityGateFinder;
import static com.google.common.base.Preconditions.checkArgument;
-import static org.sonar.server.qualitygate.QualityGateFinder.SONAR_QUALITYGATE_PROPERTY;
public class DestroyAction implements QualityGatesWsAction {
checkArgument(!defaultQualityGate.getId().equals(qualityGate.getId()), "The default quality gate cannot be removed");
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();
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.QGateWithOrgDto;
import org.sonar.db.qualitygate.QualityGateDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.component.ComponentFinder.ParamNames;
-import static org.sonar.server.qualitygate.QualityGateFinder.SONAR_QUALITYGATE_PROPERTY;
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.ACTION_SELECT;
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_GATE_ID;
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PROJECT_ID;
public void define(WebService.NewController controller) {
WebService.NewAction action = controller.createAction(ACTION_SELECT)
.setDescription("Associate a project to a quality gate.<br>" +
- "The '%s' or '%s' must be provided.<br>" +
- "Project id as a numeric value is deprecated since 6.1. Please use the id similar to '%s'.<br>" +
- "Requires the 'Administer Quality Gates' permission.",
+ "The '%s' or '%s' must be provided.<br>" +
+ "Project id as a numeric value is deprecated since 6.1. Please use the id similar to '%s'.<br>" +
+ "Requires the 'Administer Quality Gates' permission.",
PARAM_PROJECT_ID, PARAM_PROJECT_KEY,
Uuids.UUID_EXAMPLE_02)
.setPost(true)
ComponentDto project = getProject(dbSession, organization, projectId, projectKey);
wsSupport.checkCanAdminProject(organization, project);
- dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto()
- .setKey(SONAR_QUALITYGATE_PROPERTY)
- .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());
+ dbSession.commit();
} else if (!qualityGate.getUuid().equals(currentQualityGate.getUuid())) {
dbClient.projectQgateAssociationDao()
.updateProjectQGateAssociation(dbSession, project.uuid(), qualityGate.getUuid());
+ dbSession.commit();
}
-
- dbSession.commit();
}
response.noContent();
}
import org.sonar.api.server.ws.WebService;
import org.sonar.api.web.UserRole;
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.QGateWithOrgDto;
import org.sonar.db.qualitygate.QualityGateDto;
import org.sonar.server.component.TestComponentFinder;
import static org.assertj.core.api.Assertions.tuple;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_GATES;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES;
-import static org.sonar.server.qualitygate.QualityGateFinder.SONAR_QUALITYGATE_PROPERTY;
public class DeselectActionTest {
public DbTester db = DbTester.create();
private DbClient dbClient = db.getDbClient();
- private DbSession dbSession = db.getSession();
private TestDefaultOrganizationProvider organizationProvider = TestDefaultOrganizationProvider.from(db);
private DeselectAction underTest = new DeselectAction(dbClient, TestComponentFinder.from(db),
new QualityGatesWsSupport(db.getDbClient(), userSession, organizationProvider));
}
private void associateProjectToQualityGate(ComponentDto project, QualityGateDto qualityGate) {
- dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto()
- .setResourceId(project.getId())
- .setValue(qualityGate.getId().toString())
- .setKey(SONAR_QUALITYGATE_PROPERTY));
db.qualityGates().associateProjectToQualityGate(project, qualityGate);
db.commit();
}
assertThat(qGateUuid)
.isNotNull()
.isEmpty();
-
- assertThat(dbClient.propertiesDao().selectProjectProperty(project.getId(), SONAR_QUALITYGATE_PROPERTY)).isNull();
}
private void assertSelected(QGateWithOrgDto qualityGate, ComponentDto project) {
.isNotNull()
.isNotEmpty()
.hasValue(qualityGate.getUuid());
- String qGateId = dbClient.propertiesDao().selectProjectProperty(project.getId(), SONAR_QUALITYGATE_PROPERTY).getValue();
- assertThat(qGateId).isEqualTo(String.valueOf(qualityGate.getId()));
}
}
.setParam(PARAM_ORGANIZATION, organization.getKey())
.execute();
- assertThat(db.getDbClient().qualityGateDao().selectByOrganizationAndId(dbSession, organization, qualityGate.getId()))
- .isNull();
- assertThat(db.getDbClient().propertiesDao().selectProjectProperties(prj1.getDbKey()))
+ assertThat(db.getDbClient().projectQgateAssociationDao().selectQGateUuidByComponentUuid(dbSession, prj1.uuid()))
.isEmpty();
- assertThat(db.getDbClient().propertiesDao().selectProjectProperties(prj2.getDbKey()))
+ assertThat(db.getDbClient().projectQgateAssociationDao().selectQGateUuidByComponentUuid(dbSession, prj2.uuid()))
.isEmpty();
assertThat(db.getDbClient().projectQgateAssociationDao().selectQGateUuidByComponentUuid(dbSession, prj1.uuid()))
import static org.sonar.api.web.UserRole.ADMIN;
import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_GATES;
-import static org.sonar.server.qualitygate.QualityGateFinder.SONAR_QUALITYGATE_PROPERTY;
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()