diff options
author | Lukasz Jarocki <lukasz.jarocki@sonarsource.com> | 2022-02-28 09:27:47 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-03-02 20:03:47 +0000 |
commit | bb4cc98d5f0b52c3caeb52fc4f772248be8d95fa (patch) | |
tree | 52bb9ec0e6368ef52c7d254ea6d06c4d9690e0f7 /server/sonar-webserver-core | |
parent | c1db3d50a5d01d86417d40a45e0ecc769134f0c8 (diff) | |
download | sonarqube-bb4cc98d5f0b52c3caeb52fc4f772248be8d95fa.tar.gz sonarqube-bb4cc98d5f0b52c3caeb52fc4f772248be8d95fa.zip |
SONAR-13704 activity of a project now also display QG changes caused by changing issues status
Diffstat (limited to 'server/sonar-webserver-core')
2 files changed, 259 insertions, 0 deletions
diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/project/ProjectQGChangeEventListener.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/project/ProjectQGChangeEventListener.java new file mode 100644 index 00000000000..15331a38c08 --- /dev/null +++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/project/ProjectQGChangeEventListener.java @@ -0,0 +1,103 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.project; + +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import javax.annotation.Nullable; +import org.sonar.api.measures.Metric; +import org.sonar.api.utils.System2; +import org.sonar.ce.task.projectanalysis.measure.Measure; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.component.SnapshotDto; +import org.sonar.db.event.EventDto; +import org.sonar.server.qualitygate.EvaluatedQualityGate; +import org.sonar.server.qualitygate.changeevent.QGChangeEvent; +import org.sonar.server.qualitygate.changeevent.QGChangeEventListener; + +import static org.sonar.db.event.EventDto.CATEGORY_ALERT; + +public class ProjectQGChangeEventListener implements QGChangeEventListener { + + private final DbClient dbClient; + + public ProjectQGChangeEventListener(DbClient dbClient) { + this.dbClient = dbClient; + } + + @Override + public void onIssueChanges(QGChangeEvent qualityGateEvent, Set<ChangedIssue> changedIssues) { + Optional<EvaluatedQualityGate> evaluatedQualityGate = qualityGateEvent.getQualityGateSupplier().get(); + Optional<Metric.Level> previousStatusOptional = qualityGateEvent.getPreviousStatus(); + if (evaluatedQualityGate.isEmpty() || previousStatusOptional.isEmpty()) { + return; + } + Metric.Level currentStatus = evaluatedQualityGate.get().getStatus(); + Metric.Level previousStatus = previousStatusOptional.get(); + if (previousStatus.getColorName().equals(currentStatus.getColorName())) { + // QG status didn't change - no action + return; + } + + addQualityGateEventToProject(qualityGateEvent, currentStatus); + } + + private void addQualityGateEventToProject(QGChangeEvent qualityGateEvent, Metric.Level currentStatus) { + try (DbSession dbSession = dbClient.openSession(false)) { + String componentUuid = qualityGateEvent.getAnalysis().getComponentUuid(); + + SnapshotDto liveMeasureSnapshotDto = createLiveMeasureSnapshotDto(qualityGateEvent.getAnalysis().getProjectVersion(), componentUuid); + dbClient.snapshotDao().insert(dbSession, liveMeasureSnapshotDto); + + EventDto eventDto = createEventDto(componentUuid, liveMeasureSnapshotDto.getUuid(), currentStatus); + dbClient.eventDao().insert(dbSession, eventDto); + + dbSession.commit(); + } + } + + private static EventDto createEventDto(String componentUuid, String snapshotUuid, Metric.Level currentStatus) { + EventDto eventDto = new EventDto(); + eventDto.setComponentUuid(componentUuid); + eventDto.setCreatedAt(System2.INSTANCE.now()); + eventDto.setCategory(CATEGORY_ALERT); + eventDto.setDate(System2.INSTANCE.now()); + eventDto.setAnalysisUuid(snapshotUuid); + eventDto.setUuid(UUID.randomUUID().toString()); + eventDto.setName(currentStatus.getColorName().equals(Metric.Level.OK.getColorName()) ? Measure.Level.OK.getLabel() : Measure.Level.ERROR.getLabel()); + + return eventDto; + } + + private static SnapshotDto createLiveMeasureSnapshotDto(@Nullable String projectVersion, String componentUuid) { + SnapshotDto dto = new SnapshotDto(); + + dto.setUuid(UUID.randomUUID().toString()); + dto.setCreatedAt(System2.INSTANCE.now()); + dto.setProjectVersion(projectVersion); + dto.setLast(false); + dto.setStatus(SnapshotDto.STATUS_LIVE_MEASURE_COMPUTED); + dto.setComponentUuid(componentUuid); + + return dto; + } +} diff --git a/server/sonar-webserver-core/src/test/java/org/sonar/server/project/ProjectQGChangeEventListenerTest.java b/server/sonar-webserver-core/src/test/java/org/sonar/server/project/ProjectQGChangeEventListenerTest.java new file mode 100644 index 00000000000..f85417da4ea --- /dev/null +++ b/server/sonar-webserver-core/src/test/java/org/sonar/server/project/ProjectQGChangeEventListenerTest.java @@ -0,0 +1,156 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.project; + +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Supplier; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.config.Configuration; +import org.sonar.api.measures.Metric; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.component.BranchDto; +import org.sonar.db.component.ComponentDao; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.component.SnapshotDao; +import org.sonar.db.component.SnapshotDto; +import org.sonar.db.event.EventDao; +import org.sonar.db.project.ProjectDto; +import org.sonar.server.qualitygate.Condition; +import org.sonar.server.qualitygate.EvaluatedCondition; +import org.sonar.server.qualitygate.EvaluatedQualityGate; +import org.sonar.server.qualitygate.QualityGate; +import org.sonar.server.qualitygate.changeevent.QGChangeEvent; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +public class ProjectQGChangeEventListenerTest { + + private final DbClient dbClient = mock(DbClient.class); + + private final SnapshotDao snapshotDao = mock(SnapshotDao.class); + private final EventDao eventDao = mock(EventDao.class); + private final ComponentDao componentDao = mock(ComponentDao.class); + + private final Configuration projectConfiguration = mock(Configuration.class); + private final BranchDto branch = new BranchDto(); + private final Condition defaultCondition = new Condition("bugs", Condition.Operator.GREATER_THAN, "10"); + + private final ProjectQGChangeEventListener underTest = new ProjectQGChangeEventListener(dbClient); + + private ProjectDto project; + private SnapshotDto analysis; + + @Before + public void before() { + project = new ProjectDto(); + project.setUuid("uuid"); + analysis = new SnapshotDto(); + + when(dbClient.componentDao()).thenReturn(componentDao); + when(dbClient.eventDao()).thenReturn(eventDao); + when(dbClient.snapshotDao()).thenReturn(snapshotDao); + + when(dbClient.openSession(false)).thenReturn(mock(DbSession.class)); + } + + @Test + public void onIssueChanges_givenEmptyEvent_doNotInteractWithDatabase() { + Supplier<Optional<EvaluatedQualityGate>> qualityGateSupplier = Optional::empty; + + QGChangeEvent qualityGateEvent = new QGChangeEvent(project, branch, analysis, projectConfiguration, Metric.Level.OK, qualityGateSupplier); + + underTest.onIssueChanges(qualityGateEvent, Set.of()); + + verifyNoInteractions(dbClient); + } + + @Test + public void onIssueChanges_givenEventWithNoPreviousStatus_doNotInteractWithDatabase() { + EvaluatedQualityGate value = EvaluatedQualityGate.newBuilder() + .setQualityGate(createDefaultQualityGate()) + .addEvaluatedCondition(createEvaluatedCondition()) + .setStatus(Metric.Level.ERROR) + .build(); + Supplier<Optional<EvaluatedQualityGate>> qualityGateSupplier = () -> Optional.of(value); + + QGChangeEvent qualityGateEvent = new QGChangeEvent(project, branch, analysis, projectConfiguration, null, qualityGateSupplier); + + underTest.onIssueChanges(qualityGateEvent, Set.of()); + + verifyNoInteractions(dbClient); + } + + @Test + public void onIssueChanges_givenCurrentStatusTheSameAsPrevious_doNotInteractWithDatabase() { + EvaluatedQualityGate value = EvaluatedQualityGate.newBuilder() + .setQualityGate(createDefaultQualityGate()) + .addEvaluatedCondition(createEvaluatedCondition()) + .setStatus(Metric.Level.ERROR) + .build(); + Supplier<Optional<EvaluatedQualityGate>> qualityGateSupplier = () -> Optional.of(value); + + QGChangeEvent qualityGateEvent = new QGChangeEvent(project, branch, analysis, projectConfiguration, Metric.Level.ERROR, qualityGateSupplier); + + underTest.onIssueChanges(qualityGateEvent, Set.of()); + + verifyNoInteractions(dbClient); + } + + @Test + public void onIssueChanges_whenValidEvent_insertEventAndSnapshotToDatabase() { + EvaluatedQualityGate value = EvaluatedQualityGate.newBuilder() + .setQualityGate(createDefaultQualityGate()) + .addEvaluatedCondition(createEvaluatedCondition()) + .setStatus(Metric.Level.OK) + .build(); + Supplier<Optional<EvaluatedQualityGate>> qualityGateSupplier = () -> Optional.of(value); + + QGChangeEvent qualityGateEvent = new QGChangeEvent(project, branch, analysis, projectConfiguration, Metric.Level.ERROR, qualityGateSupplier); + + ComponentDto projectComponentDto = new ComponentDto(); + projectComponentDto.setQualifier("TRK"); + projectComponentDto.setUuid("uuid"); + when(componentDao.selectByProjectUuid(anyString(), any())).thenReturn(List.of(projectComponentDto)); + + underTest.onIssueChanges(qualityGateEvent, Set.of()); + + verify(eventDao, times(1)).insert(any(), any()); + verify(snapshotDao, times(1)).insert(any(DbSession.class), any(SnapshotDto.class)); + } + + private EvaluatedCondition createEvaluatedCondition() { + return new EvaluatedCondition(defaultCondition, EvaluatedCondition.EvaluationStatus.ERROR, "5"); + } + + private QualityGate createDefaultQualityGate() { + return new QualityGate("id", "name", Set.of(defaultCondition)); + } + +} |