From: lukasz-jarocki-sonarsource Date: Fri, 14 Jul 2023 10:39:17 +0000 (+0200) Subject: SONAR-19850 Apply validation fixes X-Git-Tag: 10.2.0.77647~328 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=d5110d4477e869f5318a63b20b5385ac84416666;p=sonarqube.git SONAR-19850 Apply validation fixes SONAR-19850 Fix an issue where main branch was treated as orphan branch SONAR-19850 Fix an issue where emails with reports were not sent to any receipients SONAR-19850 Fix bug with reference branch as main branch SONAR-19850 Add warning on unanalyzed branches SONAR-19850 Fix portfolio refresh when change of main branch of project --- diff --git a/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStepIT.java b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStepIT.java index 495476db7ff..7e8b3f56927 100644 --- a/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStepIT.java +++ b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStepIT.java @@ -27,6 +27,10 @@ import org.sonar.ce.task.CeTask; import org.sonar.db.DbClient; import org.sonar.db.DbTester; import org.sonar.db.component.BranchDto; +import org.sonar.db.component.ComponentDbTester; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.component.ComponentTesting; +import org.sonar.db.project.ProjectDto; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -104,6 +108,51 @@ public class IgnoreOrphanBranchStepIT { .hasMessage("entity not found in task"); } + @Test + public void execute_givenMainBranchWithDifferentUuidFromProject_shouldNotIncludeItInPurge() { + BranchDto branchDto = new BranchDto() + .setBranchType(BRANCH) + .setKey("main") + .setUuid(BRANCH_UUID) + .setProjectUuid(ENTITY_UUID) + .setIsMain(true) + .setNeedIssueSync(true) + .setExcludeFromPurge(true); + dbClient.branchDao().insert(dbTester.getSession(), branchDto); + + ProjectDto projectDto = ComponentTesting.newProjectDto().setUuid(ENTITY_UUID).setKey("component key"); + ComponentDto componentDto = ComponentTesting.newBranchComponent(projectDto, branchDto); + dbClient.componentDao().insert(dbTester.getSession(), componentDto, false); + dbClient.projectDao().insert(dbTester.getSession(), projectDto, false); + dbTester.commit(); + + underTest.execute(() -> null); + + Optional branch = dbClient.branchDao().selectByUuid(dbTester.getSession(), BRANCH_UUID); + assertThat(branch.get().isNeedIssueSync()).isTrue(); + assertThat(branch.get().isExcludeFromPurge()).isTrue(); + } + + @Test + public void execute_givenNotExistingEntityUuid_shouldIncludeItInPurge() { + BranchDto branchDto = new BranchDto() + .setBranchType(BRANCH) + .setKey("main") + .setUuid(BRANCH_UUID) + .setProjectUuid(ENTITY_UUID) + .setIsMain(false) + .setNeedIssueSync(true) + .setExcludeFromPurge(true); + dbClient.branchDao().insert(dbTester.getSession(), branchDto); + dbTester.commit(); + + underTest.execute(() -> null); + + Optional branch = dbClient.branchDao().selectByUuid(dbTester.getSession(), BRANCH_UUID); + assertThat(branch.get().isNeedIssueSync()).isFalse(); + assertThat(branch.get().isExcludeFromPurge()).isFalse(); + } + @Test public void verify_step_description() { assertThat(underTest.getDescription()).isEqualTo("Ignore orphan component"); diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStep.java index c40fee64e5e..eb672bdfb67 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStep.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStep.java @@ -27,6 +27,7 @@ import org.sonar.ce.task.step.ComputationStep; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; +import org.sonar.db.entity.EntityDto; public final class IgnoreOrphanBranchStep implements ComputationStep { private static final Logger LOG = LoggerFactory.getLogger(IgnoreOrphanBranchStep.class); @@ -44,8 +45,9 @@ public final class IgnoreOrphanBranchStep implements ComputationStep { String componentUuid = ceTask.getComponent().orElseThrow(() -> new UnsupportedOperationException("component not found in task")).getUuid(); try (DbSession dbSession = dbClient.openSession(false)) { - Optional componentDto = dbClient.componentDao().selectByUuid(dbSession, entityUuid); - if(!componentDto.isPresent()){ + Optional componentDto = dbClient.componentDao().selectByUuid(dbSession, componentUuid); + Optional entityDto = dbClient.entityDao().selectByUuid(dbSession, entityUuid); + if (componentDto.isEmpty() || entityDto.isEmpty()) { LOG.info("reindexation task has been trigger on an orphan branch. removing any exclude_from_purge flag, and skip the indexation"); dbClient.branchDao().updateExcludeFromPurge(dbSession, componentUuid, false); dbClient.branchDao().updateNeedIssueSync(dbSession, componentUuid, false); diff --git a/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx b/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx index 367048e6b7f..6da4abfc6ae 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx @@ -58,11 +58,10 @@ export function EmptyOverview(props: EmptyOverviewProps) { branchLikes.length > 2 || (branchLikes.length === 2 && branchLikes.some((branch) => isBranch(branch))); - const showWarning = isMainBranch(branchLike) && hasBranches; const showTutorial = isMainBranch(branchLike) && !hasBranches && !hasAnalyses; let warning; - if (isLoggedIn(currentUser) && showWarning && hasBadBranchConfig) { + if (isLoggedIn(currentUser) && isMainBranch(branchLike) && hasBranches && hasBadBranchConfig) { warning = translateWithParameters( 'provisioning.no_analysis_on_main_branch.bad_configuration', getBranchLikeDisplayName(branchLike), @@ -79,7 +78,7 @@ export function EmptyOverview(props: EmptyOverviewProps) {
{isLoggedIn(currentUser) ? ( <> - {showWarning && ( + {hasBranches && ( {warning} diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/SetAsMainBranchModal.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/SetAsMainBranchModal.tsx index de7c44a323f..1f5445bc23e 100644 --- a/server/sonar-web/src/main/js/apps/projectBranches/components/SetAsMainBranchModal.tsx +++ b/server/sonar-web/src/main/js/apps/projectBranches/components/SetAsMainBranchModal.tsx @@ -43,10 +43,11 @@ export default function SetAsMainBranchModal(props: SetAsMainBranchModalProps) { return ( + {translateWithParameters('project_branch_pull_request.branch.set_x_as_main', branch.name)} + + } loading={isLoading} onClose={onClose} body={ diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListener.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListener.java index 4f89a015ee7..683706c9312 100644 --- a/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListener.java +++ b/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListener.java @@ -32,7 +32,7 @@ public interface ProjectLifeCycleListener { /** * This method is called after the specified projects have branches deleted or main branch changed. */ - void onProjectBranchesChanged(Set projects); + void onProjectBranchesChanged(Set projects, Set impactedBranches); /** * This method is called after the specified projects' keys have been modified. diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListeners.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListeners.java index 981237903e3..a14e3eab4c2 100644 --- a/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListeners.java +++ b/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListeners.java @@ -34,13 +34,13 @@ public interface ProjectLifeCycleListeners { /** * This method is called after the specified project have any king of change (branch deleted, change of main branch, ...) - * This method will call method {@link ProjectLifeCycleListener#onProjectBranchesChanged(Set)} of all known + * This method will call method {@link ProjectLifeCycleListener#onProjectBranchesChanged(Set,Set)} of all known * {@link ProjectLifeCycleListener} implementations. *

* This method ensures all {@link ProjectLifeCycleListener} implementations are called, even if one or more of * them fail with an exception. */ - void onProjectBranchesChanged(Set projects); + void onProjectBranchesChanged(Set projects, Set impactedBranches); /** * This method is called after the specified project's key has been changed and will call method diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListenersImpl.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListenersImpl.java index 9d98d0f80af..5f3ce502be3 100644 --- a/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListenersImpl.java +++ b/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListenersImpl.java @@ -61,14 +61,14 @@ public class ProjectLifeCycleListenersImpl implements ProjectLifeCycleListeners } @Override - public void onProjectBranchesChanged(Set projects) { + public void onProjectBranchesChanged(Set projects, Set impactedBranches) { checkNotNull(projects, "projects can't be null"); if (projects.isEmpty()) { return; } Arrays.stream(listeners) - .forEach(safelyCallListener(listener -> listener.onProjectBranchesChanged(projects))); + .forEach(safelyCallListener(listener -> listener.onProjectBranchesChanged(projects, impactedBranches))); } @Override diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java index 600a3876564..a12bce9b353 100644 --- a/server/sonar-webserver-api/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java +++ b/server/sonar-webserver-api/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java @@ -32,12 +32,15 @@ import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.Mockito; +import static java.util.Collections.emptySet; import static java.util.Collections.singleton; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatNoException; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anySet; import static org.mockito.ArgumentMatchers.same; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyNoInteractions; @@ -128,30 +131,30 @@ public class ProjectLifeCycleListenersImplTest { @Test public void onProjectBranchesChanged_throws_NPE_if_set_is_null() { - assertThatThrownBy(() -> underTestWithListeners.onProjectBranchesChanged(null)) + assertThatThrownBy(() -> underTestWithListeners.onProjectBranchesChanged(null, null)) .isInstanceOf(NullPointerException.class) .hasMessage("projects can't be null"); } @Test public void onProjectBranchesChanged_throws_NPE_if_set_is_null_even_if_no_listeners() { - assertThatThrownBy(() -> underTestNoListeners.onProjectBranchesChanged(null)) + assertThatThrownBy(() -> underTestNoListeners.onProjectBranchesChanged(null, null)) .isInstanceOf(NullPointerException.class) .hasMessage("projects can't be null"); } @Test public void onProjectBranchesChanged_has_no_effect_if_set_is_empty() { - underTestNoListeners.onProjectBranchesChanged(Collections.emptySet()); + underTestNoListeners.onProjectBranchesChanged(Collections.emptySet(), emptySet()); - underTestWithListeners.onProjectBranchesChanged(Collections.emptySet()); + underTestWithListeners.onProjectBranchesChanged(Collections.emptySet(), emptySet()); verifyNoInteractions(listener1, listener2, listener3); } @Test @UseDataProvider("oneOrManyProjects") public void onProjectBranchesChanged_does_not_fail_if_there_is_no_listener(Set projects) { - assertThatNoException().isThrownBy(()-> underTestNoListeners.onProjectBranchesChanged(projects)); + assertThatNoException().isThrownBy(()-> underTestNoListeners.onProjectBranchesChanged(projects, emptySet())); } @Test @@ -159,11 +162,11 @@ public class ProjectLifeCycleListenersImplTest { public void onProjectBranchesChanged_calls_all_listeners_in_order_of_addition_to_constructor(Set projects) { InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3); - underTestWithListeners.onProjectBranchesChanged(projects); + underTestWithListeners.onProjectBranchesChanged(projects, emptySet()); - inOrder.verify(listener1).onProjectBranchesChanged(same(projects)); - inOrder.verify(listener2).onProjectBranchesChanged(same(projects)); - inOrder.verify(listener3).onProjectBranchesChanged(same(projects)); + inOrder.verify(listener1).onProjectBranchesChanged(same(projects), eq(emptySet())); + inOrder.verify(listener2).onProjectBranchesChanged(same(projects), eq(emptySet())); + inOrder.verify(listener3).onProjectBranchesChanged(same(projects), eq(emptySet())); inOrder.verifyNoMoreInteractions(); } @@ -173,13 +176,13 @@ public class ProjectLifeCycleListenersImplTest { InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3); doThrow(new RuntimeException("Faking listener2 throwing an exception")) .when(listener2) - .onProjectBranchesChanged(any()); + .onProjectBranchesChanged(any(), anySet()); - underTestWithListeners.onProjectBranchesChanged(projects); + underTestWithListeners.onProjectBranchesChanged(projects, emptySet()); - inOrder.verify(listener1).onProjectBranchesChanged(same(projects)); - inOrder.verify(listener2).onProjectBranchesChanged(same(projects)); - inOrder.verify(listener3).onProjectBranchesChanged(same(projects)); + inOrder.verify(listener1).onProjectBranchesChanged(same(projects), eq(emptySet())); + inOrder.verify(listener2).onProjectBranchesChanged(same(projects), eq(emptySet())); + inOrder.verify(listener3).onProjectBranchesChanged(same(projects), eq(emptySet())); inOrder.verifyNoMoreInteractions(); } @@ -189,13 +192,13 @@ public class ProjectLifeCycleListenersImplTest { InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3); doThrow(new Error("Faking listener2 throwing an Error")) .when(listener2) - .onProjectBranchesChanged(any()); + .onProjectBranchesChanged(any(), anySet()); - underTestWithListeners.onProjectBranchesChanged(projects); + underTestWithListeners.onProjectBranchesChanged(projects, emptySet()); - inOrder.verify(listener1).onProjectBranchesChanged(same(projects)); - inOrder.verify(listener2).onProjectBranchesChanged(same(projects)); - inOrder.verify(listener3).onProjectBranchesChanged(same(projects)); + inOrder.verify(listener1).onProjectBranchesChanged(same(projects), eq(emptySet())); + inOrder.verify(listener2).onProjectBranchesChanged(same(projects), eq(emptySet())); + inOrder.verify(listener3).onProjectBranchesChanged(same(projects), eq(emptySet())); inOrder.verifyNoMoreInteractions(); } diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/branch/ws/DeleteActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/branch/ws/DeleteActionIT.java index 45b7a464bf8..6f3b39b5e52 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/branch/ws/DeleteActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/branch/ws/DeleteActionIT.java @@ -39,6 +39,7 @@ import org.sonar.server.project.ProjectLifeCycleListeners; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.WsActionTester; +import static java.util.Collections.emptySet; import static java.util.Collections.singleton; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -72,7 +73,7 @@ public class DeleteActionIT { .execute(); verifyDeletedKey("branch1"); - verify(projectLifeCycleListeners).onProjectBranchesChanged(singleton(Project.fromProjectDtoWithTags(project))); + verify(projectLifeCycleListeners).onProjectBranchesChanged(singleton(Project.fromProjectDtoWithTags(project)), emptySet()); } @Test diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/branch/ws/SetMainBranchActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/branch/ws/SetMainBranchActionIT.java index a6ad1c0de56..7acaf2f7fd3 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/branch/ws/SetMainBranchActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/branch/ws/SetMainBranchActionIT.java @@ -219,7 +219,7 @@ public class SetMainBranchActionIT { .setParam(PARAM_PROJECT, projectData.projectKey()) .setParam(PARAM_BRANCH, newMainBranch.getKey()).execute(); - checkCallToProjectLifeCycleListenersOnProjectBranchesChanges(projectData.getProjectDto()); + checkCallToProjectLifeCycleListenersOnProjectBranchesChanges(projectData.getProjectDto(), projectData.getMainBranchDto().getUuid()); verify(indexers).commitAndIndexBranches(any(), eq(List.of(projectData.getMainBranchDto(), newMainBranch)), eq(Indexers.BranchEvent.SWITCH_OF_MAIN_BRANCH)); checkNewMainBranch(projectData.projectUuid(), newMainBranch.getUuid()); checkPreviousMainBranch(projectData); @@ -228,9 +228,9 @@ public class SetMainBranchActionIT { .formatted(projectData.projectKey(), newMainBranch.getKey(), projectData.getMainBranchDto().getKey())); } - private void checkCallToProjectLifeCycleListenersOnProjectBranchesChanges(ProjectDto projectDto) { + private void checkCallToProjectLifeCycleListenersOnProjectBranchesChanges(ProjectDto projectDto, String oldMainBranchUuid) { Project project = Project.from(projectDto); - verify(projectLifeCycleListeners).onProjectBranchesChanged(Set.of(project)); + verify(projectLifeCycleListeners).onProjectBranchesChanged(Set.of(project), Set.of(oldMainBranchUuid) ); } private void checkNewMainBranch(String projectUuid, String newBranchUuid) { diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/branch/ws/DeleteAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/branch/ws/DeleteAction.java index 8c1d14e85f9..efacedf5f1f 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/branch/ws/DeleteAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/branch/ws/DeleteAction.java @@ -34,6 +34,7 @@ import org.sonar.server.project.Project; import org.sonar.server.project.ProjectLifeCycleListeners; import org.sonar.server.user.UserSession; +import static java.util.Collections.emptySet; import static java.util.Collections.singleton; import static org.sonar.server.branch.ws.BranchesWs.addBranchParam; import static org.sonar.server.branch.ws.BranchesWs.addProjectParam; @@ -86,7 +87,7 @@ public class DeleteAction implements BranchWsAction { "Branch '%s' not found for project '%s'", branchKey, projectKey); componentCleanerService.deleteBranch(dbSession, branch); - projectLifeCycleListeners.onProjectBranchesChanged(singleton(Project.fromProjectDtoWithTags(project))); + projectLifeCycleListeners.onProjectBranchesChanged(singleton(Project.fromProjectDtoWithTags(project)), emptySet()); response.noContent(); } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/branch/ws/SetMainBranchAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/branch/ws/SetMainBranchAction.java index 8f62233136c..05f4a2f4575 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/branch/ws/SetMainBranchAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/branch/ws/SetMainBranchAction.java @@ -21,6 +21,7 @@ package org.sonar.server.branch.ws; import java.util.List; import java.util.Objects; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.server.ws.Request; @@ -97,7 +98,7 @@ public class SetMainBranchAction implements BranchWsAction { return; } configureProjectWithNewMainBranch(dbSession, projectDto.getKey(), oldMainBranch, newMainBranch); - refreshApplicationsAndPortfoliosComputedByProject(projectDto); + refreshApplicationsAndPortfoliosComputedByProject(projectDto, Set.of(oldMainBranch.getUuid())); indexers.commitAndIndexBranches(dbSession, List.of(oldMainBranch, newMainBranch), Indexers.BranchEvent.SWITCH_OF_MAIN_BRANCH); dbSession.commit(); @@ -121,8 +122,8 @@ public class SetMainBranchAction implements BranchWsAction { return false; } - private void refreshApplicationsAndPortfoliosComputedByProject(ProjectDto projectDto) { - projectLifeCycleListeners.onProjectBranchesChanged(singleton(Project.from(projectDto))); + private void refreshApplicationsAndPortfoliosComputedByProject(ProjectDto projectDto, Set impactedBranchesUuids) { + projectLifeCycleListeners.onProjectBranchesChanged(singleton(Project.from(projectDto)), impactedBranchesUuids); } private void updateNewMainBranch(DbSession dbSession, BranchDto newMainBranch) {