aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorJacek <jacek.poreda@sonarsource.com>2020-02-13 11:14:34 +0100
committerSonarTech <sonartech@sonarsource.com>2020-02-21 20:46:18 +0100
commit7421f46c36752c57290af1c53ea2e57e0cfa32fa (patch)
tree5c6199672477ed46cfca2ec9bf4a9955f9a2b433 /server
parentb4545b42dba5949fdea5155b23dbf00ac695574f (diff)
downloadsonarqube-7421f46c36752c57290af1c53ea2e57e0cfa32fa.tar.gz
sonarqube-7421f46c36752c57290af1c53ea2e57e0cfa32fa.zip
SONAR-12922 add edit hotspot comment ws
Diffstat (limited to 'server')
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/AddCommentAction.java2
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/EditCommentAction.java133
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/HotspotsWsModule.java1
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/hotspot/ws/edit-comment-example.json8
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/AddCommentActionTest.java2
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/EditCommentActionTest.java192
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/HotspotsWsModuleTest.java2
7 files changed, 339 insertions, 1 deletions
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/AddCommentAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/AddCommentAction.java
index 4a2be100abc..df698cfbcb2 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/AddCommentAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/AddCommentAction.java
@@ -35,6 +35,7 @@ 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 static final Integer MAXIMUM_COMMENT_LENGTH = 1000;
private final DbClient dbClient;
private final HotspotWsSupport hotspotWsSupport;
@@ -66,6 +67,7 @@ public class AddCommentAction implements HotspotsWsAction {
.setRequired(true);
action.createParam(PARAM_COMMENT)
.setDescription("Comment text.")
+ .setMaximumLength(MAXIMUM_COMMENT_LENGTH)
.setExampleValue("This is safe because user input is validated by the calling code")
.setRequired(true);
}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/EditCommentAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/EditCommentAction.java
new file mode 100644
index 00000000000..06c6023e7fe
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/EditCommentAction.java
@@ -0,0 +1,133 @@
+/*
+ * 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 java.util.Objects;
+import java.util.Optional;
+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.DateUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.issue.IssueChangeDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.markdown.Markdown;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.user.UserSession;
+import org.sonarqube.ws.Common.Comment;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.lang.String.format;
+import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+
+public class EditCommentAction implements HotspotsWsAction {
+ private static final String PARAM_COMMENT = "comment";
+ private static final String PARAM_TEXT = "text";
+ private static final Integer MAXIMUM_COMMENT_LENGTH = 1000;
+
+ private final DbClient dbClient;
+ private final HotspotWsSupport hotspotWsSupport;
+ private final UserSession userSession;
+ private final System2 system2;
+
+ public EditCommentAction(DbClient dbClient, HotspotWsSupport hotspotWsSupport, UserSession userSession, System2 system2) {
+ this.dbClient = dbClient;
+ this.hotspotWsSupport = hotspotWsSupport;
+ this.userSession = userSession;
+ this.system2 = system2;
+ }
+
+ @Override
+ public void define(WebService.NewController controller) {
+ WebService.NewAction action = controller.createAction("edit_comment")
+ .setDescription("Edit a comment.<br/>" +
+ "Requires authentication and the following permission: 'Browse' on the project of the specified hotspot.")
+ .setSince("8.2")
+ .setHandler(this)
+ .setPost(true)
+ .setInternal(true)
+ .setResponseExample(getClass().getResource("edit-comment-example.json"));
+
+ action.createParam(PARAM_COMMENT)
+ .setDescription("Comment key")
+ .setRequired(true)
+ .setExampleValue(UUID_EXAMPLE_01);
+ action.createParam(PARAM_TEXT)
+ .setDescription("Comment text")
+ .setMaximumLength(MAXIMUM_COMMENT_LENGTH)
+ .setRequired(true)
+ .setExampleValue("Safe because it doesn't apply to the context");
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ hotspotWsSupport.checkLoggedIn();
+
+ String commentKey = request.mandatoryParam(PARAM_COMMENT);
+ String text = request.mandatoryParam(PARAM_TEXT);
+
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ IssueChangeDto hotspotComment = getHotspotComment(commentKey, dbSession);
+ validate(dbSession, hotspotComment);
+ editComment(dbSession, hotspotComment, text);
+ Comment commentData = prepareResponse(dbSession, hotspotComment);
+ writeProtobuf(commentData, request, response);
+ }
+ }
+
+ private IssueChangeDto getHotspotComment(String commentKey, DbSession dbSession) {
+ return dbClient.issueChangeDao().selectCommentByKey(dbSession, commentKey)
+ .orElseThrow(() -> new NotFoundException(format("Comment with key '%s' does not exist", commentKey)));
+ }
+
+ private void validate(DbSession dbSession, IssueChangeDto issueChangeDto) {
+ hotspotWsSupport.loadAndCheckProject(dbSession, issueChangeDto.getIssueKey());
+ checkArgument(Objects.equals(issueChangeDto.getUserUuid(), userSession.getUuid()), "You can only edit your own comments");
+ }
+
+ private void editComment(DbSession dbSession, IssueChangeDto hotspotComment, String text) {
+ hotspotComment.setUpdatedAt(system2.now());
+ hotspotComment.setChangeData(text);
+ dbClient.issueChangeDao().update(dbSession, hotspotComment);
+ dbSession.commit();
+ }
+
+ private Comment prepareResponse(DbSession dbSession, IssueChangeDto hotspotComment) {
+ Comment.Builder commentBuilder = Comment.newBuilder();
+ commentBuilder.clear()
+ .setKey(hotspotComment.getKey())
+ .setUpdatable(true)
+ .setCreatedAt(DateUtils.formatDateTime(new Date(hotspotComment.getIssueChangeCreationDate())));
+ getUserByUuid(dbSession, hotspotComment.getUserUuid()).ifPresent(user -> commentBuilder.setLogin(user.getLogin()));
+ String markdown = hotspotComment.getChangeData();
+ commentBuilder
+ .setHtmlText(Markdown.convertToHtml(markdown))
+ .setMarkdown(markdown);
+ return commentBuilder.build();
+ }
+
+ private Optional<UserDto> getUserByUuid(DbSession dbSession, String userUuid) {
+ return Optional.ofNullable(dbClient.userDao().selectByUuid(dbSession, userUuid));
+ }
+}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/HotspotsWsModule.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/HotspotsWsModule.java
index 1979b02e076..a1d0024d93f 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/HotspotsWsModule.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/HotspotsWsModule.java
@@ -33,6 +33,7 @@ public class HotspotsWsModule extends Module {
ChangeStatusAction.class,
AddCommentAction.class,
DeleteCommentAction.class,
+ EditCommentAction.class,
HotspotsWs.class);
}
}
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/hotspot/ws/edit-comment-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/hotspot/ws/edit-comment-example.json
new file mode 100644
index 00000000000..e3f3d00d49f
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/hotspot/ws/edit-comment-example.json
@@ -0,0 +1,8 @@
+{
+ "key": "AXA-7jPwFopJOMlkuOJl",
+ "login": "KK0Odk5YEWrylVRK5l2wWwYkVDY43I",
+ "htmlText": "new comment",
+ "markdown": "new comment",
+ "updatable": true,
+ "createdAt": "2019-01-01T15:22:43+0100"
+}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/AddCommentActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/AddCommentActionTest.java
index 195d04ba75b..185ce575fe7 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/AddCommentActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/AddCommentActionTest.java
@@ -86,6 +86,8 @@ public class AddCommentActionTest {
@Test
public void ws_is_internal() {
assertThat(actionTester.getDef().isInternal()).isTrue();
+ assertThat(actionTester.getDef().isPost()).isTrue();
+ assertThat(actionTester.getDef().param("comment").maximumLength()).isEqualTo(1000);
}
@Test
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/EditCommentActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/EditCommentActionTest.java
new file mode 100644
index 00000000000..c4cd190fb92
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/EditCommentActionTest.java
@@ -0,0 +1,192 @@
+/*
+ * 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.Optional;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.server.ws.WebService.Param;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.issue.IssueChangeDto;
+import org.sonar.db.issue.IssueDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.TestResponse;
+import org.sonar.server.ws.WsActionTester;
+import org.sonarqube.ws.Common;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
+
+public class EditCommentActionTest {
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ @Rule
+ public UserSessionRule userSessionRule = UserSessionRule.standalone();
+
+ private DbClient dbClient = dbTester.getDbClient();
+ private System2 system2 = mock(System2.class);
+ private HotspotWsSupport hotspotWsSupport = new HotspotWsSupport(dbClient, userSessionRule, system2);
+
+ private EditCommentAction underTest = new EditCommentAction(dbClient, hotspotWsSupport, userSessionRule, system2);
+ private WsActionTester actionTester = new WsActionTester(underTest);
+
+ @Test
+ public void verify_ws_def() {
+ assertThat(actionTester.getDef().isInternal()).isTrue();
+ assertThat(actionTester.getDef().isPost()).isTrue();
+
+ Param commentKeyParam = actionTester.getDef().param("comment");
+ assertThat(commentKeyParam).isNotNull();
+ assertThat(commentKeyParam.isRequired()).isTrue();
+
+ Param textParam = actionTester.getDef().param("text");
+ assertThat(textParam).isNotNull();
+ assertThat(textParam.isRequired()).isTrue();
+ assertThat(textParam.maximumLength()).isEqualTo(1000);
+ }
+
+ @Test
+ public void edit_comment_from_hotspot_private_project() {
+ UserDto userEditingOwnComment = dbTester.users().insertUser();
+
+ ComponentDto project = dbTester.components().insertPrivateProject();
+
+ IssueDto hotspot = dbTester.issues().insertHotspot(h -> h.setProject(project));
+ IssueChangeDto comment = dbTester.issues().insertComment(hotspot, userEditingOwnComment, "Some comment");
+
+ assertThat(getHotspotCommentByKey(comment.getKey()))
+ .isNotEmpty();
+
+ userSessionRule.logIn(userEditingOwnComment);
+ userSessionRule.addProjectPermission(UserRole.USER, project);
+
+ TestRequest request = newRequest(comment.getKey(), "new comment");
+
+ Common.Comment modifiedComment = request.executeProtobuf(Common.Comment.class);
+ assertThat(modifiedComment.getKey()).isEqualTo(comment.getKey());
+ assertThat(modifiedComment.getMarkdown()).isEqualTo("new comment");
+ assertThat(modifiedComment.getHtmlText()).isEqualTo("new comment");
+ assertThat(modifiedComment.getLogin()).isEqualTo(userEditingOwnComment.getLogin());
+ }
+
+ @Test
+ public void edit_comment_from_hotspot_public_project() {
+ UserDto userEditingComment = dbTester.users().insertUser();
+
+ ComponentDto project = dbTester.components().insertPublicProject();
+
+ IssueDto hotspot = dbTester.issues().insertHotspot(h -> h.setProject(project));
+ IssueChangeDto comment = dbTester.issues().insertComment(hotspot, userEditingComment, "Some comment");
+
+ userSessionRule.logIn(userEditingComment);
+ userSessionRule.registerComponents(project);
+
+ TestRequest request = newRequest(comment.getKey(), "new comment");
+
+ Common.Comment modifiedComment = request.executeProtobuf(Common.Comment.class);
+ assertThat(modifiedComment.getKey()).isEqualTo(comment.getKey());
+ assertThat(modifiedComment.getMarkdown()).isEqualTo("new comment");
+ assertThat(modifiedComment.getHtmlText()).isEqualTo("new comment");
+ assertThat(modifiedComment.getLogin()).isEqualTo(userEditingComment.getLogin());
+ }
+
+ @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_if_comment_with_provided_key_does_not_exist() {
+ userSessionRule.logIn();
+
+ TestRequest request = newRequest("not-existing-comment-key", "some new comment");
+
+ assertThatThrownBy(request::execute)
+ .isInstanceOf(NotFoundException.class)
+ .hasMessage("Comment with key 'not-existing-comment-key' does not exist");
+ }
+
+ @Test
+ public void fails_if_trying_to_edit_comment_of_another_user_in_private_project() {
+ UserDto userTryingToDelete = dbTester.users().insertUser();
+ UserDto userWithHotspotComment = dbTester.users().insertUser();
+
+ ComponentDto project = dbTester.components().insertPrivateProject();
+
+ IssueDto hotspot = dbTester.issues().insertHotspot(h -> h.setProject(project));
+ IssueChangeDto comment = dbTester.issues().insertComment(hotspot, userWithHotspotComment, "Some comment");
+
+ userSessionRule.logIn(userTryingToDelete);
+ userSessionRule.addProjectPermission(UserRole.USER, project);
+
+ TestRequest request = newRequest(comment.getKey(), "new comment");
+
+ assertThatThrownBy(request::execute)
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("You can only edit your own comments");
+ }
+
+ @Test
+ public void fails_if_trying_to_delete_comment_of_another_user_in_public_project() {
+ UserDto userTryingToEdit = dbTester.users().insertUser();
+ UserDto userWithHotspotComment = dbTester.users().insertUser();
+
+ ComponentDto project = dbTester.components().insertPublicProject();
+
+ IssueDto hotspot = dbTester.issues().insertHotspot(h -> h.setProject(project));
+ IssueChangeDto comment = dbTester.issues().insertComment(hotspot, userWithHotspotComment, "Some comment");
+
+ userSessionRule.logIn(userTryingToEdit)
+ .registerComponents(project);
+
+ TestRequest request = newRequest(comment.getKey(), "new comment");
+
+ assertThatThrownBy(request::execute)
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("You can only edit your own comments");
+ }
+
+ private TestRequest newRequest(String commentKey, String text) {
+ return actionTester.newRequest()
+ .setParam("comment", commentKey)
+ .setParam("text", text);
+ }
+
+ private Optional<IssueChangeDto> getHotspotCommentByKey(String commentKey) {
+ return dbClient.issueChangeDao().selectCommentByKey(dbTester.getSession(), commentKey);
+ }
+}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/HotspotsWsModuleTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/HotspotsWsModuleTest.java
index 099aecbd6a2..a3278771037 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/HotspotsWsModuleTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/HotspotsWsModuleTest.java
@@ -30,7 +30,7 @@ public class HotspotsWsModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new HotspotsWsModule().configure(container);
- assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 9);
+ assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 10);
}
}