diff options
author | Alain Kermis <alain.kermis@sonarsource.com> | 2023-05-23 10:46:57 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-05-30 20:02:52 +0000 |
commit | 716a2667e6392f29f9c2d651eb7a7a9fbbc1fccb (patch) | |
tree | a9f4110e1e1b26cc6794fc06f43fa43f4aae8e87 /server/sonar-webserver-webapi | |
parent | 1998f37bb4c45c5f3160705a9b70ad73d9cc04cc (diff) | |
download | sonarqube-716a2667e6392f29f9c2d651eb7a7a9fbbc1fccb.tar.gz sonarqube-716a2667e6392f29f9c2d651eb7a7a9fbbc1fccb.zip |
SONAR-19340 Raise event whenever hotspots change in WS
Diffstat (limited to 'server/sonar-webserver-webapi')
8 files changed, 202 insertions, 36 deletions
diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/AssignActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/AssignActionIT.java index 019bda572a7..bab03f3c1df 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/AssignActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/AssignActionIT.java @@ -30,6 +30,7 @@ import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nullable; import org.assertj.core.api.Condition; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -44,6 +45,8 @@ import org.sonar.core.issue.IssueChangeContext; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; +import org.sonar.db.component.BranchDto; +import org.sonar.db.component.BranchType; import org.sonar.db.component.ComponentDto; import org.sonar.db.issue.IssueDto; import org.sonar.db.rule.RuleDto; @@ -53,6 +56,8 @@ import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.issue.IssueFieldsSetter; import org.sonar.server.issue.ws.IssueUpdater; +import org.sonar.server.pushapi.hotspots.HotspotChangeEventService; +import org.sonar.server.pushapi.hotspots.HotspotChangedEvent; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.WsActionTester; @@ -68,6 +73,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.sonar.api.issue.Issue.RESOLUTION_ACKNOWLEDGED; @@ -90,15 +96,22 @@ public class AssignActionIT { private final DbClient dbClient = dbTester.getDbClient(); private final IssueUpdater issueUpdater = mock(IssueUpdater.class); + private HotspotChangeEventService hotspotChangeEventService = mock(HotspotChangeEventService.class); private final System2 system2 = mock(System2.class); private final IssueFieldsSetter issueFieldsSetter = mock(IssueFieldsSetter.class); private final HotspotWsSupport hotspotWsSupport = new HotspotWsSupport(dbClient, userSessionRule, system2); - private final AssignAction underTest = new AssignAction(dbClient, hotspotWsSupport, issueFieldsSetter, issueUpdater); + private final AssignAction underTest = new AssignAction(dbClient, hotspotWsSupport, issueFieldsSetter, issueUpdater, hotspotChangeEventService); private final WsActionTester actionTester = new WsActionTester(underTest); + private BranchDto branchDto = mock(BranchDto.class); + + @Before + public void setMock() { + when(issueUpdater.getBranch(any(), any())).thenReturn(branchDto); + } @Test - public void ws_definition_check() { + public void wsExecution_whenDefined() { WebService.Action wsDefinition = actionTester.getDef(); assertThat(wsDefinition.isPost()).isTrue(); @@ -120,7 +133,7 @@ public class AssignActionIT { } @Test - public void assign_hotspot_to_someone_for_public_project() { + public void wsExecution_whenAssignedForPublicProject() { ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); IssueDto hotspot = dbTester.issues().insertHotspot(project, file); @@ -137,7 +150,7 @@ public class AssignActionIT { } @Test - public void unassign_hotspot_for_public_project() { + public void wsExecution_whenUnassignedForPublicProject() { ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); UserDto assignee = insertUser(randomAlphanumeric(15)); @@ -154,7 +167,7 @@ public class AssignActionIT { } @Test - public void assign_hotspot_to_me_for_public_project() { + public void wsExecution_whenMyselfAssignedForPublicProject() { ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); IssueDto hotspot = dbTester.issues().insertHotspot(project, file); @@ -170,7 +183,7 @@ public class AssignActionIT { } @Test - public void unassign_hotspot_myself_for_public_project() { + public void wsExecution_whenMyselfUnassignedForPublicProject() { ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); UserDto me = insertUser(randomAlphanumeric(10)); @@ -185,7 +198,7 @@ public class AssignActionIT { } @Test - public void assign_hotspot_to_someone_for_private_project() { + public void wsExecution_whenAssigneeForPrivateProject() { ComponentDto project = dbTester.components().insertPrivateProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); IssueDto hotspot = dbTester.issues().insertHotspot(project, file); @@ -201,7 +214,7 @@ public class AssignActionIT { } @Test - public void unassign_hotspot_for_private_project() { + public void wsExecution_whenUnassignedForPrivateProject() { ComponentDto project = dbTester.components().insertPrivateProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); UserDto assignee = insertUser(randomAlphanumeric(15)); @@ -217,7 +230,7 @@ public class AssignActionIT { } @Test - public void assign_hotspot_to_someone_for_private_project_branch() { + public void wsExecution_whenAssigneeForPrivateProjectBranch() { ComponentDto project = dbTester.components().insertPrivateProject().getMainBranchComponent(); ComponentDto branch = dbTester.components().insertProjectBranch(project); ComponentDto file = dbTester.components().insertComponent(newFileDto(branch, project.uuid())); @@ -235,7 +248,7 @@ public class AssignActionIT { } @Test - public void fail_if_assignee_does_not_have_access_for_private_project() { + public void wsExecution_whenAssigneeDoesNotHaveAccessToPrivateProject_shouldFail() { ComponentDto project = dbTester.components().insertPrivateProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); IssueDto hotspot = dbTester.issues().insertHotspot(project, file); @@ -252,7 +265,7 @@ public class AssignActionIT { } @Test - public void fail_if_assignee_does_not_have_access_for_private_project_branch() { + public void wsExecution_whenAssigneeDoesNotHaveAccessToPrivateProjectBranch_shouldFail() { ComponentDto project = dbTester.components().insertPrivateProject().getMainBranchComponent(); ComponentDto branch = dbTester.components().insertProjectBranch(project); ComponentDto file = dbTester.components().insertComponent(newFileDto(branch, project.uuid())); @@ -271,7 +284,7 @@ public class AssignActionIT { } @Test - public void assign_hotspot_to_me_for_private_project() { + public void wsExecution_whenAssignHotspotToMeForPrivateProject() { ComponentDto project = dbTester.components().insertPrivateProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); @@ -287,7 +300,7 @@ public class AssignActionIT { } @Test - public void assign_hotspot_with_comment() { + public void wsExecution_whenAssignHotspotWithComment() { ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); @@ -307,7 +320,7 @@ public class AssignActionIT { } @Test - public void assign_twice_same_user_to_hotspot_does_not_reload() { + public void wsExecution_whenAssignTwiceSameUserHotspotDoesNotReload_shouldFail() { ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); IssueDto hotspot = dbTester.issues().insertHotspot(project, file); @@ -326,7 +339,42 @@ public class AssignActionIT { } @Test - public void fail_if_assigning_to_not_existing_user() { + public void wsExecution_whenBranchTypeIsBranch_shouldDistributeEvents() { + ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); + ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); + IssueDto hotspot = dbTester.issues().insertHotspot(project, file); + + UserDto userDto = insertUser(randomAlphanumeric(10)); + userSessionRule.logIn(userDto).registerComponents(project); + + UserDto assignee = insertUser(randomAlphanumeric(15)); + when(branchDto.getBranchType()).thenReturn(BranchType.BRANCH); + when(issueFieldsSetter.assign(eq(hotspot.toDefaultIssue()), userMatcher(assignee), any(IssueChangeContext.class))).thenReturn(true); + + executeRequest(hotspot, assignee.getLogin(), null); + verify(hotspotChangeEventService).distributeHotspotChangedEvent(eq(project.uuid()), any(HotspotChangedEvent.class)); + } + + @Test + public void wsExecution_whenBranchIsPullRequest_shouldNotDistributeEvents() { + ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); + ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); + IssueDto hotspot = dbTester.issues().insertHotspot(project, file); + + UserDto userDto = insertUser(randomAlphanumeric(10)); + userSessionRule.logIn(userDto).registerComponents(project); + + UserDto assignee = insertUser(randomAlphanumeric(15)); + when(branchDto.getBranchType()).thenReturn(BranchType.PULL_REQUEST); + when(issueFieldsSetter.assign(eq(hotspot.toDefaultIssue()), userMatcher(assignee), any(IssueChangeContext.class))).thenReturn(true); + + executeRequest(hotspot, assignee.getLogin(), null); + verifyNoInteractions(hotspotChangeEventService); + } + + + @Test + public void wsExecution_whenAssigningToNonExistingUser_shouldFail() { ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); IssueDto hotspot = dbTester.issues().insertHotspot(project, file); @@ -343,7 +391,7 @@ public class AssignActionIT { @Test @UseDataProvider("allIssueStatusesAndResolutionsThatThrowOnAssign") - public void fail_if_assign_user_to_hotspot_for_which_it_is_forbidden(String status, String resolution) { + public void wsExecution_whenAssigningToUserIfForbidden_shouldFail(String status, String resolution) { ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); @@ -373,7 +421,7 @@ public class AssignActionIT { @Test @UseDataProvider("allIssueStatusesAndResolutionsThatDoNotThrowOnAssign") - public void fail_if_assign_user_to_hotspot_for_which_it_is_allowed(String status, String resolution) { + public void wsExecution_whenAssigningToUserIfAllowed_shouldNotFail(String status, String resolution) { ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); IssueDto hotspot = dbTester.issues().insertHotspot(project, file, h -> { @@ -397,7 +445,7 @@ public class AssignActionIT { } @Test - public void fail_if_not_authenticated() { + public void wsExecution_whenNotAuthenticated_shouldFail() { ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); IssueDto hotspot = dbTester.issues().insertHotspot(project, file); @@ -413,7 +461,7 @@ public class AssignActionIT { } @Test - public void fail_if_missing_browse_permission() { + public void wsExecution_whenMissingBrowserAthentication_shouldFail() { ComponentDto project = dbTester.components().insertPrivateProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); IssueDto hotspot = dbTester.issues().insertHotspot(project, file); @@ -429,7 +477,7 @@ public class AssignActionIT { } @Test - public void fail_if_hotspot_does_not_exist() { + public void wsExecution_whenHotspotDoesNotExist_shouldFail() { ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); UserDto me = insertUser(randomAlphanumeric(10)); @@ -444,7 +492,7 @@ public class AssignActionIT { @Test @UseDataProvider("allRuleTypesWithStatusesExceptHotspot") - public void fail_if_trying_to_assign_issue(RuleType ruleType, String status) { + public void wsExecution_whenAssigningToNonexistantIssue_shouldFail(RuleType ruleType, String status) { ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); RuleDto rule = newRule(ruleType); @@ -478,7 +526,7 @@ public class AssignActionIT { } @Test - public void fail_with_NotFoundException_if_hotspot_is_closed() { + public void wsExecution_whenHotspotIsClosed_shouldFailWithNotFoundException() { ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); RuleDto rule = newRule(SECURITY_HOTSPOT); diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/ChangeStatusActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/ChangeStatusActionIT.java index 9249030980e..b0b02683772 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/ChangeStatusActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/ChangeStatusActionIT.java @@ -29,6 +29,7 @@ import java.util.Random; import java.util.function.Consumer; import java.util.stream.Stream; import javax.annotation.Nullable; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,9 +44,13 @@ import org.sonar.core.issue.IssueChangeContext; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; +import org.sonar.db.component.BranchDto; +import org.sonar.db.component.BranchType; +import org.sonar.db.component.ComponentDbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.issue.IssueDto; import org.sonar.db.issue.IssueTesting; +import org.sonar.db.project.ProjectDto; import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleTesting; import org.sonar.server.exceptions.ForbiddenException; @@ -54,6 +59,8 @@ import org.sonar.server.exceptions.UnauthorizedException; import org.sonar.server.issue.IssueFieldsSetter; import org.sonar.server.issue.TransitionService; import org.sonar.server.issue.ws.IssueUpdater; +import org.sonar.server.pushapi.hotspots.HotspotChangeEventService; +import org.sonar.server.pushapi.hotspots.HotspotChangedEvent; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.WsActionTester; @@ -92,11 +99,19 @@ public class ChangeStatusActionIT { private DbClient dbClient = dbTester.getDbClient(); private TransitionService transitionService = mock(TransitionService.class); private IssueUpdater issueUpdater = mock(IssueUpdater.class); + private HotspotChangeEventService hotspotChangeEventService = mock(HotspotChangeEventService.class); private System2 system2 = mock(System2.class); private IssueFieldsSetter issueFieldsSetter = mock(IssueFieldsSetter.class); private HotspotWsSupport hotspotWsSupport = new HotspotWsSupport(dbClient, userSessionRule, system2); - private ChangeStatusAction underTest = new ChangeStatusAction(dbClient, hotspotWsSupport, transitionService, issueFieldsSetter, issueUpdater); + private ChangeStatusAction underTest = new ChangeStatusAction(dbClient, hotspotWsSupport, transitionService, issueFieldsSetter, issueUpdater, hotspotChangeEventService); private WsActionTester actionTester = new WsActionTester(underTest); + private final ComponentDbTester componentDbTester = new ComponentDbTester(dbTester); + private BranchDto branchDto = mock(BranchDto.class); + + @Before + public void setMock() { + when(issueUpdater.getBranch(any(), any())).thenReturn(branchDto); + } @Test public void define_shouldMarkWebServiceAsPublic() { @@ -426,6 +441,55 @@ public class ChangeStatusActionIT { } } + @Test + public void wsExecution_whenOnMainBranch_shouldDistributeEvents() { + ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); + userSessionRule.logIn().registerComponents(project) + .addProjectPermission(UserRole.SECURITYHOTSPOT_ADMIN, project); + ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); + when(branchDto.getBranchType()).thenReturn(BranchType.BRANCH); + IssueDto hotspot = dbTester.issues().insertHotspot(project, file); + when(transitionService.doTransition(any(), any(), any())).thenReturn(true); + + newRequest(hotspot, STATUS_REVIEWED, RESOLUTION_FIXED, NO_COMMENT).execute(); + + verify(hotspotChangeEventService).distributeHotspotChangedEvent(eq(project.uuid()), any(HotspotChangedEvent.class)); + } + + @Test + public void wsExecution_whenOnNonMainBranch_shouldDistributeEvents() { + ProjectDto project = dbTester.components().insertPublicProject().getProjectDto(); + ComponentDto projectComponentDto = dbTester.components().getComponentDto(project); + BranchDto branch = dbTester.components().insertProjectBranch(project, b -> b.setKey("develop")); + ComponentDto branchComponentDto = dbTester.components().getComponentDto(branch); + + ComponentDto file = dbTester.components().insertComponent(newFileDto(branchComponentDto)); + when(branchDto.getBranchType()).thenReturn(BranchType.BRANCH); + IssueDto hotspot = dbTester.issues().insertHotspot(branchComponentDto, file); + when(transitionService.doTransition(any(), any(), any())).thenReturn(true); + + userSessionRule.logIn().registerComponents(projectComponentDto) + .addProjectPermission(UserRole.SECURITYHOTSPOT_ADMIN, projectComponentDto, branchComponentDto); + newRequest(hotspot, STATUS_REVIEWED, RESOLUTION_FIXED, NO_COMMENT).execute(); + + verify(hotspotChangeEventService).distributeHotspotChangedEvent(eq(branchComponentDto.uuid()), any(HotspotChangedEvent.class)); + } + + @Test + public void wsExecution_whenBranchTypeIsPullRequest_shouldNotDistributeEvents() { + ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent(); + userSessionRule.logIn().registerComponents(project) + .addProjectPermission(UserRole.SECURITYHOTSPOT_ADMIN, project); + ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); + when(branchDto.getBranchType()).thenReturn(BranchType.PULL_REQUEST); + IssueDto hotspot = dbTester.issues().insertHotspot(project, file); + when(transitionService.doTransition(any(), any(), any())).thenReturn(true); + + newRequest(hotspot, STATUS_REVIEWED, RESOLUTION_FIXED, NO_COMMENT).execute(); + + verifyNoInteractions(hotspotChangeEventService); + } + @DataProvider public static Object[][] reviewedResolutionsAndExpectedTransitionKey() { return new Object[][] { diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/AssignAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/AssignAction.java index 31b961e38e0..24f141d7efb 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/AssignAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/AssignAction.java @@ -30,15 +30,19 @@ import org.sonar.core.issue.IssueChangeContext; import org.sonar.core.util.Uuids; import org.sonar.db.DbClient; import org.sonar.db.DbSession; +import org.sonar.db.component.BranchDto; import org.sonar.db.issue.IssueDto; import org.sonar.db.project.ProjectDto; import org.sonar.db.user.UserDto; import org.sonar.server.issue.IssueFieldsSetter; import org.sonar.server.issue.ws.IssueUpdater; +import org.sonar.server.pushapi.hotspots.HotspotChangeEventService; +import org.sonar.server.pushapi.hotspots.HotspotChangedEvent; import static com.google.common.base.Strings.isNullOrEmpty; import static org.sonar.api.issue.Issue.RESOLUTION_ACKNOWLEDGED; import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW; +import static org.sonar.db.component.BranchType.BRANCH; import static org.sonar.server.exceptions.NotFoundException.checkFound; import static org.sonar.server.exceptions.NotFoundException.checkFoundWithOptional; @@ -53,13 +57,15 @@ public class AssignAction implements HotspotsWsAction { private final HotspotWsSupport hotspotWsSupport; private final IssueFieldsSetter issueFieldsSetter; private final IssueUpdater issueUpdater; + private final HotspotChangeEventService hotspotChangeEventService; public AssignAction(DbClient dbClient, HotspotWsSupport hotspotWsSupport, IssueFieldsSetter issueFieldsSetter, - IssueUpdater issueUpdater) { + IssueUpdater issueUpdater, HotspotChangeEventService hotspotChangeEventService) { this.dbClient = dbClient; this.hotspotWsSupport = hotspotWsSupport; this.issueFieldsSetter = issueFieldsSetter; this.issueUpdater = issueUpdater; + this.hotspotChangeEventService = hotspotChangeEventService; } @Override @@ -121,6 +127,12 @@ public class AssignAction implements HotspotsWsAction { if (issueFieldsSetter.assign(defaultIssue, assignee, context)) { issueUpdater.saveIssueAndPreloadSearchResponseData(dbSession, defaultIssue, context); + + BranchDto branch = issueUpdater.getBranch(dbSession, defaultIssue); + if (BRANCH.equals(branch.getBranchType())) { + HotspotChangedEvent hotspotChangedEvent = buildEventData(defaultIssue, assignee, hotspotDto.getFilePath()); + hotspotChangeEventService.distributeHotspotChangedEvent(defaultIssue.projectUuid(), hotspotChangedEvent); + } } } } @@ -147,4 +159,16 @@ public class AssignAction implements HotspotsWsAction { private boolean hasProjectPermission(DbSession dbSession, String userUuid, String projectUuid) { return dbClient.authorizationDao().selectProjectPermissions(dbSession, projectUuid, userUuid).contains(UserRole.USER); } + + private static HotspotChangedEvent buildEventData(DefaultIssue defaultIssue, @Nullable UserDto assignee, String filePath) { + return new HotspotChangedEvent.Builder() + .setKey(defaultIssue.key()) + .setProjectKey(defaultIssue.projectKey()) + .setStatus(defaultIssue.status()) + .setResolution(defaultIssue.resolution()) + .setUpdateDate(defaultIssue.updateDate()) + .setAssignee(assignee == null ? null : assignee.getLogin()) + .setFilePath(filePath) + .build(); + } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/ChangeStatusAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/ChangeStatusAction.java index 8a7cf079fb7..98cb5a079a0 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/ChangeStatusAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/ChangeStatusAction.java @@ -33,10 +33,13 @@ import org.sonar.core.issue.IssueChangeContext; import org.sonar.core.util.Uuids; import org.sonar.db.DbClient; import org.sonar.db.DbSession; +import org.sonar.db.component.BranchDto; import org.sonar.db.issue.IssueDto; import org.sonar.server.issue.IssueFieldsSetter; import org.sonar.server.issue.TransitionService; import org.sonar.server.issue.ws.IssueUpdater; +import org.sonar.server.pushapi.hotspots.HotspotChangeEventService; +import org.sonar.server.pushapi.hotspots.HotspotChangedEvent; import static com.google.common.base.Preconditions.checkArgument; import static org.apache.commons.lang.StringUtils.trimToNull; @@ -45,6 +48,7 @@ import static org.sonar.api.issue.Issue.RESOLUTION_FIXED; import static org.sonar.api.issue.Issue.SECURITY_HOTSPOT_RESOLUTIONS; import static org.sonar.api.issue.Issue.STATUS_REVIEWED; import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW; +import static org.sonar.db.component.BranchType.BRANCH; public class ChangeStatusAction implements HotspotsWsAction { @@ -58,14 +62,16 @@ public class ChangeStatusAction implements HotspotsWsAction { private final TransitionService transitionService; private final IssueFieldsSetter issueFieldsSetter; private final IssueUpdater issueUpdater; + private final HotspotChangeEventService hotspotChangeEventService; public ChangeStatusAction(DbClient dbClient, HotspotWsSupport hotspotWsSupport, TransitionService transitionService, - IssueFieldsSetter issueFieldsSetter, IssueUpdater issueUpdater) { + IssueFieldsSetter issueFieldsSetter, IssueUpdater issueUpdater, HotspotChangeEventService hotspotChangeEventService) { this.dbClient = dbClient; this.hotspotWsSupport = hotspotWsSupport; this.transitionService = transitionService; this.issueFieldsSetter = issueFieldsSetter; this.issueUpdater = issueUpdater; + this.hotspotChangeEventService = hotspotChangeEventService; } @Override @@ -155,8 +161,27 @@ public class ChangeStatusAction implements HotspotsWsAction { if (comment != null) { issueFieldsSetter.addComment(defaultIssue, comment, context); } + issueUpdater.saveIssueAndPreloadSearchResponseData(session, defaultIssue, context); + + BranchDto branch = issueUpdater.getBranch(session, defaultIssue); + if (BRANCH.equals(branch.getBranchType())) { + HotspotChangedEvent hotspotChangedEvent = buildEventData(defaultIssue, issueDto); + hotspotChangeEventService.distributeHotspotChangedEvent(defaultIssue.projectUuid(), hotspotChangedEvent); + } } } + private static HotspotChangedEvent buildEventData(DefaultIssue defaultIssue, IssueDto issueDto) { + return new HotspotChangedEvent.Builder() + .setKey(defaultIssue.key()) + .setProjectKey(defaultIssue.projectKey()) + .setStatus(defaultIssue.status()) + .setResolution(defaultIssue.resolution()) + .setUpdateDate(defaultIssue.updateDate()) + .setAssignee(issueDto.getAssigneeLogin()) + .setFilePath(issueDto.getFilePath()) + .build(); + } + } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java index f9db25c4734..134e68f7e21 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java @@ -119,7 +119,7 @@ public class DoTransitionAction implements IssuesWsAction { IssueChangeContext context = issueChangeContextByUserBuilder(new Date(system2.now()), userSession.getUuid()).withRefreshMeasures().build(); transitionService.checkTransitionPermission(transitionKey, defaultIssue); if (transitionService.doTransition(defaultIssue, context, transitionKey)) { - BranchDto branch = issueUpdater.getBranch(session, defaultIssue, defaultIssue.projectUuid()); + BranchDto branch = issueUpdater.getBranch(session, defaultIssue); SearchResponseData response = issueUpdater.saveIssueAndPreloadSearchResponseData(session, defaultIssue, context, branch); if (branch.getBranchType().equals(BRANCH) && response.getComponentByUuid(defaultIssue.projectUuid()) != null) { diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/IssueUpdater.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/IssueUpdater.java index 85669dfdf57..680f3912abf 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/IssueUpdater.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/IssueUpdater.java @@ -19,6 +19,7 @@ */ package org.sonar.server.issue.ws; +import java.util.Date; import java.util.List; import java.util.Optional; import javax.annotation.Nullable; @@ -70,7 +71,7 @@ public class IssueUpdater { } public SearchResponseData saveIssueAndPreloadSearchResponseData(DbSession dbSession, DefaultIssue issue, IssueChangeContext context) { - BranchDto branch = getBranch(dbSession, issue, issue.projectUuid()); + BranchDto branch = getBranch(dbSession, issue); return saveIssueAndPreloadSearchResponseData(dbSession, issue, context, branch); } @@ -94,20 +95,24 @@ public class IssueUpdater { return result; } - protected BranchDto getBranch(DbSession dbSession, DefaultIssue issue, @Nullable String projectUuid) { + public BranchDto getBranch(DbSession dbSession, DefaultIssue issue) { String issueKey = issue.key(); - checkState(projectUuid != null, "Issue '%s' has no project", issueKey); - BranchDto component = dbClient.branchDao().selectByUuid(dbSession, projectUuid).orElse(null); - checkState(component != null, "Branch uuid '%s' for issue key '%s' cannot be found", projectUuid, issueKey); - return component; + String componentUuid = issue.componentUuid(); + checkState(componentUuid != null, "Component uuid for issue key '%s' cannot be null", issueKey); + Optional<ComponentDto> componentDto = dbClient.componentDao().selectByUuid(dbSession, componentUuid); + checkState(componentDto.isPresent(), "Component not found for issue with key '%s'", issueKey); + BranchDto branchDto = dbClient.branchDao().selectByUuid(dbSession, componentDto.get().branchUuid()).orElse(null); + checkState(branchDto != null, "Branch not found for issue with key '%s'", issueKey); + return branchDto; } private IssueDto doSaveIssue(DbSession session, DefaultIssue issue, IssueChangeContext context, @Nullable RuleDto ruleDto, ComponentDto project, BranchDto branchDto) { IssueDto issueDto = issueStorage.save(session, singletonList(issue)).iterator().next(); + Date updateDate = issue.updateDate(); if ( // since this method is called after an update of the issue, date should never be null - issue.updateDate() == null + updateDate == null // name of rule is displayed in notification, rule must therefor be present || ruleDto == null // notification are not supported on PRs @@ -133,7 +138,7 @@ public class IssueUpdater { .setBranchName(branchDto.isMain() ? null : branchDto.getKey()) .build()) .build()), - new UserChange(issue.updateDate().getTime(), new User(author.getUuid(), author.getLogin(), author.getName()))); + new UserChange(updateDate.getTime(), new User(author.getUuid(), author.getLogin(), author.getName()))); notificationService.scheduleForSending(notificationSerializer.serialize(notificationBuilder)); return issueDto; } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetSeverityAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetSeverityAction.java index a00dd2afa7d..d5ce9ebe9e1 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetSeverityAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetSeverityAction.java @@ -115,7 +115,7 @@ public class SetSeverityAction implements IssuesWsAction { IssueChangeContext context = issueChangeContextByUserBuilder(new Date(), userSession.getUuid()).withRefreshMeasures().build(); if (issueFieldsSetter.setManualSeverity(issue, severity, context)) { - BranchDto branch = issueUpdater.getBranch(session, issue, issue.projectUuid()); + BranchDto branch = issueUpdater.getBranch(session, issue); SearchResponseData response = issueUpdater.saveIssueAndPreloadSearchResponseData(session, issue, context, branch); if (branch.getBranchType().equals(BRANCH) && response.getComponentByUuid(issue.projectUuid()) != null) { diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetTypeAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetTypeAction.java index 6807f29186d..dbaf40cfd1f 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetTypeAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetTypeAction.java @@ -120,7 +120,7 @@ public class SetTypeAction implements IssuesWsAction { IssueChangeContext context = issueChangeContextByUserBuilder(new Date(system2.now()), userSession.getUuid()).withRefreshMeasures().build(); if (issueFieldsSetter.setType(issue, ruleType, context)) { - BranchDto branch = issueUpdater.getBranch(session, issue, issue.projectUuid()); + BranchDto branch = issueUpdater.getBranch(session, issue); SearchResponseData response = issueUpdater.saveIssueAndPreloadSearchResponseData(session, issue, context, branch); if (branch.getBranchType().equals(BRANCH) && response.getComponentByUuid(issue.projectUuid()) != null) { issueChangeEventService.distributeIssueChangeEvent(issue, null, ruleType.name(), null, branch, |