diff options
13 files changed, 407 insertions, 9 deletions
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Response.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Response.java index b22d92d1455..41e653343f3 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Response.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Response.java @@ -46,4 +46,6 @@ public interface Response { Stream stream(); + void noContent(); + } diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index c11aa773ccf..08a4dd0a259 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -97,9 +97,7 @@ import org.sonar.server.permission.PermissionFinder; import org.sonar.server.plugins.*; import org.sonar.server.qualityprofile.*; import org.sonar.server.rule.*; -import org.sonar.server.rule.ws.RuleShowWsHandler; -import org.sonar.server.rule.ws.RuleTagsWs; -import org.sonar.server.rule.ws.RulesWs; +import org.sonar.server.rule.ws.*; import org.sonar.server.source.SourceService; import org.sonar.server.source.ws.SourcesShowWsHandler; import org.sonar.server.source.ws.SourcesWs; @@ -351,6 +349,8 @@ public final class Platform { servicesContainer.addSingleton(RuleRepositories.class); servicesContainer.addSingleton(RulesWs.class); servicesContainer.addSingleton(RuleShowWsHandler.class); + servicesContainer.addSingleton(AddTagsWsHandler.class); + servicesContainer.addSingleton(RemoveTagsWsHandler.class); // rule tags servicesContainer.addSingleton(ESRuleTags.class); diff --git a/sonar-server/src/main/java/org/sonar/server/rule/ws/AbstractUpdateTagsWsHandler.java b/sonar-server/src/main/java/org/sonar/server/rule/ws/AbstractUpdateTagsWsHandler.java new file mode 100644 index 00000000000..bd5c74d0ff2 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule/ws/AbstractUpdateTagsWsHandler.java @@ -0,0 +1,53 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.rule.ws; + +import com.google.common.collect.Sets; +import org.elasticsearch.common.collect.Lists; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.RequestHandler; +import org.sonar.api.server.ws.Response; +import org.sonar.server.rule.Rule; +import org.sonar.server.rule.Rules; + +import java.util.Set; + +public abstract class AbstractUpdateTagsWsHandler implements RequestHandler { + + private final Rules rules; + + protected AbstractUpdateTagsWsHandler(Rules rules) { + this.rules = rules; + } + + @Override + public void handle(Request request, Response response) { + Rule rule = rules.findByKey(RuleKey.parse(request.requiredParam("key"))); + Set<String> allAdminTags = Sets.newHashSet(rule.adminTags()); + String[] tagsFromRequest = request.requiredParam("tags").split(","); + updateTags(allAdminTags, tagsFromRequest); + rules.updateRuleTags(rule.id(), Lists.newArrayList(allAdminTags)); + + response.noContent(); + } + + protected abstract void updateTags(Set<String> currentTags, String[] tagsFromRequest); +} diff --git a/sonar-server/src/main/java/org/sonar/server/rule/ws/AddTagsWsHandler.java b/sonar-server/src/main/java/org/sonar/server/rule/ws/AddTagsWsHandler.java new file mode 100644 index 00000000000..48c9707db98 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule/ws/AddTagsWsHandler.java @@ -0,0 +1,37 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.rule.ws; + +import org.sonar.server.rule.Rules; + +import java.util.Arrays; +import java.util.Set; + +public class AddTagsWsHandler extends AbstractUpdateTagsWsHandler { + + public AddTagsWsHandler(Rules rules) { + super(rules); + } + + @Override + protected void updateTags(Set<String> currentTags, String[] tagsFromRequest) { + currentTags.addAll(Arrays.asList(tagsFromRequest)); + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/rule/ws/RemoveTagsWsHandler.java b/sonar-server/src/main/java/org/sonar/server/rule/ws/RemoveTagsWsHandler.java new file mode 100644 index 00000000000..c0dd4c6492b --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule/ws/RemoveTagsWsHandler.java @@ -0,0 +1,37 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.rule.ws; + +import org.sonar.server.rule.Rules; + +import java.util.Arrays; +import java.util.Set; + +public class RemoveTagsWsHandler extends AbstractUpdateTagsWsHandler { + + public RemoveTagsWsHandler(Rules rules) { + super(rules); + } + + @Override + protected void updateTags(Set<String> currentTags, String[] tagsFromRequest) { + currentTags.removeAll(Arrays.asList(tagsFromRequest)); + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/rule/ws/RulesWs.java b/sonar-server/src/main/java/org/sonar/server/rule/ws/RulesWs.java index 081e6a6be11..26f9ad15676 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/ws/RulesWs.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/ws/RulesWs.java @@ -27,9 +27,13 @@ import org.sonar.api.server.ws.WebService; public class RulesWs implements WebService { private final RuleShowWsHandler showHandler; + private final AddTagsWsHandler addTagsWsHandler; + private final RemoveTagsWsHandler removeTagsWsHandler; - public RulesWs(RuleShowWsHandler showHandler) { + public RulesWs(RuleShowWsHandler showHandler, AddTagsWsHandler addTagsWsHandler, RemoveTagsWsHandler removeTagsWsHandler) { this.showHandler = showHandler; + this.addTagsWsHandler = addTagsWsHandler; + this.removeTagsWsHandler = removeTagsWsHandler; } @Override @@ -52,6 +56,18 @@ public class RulesWs implements WebService { .setSince("4.2") .setHandler(showHandler); + addTagParams(controller.newAction("add_tags") + .setDescription("Add tags to a rule") + .setSince("4.2") + .setPost(true) + .setHandler(addTagsWsHandler)); + + addTagParams(controller.newAction("remove_tags") + .setDescription("Remove tags from a rule") + .setSince("4.2") + .setPost(true) + .setHandler(removeTagsWsHandler)); + controller.done(); } @@ -61,4 +77,9 @@ public class RulesWs implements WebService { .endObject() .close(); } + + private void addTagParams(final NewAction action) { + action.newParam("key", "Full key of the rule"); + action.newParam("tags", "Comma separated list of tags"); + } } diff --git a/sonar-server/src/main/java/org/sonar/server/ws/ServletResponse.java b/sonar-server/src/main/java/org/sonar/server/ws/ServletResponse.java index cabb16ba9a3..e1bdeb49526 100644 --- a/sonar-server/src/main/java/org/sonar/server/ws/ServletResponse.java +++ b/sonar-server/src/main/java/org/sonar/server/ws/ServletResponse.java @@ -27,6 +27,7 @@ import org.sonar.api.utils.text.XmlWriter; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.OutputStream; @@ -85,6 +86,16 @@ public class ServletResponse implements Response { return this; } + @Override + public void noContent() { + setStatus(204); + try { + this.source.flushBuffer(); + } catch(IOException ioex) { + throw new IllegalStateException("Fail to send 'no content' to client"); + } + } + private class Buffer extends StringWriter { private final String mediaType; diff --git a/sonar-server/src/test/java/org/sonar/server/rule/ws/AddTagsWsHandlerTest.java b/sonar-server/src/test/java/org/sonar/server/rule/ws/AddTagsWsHandlerTest.java new file mode 100644 index 00000000000..937dc82d929 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/rule/ws/AddTagsWsHandlerTest.java @@ -0,0 +1,84 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.rule.ws; + +import com.google.common.collect.ImmutableList; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.ws.WsTester; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.server.rule.Rule; +import org.sonar.server.rule.Rules; +import org.sonar.server.user.MockUserSession; + +import java.util.List; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class AddTagsWsHandlerTest { + + @Mock + Rules rules; + + WsTester tester; + + @Before + public void setUp() throws Exception { + tester = new WsTester(new RulesWs(mock(RuleShowWsHandler.class), new AddTagsWsHandler(rules), mock(RemoveTagsWsHandler.class))); + } + + @Test + public void add_tags() throws Exception { + String ruleKey = "squid:AvoidCycle"; + Rule rule = createStandardRule(); + when(rules.findByKey(RuleKey.of("squid", "AvoidCycle"))).thenReturn(rule); + + MockUserSession.set().setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN); + WsTester.TestRequest request = tester.newRequest("add_tags").setParam("key", ruleKey).setParam("tags", "tag1,tag2"); + request.execute().assertNoContent(); + + ArgumentCaptor<Object> newTagsCaptor = ArgumentCaptor.forClass(Object.class); + verify(rules).updateRuleTags(isA(Integer.class), newTagsCaptor.capture()); + Object newTags = newTagsCaptor.getValue(); + assertThat(newTags).isInstanceOf(List.class); + assertThat((List<String>) newTags).hasSize(4).containsOnly("admin1", "admin2", "tag1", "tag2"); + } + + private Rule create(String repoKey, String key, String name, String description) { + Rule mockRule = mock(Rule.class); + when(mockRule.adminTags()).thenReturn(ImmutableList.of("admin1", "admin2")); + return mockRule; + } + + private Rule createStandardRule() { + return create("xoo", "RuleWithTags", "Rule with tags", "This rule has tags set"); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/rule/ws/RemoveTagsWsHandlerTest.java b/sonar-server/src/test/java/org/sonar/server/rule/ws/RemoveTagsWsHandlerTest.java new file mode 100644 index 00000000000..2e033536d5b --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/rule/ws/RemoveTagsWsHandlerTest.java @@ -0,0 +1,84 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.rule.ws; + +import com.google.common.collect.ImmutableList; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.ws.WsTester; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.server.rule.Rule; +import org.sonar.server.rule.Rules; +import org.sonar.server.user.MockUserSession; + +import java.util.List; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class RemoveTagsWsHandlerTest { + + @Mock + Rules rules; + + WsTester tester; + + @Before + public void setUp() throws Exception { + tester = new WsTester(new RulesWs(mock(RuleShowWsHandler.class), mock(AddTagsWsHandler.class), new RemoveTagsWsHandler(rules))); + } + + @Test + public void add_tags() throws Exception { + String ruleKey = "squid:AvoidCycle"; + Rule rule = createStandardRule(); + when(rules.findByKey(RuleKey.of("squid", "AvoidCycle"))).thenReturn(rule); + + MockUserSession.set().setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN); + WsTester.TestRequest request = tester.newRequest("remove_tags").setParam("key", ruleKey).setParam("tags", "admin2,admin3,polop"); + request.execute().assertNoContent(); + + ArgumentCaptor<Object> newTagsCaptor = ArgumentCaptor.forClass(Object.class); + verify(rules).updateRuleTags(isA(Integer.class), newTagsCaptor.capture()); + Object newTags = newTagsCaptor.getValue(); + assertThat(newTags).isInstanceOf(List.class); + assertThat((List<String>) newTags).hasSize(2).containsOnly("admin1", "admin4"); + } + + private Rule create(String repoKey, String key, String name, String description) { + Rule mockRule = mock(Rule.class); + when(mockRule.adminTags()).thenReturn(ImmutableList.of("admin1", "admin2", "admin3", "admin4")); + return mockRule; + } + + private Rule createStandardRule() { + return create("xoo", "RuleWithTags", "Rule with tags", "This rule has tags set"); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleShowWsHandlerTest.java b/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleShowWsHandlerTest.java index 759f83278df..09ff30706aa 100644 --- a/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleShowWsHandlerTest.java +++ b/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleShowWsHandlerTest.java @@ -56,7 +56,7 @@ public class RuleShowWsHandlerTest { @Before public void setUp() throws Exception { - tester = new WsTester(new RulesWs(new RuleShowWsHandler(rules, i18n))); + tester = new WsTester(new RulesWs(new RuleShowWsHandler(rules, i18n), mock(AddTagsWsHandler.class), mock(RemoveTagsWsHandler.class))); } @Test diff --git a/sonar-server/src/test/java/org/sonar/server/rule/ws/RulesWsTest.java b/sonar-server/src/test/java/org/sonar/server/rule/ws/RulesWsTest.java index 3284293d6f2..17c8b893cfa 100644 --- a/sonar-server/src/test/java/org/sonar/server/rule/ws/RulesWsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/rule/ws/RulesWsTest.java @@ -19,17 +19,34 @@ */ package org.sonar.server.rule.ws; +import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WsTester; import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Mockito.mock; +@RunWith(MockitoJUnitRunner.class) public class RulesWsTest { - RuleShowWsHandler showHandler = mock(RuleShowWsHandler.class); - WsTester tester = new WsTester(new RulesWs(showHandler)); + @Mock + RuleShowWsHandler showHandler; + + @Mock + AddTagsWsHandler addTagsWsHandler; + + @Mock + RemoveTagsWsHandler removeTagsWsHandler; + + WsTester tester; + + @Before + public void setUp() { + tester = new WsTester(new RulesWs(showHandler, addTagsWsHandler, removeTagsWsHandler)); + } @Test public void define_ws() throws Exception { @@ -37,7 +54,7 @@ public class RulesWsTest { assertThat(controller).isNotNull(); assertThat(controller.path()).isEqualTo("api/rules"); assertThat(controller.description()).isNotEmpty(); - assertThat(controller.actions()).hasSize(2); + assertThat(controller.actions()).hasSize(4); WebService.Action search = controller.action("list"); assertThat(search).isNotNull(); @@ -52,6 +69,22 @@ public class RulesWsTest { assertThat(show.since()).isEqualTo("4.2"); assertThat(show.isPost()).isFalse(); assertThat(show.isPrivate()).isFalse(); + + WebService.Action addTags = controller.action("add_tags"); + assertThat(addTags).isNotNull(); + assertThat(addTags.handler()).isNotNull(); + assertThat(addTags.since()).isEqualTo("4.2"); + assertThat(addTags.isPost()).isTrue(); + assertThat(addTags.isPrivate()).isFalse(); + assertThat(addTags.params()).hasSize(2); + + WebService.Action removeTags = controller.action("remove_tags"); + assertThat(removeTags).isNotNull(); + assertThat(removeTags.handler()).isNotNull(); + assertThat(removeTags.since()).isEqualTo("4.2"); + assertThat(removeTags.isPost()).isTrue(); + assertThat(removeTags.isPrivate()).isFalse(); + assertThat(removeTags.params()).hasSize(2); } @Test diff --git a/sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java b/sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java index 1511490a470..b5f6cfdc165 100644 --- a/sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java +++ b/sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java @@ -32,6 +32,7 @@ import org.sonar.api.utils.text.JsonWriter; import org.sonar.api.utils.text.XmlWriter; import javax.annotation.CheckForNull; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -127,6 +128,12 @@ public class WebServiceEngineTest { return this; } + @Override + public void noContent() { + setStatus(204); + IOUtils.closeQuietly(output); + } + public String outputAsString() { return new String(output.toByteArray(), Charsets.UTF_8); } @@ -161,6 +168,16 @@ public class WebServiceEngineTest { } @Test + public void no_content() throws Exception { + InternalRequest request = new SimpleRequest(); + SimpleResponse response = new SimpleResponse(); + engine.execute(request, response, "api/system", "alive"); + + assertThat(response.outputAsString()).isEmpty(); + assertThat(response.status()).isEqualTo(204); + } + + @Test public void bad_controller() throws Exception { InternalRequest request = new SimpleRequest(); SimpleResponse response = new SimpleResponse(); @@ -276,6 +293,13 @@ public class WebServiceEngineTest { throw new IllegalStateException("Unexpected"); } }); + newController.newAction("alive") + .setHandler(new RequestHandler() { + @Override + public void handle(Request request, Response response) { + response.noContent(); + } + }); // parameter "message" is required but not "author" newController.newAction("print") diff --git a/sonar-testing-harness/src/main/java/org/sonar/api/server/ws/WsTester.java b/sonar-testing-harness/src/main/java/org/sonar/api/server/ws/WsTester.java index 6ae58c338cb..664cae76f43 100644 --- a/sonar-testing-harness/src/main/java/org/sonar/api/server/ws/WsTester.java +++ b/sonar-testing-harness/src/main/java/org/sonar/api/server/ws/WsTester.java @@ -26,6 +26,7 @@ import org.sonar.api.utils.text.JsonWriter; import org.sonar.api.utils.text.XmlWriter; import javax.annotation.CheckForNull; + import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; @@ -142,6 +143,12 @@ public class WsTester { this.status = httpStatus; return this; } + + @Override + public void noContent() { + setStatus(204); + IOUtils.closeQuietly(output); + } } @@ -157,6 +164,11 @@ public class WsTester { return this; } + public Result assertNoContent() { + assertStatus(204); + return this; + } + public String outputAsString() { return new String(response.output.toByteArray(), Charsets.UTF_8); } |