import org.sonar.server.rule.ws.ActiveRuleCompleter;
import org.sonar.server.rule.ws.AppAction;
import org.sonar.server.rule.ws.DeleteAction;
+import org.sonar.server.rule.ws.RepositoriesAction;
import org.sonar.server.rule.ws.RuleMapping;
import org.sonar.server.rule.ws.RulesWebService;
import org.sonar.server.rule.ws.SearchAction;
pico.addSingleton(TagsAction.class);
pico.addSingleton(RuleMapping.class);
pico.addSingleton(ActiveRuleCompleter.class);
+ pico.addSingleton(RepositoriesAction.class);
pico.addSingleton(AppAction.class);
// languages
import org.sonar.api.server.debt.DebtModel;
import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
import org.sonar.api.server.ws.Request;
-import org.sonar.api.server.ws.RequestHandler;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.text.JsonWriter;
/**
* @since 4.4
*/
-public class AppAction implements RequestHandler {
+public class AppAction implements RulesAction {
private final Languages languages;
private final RuleRepositories ruleRepositories;
json.endArray();
}
- void define(WebService.NewController controller) {
+ @Override
+ public void define(WebService.NewController controller) {
controller.createAction("app")
.setDescription("Data required for rendering the page 'Coding Rules'")
.setInternal(true)
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rule.Severity;
import org.sonar.api.server.ws.Request;
-import org.sonar.api.server.ws.RequestHandler;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.KeyValueFormat;
/**
* @since 4.4
*/
-public class CreateAction implements RequestHandler {
+public class CreateAction implements RulesAction {
public static final String PARAM_CUSTOM_KEY = "custom_key";
public static final String PARAM_MANUAL_KEY = "manual_key";
this.mapping = mapping;
}
- void define(WebService.NewController controller) {
+ @Override
+ public void define(WebService.NewController controller) {
WebService.NewAction action = controller
.createAction("create")
.setDescription("Create a custom rule or a manual rule")
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.api.server.ws.WebService;
import org.sonar.server.rule.RuleService;
/**
* @since 4.4
*/
-public class DeleteAction implements RequestHandler {
+public class DeleteAction implements RulesAction {
public static final String PARAM_KEY = "key";
this.service = service;
}
- void define(WebService.NewController controller) {
+ @Override
+ public void define(WebService.NewController controller) {
WebService.NewAction action = controller
.createAction("delete")
.setDescription("Delete custom rule or manual rule")
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.io.Resources;
+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.server.ws.WebService.NewAction;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.server.rule.RuleRepositories;
+import org.sonar.server.rule.RuleRepositories.Repository;
+
+import javax.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.regex.Pattern;
+
+/**
+ * @since 5.1
+ */
+public class RepositoriesAction implements RulesAction {
+
+ private static final String MATCH_ALL = ".*";
+ private final RuleRepositories repositories;
+
+ public RepositoriesAction(RuleRepositories repositories) {
+ this.repositories = repositories;
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ String query = request.param("q");
+ String languageKey = request.param("language");
+ int pageSize = request.mandatoryParamAsInt("ps");
+
+ JsonWriter json = response.newJsonWriter().beginObject().name("repositories").beginArray();
+ for (Repository repo : listMatchingRepositories(query, languageKey, pageSize)) {
+ json.beginObject().prop("key", repo.key()).prop("name", repo.name()).prop("language", repo.language()).endObject();
+ }
+ json.endArray().endObject().close();
+ }
+
+ private Collection<Repository> listMatchingRepositories(@Nullable String query, @Nullable String languageKey, int pageSize) {
+ Pattern pattern = Pattern.compile(query == null ? MATCH_ALL : MATCH_ALL + query + MATCH_ALL, Pattern.CASE_INSENSITIVE);
+
+ SortedMap<String, Repository> reposByName = Maps.newTreeMap();
+ Collection<Repository> repos = languageKey == null ? repositories.repositories() : repositories.repositoriesForLang(languageKey);
+
+ for (Repository repo : repos) {
+ if (pattern.matcher(repo.key()).matches() || pattern.matcher(repo.name()).matches()) {
+ reposByName.put(repo.name() + " -- " + repo.language(), repo);
+ }
+ }
+ List<Repository> result = Lists.newArrayList(reposByName.values());
+ if (pageSize > 0 && pageSize < result.size()) {
+ result = result.subList(0, pageSize);
+ }
+ return result;
+ }
+
+ @Override
+ public void define(WebService.NewController controller) {
+ NewAction action = controller.createAction("repositories")
+ .setDescription("List available rule repositories")
+ .setHandler(this)
+ .setResponseExample(Resources.getResource(getClass(), "example-repositories.json"));
+
+ action.createParam("q")
+ .setDescription("A pattern to match repository keys/names against")
+ .setExampleValue("squid");
+ action.createParam("language")
+ .setDescription("A language key; if provided, only repositories for the given language will be returned")
+ .setExampleValue("java");
+ action.createParam("ps")
+ .setDescription("The size of the list to return, 0 for all repositories")
+ .setExampleValue("25")
+ .setDefaultValue("0");
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.WebService;
+
+/**
+ * Marker interface for coding rule related actions
+ * @author jblievremont
+ *
+ */
+interface RulesAction extends RequestHandler {
+
+ void define(WebService.NewController controller);
+}
public class RulesWebService implements WebService {
- private final SearchAction search;
- private final ShowAction show;
- private final TagsAction tags;
- private final CreateAction create;
- private final AppAction app;
- private final UpdateAction update;
- private final DeleteAction delete;
+ private final RulesAction[] actions;
- public RulesWebService(SearchAction search, ShowAction show, TagsAction tags, CreateAction create,
- AppAction app, UpdateAction update, DeleteAction delete) {
- this.search = search;
- this.show = show;
- this.tags = tags;
- this.create = create;
- this.app = app;
- this.update = update;
- this.delete = delete;
+ public RulesWebService(RulesAction... actions) {
+ this.actions = actions;
}
@Override
.createController("api/rules")
.setDescription("Coding rules");
- search.define(controller);
- show.define(controller);
- tags.define(controller);
- app.define(controller);
- update.define(controller);
- create.define(controller);
- delete.define(controller);
+ for (RulesAction action : actions) {
+ action.define(controller);
+ }
controller.done();
}
/**
* @since 4.4
*/
-public class SearchAction extends SearchRequestHandler<RuleQuery, Rule> {
+public class SearchAction extends SearchRequestHandler<RuleQuery, Rule> implements RulesAction {
public static final String PARAM_REPOSITORIES = "repositories";
public static final String PARAM_KEY = "rule_key";
import com.google.common.io.Resources;
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.api.server.ws.WebService;
import org.sonar.api.utils.text.JsonWriter;
/**
* @since 4.4
*/
-public class ShowAction implements RequestHandler {
+public class ShowAction implements RulesAction {
public static final String PARAM_KEY = "key";
public static final String PARAM_ACTIVES = "actives";
this.activeRuleCompleter = activeRuleCompleter;
}
- void define(WebService.NewController controller) {
+ @Override
+ public void define(WebService.NewController controller) {
WebService.NewAction action = controller
.createAction("show")
.setDescription("Get detailed information about a rule")
import com.google.common.io.Resources;
import org.sonar.api.server.ws.Request;
-import org.sonar.api.server.ws.RequestHandler;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.WebService.NewAction;
import java.util.Set;
-public class TagsAction implements RequestHandler {
+public class TagsAction implements RulesAction {
private final RuleService service;
this.service = service;
}
- void define(WebService.NewController controller) {
+ @Override
+ public void define(WebService.NewController controller) {
NewAction action = controller
.createAction("tags")
.setDescription("List rule tags")
import org.sonar.api.server.debt.DebtRemediationFunction;
import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction;
import org.sonar.api.server.ws.Request;
-import org.sonar.api.server.ws.RequestHandler;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.KeyValueFormat;
import org.sonar.server.rule.RuleService;
import org.sonar.server.rule.RuleUpdate;
-public class UpdateAction implements RequestHandler {
+public class UpdateAction implements RulesAction {
public static final String PARAM_KEY = "key";
public static final String PARAM_TAGS = "tags";
this.mapping = mapping;
}
- void define(WebService.NewController controller) {
+ @Override
+ public void define(WebService.NewController controller) {
WebService.NewAction action = controller
.createAction("update")
.setPost(true)
--- /dev/null
+{
+ "repositories": [
+ {"key": "clirr", "name": "Clirr", "language": "java"},
+ {"key": "common-c", "name": "Common SonarQube", "language": "c"},
+ {"key": "common-cpp", "name": "Common SonarQube", "language": "cpp"},
+ {"key": "common-cs", "name": "Common SonarQube", "language": "cs"},
+ {"key": "common-java", "name": "Common SonarQube", "language": "java"},
+ {"key": "common-js", "name": "Common SonarQube", "language": "js"},
+ {"key": "common-objc", "name": "Common SonarQube", "language": "objc"},
+ {"key": "common-php", "name": "Common SonarQube", "language": "php"},
+ {"key": "c-cppcheck", "name": "Cppcheck", "language": "c"},
+ {"key": "cpp-cppcheck", "name": "Cppcheck", "language": "cpp"},
+ {"key": "fb-contrib", "name": "fb-contrib", "language": "java"},
+ {"key": "findbugs", "name": "FindBugs", "language": "java"},
+ {"key": "fxcop", "name": "FxCop / Code Analysis", "language": "cs"},
+ {"key": "resharper-cs", "name": "ReSharper", "language": "cs"},
+ {"key": "resharper-vbnet", "name": "ReSharper", "language": "vbnet"},
+ {"key": "c", "name": "SonarQube", "language": "c"},
+ {"key": "cpp", "name": "SonarQube", "language": "cpp"},
+ {"key": "csharpsquid", "name": "SonarQube", "language": "cs"},
+ {"key": "javascript", "name": "SonarQube", "language": "js"},
+ {"key": "objc", "name": "SonarQube", "language": "objc"},
+ {"key": "php", "name": "SonarQube", "language": "php"},
+ {"key": "squid", "name": "SonarQube", "language": "java"},
+ {"key": "stylecop", "name": "StyleCop", "language": "cs"}
+ ]
+}
@Test
public void should_generate_app_init_info() throws Exception {
AppAction app = new AppAction(languages, ruleRepositories, i18n, debtModel, profileLoader);
- WsTester tester = new WsTester(new RulesWebService(
- new SearchAction(null, null, null), mock(ShowAction.class), mock(TagsAction.class), mock(CreateAction.class),
- app, mock(UpdateAction.class), mock(DeleteAction.class)));
+ WsTester tester = new WsTester(new RulesWebService(app));
MockUserSession.set().setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN);
import org.sonar.server.rule.RuleService;
import org.sonar.server.ws.WsTester;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
@Before
public void setUp() throws Exception {
- tester = new WsTester(new RulesWebService(new SearchAction(null, null, null), mock(ShowAction.class), mock(TagsAction.class), mock(CreateAction.class), mock(AppAction.class),
- mock(UpdateAction.class), new DeleteAction(ruleService)));
+ tester = new WsTester(new RulesWebService(new DeleteAction(ruleService)));
}
@Test
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.server.rule.RuleRepositories;
+import org.sonar.server.ws.WsTester;
+import org.sonar.server.ws.WsTester.TestRequest;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class RepositoriesActionTest {
+
+ private WsTester tester;
+
+ @Mock
+ private RuleRepositories repositories;
+
+ @Before
+ public void setUp() {
+ tester = new WsTester(new RulesWebService(new RepositoriesAction(repositories)));
+
+ RuleRepositories.Repository repo1 = mock(RuleRepositories.Repository.class);
+ when(repo1.key()).thenReturn("xoo");
+ when(repo1.name()).thenReturn("SonarQube");
+ when(repo1.language()).thenReturn("xoo");
+
+ RuleRepositories.Repository repo2 = mock(RuleRepositories.Repository.class);
+ when(repo2.key()).thenReturn("squid");
+ when(repo2.name()).thenReturn("SonarQube");
+ when(repo2.language()).thenReturn("ws");
+
+ RuleRepositories.Repository repo3 = mock(RuleRepositories.Repository.class);
+ when(repo3.key()).thenReturn("common-ws");
+ when(repo3.name()).thenReturn("SonarQube Common");
+ when(repo3.language()).thenReturn("ws");
+
+ when(repositories.repositories()).thenReturn(ImmutableList.of(repo1, repo2, repo3));
+ when(repositories.repositoriesForLang("xoo")).thenReturn(ImmutableList.of(repo1));
+ when(repositories.repositoriesForLang("ws")).thenReturn(ImmutableList.of(repo2, repo3));
+ }
+
+ @Test
+ public void should_list_repositories() throws Exception {
+
+ tester = new WsTester(new RulesWebService(new RepositoriesAction(repositories)));
+
+ newRequest().execute().assertJson(this.getClass(), "repositories.json");
+ newRequest().setParam("language", "xoo").execute().assertJson(this.getClass(), "repositories_xoo.json");
+ newRequest().setParam("language", "ws").execute().assertJson(this.getClass(), "repositories_ws.json");
+ newRequest().setParam("q", "common").execute().assertJson(this.getClass(), "repositories_common.json");
+ newRequest().setParam("q", "squid").execute().assertJson(this.getClass(), "repositories_squid.json");
+ newRequest().setParam("q", "sonar").execute().assertJson(this.getClass(), "repositories.json");
+ newRequest().setParam("ps", "2").execute().assertJson(this.getClass(), "repositories_limited.json");
+ newRequest().setParam("ps", "3").execute().assertJson(this.getClass(), "repositories.json");
+ newRequest().setParam("ps", "100").execute().assertJson(this.getClass(), "repositories.json");
+ }
+
+ protected TestRequest newRequest() {
+ return tester.newGetRequest("api/rules", "repositories");
+ }
+}
WebService.Controller controller = context.controller(API_ENDPOINT);
assertThat(controller).isNotNull();
- assertThat(controller.actions()).hasSize(7);
+ assertThat(controller.actions()).hasSize(8);
assertThat(controller.action(API_SEARCH_METHOD)).isNotNull();
assertThat(controller.action(API_SHOW_METHOD)).isNotNull();
assertThat(controller.action(API_TAGS_METHOD)).isNotNull();
assertThat(controller.action("update")).isNotNull();
assertThat(controller.action("create")).isNotNull();
assertThat(controller.action("delete")).isNotNull();
+ assertThat(controller.action("repositories")).isNotNull();
assertThat(controller.action("app")).isNotNull();
}
--- /dev/null
+{
+ "repositories": [
+ {
+ "key": "squid",
+ "name": "SonarQube",
+ "language": "ws"
+ },
+ {
+ "key": "xoo",
+ "name": "SonarQube",
+ "language": "xoo"
+ },
+ {
+ "key": "common-ws",
+ "name": "SonarQube Common",
+ "language": "ws"
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "repositories": [
+ {
+ "key": "common-ws",
+ "name": "SonarQube Common",
+ "language": "ws"
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "repositories": [
+ {
+ "key": "squid",
+ "name": "SonarQube",
+ "language": "ws"
+ },
+ {
+ "key": "xoo",
+ "name": "SonarQube",
+ "language": "xoo"
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "repositories": [
+ {
+ "key": "squid",
+ "name": "SonarQube",
+ "language": "ws"
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "repositories": [
+ {
+ "key": "squid",
+ "name": "SonarQube",
+ "language": "ws"
+ },
+ {
+ "key": "common-ws",
+ "name": "SonarQube Common",
+ "language": "ws"
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "repositories": [
+ {
+ "key": "xoo",
+ "name": "SonarQube",
+ "language": "xoo"
+ }
+ ]
+}
\ No newline at end of file