diff options
19 files changed, 116 insertions, 244 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGate.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGate.java index 3c1fd4e542d..b65f4766046 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGate.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGate.java @@ -19,12 +19,13 @@ */ package org.sonar.ce.task.projectanalysis.qualitygate; +import java.util.Collection; +import java.util.LinkedHashSet; import java.util.Objects; import java.util.Set; import javax.annotation.concurrent.Immutable; -import static com.google.common.base.Predicates.notNull; -import static com.google.common.collect.FluentIterable.from; +import static java.util.Collections.unmodifiableSet; @Immutable public class QualityGate { @@ -32,10 +33,10 @@ public class QualityGate { private final String name; private final Set<Condition> conditions; - public QualityGate(String uuid, String name, Iterable<Condition> conditions) { + public QualityGate(String uuid, String name, Collection<Condition> conditions) { this.uuid = uuid; this.name = Objects.requireNonNull(name); - this.conditions = from(conditions).filter(notNull()).toSet(); + this.conditions = unmodifiableSet(new LinkedHashSet<>(conditions)); } public String getUuid() { diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGateService.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGateService.java index 69d2373f9e7..88d5dec22ff 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGateService.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGateService.java @@ -19,25 +19,11 @@ */ package org.sonar.ce.task.projectanalysis.qualitygate; -import java.util.Optional; import org.sonar.server.project.Project; public interface QualityGateService { - - /** - * Retrieve the {@link QualityGate} from the database with the specified uuid, if it exists. - */ - Optional<QualityGate> findByUuid(String uuid); - /** - * Retrieve the {@link QualityGate} from the database. - * @throws IllegalStateException if database is corrupted and default gate can't be found. + * Retrieve the {@link QualityGate} from the database associated with project. If there's none, it returns the default quality gate. */ - QualityGate findDefaultQualityGate(); - - /** - * Retrieve the {@link QualityGate} from the database associated with project. - */ - Optional<QualityGate> findQualityGate(Project project); - + QualityGate findEffectiveQualityGate(Project project); } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGateServiceImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGateServiceImpl.java index 53377ab04ee..649a2fd82e5 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGateServiceImpl.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGateServiceImpl.java @@ -25,7 +25,6 @@ import java.util.Optional; import org.sonar.ce.task.projectanalysis.metric.MetricRepository; import org.sonar.db.DbClient; import org.sonar.db.DbSession; -import org.sonar.db.property.PropertyDto; import org.sonar.db.qualitygate.QualityGateConditionDto; import org.sonar.db.qualitygate.QualityGateDto; import org.sonar.server.project.Project; @@ -33,8 +32,6 @@ import org.sonar.server.project.Project; import static org.sonar.core.util.stream.MoreCollectors.toList; public class QualityGateServiceImpl implements QualityGateService { - private static final String DEFAULT_QUALITY_GATE_PROPERTY_NAME = "qualitygate.default"; - private final DbClient dbClient; private final MetricRepository metricRepository; @@ -44,20 +41,13 @@ public class QualityGateServiceImpl implements QualityGateService { } @Override - public Optional<QualityGate> findByUuid(String uuid) { - try (DbSession dbSession = dbClient.openSession(false)) { - QualityGateDto qualityGateDto = dbClient.qualityGateDao().selectByUuid(dbSession, uuid); - if (qualityGateDto == null) { - return Optional.empty(); - } - return Optional.of(toQualityGate(dbSession, qualityGateDto)); - } + public QualityGate findEffectiveQualityGate(Project project) { + return findQualityGate(project).orElseGet(this::findDefaultQualityGate); } - @Override - public QualityGate findDefaultQualityGate() { + private QualityGate findDefaultQualityGate() { try (DbSession dbSession = dbClient.openSession(false)) { - QualityGateDto qualityGateDto = getDefaultQualityGate(dbSession); + QualityGateDto qualityGateDto = dbClient.qualityGateDao().selectDefault(dbSession); if (qualityGateDto == null) { throw new IllegalStateException("The default Quality gate is missing"); } @@ -65,29 +55,17 @@ public class QualityGateServiceImpl implements QualityGateService { } } - private QualityGateDto getDefaultQualityGate(DbSession dbSession) { - PropertyDto propertyDto = Optional.ofNullable(dbClient.propertiesDao() - .selectGlobalProperty(dbSession, DEFAULT_QUALITY_GATE_PROPERTY_NAME)) - .orElseThrow(() -> new IllegalStateException("The default Quality Gate property is missing")); - return Optional.ofNullable(dbClient.qualityGateDao().selectByUuid(dbSession, propertyDto.getValue())) - .orElseThrow(() -> new IllegalStateException("The default Quality gate is missing")); - } - - @Override - public Optional<QualityGate> findQualityGate(Project project) { + private 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)); + return Optional.ofNullable(dbClient.qualityGateDao().selectByProjectUuid(dbSession, project.getUuid())) + .map(qg -> toQualityGate(dbSession, qg)); } } private QualityGate toQualityGate(DbSession dbSession, QualityGateDto qualityGateDto) { Collection<QualityGateConditionDto> dtos = dbClient.gateConditionDao().selectForQualityGate(dbSession, qualityGateDto.getUuid()); - Iterable<Condition> conditions = dtos.stream() + Collection<Condition> conditions = dtos.stream() .map(input -> metricRepository.getOptionalByUuid(input.getMetricUuid()) .map(metric -> new Condition(metric, input.getOperator(), input.getErrorThreshold())) .orElse(null)) diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadQualityGateStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadQualityGateStep.java index d37230ed824..8b812112725 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadQualityGateStep.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadQualityGateStep.java @@ -19,7 +19,6 @@ */ package org.sonar.ce.task.projectanalysis.step; -import java.util.Optional; import java.util.stream.Collectors; import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder; import org.sonar.ce.task.projectanalysis.qualitygate.Condition; @@ -39,8 +38,7 @@ public class LoadQualityGateStep implements ComputationStep { private final MutableQualityGateHolder qualityGateHolder; private final AnalysisMetadataHolder analysisMetadataHolder; - public LoadQualityGateStep(QualityGateService qualityGateService, MutableQualityGateHolder qualityGateHolder, - AnalysisMetadataHolder analysisMetadataHolder) { + public LoadQualityGateStep(QualityGateService qualityGateService, MutableQualityGateHolder qualityGateHolder, AnalysisMetadataHolder analysisMetadataHolder) { this.qualityGateService = qualityGateService; this.qualityGateHolder = qualityGateHolder; this.analysisMetadataHolder = analysisMetadataHolder; @@ -48,31 +46,22 @@ public class LoadQualityGateStep implements ComputationStep { @Override public void execute(ComputationStep.Context context) { - Optional<QualityGate> qualityGate = getProjectQualityGate(); - if (!qualityGate.isPresent()) { - // No QG defined for the project, let's retrieve the default QG - qualityGate = Optional.of(getDefaultQualityGate()); - } + QualityGate qualityGate = getProjectQualityGate(); if (analysisMetadataHolder.isPullRequest()) { qualityGate = filterQGForPR(qualityGate); } - qualityGateHolder.setQualityGate(qualityGate.orElseThrow(() -> new IllegalStateException("Quality gate not present"))); + qualityGateHolder.setQualityGate(qualityGate); } - private static Optional<QualityGate> filterQGForPR(Optional<QualityGate> qualityGate) { - return qualityGate.map(qg -> new QualityGate(qg.getUuid(), qg.getName(), - qg.getConditions().stream().filter(Condition::useVariation).collect(Collectors.toList()))); + private static QualityGate filterQGForPR(QualityGate qg) { + return new QualityGate(qg.getUuid(), qg.getName(), qg.getConditions().stream().filter(Condition::useVariation).collect(Collectors.toList())); } - private Optional<QualityGate> getProjectQualityGate() { + private QualityGate getProjectQualityGate() { Project project = analysisMetadataHolder.getProject(); - return qualityGateService.findQualityGate(project); - } - - private QualityGate getDefaultQualityGate() { - return qualityGateService.findDefaultQualityGate(); + return qualityGateService.findEffectiveQualityGate(project); } @Override diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGateServiceImplTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGateServiceImplTest.java index 28dda850867..12d564dfe46 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGateServiceImplTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGateServiceImplTest.java @@ -57,80 +57,22 @@ public class QualityGateServiceImplTest { private final QualityGateDao qualityGateDao = mock(QualityGateDao.class); private final QualityGateConditionDao qualityGateConditionDao = mock(QualityGateConditionDao.class); - private final PropertiesDao propertiesDao = mock(PropertiesDao.class); private final MetricRepository metricRepository = mock(MetricRepository.class); private final DbClient dbClient = mock(DbClient.class); - private final QualityGateServiceImpl underTest = new QualityGateServiceImpl(dbClient, metricRepository); + private final QualityGateService underTest = new QualityGateServiceImpl(dbClient, metricRepository); @Before public void setUp() { when(dbClient.qualityGateDao()).thenReturn(qualityGateDao); when(dbClient.gateConditionDao()).thenReturn(qualityGateConditionDao); - when(dbClient.propertiesDao()).thenReturn(propertiesDao); when(METRIC_1.getKey()).thenReturn("metric"); when(METRIC_2.getKey()).thenReturn("new_metric"); } @Test - public void findById_returns_absent_when_QualityGateDto_does_not_exist() { - assertThat(underTest.findByUuid(SOME_UUID)).isNotPresent(); - } - - @Test - public void findById_returns_QualityGate_with_empty_set_of_conditions_when_there_is_none_in_DB() { - when(qualityGateDao.selectByUuid(any(), eq(SOME_UUID))).thenReturn(QUALITY_GATE_DTO); - when(qualityGateConditionDao.selectForQualityGate(any(), eq(SOME_UUID))).thenReturn(Collections.emptyList()); - - Optional<QualityGate> res = underTest.findByUuid(SOME_UUID); - - assertThat(res).isPresent(); - assertThat(res.get().getUuid()).isEqualTo(SOME_UUID); - assertThat(res.get().getName()).isEqualTo(SOME_NAME); - assertThat(res.get().getConditions()).isEmpty(); - } - - @Test - public void findById_returns_conditions_when_there_is_some_in_DB() { - when(qualityGateDao.selectByUuid(any(), eq(SOME_UUID))).thenReturn(QUALITY_GATE_DTO); - when(qualityGateConditionDao.selectForQualityGate(any(), eq(SOME_UUID))).thenReturn(ImmutableList.of(CONDITION_1, CONDITION_2)); - // metrics are always supposed to be there - when(metricRepository.getOptionalByUuid(METRIC_UUID_1)).thenReturn(Optional.of(METRIC_1)); - when(metricRepository.getOptionalByUuid(METRIC_UUID_2)).thenReturn(Optional.of(METRIC_2)); - - Optional<QualityGate> res = underTest.findByUuid(SOME_UUID); - - assertThat(res).isPresent(); - assertThat(res.get().getUuid()).isEqualTo(SOME_UUID); - assertThat(res.get().getName()).isEqualTo(SOME_NAME); - assertThat(res.get().getConditions()).containsOnly( - new Condition(METRIC_1, CONDITION_1.getOperator(), CONDITION_1.getErrorThreshold()), - new Condition(METRIC_2, CONDITION_2.getOperator(), CONDITION_2.getErrorThreshold())); - } - - @Test - public void findById_ignores_conditions_on_missing_metrics() { - when(qualityGateDao.selectByUuid(any(), eq(SOME_UUID))).thenReturn(QUALITY_GATE_DTO); - when(qualityGateConditionDao.selectForQualityGate(any(), eq(SOME_UUID))).thenReturn(ImmutableList.of(CONDITION_1, CONDITION_2)); - // metrics are always supposed to be there - when(metricRepository.getOptionalByUuid(METRIC_UUID_1)).thenReturn(Optional.empty()); - when(metricRepository.getOptionalByUuid(METRIC_UUID_2)).thenReturn(Optional.of(METRIC_2)); - - Optional<QualityGate> res = underTest.findByUuid(SOME_UUID); - - assertThat(res).isPresent(); - assertThat(res.get().getUuid()).isEqualTo(SOME_UUID); - assertThat(res.get().getName()).isEqualTo(SOME_NAME); - assertThat(res.get().getConditions()).containsOnly( - new Condition(METRIC_2, CONDITION_2.getOperator(), CONDITION_2.getErrorThreshold())); - } - - @Test public void findDefaultQualityGate_by_property_not_found() { - when(propertiesDao.selectGlobalProperty(any(), any())).thenReturn(null); - - assertThatThrownBy(() -> underTest.findDefaultQualityGate()) - .isInstanceOf(IllegalStateException.class); + assertThatThrownBy(() -> underTest.findEffectiveQualityGate(mock(Project.class))).isInstanceOf(IllegalStateException.class); } @Test @@ -138,46 +80,33 @@ public class QualityGateServiceImplTest { QualityGateDto qualityGateDto = new QualityGateDto(); qualityGateDto.setUuid(QUALITY_GATE_DTO.getUuid()); qualityGateDto.setName(QUALITY_GATE_DTO.getName()); - when(propertiesDao.selectGlobalProperty(any(), any())).thenReturn(new PropertyDto().setValue(QUALITY_GATE_DTO.getUuid())); - when(qualityGateDao.selectByUuid(any(), any())).thenReturn(qualityGateDto); + when(qualityGateDao.selectDefault(any())).thenReturn(qualityGateDto); when(qualityGateConditionDao.selectForQualityGate(any(), eq(SOME_UUID))).thenReturn(ImmutableList.of(CONDITION_1, CONDITION_2)); when(metricRepository.getOptionalByUuid(METRIC_UUID_1)).thenReturn(Optional.empty()); when(metricRepository.getOptionalByUuid(METRIC_UUID_2)).thenReturn(Optional.of(METRIC_2)); - QualityGate result = underTest.findDefaultQualityGate(); + QualityGate result = underTest.findEffectiveQualityGate(mock(Project.class)); assertThat(result).isNotNull(); assertThat(result.getUuid()).isEqualTo(QUALITY_GATE_DTO.getUuid()); - 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() { QualityGateDto qualityGateDto = new QualityGateDto(); qualityGateDto.setUuid(QUALITY_GATE_DTO.getUuid()); qualityGateDto.setName(QUALITY_GATE_DTO.getName()); + when(qualityGateDao.selectByProjectUuid(any(), any())).thenReturn(qualityGateDto); when(qualityGateConditionDao.selectForQualityGate(any(), eq(SOME_UUID))).thenReturn(ImmutableList.of(CONDITION_1, CONDITION_2)); when(metricRepository.getOptionalByUuid(METRIC_UUID_1)).thenReturn(Optional.empty()); when(metricRepository.getOptionalByUuid(METRIC_UUID_2)).thenReturn(Optional.of(METRIC_2)); - Optional<QualityGate> result = underTest.findQualityGate(mock(Project.class)); + QualityGate result = underTest.findEffectiveQualityGate(mock(Project.class)); - assertThat(result).isNotNull(); - assertThat(result).isNotEmpty(); - - QualityGate resultData = result.get(); - assertThat(resultData.getUuid()).isEqualTo(QUALITY_GATE_DTO.getUuid()); - assertThat(resultData.getName()).isNotBlank(); - assertThat(resultData.getName()).isEqualTo(QUALITY_GATE_DTO.getName()); + assertThat(result.getUuid()).isEqualTo(QUALITY_GATE_DTO.getUuid()); + assertThat(result.getName()).isEqualTo(QUALITY_GATE_DTO.getName()); } } diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadQualityGateStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadQualityGateStepTest.java index f0d791aae40..5d49766a47d 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadQualityGateStepTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadQualityGateStepTest.java @@ -20,7 +20,6 @@ package org.sonar.ce.task.projectanalysis.step; import java.util.Arrays; -import java.util.Optional; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -30,28 +29,27 @@ import org.sonar.ce.task.projectanalysis.metric.MetricImpl; import org.sonar.ce.task.projectanalysis.qualitygate.Condition; import org.sonar.ce.task.projectanalysis.qualitygate.MutableQualityGateHolderRule; import org.sonar.ce.task.projectanalysis.qualitygate.QualityGate; -import org.sonar.ce.task.projectanalysis.qualitygate.QualityGateService; +import org.sonar.ce.task.projectanalysis.qualitygate.QualityGateServiceImpl; 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; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class LoadQualityGateStepTest { - @Rule public MutableQualityGateHolderRule mutableQualityGateHolder = new MutableQualityGateHolderRule(); private final AnalysisMetadataHolder analysisMetadataHolder = mock(AnalysisMetadataHolder.class); - private final QualityGateService qualityGateService = mock(QualityGateService.class); + private final QualityGateServiceImpl qualityGateService = mock(QualityGateServiceImpl.class); private final LoadQualityGateStep underTest = new LoadQualityGateStep(qualityGateService, mutableQualityGateHolder, analysisMetadataHolder); + private final Project project = mock(Project.class); @Before - public void setUp() { + public void before() { + when(analysisMetadataHolder.getProject()).thenReturn(project); } @Test @@ -63,7 +61,7 @@ public class LoadQualityGateStepTest { when(analysisMetadataHolder.isPullRequest()).thenReturn(true); QualityGate defaultGate = new QualityGate("1", "qg", Arrays.asList(variation, condition)); - when(qualityGateService.findDefaultQualityGate()).thenReturn(defaultGate); + when(qualityGateService.findEffectiveQualityGate(project)).thenReturn(defaultGate); underTest.execute(new TestComputationStepContext()); @@ -71,25 +69,12 @@ public class LoadQualityGateStepTest { } @Test - public void execute_sets_default_QualityGate_when_project_has_no_settings() { - QualityGate defaultGate = mock(QualityGate.class); - when(qualityGateService.findDefaultQualityGate()).thenReturn(defaultGate); + public void execute_sets_effective_quality_gate() { + QualityGate qg = mock(QualityGate.class); + when(qualityGateService.findEffectiveQualityGate(project)).thenReturn(qg); underTest.execute(new TestComputationStepContext()); - assertThat(mutableQualityGateHolder.getQualityGate().get()).isSameAs(defaultGate); + assertThat(mutableQualityGateHolder.getQualityGate()).containsSame(qg); } - - @Test - public void execute_sets_QualityGate_if_it_can_be_found_by_service() { - QualityGate qualityGate = new QualityGate("10", "name", emptyList()); - - when(analysisMetadataHolder.getProject()).thenReturn(mock(Project.class)); - when(qualityGateService.findQualityGate(any(Project.class))).thenReturn(Optional.of(qualityGate)); - - underTest.execute(new TestComputationStepContext()); - - assertThat(mutableQualityGateHolder.getQualityGate().get()).isSameAs(qualityGate); - } - } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDao.java index 3b1659aaa95..7bbdd0c44e6 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDao.java @@ -52,8 +52,7 @@ public class LiveMeasureDao implements Dao { componentUuids -> mapper(dbSession).selectByComponentUuidsAndMetricUuids(componentUuids, metricUuis)); } - public void scrollSelectByComponentUuidAndMetricKeys(DbSession dbSession, String componentUuid, Collection<String> metricKeys, - ResultHandler<LiveMeasureDto> handler) { + public void scrollSelectByComponentUuidAndMetricKeys(DbSession dbSession, String componentUuid, Collection<String> metricKeys, ResultHandler<LiveMeasureDto> handler) { if (metricKeys.isEmpty()) { return; } 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 e51ee8e4ff3..d05ce597cb0 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 @@ -56,6 +56,11 @@ public class QualityGateDao implements Dao { return mapper(session).selectByUuid(uuid); } + @CheckForNull + public QualityGateDto selectDefault(DbSession session) { + return mapper(session).selectDefault(); + } + public void delete(QualityGateDto qGate, DbSession session) { mapper(session).delete(qGate.getUuid()); } @@ -81,6 +86,7 @@ public class QualityGateDao implements Dao { return session.getMapper(QualityGateMapper.class); } + @CheckForNull public QualityGateDto selectByProjectUuid(DbSession dbSession, String projectUuid) { return mapper(dbSession).selectByProjectUuid(projectUuid); } 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 5bb80ba9467..ec4901e102b 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 @@ -43,5 +43,7 @@ public interface QualityGateMapper { QualityGateDto selectByUuid(String uuid); + QualityGateDto selectDefault(); + QualityGateDto selectByProjectUuid(@Param("projectUuid") String projectUuid); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java index 315027c18e6..b692d37f315 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java @@ -159,8 +159,12 @@ public class QualityProfileDao implements Dao { return mapper(dbSession).selectAssociatedToProjectUuidAndLanguage(project.getUuid(), language); } - public List<QProfileDto> selectAssociatedToProjectUuidAndLanguages(DbSession dbSession, ProjectDto project, Collection<String> languages) { - return executeLargeInputs(languages, partition -> mapper(dbSession).selectAssociatedToProjectUuidAndLanguages(project.getUuid(), partition)); + public List<QProfileDto> selectAssociatedToProjectUuidAndLanguages(DbSession dbSession, String projectUuid, Collection<String> languages) { + return executeLargeInputs(languages, partition -> mapper(dbSession).selectAssociatedToProjectUuidAndLanguages(projectUuid, partition)); + } + + public List<QProfileDto> selectAssociatedToProjectAndLanguages(DbSession dbSession, ProjectDto project, Collection<String> languages) { + return selectAssociatedToProjectUuidAndLanguages(dbSession, project.getUuid(), languages); } public List<QProfileDto> selectByLanguage(DbSession dbSession, String language) { 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 494bccb3f6d..0b52c10da77 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 @@ -4,7 +4,7 @@ <mapper namespace="org.sonar.db.qualitygate.QualityGateMapper"> <sql id="gateColumns"> - uuid, name, is_built_in as isBuiltIn, created_at as createdAt, updated_at as updatedAt + qg.uuid, qg.name, qg.is_built_in as isBuiltIn, qg.created_at as createdAt, qg.updated_at as updatedAt </sql> <insert id="insertQualityGate" parameterType="QualityGate" useGeneratedKeys="false"> @@ -22,14 +22,14 @@ <select id="selectByName" parameterType="String" resultType="QualityGate"> select <include refid="gateColumns"/> - from quality_gates + from quality_gates qg where name=#{name, jdbcType=VARCHAR} </select> <select id="selectByUuid" parameterType="String" resultType="QualityGate"> select <include refid="gateColumns"/> - from quality_gates + from quality_gates qg where uuid=#{uuid, jdbcType=VARCHAR} </select> @@ -45,11 +45,21 @@ <select id="selectBuiltIn" resultType="org.sonar.db.qualitygate.QualityGateDto"> SELECT <include refid="gateColumns"/> - FROM quality_gates + FROM quality_gates qg WHERE is_built_in = ${_true} </select> + <select id="selectDefault" resultType="org.sonar.db.qualitygate.QualityGateDto"> + SELECT + <include refid="gateColumns"/> + FROM quality_gates qg INNER JOIN properties p ON qg.uuid = p.text_value + WHERE + p.prop_key = 'qualitygate.default' + and p.component_uuid is null + and p.user_uuid is null + </select> + <update id="delete" parameterType="String"> delete from quality_gates where uuid=#{uuid} </update> diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileDaoTest.java index 45d0290e2dd..026fb17668e 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileDaoTest.java @@ -656,17 +656,17 @@ public class QualityProfileDaoTest { QProfileDto jsProfile = db.qualityProfiles().insert(p -> p.setLanguage("js")); db.qualityProfiles().associateWithProject(project1, javaProfile, jsProfile); - assertThat(underTest.selectAssociatedToProjectUuidAndLanguages(dbSession, project1, singletonList("java"))) + assertThat(underTest.selectAssociatedToProjectAndLanguages(dbSession, project1, singletonList("java"))) .extracting(QProfileDto::getKee).containsOnly(javaProfile.getKee()); - assertThat(underTest.selectAssociatedToProjectUuidAndLanguages(dbSession, project1, singletonList("unknown"))) + assertThat(underTest.selectAssociatedToProjectAndLanguages(dbSession, project1, singletonList("unknown"))) .isEmpty(); - assertThat(underTest.selectAssociatedToProjectUuidAndLanguages(dbSession, project1, of("java", "unknown"))) + assertThat(underTest.selectAssociatedToProjectAndLanguages(dbSession, project1, of("java", "unknown"))) .extracting(QProfileDto::getKee).containsExactly(javaProfile.getKee()); - assertThat(underTest.selectAssociatedToProjectUuidAndLanguages(dbSession, project1, of("java", "js"))) + assertThat(underTest.selectAssociatedToProjectAndLanguages(dbSession, project1, of("java", "js"))) .extracting(QProfileDto::getKee).containsExactlyInAnyOrder(javaProfile.getKee(), jsProfile.getKee()); - assertThat(underTest.selectAssociatedToProjectUuidAndLanguages(dbSession, project2, singletonList("java"))) + assertThat(underTest.selectAssociatedToProjectAndLanguages(dbSession, project2, singletonList("java"))) .isEmpty(); - assertThat(underTest.selectAssociatedToProjectUuidAndLanguages(dbSession, project2, Collections.emptyList())) + assertThat(underTest.selectAssociatedToProjectAndLanguages(dbSession, project2, Collections.emptyList())) .isEmpty(); } diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/QualityGateFinder.java b/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/QualityGateFinder.java index b54651cade1..0dd9bd82eab 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/QualityGateFinder.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/QualityGateFinder.java @@ -23,25 +23,20 @@ import java.util.Optional; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.project.ProjectDto; -import org.sonar.db.property.PropertyDto; import org.sonar.db.qualitygate.QualityGateDto; -import static com.google.common.base.Preconditions.checkState; - public class QualityGateFinder { - private static final String DEFAULT_QUALITY_GATE_PROPERTY_NAME = "qualitygate.default"; - private final DbClient dbClient; public QualityGateFinder(DbClient dbClient) { this.dbClient = dbClient; } - public QualityGateData getQualityGate(DbSession dbSession, ProjectDto projectDto) { - return getQualityGate(dbSession, projectDto.getUuid()); + public QualityGateData getEffectiveQualityGate(DbSession dbSession, ProjectDto projectDto) { + return getEffectiveQualityGate(dbSession, projectDto.getUuid()); } - public QualityGateData getQualityGate(DbSession dbSession, String projectUuid) { + public QualityGateData getEffectiveQualityGate(DbSession dbSession, String projectUuid) { Optional<QualityGateData> res = getQualityGateForProject(dbSession, projectUuid); if (res.isPresent()) { return res.get(); @@ -57,30 +52,32 @@ public class QualityGateFinder { } public QualityGateDto getDefault(DbSession dbSession) { - PropertyDto qGateDefaultUuidProperty = dbClient.propertiesDao().selectGlobalProperty(dbSession, DEFAULT_QUALITY_GATE_PROPERTY_NAME); - checkState(qGateDefaultUuidProperty != null, "Default quality gate property is missing"); - dbClient.qualityGateDao().selectByUuid(dbSession, qGateDefaultUuidProperty.getValue()); - return Optional.ofNullable(dbClient.qualityGateDao().selectByUuid(dbSession, qGateDefaultUuidProperty.getValue())) - .orElseThrow(() -> new IllegalStateException("Default quality gate is missing")); - } - - public QualityGateDto getBuiltInQualityGate(DbSession dbSession) { - QualityGateDto builtIn = dbClient.qualityGateDao().selectBuiltIn(dbSession); - checkState(builtIn != null, "Builtin quality gate is missing."); - return builtIn; + return Optional.ofNullable(dbClient.qualityGateDao().selectDefault(dbSession)).orElseThrow(() -> new IllegalStateException("Default quality gate is missing")); } public static class QualityGateData { - private final QualityGateDto qualityGate; + private final String uuid; + private final String name; private final boolean isDefault; + private final boolean builtIn; private QualityGateData(QualityGateDto qualityGate, boolean isDefault) { - this.qualityGate = qualityGate; + this.uuid = qualityGate.getUuid(); + this.name = qualityGate.getName(); this.isDefault = isDefault; + this.builtIn = qualityGate.isBuiltIn(); + } + + public boolean isBuiltIn() { + return builtIn; + } + + public String getUuid() { + return uuid; } - public QualityGateDto getQualityGate() { - return qualityGate; + public String getName() { + return name; } public boolean isDefault() { diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/QualityGateFinderTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/QualityGateFinderTest.java index f90437139a3..7b5ac3714af 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/QualityGateFinderTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/QualityGateFinderTest.java @@ -44,9 +44,9 @@ public class QualityGateFinderTest { ProjectDto project = db.components().insertPrivateProjectDto(); QualityGateDto dbQualityGate = db.qualityGates().createDefaultQualityGate(qg -> qg.setName("Sonar way")); - QualityGateFinder.QualityGateData result = underTest.getQualityGate(dbSession, project); + QualityGateFinder.QualityGateData result = underTest.getEffectiveQualityGate(dbSession, project); - assertThat(result.getQualityGate().getUuid()).isEqualTo(dbQualityGate.getUuid()); + assertThat(result.getUuid()).isEqualTo(dbQualityGate.getUuid()); assertThat(result.isDefault()).isTrue(); } @@ -57,9 +57,9 @@ public class QualityGateFinderTest { QualityGateDto dbQualityGate = db.qualityGates().insertQualityGate(qg -> qg.setName("My team QG")); db.qualityGates().associateProjectToQualityGate(project, dbQualityGate); - QualityGateFinder.QualityGateData result = underTest.getQualityGate(dbSession, project); + QualityGateFinder.QualityGateData result = underTest.getEffectiveQualityGate(dbSession, project); - assertThat(result.getQualityGate().getUuid()).isEqualTo(dbQualityGate.getUuid()); + assertThat(result.getUuid()).isEqualTo(dbQualityGate.getUuid()); assertThat(result.isDefault()).isFalse(); } @@ -70,7 +70,7 @@ public class QualityGateFinderTest { db.getDbClient().qualityGateDao().delete(dbQualityGate, dbSession); db.commit(); - assertThatThrownBy(() -> underTest.getQualityGate(dbSession, project)) + assertThatThrownBy(() -> underTest.getEffectiveQualityGate(dbSession, project)) .isInstanceOf(IllegalStateException.class) .hasMessage("Default quality gate is missing"); } @@ -83,7 +83,7 @@ public class QualityGateFinderTest { db.qualityGates().associateProjectToQualityGate(project, dbQualityGate); db.getDbClient().qualityGateDao().delete(dbQualityGate, dbSession); - assertThatThrownBy(() -> underTest.getQualityGate(dbSession, project)) + assertThatThrownBy(() -> underTest.getEffectiveQualityGate(dbSession, project)) .isInstanceOf(IllegalStateException.class) .hasMessage("Default quality gate is missing"); } @@ -94,9 +94,9 @@ public class QualityGateFinderTest { QualityGateDto dbQualityGate = db.qualityGates().insertQualityGate(qg -> qg.setName("My team QG")); db.getDbClient().qualityGateDao().delete(dbQualityGate, dbSession); - assertThatThrownBy(() -> underTest.getQualityGate(dbSession, project)) + assertThatThrownBy(() -> underTest.getEffectiveQualityGate(dbSession, project)) .isInstanceOf(IllegalStateException.class) - .hasMessage("Default quality gate property is missing"); + .hasMessage("Default quality gate is missing"); } @Test diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/LiveQualityGateComputerImpl.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/LiveQualityGateComputerImpl.java index a844ae43f30..dd40744a261 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/LiveQualityGateComputerImpl.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/LiveQualityGateComputerImpl.java @@ -38,13 +38,13 @@ import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.metric.MetricDto; import org.sonar.db.project.ProjectDto; import org.sonar.db.qualitygate.QualityGateConditionDto; -import org.sonar.db.qualitygate.QualityGateDto; import org.sonar.server.qualitygate.Condition; import org.sonar.server.qualitygate.EvaluatedQualityGate; import org.sonar.server.qualitygate.QualityGate; import org.sonar.server.qualitygate.QualityGateConverter; import org.sonar.server.qualitygate.QualityGateEvaluator; import org.sonar.server.qualitygate.QualityGateFinder; +import org.sonar.server.qualitygate.QualityGateFinder.QualityGateData; import static org.sonar.core.util.stream.MoreCollectors.toHashSet; import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex; @@ -63,9 +63,8 @@ public class LiveQualityGateComputerImpl implements LiveQualityGateComputer { @Override public QualityGate loadQualityGate(DbSession dbSession, ProjectDto project, BranchDto branch) { - QualityGateDto gateDto = qGateFinder.getQualityGate(dbSession, project) - .getQualityGate(); - Collection<QualityGateConditionDto> conditionDtos = dbClient.gateConditionDao().selectForQualityGate(dbSession, gateDto.getUuid()); + QualityGateData qg = qGateFinder.getEffectiveQualityGate(dbSession, project); + Collection<QualityGateConditionDto> conditionDtos = dbClient.gateConditionDao().selectForQualityGate(dbSession, qg.getUuid()); Set<String> metricUuids = conditionDtos.stream().map(QualityGateConditionDto::getMetricUuid) .collect(toHashSet(conditionDtos.size())); Map<String, MetricDto> metricsByUuid = dbClient.metricDao().selectByUuids(dbSession, metricUuids).stream() @@ -81,7 +80,7 @@ public class LiveQualityGateComputerImpl implements LiveQualityGateComputer { conditions = conditions.filter(Condition::isOnLeakPeriod); } - return new QualityGate(String.valueOf(gateDto.getUuid()), gateDto.getName(), conditions.collect(toHashSet(conditionDtos.size()))); + return new QualityGate(String.valueOf(qg.getUuid()), qg.getName(), conditions.collect(toHashSet(conditionDtos.size()))); } @Override diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/GetByProjectAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/GetByProjectAction.java index 4463d72c11b..98e0c48bfed 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/GetByProjectAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/GetByProjectAction.java @@ -91,20 +91,19 @@ public class GetByProjectAction implements QualityGatesWsAction { throw insufficientPrivilegesException(); } - QualityGateData data = qualityGateFinder.getQualityGate(dbSession, project); + QualityGateData data = qualityGateFinder.getEffectiveQualityGate(dbSession, project); writeProtobuf(buildResponse(data), request, response); } } - private static GetByProjectResponse buildResponse(QualityGateData data) { - QualityGateDto qualityGate = data.getQualityGate(); + private static GetByProjectResponse buildResponse(QualityGateData qg) { GetByProjectResponse.Builder response = GetByProjectResponse.newBuilder(); response.getQualityGateBuilder() - .setId(qualityGate.getUuid()) - .setName(qualityGate.getName()) - .setDefault(data.isDefault()); + .setId(qg.getUuid()) + .setName(qg.getName()) + .setDefault(qg.isDefault()); return response.build(); } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/SearchAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/SearchAction.java index eae65ad2eda..4c696b13ff8 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/SearchAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/SearchAction.java @@ -219,7 +219,7 @@ public class SearchAction implements QProfileWsAction { return p -> true; } Map<String, QProfileDto> effectiveProfiles = defaultProfiles.stream().collect(Collectors.toMap(QProfileDto::getLanguage, identity())); - effectiveProfiles.putAll(dbClient.qualityProfileDao().selectAssociatedToProjectUuidAndLanguages(dbSession, project, getLanguageKeys()).stream() + effectiveProfiles.putAll(dbClient.qualityProfileDao().selectAssociatedToProjectAndLanguages(dbSession, project, getLanguageKeys()).stream() .collect(MoreCollectors.uniqueIndex(QProfileDto::getLanguage))); return p -> Objects.equals(p.getKee(), effectiveProfiles.get(p.getLanguage()).getKee()); } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ui/ws/ComponentAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ui/ws/ComponentAction.java index 3726be2a3ee..163d8074269 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ui/ws/ComponentAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ui/ws/ComponentAction.java @@ -259,11 +259,10 @@ public class ComponentAction implements NavigationWsAction { } private void writeQualityGate(JsonWriter json, DbSession session, ComponentDto component) { - QualityGateFinder.QualityGateData qualityGateData = qualityGateFinder.getQualityGate(session, component.uuid()); - QualityGateDto qualityGateDto = qualityGateData.getQualityGate(); + var qualityGateData = qualityGateFinder.getEffectiveQualityGate(session, component.uuid()); json.name("qualityGate").beginObject() - .prop("key", qualityGateDto.getUuid()) - .prop("name", qualityGateDto.getName()) + .prop("key", qualityGateData.getUuid()) + .prop("name", qualityGateData.getName()) .prop("isDefault", qualityGateData.isDefault()) .endObject(); } diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/RegisterQualityGatesTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/RegisterQualityGatesTest.java index 22a71234a67..bfb1dc89d63 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/RegisterQualityGatesTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/RegisterQualityGatesTest.java @@ -200,17 +200,6 @@ public class RegisterQualityGatesTest { } @Test - public void ensure_only_that_builtin_is_set_as_default_when_no_default_quality_gate() { - insertMetrics(); - QualityGateDto builtInQualityGate = db.qualityGates().insertBuiltInQualityGate(); - - underTest.start(); - - assertThat(qualityGateFinder.getBuiltInQualityGate(dbSession)).isNotNull(); - assertThat(qualityGateFinder.getBuiltInQualityGate(dbSession).getUuid()).isEqualTo(builtInQualityGate.getUuid()); - } - - @Test public void builtin_quality_gate_with_incorrect_metricuuid_should_not_throw_an_exception() { insertMetrics(); QualityGateConditionDto conditionDto = new QualityGateConditionDto() |