--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.hotspot.ws;
+
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.core.issue.DefaultIssue;
+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.issue.IssueDto;
+import org.sonar.server.issue.IssueFieldsSetter;
+import org.sonar.server.issue.ws.IssueUpdater;
+
+public class AddCommentAction implements HotspotsWsAction {
+ private static final String PARAM_HOTSPOT_KEY = "hotspot";
+ private static final String PARAM_COMMENT = "comment";
+
+ private final DbClient dbClient;
+ private final HotspotWsSupport hotspotWsSupport;
+ private final IssueFieldsSetter issueFieldsSetter;
+ private final IssueUpdater issueUpdater;
+
+ public AddCommentAction(DbClient dbClient, HotspotWsSupport hotspotWsSupport,
+ IssueFieldsSetter issueFieldsSetter, IssueUpdater issueUpdater) {
+ this.dbClient = dbClient;
+ this.hotspotWsSupport = hotspotWsSupport;
+ this.issueFieldsSetter = issueFieldsSetter;
+ this.issueUpdater = issueUpdater;
+ }
+
+ @Override
+ public void define(WebService.NewController controller) {
+ WebService.NewAction action = controller
+ .createAction("add_comment")
+ .setHandler(this)
+ .setPost(true)
+ .setDescription("Add a comment to a Security Hotpot.")
+ .setSince("8.1")
+ .setInternal(true);
+
+ action.createParam(PARAM_HOTSPOT_KEY)
+ .setDescription("Key of the Security Hotspot")
+ .setExampleValue(Uuids.UUID_EXAMPLE_03)
+ .setRequired(true);
+ action.createParam(PARAM_COMMENT)
+ .setDescription("Comment text.")
+ .setExampleValue("This is safe because user input is validated by the calling code")
+ .setRequired(true);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ hotspotWsSupport.checkLoggedIn();
+
+ String hotspotKey = request.mandatoryParam(PARAM_HOTSPOT_KEY);
+ String comment = request.mandatoryParam(PARAM_COMMENT);
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ IssueDto hotspot = hotspotWsSupport.loadHotspot(dbSession, hotspotKey);
+ hotspotWsSupport.loadAndCheckProject(dbSession, hotspot);
+
+ DefaultIssue defaultIssue = hotspot.toDefaultIssue();
+ IssueChangeContext context = hotspotWsSupport.newIssueChangeContext();
+ issueFieldsSetter.addComment(defaultIssue, comment, context);
+ issueUpdater.saveIssueAndPreloadSearchResponseData(dbSession, defaultIssue, context, false);
+ response.noContent();
+ }
+ }
+}
*/
package org.sonar.server.hotspot.ws;
-import java.util.Date;
import java.util.Objects;
import javax.annotation.Nullable;
import org.sonar.api.issue.DefaultTransitions;
-import org.sonar.api.rules.RuleType;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
-import org.sonar.api.utils.System2;
-import org.sonar.api.web.UserRole;
import org.sonar.core.issue.DefaultIssue;
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.ComponentDto;
import org.sonar.db.issue.IssueDto;
-import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.issue.IssueFieldsSetter;
import org.sonar.server.issue.TransitionService;
import org.sonar.server.issue.ws.IssueUpdater;
-import org.sonar.server.user.UserSession;
import static com.google.common.base.Preconditions.checkArgument;
-import static java.lang.String.format;
import static org.apache.commons.lang.StringUtils.trimToNull;
import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
import static org.sonar.api.issue.Issue.SECURITY_HOTSPOT_RESOLUTIONS;
private static final String PARAM_COMMENT = "comment";
private final DbClient dbClient;
- private final UserSession userSession;
+ private final HotspotWsSupport hotspotWsSupport;
private final TransitionService transitionService;
private final IssueFieldsSetter issueFieldsSetter;
- private final System2 system2;
private final IssueUpdater issueUpdater;
- public ChangeStatusAction(DbClient dbClient, UserSession userSession, TransitionService transitionService,
- IssueFieldsSetter issueFieldsSetter, System2 system2, IssueUpdater issueUpdater) {
+ public ChangeStatusAction(DbClient dbClient, HotspotWsSupport hotspotWsSupport, TransitionService transitionService,
+ IssueFieldsSetter issueFieldsSetter, IssueUpdater issueUpdater) {
this.dbClient = dbClient;
- this.userSession = userSession;
+ this.hotspotWsSupport = hotspotWsSupport;
this.transitionService = transitionService;
this.issueFieldsSetter = issueFieldsSetter;
- this.system2 = system2;
this.issueUpdater = issueUpdater;
}
@Override
public void handle(Request request, Response response) throws Exception {
- userSession.checkLoggedIn();
+ hotspotWsSupport.checkLoggedIn();
String hotspotKey = request.mandatoryParam(PARAM_HOTSPOT_KEY);
String newStatus = request.mandatoryParam(PARAM_STATUS);
String newResolution = resolutionParam(request, newStatus);
try (DbSession dbSession = dbClient.openSession(false)) {
- IssueDto hotspot = dbClient.issueDao().selectByKey(dbSession, hotspotKey)
- .filter(t -> t.getType() == RuleType.SECURITY_HOTSPOT.getDbConstant())
- .orElseThrow(() -> new NotFoundException(format("Hotspot '%s' does not exist", hotspotKey)));
- loadAndCheckProject(dbSession, hotspot);
+ IssueDto hotspot = hotspotWsSupport.loadHotspot(dbSession, hotspotKey);
+ hotspotWsSupport.loadAndCheckProject(dbSession, hotspot);
if (needStatusUpdate(hotspot, newStatus, newResolution)) {
String transitionKey = toTransitionKey(newStatus, newResolution);
return resolution;
}
- private void loadAndCheckProject(DbSession dbSession, IssueDto hotspot) {
- String projectUuid = hotspot.getProjectUuid();
- checkArgument(projectUuid != null, "Hotspot '%s' has no project", hotspot.getKee());
-
- ComponentDto project = dbClient.componentDao().selectByUuid(dbSession, projectUuid)
- .orElseThrow(() -> new NotFoundException(format("Project with uuid '%s' does not exist", projectUuid)));
- userSession.checkComponentPermission(UserRole.USER, project);
- }
-
private static boolean needStatusUpdate(IssueDto hotspot, String newStatus, String newResolution) {
return !(hotspot.getStatus().equals(newStatus) && Objects.equals(hotspot.getResolution(), newResolution));
}
private void doTransition(DbSession session, IssueDto issueDto, String transitionKey, @Nullable String comment) {
DefaultIssue defaultIssue = issueDto.toDefaultIssue();
- IssueChangeContext context = IssueChangeContext.createUser(new Date(system2.now()), userSession.getUuid());
+ IssueChangeContext context = hotspotWsSupport.newIssueChangeContext();
transitionService.checkTransitionPermission(transitionKey, defaultIssue);
if (transitionService.doTransition(defaultIssue, context, transitionKey)) {
if (comment != null) {
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.hotspot.ws;
+
+import java.util.Date;
+import org.sonar.api.rules.RuleType;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.issue.IssueChangeContext;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.issue.IssueDto;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.user.UserSession;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.lang.String.format;
+
+public class HotspotWsSupport {
+ private final DbClient dbClient;
+ private final UserSession userSession;
+ private final System2 system2;
+
+ public HotspotWsSupport(DbClient dbClient, UserSession userSession, System2 system2) {
+ this.dbClient = dbClient;
+ this.userSession = userSession;
+ this.system2 = system2;
+ }
+
+ String checkLoggedIn() {
+ return userSession.checkLoggedIn().getUuid();
+ }
+
+ IssueDto loadHotspot(DbSession dbSession, String hotspotKey) {
+ return dbClient.issueDao().selectByKey(dbSession, hotspotKey)
+ .filter(t -> t.getType() == RuleType.SECURITY_HOTSPOT.getDbConstant())
+ .orElseThrow(() -> new NotFoundException(format("Hotspot '%s' does not exist", hotspotKey)));
+ }
+
+ ComponentDto loadAndCheckProject(DbSession dbSession, IssueDto hotspot) {
+ String projectUuid = hotspot.getProjectUuid();
+ checkArgument(projectUuid != null, "Hotspot '%s' has no project", hotspot.getKee());
+
+ ComponentDto project = dbClient.componentDao().selectByUuid(dbSession, projectUuid)
+ .orElseThrow(() -> new NotFoundException(format("Project with uuid '%s' does not exist", projectUuid)));
+ userSession.checkComponentPermission(UserRole.USER, project);
+
+ return project;
+ }
+
+ IssueChangeContext newIssueChangeContext() {
+ return IssueChangeContext.createUser(new Date(system2.now()), checkLoggedIn());
+ }
+}
protected void configureModule() {
add(
HotspotWsResponseFormatter.class,
+ HotspotWsSupport.class,
SearchAction.class,
ShowAction.class,
ChangeStatusAction.class,
+ AddCommentAction.class,
HotspotsWs.class);
}
}
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.rule.RuleKey;
-import org.sonar.api.rules.RuleType;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
-import org.sonar.api.web.UserRole;
import org.sonar.core.util.Uuids;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.server.issue.TextRangeResponseFormatter;
import org.sonar.server.issue.ws.UserResponseFormatter;
import org.sonar.server.security.SecurityStandards;
-import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Common;
import org.sonarqube.ws.Hotspots;
import org.sonarqube.ws.Hotspots.ShowWsResponse;
private static final String PARAM_HOTSPOT_KEY = "hotspot";
private final DbClient dbClient;
- private final UserSession userSession;
+ private final HotspotWsSupport hotspotWsSupport;
private final HotspotWsResponseFormatter responseFormatter;
private final TextRangeResponseFormatter textRangeFormatter;
private final UserResponseFormatter userFormatter;
private final IssueChangelog issueChangelog;
- public ShowAction(DbClient dbClient, UserSession userSession, HotspotWsResponseFormatter responseFormatter,
- TextRangeResponseFormatter textRangeFormatter, UserResponseFormatter userFormatter,
- IssueChangelog issueChangelog) {
+ public ShowAction(DbClient dbClient, HotspotWsSupport hotspotWsSupport,
+ HotspotWsResponseFormatter responseFormatter, TextRangeResponseFormatter textRangeFormatter,
+ UserResponseFormatter userFormatter, IssueChangelog issueChangelog) {
this.dbClient = dbClient;
- this.userSession = userSession;
+ this.hotspotWsSupport = hotspotWsSupport;
this.responseFormatter = responseFormatter;
this.textRangeFormatter = textRangeFormatter;
this.userFormatter = userFormatter;
public void handle(Request request, Response response) throws Exception {
String hotspotKey = request.mandatoryParam(PARAM_HOTSPOT_KEY);
try (DbSession dbSession = dbClient.openSession(false)) {
- IssueDto hotspot = dbClient.issueDao().selectByKey(dbSession, hotspotKey)
- .filter(t -> t.getType() == RuleType.SECURITY_HOTSPOT.getDbConstant())
- .orElseThrow(() -> new NotFoundException(format("Hotspot '%s' does not exist", hotspotKey)));
+ IssueDto hotspot = hotspotWsSupport.loadHotspot(dbSession, hotspotKey);
Components components = loadComponents(dbSession, hotspot);
Users users = loadUsers(dbSession, hotspot);
}
private Components loadComponents(DbSession dbSession, IssueDto hotspot) {
- String projectUuid = hotspot.getProjectUuid();
String componentUuid = hotspot.getComponentUuid();
- checkArgument(projectUuid != null, "Hotspot '%s' has no project", hotspot.getKee());
- checkArgument(componentUuid != null, "Hotspot '%s' has no component", hotspot.getKee());
- ComponentDto project = dbClient.componentDao().selectByUuid(dbSession, projectUuid)
- .orElseThrow(() -> new NotFoundException(format("Project with uuid '%s' does not exist", projectUuid)));
- userSession.checkComponentPermission(UserRole.USER, project);
+ ComponentDto project = hotspotWsSupport.loadAndCheckProject(dbSession, hotspot);
- boolean hotspotOnProject = Objects.equals(projectUuid, componentUuid);
+ checkArgument(componentUuid != null, "Hotspot '%s' has no component", hotspot.getKee());
+ boolean hotspotOnProject = Objects.equals(project.uuid(), componentUuid);
ComponentDto component = hotspotOnProject ? project
: dbClient.componentDao().selectByUuid(dbSession, componentUuid)
.orElseThrow(() -> new NotFoundException(format("Component with uuid '%s' does not exist", componentUuid)));
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.hotspot.ws;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Random;
+import java.util.function.Consumer;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.rules.RuleType;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.issue.DefaultIssue;
+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.ComponentDto;
+import org.sonar.db.issue.IssueDto;
+import org.sonar.db.issue.IssueTesting;
+import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.db.rule.RuleTesting;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.issue.IssueFieldsSetter;
+import org.sonar.server.issue.ws.IssueUpdater;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.WsActionTester;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
+import static org.sonar.api.issue.Issue.RESOLUTION_SAFE;
+import static org.sonar.api.issue.Issue.STATUS_REVIEWED;
+import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
+import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+
+@RunWith(DataProviderRunner.class)
+public class AddCommentActionTest {
+ private static final Random RANDOM = new Random();
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+ @Rule
+ public UserSessionRule userSessionRule = UserSessionRule.standalone();
+
+ private DbClient dbClient = dbTester.getDbClient();
+ private IssueUpdater issueUpdater = mock(IssueUpdater.class);
+ private System2 system2 = mock(System2.class);
+ private IssueFieldsSetter issueFieldsSetter = mock(IssueFieldsSetter.class);
+ private HotspotWsSupport hotspotWsSupport = new HotspotWsSupport(dbClient, userSessionRule, system2);
+ private AddCommentAction underTest = new AddCommentAction(dbClient, hotspotWsSupport, issueFieldsSetter, issueUpdater);
+ private WsActionTester actionTester = new WsActionTester(underTest);
+
+ @Test
+ public void ws_is_internal() {
+ assertThat(actionTester.getDef().isInternal()).isTrue();
+ }
+
+ @Test
+ public void fails_with_UnauthorizedException_if_user_is_anonymous() {
+ userSessionRule.anonymous();
+
+ TestRequest request = actionTester.newRequest();
+
+ assertThatThrownBy(request::execute)
+ .isInstanceOf(UnauthorizedException.class)
+ .hasMessage("Authentication is required");
+ }
+
+ @Test
+ public void fails_with_IAE_if_parameter_hotspot_is_missing() {
+ userSessionRule.logIn();
+ TestRequest request = actionTester.newRequest();
+
+ assertThatThrownBy(request::execute)
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("The 'hotspot' parameter is missing");
+ }
+
+ @Test
+ public void fails_with_IAE_if_parameter_comment_is_missing() {
+ String key = randomAlphabetic(12);
+ userSessionRule.logIn();
+ TestRequest request = actionTester.newRequest()
+ .setParam("hotspot", key);
+
+ assertThatThrownBy(request::execute)
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("The 'comment' parameter is missing");
+ }
+
+ @Test
+ public void fails_with_NotFoundException_if_hotspot_does_not_exist() {
+ String key = randomAlphabetic(12);
+ userSessionRule.logIn();
+ TestRequest request = actionTester.newRequest()
+ .setParam("hotspot", key)
+ .setParam("comment", randomAlphabetic(10));
+
+ assertThatThrownBy(request::execute)
+ .isInstanceOf(NotFoundException.class)
+ .hasMessage("Hotspot '%s' does not exist", key);
+ }
+
+ @Test
+ @UseDataProvider("ruleTypesByHotspot")
+ public void fails_with_NotFoundException_if_issue_is_not_a_hotspot(RuleType ruleType) {
+ ComponentDto project = dbTester.components().insertPublicProject();
+ ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
+ RuleDefinitionDto rule = newRule(ruleType);
+ IssueDto notAHotspot = dbTester.issues().insertIssue(IssueTesting.newIssue(rule, project, file).setType(ruleType));
+ userSessionRule.logIn();
+ TestRequest request = newRequest(notAHotspot, randomAlphabetic(12));
+
+ assertThatThrownBy(request::execute)
+ .isInstanceOf(NotFoundException.class)
+ .hasMessage("Hotspot '%s' does not exist", notAHotspot.getKey());
+ }
+
+ @DataProvider
+ public static Object[][] ruleTypesByHotspot() {
+ return Arrays.stream(RuleType.values())
+ .filter(t -> t != RuleType.SECURITY_HOTSPOT)
+ .map(t -> new Object[] {t})
+ .toArray(Object[][]::new);
+ }
+
+ @Test
+ public void fails_with_ForbiddenException_if_project_is_private_and_not_allowed() {
+ ComponentDto project = dbTester.components().insertPrivateProject();
+ userSessionRule.logIn().registerComponents(project);
+ ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
+ RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
+ IssueDto hotspot = dbTester.issues().insertIssue(newHotspot(project, file, rule));
+ String comment = randomAlphabetic(12);
+ TestRequest request = newRequest(hotspot, comment);
+
+ assertThatThrownBy(request::execute)
+ .isInstanceOf(ForbiddenException.class)
+ .hasMessage("Insufficient privileges");
+ }
+
+ @Test
+ public void succeeds_on_public_project() {
+ ComponentDto project = dbTester.components().insertPublicProject();
+ userSessionRule.logIn().registerComponents(project);
+ ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
+ RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
+ IssueDto hotspot = dbTester.issues().insertIssue(newHotspot(project, file, rule));
+ String comment = randomAlphabetic(12);
+
+ newRequest(hotspot, comment).execute().assertNoContent();
+ }
+
+ @Test
+ public void succeeds_on_private_project_with_permission() {
+ ComponentDto project = dbTester.components().insertPrivateProject();
+ userSessionRule.logIn().registerComponents(project).addProjectPermission(UserRole.USER, project);
+ ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
+ RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
+ IssueDto hotspot = dbTester.issues().insertIssue(newHotspot(project, file, rule));
+ String comment = randomAlphabetic(12);
+
+ newRequest(hotspot, comment).execute().assertNoContent();
+ }
+
+ @Test
+ @UseDataProvider("validStatusAndResolutions")
+ public void persists_comment_if_hotspot_status_changes_and_transition_done(String currentStatus, @Nullable String currentResolution) {
+ long now = RANDOM.nextInt(232_323);
+ when(system2.now()).thenReturn(now);
+ ComponentDto project = dbTester.components().insertPublicProject();
+ userSessionRule.logIn().registerComponents(project);
+ ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
+ RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
+ IssueDto hotspot = dbTester.issues().insertIssue(newHotspot(project, file, rule).setStatus(currentStatus).setResolution(currentResolution));
+ String comment = randomAlphabetic(12);
+
+ newRequest(hotspot, comment).execute().assertNoContent();
+
+ IssueChangeContext issueChangeContext = IssueChangeContext.createUser(new Date(now), userSessionRule.getUuid());
+ ArgumentCaptor<DefaultIssue> defaultIssueCaptor = ArgumentCaptor.forClass(DefaultIssue.class);
+ verify(issueFieldsSetter).addComment(defaultIssueCaptor.capture(), eq(comment), eq(issueChangeContext));
+ verify(issueUpdater).saveIssueAndPreloadSearchResponseData(
+ any(DbSession.class),
+ defaultIssueCaptor.capture(),
+ eq(issueChangeContext),
+ eq(false));
+
+ // because it is mutated by FieldSetter and IssueUpdater, the same object must be passed to all methods
+ List<DefaultIssue> capturedDefaultIssues = defaultIssueCaptor.getAllValues();
+ assertThat(capturedDefaultIssues).hasSize(2);
+ assertThat(capturedDefaultIssues.get(0))
+ .isSameAs(capturedDefaultIssues.get(1));
+ }
+
+ @DataProvider
+ public static Object[][] validStatusAndResolutions() {
+ return new Object[][] {
+ {STATUS_TO_REVIEW, null},
+ {STATUS_REVIEWED, RESOLUTION_FIXED},
+ {STATUS_REVIEWED, RESOLUTION_SAFE}
+ };
+ }
+
+ private static IssueDto newHotspot(ComponentDto project, ComponentDto file, RuleDefinitionDto rule) {
+ return IssueTesting.newIssue(rule, project, file).setType(SECURITY_HOTSPOT);
+ }
+
+ private TestRequest newRequest(IssueDto hotspot, String comment) {
+ return actionTester.newRequest()
+ .setParam("hotspot", hotspot.getKey())
+ .setParam("comment", comment);
+ }
+
+ private RuleDefinitionDto newRule(RuleType ruleType) {
+ return newRule(ruleType, t -> {
+ });
+ }
+
+ private RuleDefinitionDto newRule(RuleType ruleType, Consumer<RuleDefinitionDto> populate) {
+ RuleDefinitionDto ruleDefinition = RuleTesting.newRule()
+ .setType(ruleType);
+ populate.accept(ruleDefinition);
+ dbTester.rules().insert(ruleDefinition);
+ return ruleDefinition;
+ }
+
+}
private IssueUpdater issueUpdater = mock(IssueUpdater.class);
private System2 system2 = mock(System2.class);
private IssueFieldsSetter issueFieldsSetter = mock(IssueFieldsSetter.class);
- private ChangeStatusAction underTest = new ChangeStatusAction(dbClient, userSessionRule, transitionService, issueFieldsSetter, system2, issueUpdater);
+ private HotspotWsSupport hotspotWsSupport = new HotspotWsSupport(dbClient, userSessionRule, system2);
+ private ChangeStatusAction underTest = new ChangeStatusAction(dbClient, hotspotWsSupport, transitionService, issueFieldsSetter, issueUpdater);
private WsActionTester actionTester = new WsActionTester(underTest);
@Test
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new HotspotsWsModule().configure(container);
- assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 5);
+ assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 7);
}
}
private AvatarResolver avatarResolver = new AvatarResolverImpl();
private HotspotWsResponseFormatter responseFormatter = new HotspotWsResponseFormatter(defaultOrganizationProvider);
private IssueChangelog issueChangelog = Mockito.mock(IssueChangelog.class);
+ private HotspotWsSupport hotspotWsSupport = new HotspotWsSupport(dbClient, userSessionRule, System2.INSTANCE);
private UserResponseFormatter userFormatter = new UserResponseFormatter(new AvatarResolverImpl());
private TextRangeResponseFormatter textRangeFormatter = new TextRangeResponseFormatter();
- private ShowAction underTest = new ShowAction(dbClient, userSessionRule, responseFormatter, textRangeFormatter, userFormatter, issueChangelog);
+ private ShowAction underTest = new ShowAction(dbClient, hotspotWsSupport, responseFormatter, textRangeFormatter, userFormatter, issueChangelog);
private WsActionTester actionTester = new WsActionTester(underTest);
@Test