]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5087 Add WS to find rules for issues UI
authorJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Mon, 3 Mar 2014 17:32:10 +0000 (18:32 +0100)
committerJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Tue, 4 Mar 2014 08:48:18 +0000 (09:48 +0100)
20 files changed:
sonar-server/src/main/java/org/sonar/server/paging/PagedResult.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/paging/Paging.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/paging/PagingResult.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/Paging.java [deleted file]
sonar-server/src/main/java/org/sonar/server/qualityprofile/PagingResult.java [deleted file]
sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRuleLookup.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfiles.java
sonar-server/src/main/java/org/sonar/server/rule/RuleQuery.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/rule/RuleRegistry.java
sonar-server/src/main/java/org/sonar/server/rule/Rules.java
sonar-server/src/main/java/org/sonar/server/rule/ws/RuleSearchWsHandler.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/rule/ws/RulesWs.java
sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRuleLookupTest.java
sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfilesTest.java
sonar-server/src/test/java/org/sonar/server/rule/ws/AddTagsWsHandlerTest.java
sonar-server/src/test/java/org/sonar/server/rule/ws/RemoveTagsWsHandlerTest.java
sonar-server/src/test/java/org/sonar/server/rule/ws/RuleSearchWsHandlerTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/rule/ws/RuleShowWsHandlerTest.java
sonar-server/src/test/java/org/sonar/server/rule/ws/RulesWsTest.java

diff --git a/sonar-server/src/main/java/org/sonar/server/paging/PagedResult.java b/sonar-server/src/main/java/org/sonar/server/paging/PagedResult.java
new file mode 100644 (file)
index 0000000..d960093
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.paging;
+
+import java.util.Collection;
+
+/**
+ * @since 4.3
+ */
+public class PagedResult<T> {
+
+  private Collection<T> results;
+
+  private PagingResult paging;
+
+  public PagedResult(Collection<T> results, PagingResult paging) {
+    this.results = results;
+    this.paging = paging;
+  }
+
+  public Collection<T> results() {
+    return this.results;
+  }
+
+  public PagingResult paging() {
+    return this.paging;
+  }
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/paging/Paging.java b/sonar-server/src/main/java/org/sonar/server/paging/Paging.java
new file mode 100644 (file)
index 0000000..10b2f43
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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.paging;
+
+/**
+ * Heavily inspired by {@link org.sonar.api.utils.Paging}
+ * @since 4.2
+ */
+public class Paging {
+
+  private final int pageSize;
+  private final int pageIndex;
+
+  protected Paging(int pageSize, int pageIndex) {
+    this.pageSize = pageSize;
+    this.pageIndex = pageIndex;
+  }
+
+  /**
+   * Page index, starting with 1.
+   */
+  public int pageIndex() {
+    return pageIndex;
+  }
+
+  /**
+   * Maximum number of items per page. It is greater than 0.
+   */
+  public int pageSize() {
+    return pageSize;
+  }
+
+  public int offset(){
+    return (pageIndex - 1) * pageSize;
+  }
+
+  public static Paging create(int pageSize, int pageIndex) {
+    checkPageSize(pageSize);
+    checkPageIndex(pageIndex);
+    return new Paging(pageSize, pageIndex);
+  }
+
+  protected static void checkPageIndex(int pageIndex) {
+    if (pageIndex<1) {
+      throw new IllegalArgumentException("Page index must be strictly positive. Got " + pageIndex);
+    }
+  }
+
+  protected static void checkPageSize(int pageSize) {
+    if (pageSize<1) {
+      throw new IllegalArgumentException("Page size must be strictly positive. Got " + pageSize);
+    }
+  }
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/paging/PagingResult.java b/sonar-server/src/main/java/org/sonar/server/paging/PagingResult.java
new file mode 100644 (file)
index 0000000..14ec120
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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.paging;
+
+/**
+ * Heavily inspired by {@link org.sonar.api.utils.Paging}
+ * @since 4.2
+ */
+public class PagingResult extends Paging {
+
+  private final long total;
+
+  private PagingResult(int pageSize, int pageIndex, long total) {
+    super(pageSize, pageIndex);
+    this.total = total;
+  }
+
+  /**
+   * Number of pages. It is greater than or equal 0.
+   */
+  public long pages() {
+    long p = total / pageSize();
+    if (total % pageSize() > 0) {
+      p++;
+    }
+    return p;
+  }
+
+  public boolean hasNextPage(){
+    return pageIndex() < pages();
+  }
+
+  /**
+   * Total number of items. It is greater than or equal 0.
+   */
+  public long total() {
+    return total;
+  }
+
+  public static PagingResult create(int pageSize, int pageIndex, long totalItems) {
+    checkPageSize(pageSize);
+    checkPageIndex(pageIndex);
+    checkTotalItems(totalItems);
+
+    return new PagingResult(pageSize, pageIndex, totalItems);
+  }
+
+  protected static void checkTotalItems(long totalItems) {
+    if (totalItems<0) {
+      throw new IllegalArgumentException("Total items must be positive. Got " + totalItems);
+    }
+  }
+}
index 9417925697a0df844708e32a843c30f37bc77656..17ab1030baa98ea7d68c81243c77e3eab6ca4629 100644 (file)
@@ -373,6 +373,7 @@ public final class Platform {
     servicesContainer.addSingleton(RuleRepositories.class);
     servicesContainer.addSingleton(RulesWs.class);
     servicesContainer.addSingleton(RuleShowWsHandler.class);
+    servicesContainer.addSingleton(RuleSearchWsHandler.class);
     servicesContainer.addSingleton(AddTagsWsHandler.class);
     servicesContainer.addSingleton(RemoveTagsWsHandler.class);
 
diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/Paging.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/Paging.java
deleted file mode 100644 (file)
index 2b46486..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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.qualityprofile;
-
-/**
- * Heavily inspired by {@link org.sonar.api.utils.Paging}
- * @since 4.2
- */
-public class Paging {
-
-  private final int pageSize;
-  private final int pageIndex;
-
-  protected Paging(int pageSize, int pageIndex) {
-    this.pageSize = pageSize;
-    this.pageIndex = pageIndex;
-  }
-
-  /**
-   * Page index, starting with 1.
-   */
-  public int pageIndex() {
-    return pageIndex;
-  }
-
-  /**
-   * Maximum number of items per page. It is greater than 0.
-   */
-  public int pageSize() {
-    return pageSize;
-  }
-
-  public int offset(){
-    return (pageIndex - 1) * pageSize;
-  }
-
-  public static Paging create(int pageSize, int pageIndex) {
-    checkPageSize(pageSize);
-    checkPageIndex(pageIndex);
-    return new Paging(pageSize, pageIndex);
-  }
-
-  protected static void checkPageIndex(int pageIndex) {
-    if (pageIndex<1) {
-      throw new IllegalArgumentException("Page index must be strictly positive. Got " + pageIndex);
-    }
-  }
-
-  protected static void checkPageSize(int pageSize) {
-    if (pageSize<1) {
-      throw new IllegalArgumentException("Page size must be strictly positive. Got " + pageSize);
-    }
-  }
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/PagingResult.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/PagingResult.java
deleted file mode 100644 (file)
index aa82b4b..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.qualityprofile;
-
-/**
- * Heavily inspired by {@link org.sonar.api.utils.Paging}
- * @since 4.2
- */
-public class PagingResult extends Paging {
-
-  private final long total;
-
-  private PagingResult(int pageSize, int pageIndex, long total) {
-    super(pageSize, pageIndex);
-    this.total = total;
-  }
-
-  /**
-   * Number of pages. It is greater than or equal 0.
-   */
-  public long pages() {
-    long p = total / pageSize();
-    if (total % pageSize() > 0) {
-      p++;
-    }
-    return p;
-  }
-
-  public boolean hasNextPage(){
-    return pageIndex() < pages();
-  }
-
-  /**
-   * Total number of items. It is greater than or equal 0.
-   */
-  public long total() {
-    return total;
-  }
-
-  public static PagingResult create(int pageSize, int pageIndex, long totalItems) {
-    checkPageSize(pageSize);
-    checkPageIndex(pageIndex);
-    checkTotalItems(totalItems);
-
-    return new PagingResult(pageSize, pageIndex, totalItems);
-  }
-
-  protected static void checkTotalItems(long totalItems) {
-    if (totalItems<0) {
-      throw new IllegalArgumentException("Total items must be positive. Got " + totalItems);
-    }
-  }
-}
index 15c7ba8f3d843a6d9f1b0eb173c9978577fc9748..a68554a45d191ab6b99832a764de4434f0430af7 100644 (file)
@@ -19,6 +19,9 @@
  */
 package org.sonar.server.qualityprofile;
 
+import org.sonar.server.paging.Paging;
+import org.sonar.server.paging.PagingResult;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Maps;
 import org.apache.commons.lang.StringUtils;
index 4442553a8c394f032ad1da9d78bf70c2e7f0b484..3de9aa427ab0bae9f218cbf61646efa90874da4a 100644 (file)
@@ -20,6 +20,8 @@
 
 package org.sonar.server.qualityprofile;
 
+import org.sonar.server.paging.Paging;
+
 import com.google.common.base.Strings;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.component.Component;
diff --git a/sonar-server/src/main/java/org/sonar/server/rule/RuleQuery.java b/sonar-server/src/main/java/org/sonar/server/rule/RuleQuery.java
new file mode 100644 (file)
index 0000000..1c99160
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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;
+
+import org.sonar.server.paging.Paging;
+
+import javax.annotation.Nullable;
+
+/**
+ * @since 4.3
+ */
+public class RuleQuery {
+
+  private static final int DEFAULT_PAGE_SIZE = 25;
+
+  private String key;
+
+  private String query;
+
+  private Paging paging;
+
+  private RuleQuery(@Nullable String key, @Nullable String query, Paging paging) {
+    this.key = key;
+    this.query = query;
+    this.paging = paging;
+  }
+
+  public String key() {
+    return this.key;
+  }
+
+  public String query() {
+    return this.query;
+  }
+
+  public Paging paging() {
+    return this.paging;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+
+    private String key;
+
+    private String query;
+
+    private int pageSize;
+
+    private int page;
+
+    private Builder() {
+      this.page = 0;
+      this.pageSize = DEFAULT_PAGE_SIZE;
+    }
+
+    public Builder withKey(String key) {
+      this.key = key;
+      return this;
+    }
+
+    public Builder withSearchQuery(String query) {
+      this.query = query;
+      return this;
+    }
+
+    public Builder withPageSize(int pageSize) {
+      this.pageSize = pageSize;
+      return this;
+    }
+
+    public Builder withPage(int page) {
+      this.page = page;
+      return this;
+    }
+
+    public RuleQuery build() {
+      return new RuleQuery(key, query, Paging.create(pageSize, page));
+    }
+  }
+}
index f89cf8ebf3f6e0227a165787e1e6b902df96a762..c8570d771e389a596ebbed66c1ef8a105c080ec2 100644 (file)
@@ -20,6 +20,8 @@
 
 package org.sonar.server.rule;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
 import com.google.common.collect.Multimap;
 import org.elasticsearch.ElasticSearchException;
 import org.elasticsearch.common.collect.Lists;
@@ -28,13 +30,17 @@ import org.elasticsearch.common.io.BytesStream;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.index.query.FilterBuilders;
+import org.elasticsearch.search.SearchHit;
 import org.elasticsearch.search.SearchHits;
+import org.elasticsearch.search.sort.SortOrder;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.utils.TimeProfiler;
 import org.sonar.core.rule.*;
 import org.sonar.server.es.ESIndex;
 import org.sonar.server.es.SearchQuery;
 import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.paging.PagedResult;
+import org.sonar.server.paging.PagingResult;
 
 import javax.annotation.CheckForNull;
 
@@ -45,6 +51,7 @@ import java.util.List;
 import java.util.Map;
 
 import static com.google.common.collect.Lists.newArrayList;
+import static org.sonar.api.rules.Rule.STATUS_REMOVED;
 
 /**
  * Fill search index with rules
@@ -97,7 +104,7 @@ public class RuleRegistry {
       searchQuery.searchString(params.remove(PARAM_NAMEORKEY));
     }
     if (!params.containsKey(PARAM_STATUS)) {
-      searchQuery.notField(PARAM_STATUS, org.sonar.api.rules.Rule.STATUS_REMOVED);
+      searchQuery.notField(PARAM_STATUS, STATUS_REMOVED);
     }
 
     for (Map.Entry<String, String> param : params.entrySet()) {
@@ -115,6 +122,25 @@ public class RuleRegistry {
     }
   }
 
+  public PagedResult<Rule> find(RuleQuery query) {
+    SearchHits hits = searchIndex.executeRequest(
+      searchIndex.client().prepareSearch(INDEX_RULES).setTypes(TYPE_RULE)
+        .setPostFilter(
+          FilterBuilders.boolFilter()
+            .must(FilterBuilders.termFilter(RuleDocument.FIELD_NAME, query.query()))
+            .mustNot(FilterBuilders.termFilter(RuleDocument.FIELD_STATUS, STATUS_REMOVED))
+        )
+        .addSort(RuleDocument.FIELD_NAME, SortOrder.ASC)
+        .setSize(query.paging().pageSize())
+        .setFrom(query.paging().offset()));
+
+    Builder<Rule> rulesBuilder = ImmutableList.builder();
+    for (SearchHit hit: hits.hits()) {
+      rulesBuilder.add(RuleDocumentParser.parse(hit.sourceAsMap()));
+    }
+    return new PagedResult<Rule>(rulesBuilder.build(), PagingResult.create(query.paging().pageSize(), query.paging().pageIndex(), hits.getTotalHits()));
+  }
+
   /**
    * Create or update definition of rule identified by <code>ruleId</code>
    *
index 0f9d4731765cd7241622448d4b403531a5456347..4faac5f6ae2762cd1ed56ac53d8fb4cc4edf5291 100644 (file)
@@ -27,6 +27,7 @@ import org.sonar.core.rule.RuleDao;
 import org.sonar.core.rule.RuleDto;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.paging.PagedResult;
 import org.sonar.server.qualityprofile.QProfileValidations;
 import org.sonar.server.user.UserSession;
 import org.sonar.server.util.RubyUtils;
@@ -94,6 +95,10 @@ public class Rules implements ServerExtension {
     return ruleRegistry.findByKey(key);
   }
 
+  public PagedResult<Rule> find(RuleQuery query) {
+    return ruleRegistry.find(query);
+  }
+
   //
   // Rule validation
   //
diff --git a/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleSearchWsHandler.java b/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleSearchWsHandler.java
new file mode 100644 (file)
index 0000000..ed76a8c
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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.api.resources.Language;
+import org.sonar.api.resources.Languages;
+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.utils.text.JsonWriter;
+import org.sonar.server.paging.PagedResult;
+import org.sonar.server.rule.Rule;
+import org.sonar.server.rule.RuleQuery;
+import org.sonar.server.rule.Rules;
+
+import javax.annotation.CheckForNull;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public class RuleSearchWsHandler implements RequestHandler {
+
+  private final Rules rules;
+  private final Languages languages;
+
+  public RuleSearchWsHandler(Rules rules, Languages languages) {
+    this.rules = rules;
+    this.languages = languages;
+  }
+
+  @Override
+  public void handle(Request request, Response response) {
+    final String ruleKeyParam = request.param("k");
+    Collection<Rule> foundRules = Collections.emptyList();
+    boolean hasMore = false;
+    if (ruleKeyParam == null) {
+      final String ruleSearchParam = request.param("s");
+      final int pageSize = request.paramAsInt("ps", 25);
+      final int pageIndex = request.paramAsInt("p", 1);
+      PagedResult<Rule> searchResult = rules.find(RuleQuery.builder()
+          .withSearchQuery(ruleSearchParam)
+          .withPageSize(pageSize)
+          .withPage(pageIndex)
+          .build());
+      foundRules = searchResult.results();
+      hasMore = searchResult.paging().hasNextPage();
+    } else {
+      RuleKey ruleKey = RuleKey.parse(ruleKeyParam);
+      Rule rule = findRule(ruleKey);
+      if (rule != null) {
+        foundRules = Collections.singleton(rule);
+      }
+      hasMore = false;
+    }
+
+    JsonWriter json = response.newJsonWriter();
+    json.beginObject().name("results").beginArray();
+    for(Rule rule: foundRules) {
+      json.beginObject();
+      writeRule(rule, json);
+      json.endObject();
+    }
+    json.endArray().prop("more", hasMore).endObject().close();
+  }
+
+  @CheckForNull
+  private Rule findRule(RuleKey ruleKey) {
+    return rules.findByKey(ruleKey);
+  }
+
+  private void writeRule(Rule rule, JsonWriter json) {
+    String languageName = null;
+    String languageKey = rule.language();
+    if (languageKey != null) {
+      Language language = languages.get(languageKey);
+      if (language != null) {
+        languageName = language.getName();
+      } else {
+        languageName = languageKey;
+      }
+    }
+    json
+      .prop("key", rule.ruleKey().toString())
+      .prop("name", rule.name())
+      .prop("language", languageName)
+    ;
+  }
+}
index b225e1a57f5f45f5cd7a09b3c90932ba961476d2..30f363d37ccf32dddf1cb6bf220daf15b95559bb 100644 (file)
@@ -23,11 +23,13 @@ import org.sonar.api.server.ws.WebService;
 
 public class RulesWs implements WebService {
 
+  private final RuleSearchWsHandler searchHandler;
   private final RuleShowWsHandler showHandler;
   private final AddTagsWsHandler addTagsWsHandler;
   private final RemoveTagsWsHandler removeTagsWsHandler;
 
-  public RulesWs(RuleShowWsHandler showHandler, AddTagsWsHandler addTagsWsHandler, RemoveTagsWsHandler removeTagsWsHandler) {
+  public RulesWs(RuleSearchWsHandler searchHandler, RuleShowWsHandler showHandler, AddTagsWsHandler addTagsWsHandler, RemoveTagsWsHandler removeTagsWsHandler) {
+    this.searchHandler = searchHandler;
     this.showHandler = showHandler;
     this.addTagsWsHandler = addTagsWsHandler;
     this.removeTagsWsHandler = removeTagsWsHandler;
@@ -38,6 +40,15 @@ public class RulesWs implements WebService {
     NewController controller = context.newController("api/rules")
       .setDescription("Coding rules");
 
+    controller.newAction("list")
+      .setDescription("List rules that match the given criteria")
+      .setSince("4.3")
+      .setHandler(searchHandler)
+      .newParam("s", "An optional query that will be matched against rule titles.")
+      .newParam("k", "An optional query that will be matched exactly agains rule keys.")
+      .newParam("ps", "Optional page size (default is 25).")
+      .newParam("p", "Optional page number (default is 0).");
+
     controller.newAction("show")
       .setDescription("Detail of rule")
       .setSince("4.2")
index b1a0a4838dbf7d467ca0b4b636394e3f2cef5f5c..6b8d5b228cfdb21be3037a005a223daa394ddcdc 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.server.qualityprofile;
 
+import org.sonar.server.paging.Paging;
+
 import com.github.tlrx.elasticsearch.test.EsSetup;
 import org.apache.commons.io.IOUtils;
 import org.elasticsearch.client.Requests;
index 56e569d3c896e6a01f20bb31e77cba327cf19211..b14fa1ab1e4faacfa59f2915fbee595762efe6d3 100644 (file)
@@ -20,6 +20,8 @@
 
 package org.sonar.server.qualityprofile;
 
+import org.sonar.server.paging.Paging;
+
 import com.google.common.collect.Maps;
 import org.junit.Before;
 import org.junit.Test;
index 937dc82d929c52f5a11dafe2a48eb1a81f8ba514..158df4e432cc89ceef33b0ed9a3c9a213f8898d1 100644 (file)
@@ -52,7 +52,7 @@ public class AddTagsWsHandlerTest {
 
   @Before
   public void setUp() throws Exception {
-    tester = new WsTester(new RulesWs(mock(RuleShowWsHandler.class), new AddTagsWsHandler(rules), mock(RemoveTagsWsHandler.class)));
+    tester = new WsTester(new RulesWs(mock(RuleSearchWsHandler.class), mock(RuleShowWsHandler.class), new AddTagsWsHandler(rules), mock(RemoveTagsWsHandler.class)));
   }
 
   @Test
index 2e033536d5b00b0f12b99e5b9b37d30eeaf75394..0d3f045de3d75f5907d64ca721d8c4b3390f2ef7 100644 (file)
@@ -52,7 +52,7 @@ public class RemoveTagsWsHandlerTest {
 
   @Before
   public void setUp() throws Exception {
-    tester = new WsTester(new RulesWs(mock(RuleShowWsHandler.class), mock(AddTagsWsHandler.class), new RemoveTagsWsHandler(rules)));
+    tester = new WsTester(new RulesWs(mock(RuleSearchWsHandler.class), mock(RuleShowWsHandler.class), mock(AddTagsWsHandler.class), new RemoveTagsWsHandler(rules)));
   }
 
   @Test
diff --git a/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleSearchWsHandlerTest.java b/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleSearchWsHandlerTest.java
new file mode 100644 (file)
index 0000000..af418c5
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.resources.Language;
+import org.sonar.api.resources.Languages;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.server.ws.WsTester;
+import org.sonar.server.paging.PagedResult;
+import org.sonar.server.paging.PagingResult;
+import org.sonar.server.rule.Rule;
+import org.sonar.server.rule.RuleQuery;
+import org.sonar.server.rule.Rules;
+import org.sonar.server.user.MockUserSession;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class RuleSearchWsHandlerTest {
+
+  @Mock
+  Rules rules;
+
+  @Mock
+  Languages languages;
+
+  Rule.Builder ruleBuilder = new Rule.Builder()
+    .setKey("AvoidCycle")
+    .setRepositoryKey("squid")
+    .setName("Avoid cycle")
+    .setDescription("Avoid cycle between packages")
+    .setLanguage("java");
+
+  WsTester tester;
+
+  @Before
+  public void setUp() throws Exception {
+    tester = new WsTester(new RulesWs(new RuleSearchWsHandler(rules, languages), mock(RuleShowWsHandler.class), mock(AddTagsWsHandler.class), mock(RemoveTagsWsHandler.class)));
+  }
+
+  @Test
+  public void search_rules() throws Exception {
+    final int pageSize = 10;
+    final int pageIndex = 2;
+    Rule rule = ruleBuilder.build();
+
+    when(rules.find(any(RuleQuery.class))).thenReturn(
+      new PagedResult<Rule>(ImmutableList.of(rule), PagingResult.create(pageSize, pageIndex, 1)));
+    Language lang = mock(Language.class);
+    when(lang.getName()).thenReturn("Java");
+    when(languages.get("java")).thenReturn(lang);
+
+    MockUserSession.set();
+    WsTester.TestRequest request = tester.newRequest("list").setParam("ps", "10").setParam("p", "2");
+    request.execute().assertJson("{'more':false,'results':["
+      + "{'key':'squid:AvoidCycle','name':'Avoid cycle','language':'Java'}"
+      + "]}");
+  }
+
+  @Test
+  public void search_rule_by_key() throws Exception {
+    String ruleKey = "squid:AvoidCycle";
+    Rule rule = ruleBuilder.build();
+
+    when(rules.findByKey(RuleKey.parse(ruleKey))).thenReturn(rule);
+    Language lang = mock(Language.class);
+    when(lang.getName()).thenReturn("Java");
+    when(languages.get("java")).thenReturn(lang);
+
+    MockUserSession.set();
+    WsTester.TestRequest request = tester.newRequest("list").setParam("k", ruleKey);
+    request.execute().assertJson("{'more':false,'results':["
+      + "{'key':'squid:AvoidCycle','name':'Avoid cycle','language':'Java'}"
+      + "]}");
+  }
+}
index 5a94be3938799de9315bd807643dbd30e3331775..bfb526c84f29533f42b149a8bd9dfc14f9d36cf3 100644 (file)
@@ -69,7 +69,7 @@ public class RuleShowWsHandlerTest {
 
   @Before
   public void setUp() throws Exception {
-    tester = new WsTester(new RulesWs(new RuleShowWsHandler(rules, ruleFinder, i18n), mock(AddTagsWsHandler.class), mock(RemoveTagsWsHandler.class)));
+    tester = new WsTester(new RulesWs(mock(RuleSearchWsHandler.class), new RuleShowWsHandler(rules, ruleFinder, i18n), mock(AddTagsWsHandler.class), mock(RemoveTagsWsHandler.class)));
   }
 
   @Test
index 9537435bab50deb5753d301906dc558bd0bd6371..d41159a2ddc3d6dcd59cbee15dcc34ebc2d0d6bd 100644 (file)
@@ -32,6 +32,9 @@ import static org.fest.assertions.Assertions.assertThat;
 @RunWith(MockitoJUnitRunner.class)
 public class RulesWsTest {
 
+  @Mock
+  RuleSearchWsHandler searchHandler;
+
   @Mock
   RuleShowWsHandler showHandler;
 
@@ -45,7 +48,7 @@ public class RulesWsTest {
 
   @Before
   public void setUp() {
-    tester = new WsTester(new RulesWs(showHandler, addTagsWsHandler, removeTagsWsHandler));
+    tester = new WsTester(new RulesWs(searchHandler, showHandler, addTagsWsHandler, removeTagsWsHandler));
   }
 
   @Test
@@ -54,7 +57,15 @@ public class RulesWsTest {
     assertThat(controller).isNotNull();
     assertThat(controller.path()).isEqualTo("api/rules");
     assertThat(controller.description()).isNotEmpty();
-    assertThat(controller.actions()).hasSize(3);
+    assertThat(controller.actions()).hasSize(4);
+
+    WebService.Action list = controller.action("list");
+    assertThat(list).isNotNull();
+    assertThat(list.handler()).isNotNull();
+    assertThat(list.since()).isEqualTo("4.3");
+    assertThat(list.isPost()).isFalse();
+    assertThat(list.isInternal()).isFalse();
+    assertThat(list.params()).hasSize(4);
 
     WebService.Action show = controller.action("show");
     assertThat(show).isNotNull();