]> source.dussan.org Git - sonarqube.git/commitdiff
Drop UnsuportedException and improve rule search
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Thu, 1 May 2014 11:48:41 +0000 (13:48 +0200)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Thu, 1 May 2014 11:48:41 +0000 (13:48 +0200)
sonar-core/src/main/java/org/sonar/core/db/UnsuportedException.java [deleted file]
sonar-server/src/main/java/org/sonar/server/rule2/RuleDao.java
sonar-server/src/main/java/org/sonar/server/rule2/RuleNormalizer.java
sonar-server/src/main/java/org/sonar/server/rule2/RuleQuery.java
sonar-server/src/main/java/org/sonar/server/rule2/ws/SearchAction.java
sonar-server/src/main/java/org/sonar/server/search/QueryOptions.java
sonar-server/src/main/java/org/sonar/server/ws/WebServiceEngine.java
sonar-server/src/test/java/org/sonar/server/rule2/RuleMediumTest.java [deleted file]
sonar-server/src/test/java/org/sonar/server/rule2/RuleSearchMediumTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/rule2/RuleServiceMediumTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java

diff --git a/sonar-core/src/main/java/org/sonar/core/db/UnsuportedException.java b/sonar-core/src/main/java/org/sonar/core/db/UnsuportedException.java
deleted file mode 100644 (file)
index 6e593f2..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.core.db;
-
-public class UnsuportedException extends RuntimeException {
-
-  public UnsuportedException(String cause) {
-    super(cause);
-  }
-
-}
index 87be472f55a006b6476b31d73c7230114d7f0d2f..7a7fa10f9b036350b0ea2adc960d88571445503b 100644 (file)
@@ -26,7 +26,6 @@ import org.apache.ibatis.session.SqlSession;
 import org.sonar.api.BatchComponent;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.rule.RuleKey;
-import org.sonar.core.db.UnsuportedException;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.MyBatis;
 import org.sonar.core.rule.RuleConstants;
@@ -37,7 +36,6 @@ import org.sonar.core.rule.RuleRuleTagDto;
 import org.sonar.server.db.BaseDao;
 
 import javax.annotation.CheckForNull;
-
 import java.sql.Timestamp;
 import java.util.Collection;
 import java.util.List;
@@ -77,12 +75,12 @@ public class RuleDao extends BaseDao<RuleDto, RuleKey>
 
   @Override
   protected void doDelete(RuleDto item, SqlSession session) {
-    throw new UnsuportedException("Rules cannot be deleted");
+    throw new UnsupportedOperationException("Rules cannot be deleted");
   }
 
   @Override
   protected void doDeleteByKey(RuleKey key, SqlSession session) {
-    throw new UnsuportedException("Rules cannot be deleted");
+    throw new UnsupportedOperationException("Rules cannot be deleted");
   }
 
   public List<RuleDto> selectAll() {
index 27e8a348673aeaf6e29f1c6b33191e7e3cdba6c3..894984a8b3b8ee4c4ec6fd3f111a202865d92c1f 100644 (file)
@@ -37,7 +37,7 @@ public class RuleNormalizer extends BaseNormalizer<RuleDto, RuleKey> {
 
   private RuleDao ruleDao;
 
-  public enum RuleField {
+  public static enum RuleField {
     KEY("key"),
     REPOSITORY("repo"),
     NAME("name"),
index e60fe20540d813e52cd9d31b92b07635fa52c280..258b07d36bfc917b3760f4d97baef0de2ffe9343 100644 (file)
@@ -30,7 +30,38 @@ import java.util.Collection;
 public class RuleQuery {
 
   public static enum SortField {
-    NAME, CREATED_AT
+    KEY(RuleNormalizer.RuleField.KEY),
+    REPOSITORY(RuleNormalizer.RuleField.REPOSITORY),
+    NAME(RuleNormalizer.RuleField.NAME),
+    CREATED_AT(RuleNormalizer.RuleField.CREATED_AT),
+    DESCRIPTION(RuleNormalizer.RuleField.DESCRIPTION),
+    SEVERITY(RuleNormalizer.RuleField.SEVERITY),
+    STATUS(RuleNormalizer.RuleField.STATUS),
+    LANGUAGE(RuleNormalizer.RuleField.LANGUAGE);
+
+    private final RuleNormalizer.RuleField field;
+
+    private SortField(RuleNormalizer.RuleField field) {
+      this.field = field;
+    }
+
+    RuleNormalizer.RuleField field() {
+      return field;
+    }
+
+    @Override
+    public String toString() {
+      return field.toString();
+    }
+
+    /**
+     * Same than {@link #valueOf(String)} but returns <code>null</code> if parameter
+     * is <code>null</code>
+     */
+    @CheckForNull
+    public static SortField valueOfOrNull(@Nullable String s) {
+      return s == null ? null : valueOf(s);
+    }
   }
 
   private String key;
index 25fa57c060a2b6f7bb48357d8fe1b20304015612..e78ec9f345af39bc1de59b620297afcf859b2d67 100644 (file)
@@ -52,9 +52,19 @@ public class SearchAction implements RequestHandler {
   private static final String PARAM_SEVERITIES = "severities";
   private static final String PARAM_STATUSES = "statuses";
   private static final String PARAM_LANGUAGES = "languages";
+  private static final String PARAM_DEBT_CHARACTERISTICS = "debtCharacteristics";
+  private static final String PARAM_HAS_DEBT_CHARACTERISTIC = "hasDebtCharacteristic";
   private static final String PARAM_TAGS = "tags";
+  private static final String PARAM_ALL_OF_TAGS = "allOfTags";
 
 
+  // generic search parameters
+  private static final String PARAM_PAGE = "p";
+  private static final String PARAM_PAGE_SIZE = "ps";
+  private static final String PARAM_FIELDS = "f";
+  private static final String PARAM_SORT = "s";
+  private static final String PARAM_ASCENDING = "asc";
+
   private final RuleService service;
 
   public SearchAction(RuleService service) {
@@ -84,6 +94,11 @@ public class SearchAction implements RequestHandler {
       .setPossibleValues(Severity.ALL)
       .setExampleValue("CRITICAL,BLOCKER");
 
+    action
+      .createParam(PARAM_LANGUAGES)
+      .setDescription("Comma-separated list of languages")
+      .setExampleValue("java,js");
+
     action
       .createParam(PARAM_STATUSES)
       .setDescription("Comma-separated list of status codes")
@@ -91,7 +106,22 @@ public class SearchAction implements RequestHandler {
       .setExampleValue(RuleStatus.READY.toString());
 
     action
-      .createParam("tags")
+      .createParam(PARAM_DEBT_CHARACTERISTICS)
+      .setDescription("Comma-separated list of technical debt characteristics or sub-characteristics")
+      .setExampleValue("RELIABILITY");
+
+    action
+      .createParam(PARAM_HAS_DEBT_CHARACTERISTIC)
+      .setDescription("Filter rules that have a technical debt characteristic")
+      .setPossibleValues("false", "true");
+
+    action
+      .createParam(PARAM_TAGS)
+      .setDescription("Comma-separated list of tags. Returned rules match any of the tags (OR operator)")
+      .setExampleValue("security,java8");
+
+    action
+      .createParam(PARAM_ALL_OF_TAGS)
       .setDescription("Comma-separated list of tags. Returned rules match all the tags (AND operator)")
       .setExampleValue("security,java8");
 
@@ -106,10 +136,34 @@ public class SearchAction implements RequestHandler {
       .setExampleValue("java:Sonar way");
 
     action
-      .createParam("fields")
+      .createParam(PARAM_FIELDS)
       .setDescription("Comma-separated list of the fields to be returned in response. All the fields are returned by default.")
       .setPossibleValues(RuleIndex.PUBLIC_FIELDS)
       .setExampleValue(String.format("%s,%s,%s", RuleNormalizer.RuleField.KEY, RuleNormalizer.RuleField.REPOSITORY, RuleNormalizer.RuleField.LANGUAGE));
+
+    action
+      .createParam(PARAM_PAGE)
+      .setDescription("1-based page number")
+      .setExampleValue("42")
+      .setDefaultValue("1");
+
+    action
+      .createParam(PARAM_PAGE_SIZE)
+      .setDescription("Page size. Must be greater than 0.")
+      .setExampleValue("10")
+      .setDefaultValue("25");
+
+    // TODO limit the fields to sort on + document possible values + default value ?
+    action
+      .createParam(PARAM_SORT)
+      .setDescription("Sort field")
+      .setExampleValue(RuleNormalizer.RuleField.LANGUAGE.key());
+
+    action
+      .createParam(PARAM_ASCENDING)
+      .setDescription("Ascending sort")
+      .setPossibleValues("false", "true")
+      .setDefaultValue("true");
   }
 
   @Override
@@ -120,9 +174,33 @@ public class SearchAction implements RequestHandler {
     query.setRepositories(request.paramAsStrings(PARAM_REPOSITORIES));
     query.setStatuses(toStatuses(request.paramAsStrings(PARAM_STATUSES)));
     query.setLanguages(request.paramAsStrings(PARAM_LANGUAGES));
+    query.setDebtCharacteristics(request.paramAsStrings(PARAM_DEBT_CHARACTERISTICS));
+    query.setHasDebtCharacteristic(request.paramAsBoolean(PARAM_HAS_DEBT_CHARACTERISTIC));
+
+    // TODO move to QueryOptions ?
+    query.setSortField(RuleQuery.SortField.valueOfOrNull(request.param(PARAM_SORT)));
+    query.setAscendingSort(request.paramAsBoolean(PARAM_ASCENDING, true));
+
+    QueryOptions options = new QueryOptions();
+    options.setFieldsToReturn(request.paramAsStrings(PARAM_FIELDS));
+    options.setPage(
+      request.paramAsInt(PARAM_PAGE, 1),
+      request.paramAsInt(PARAM_PAGE_SIZE, 25));
+
+    Results results = service.search(query, options);
+    JsonWriter json = response.newJsonWriter().beginObject();
+    writeStatistics(results, json);
+    writeHits(results, json);
+    json.close();
+  }
+
+  private void writeStatistics(Results results, JsonWriter json) {
+    json.prop("total", results.getTotal());
+    json.prop("time", results.getTime());
+  }
 
-    Results results = service.search(query, new QueryOptions());
-    JsonWriter json = response.newJsonWriter().beginObject().name("hits").beginArray();
+  private void writeHits(Results results, JsonWriter json) {
+    json.name("hits").beginArray();
     for (Hit hit : results.getHits()) {
       json.beginObject();
       for (Map.Entry<String, Object> entry : hit.getFields().entrySet()) {
@@ -132,7 +210,7 @@ public class SearchAction implements RequestHandler {
       json.endObject();
     }
     json.endArray();
-    json.endObject().close();
+    json.endObject();
   }
 
   @CheckForNull
index 19d2719538cc6bacf9bed1161aab0f19bc5f0225..c4a4f242a89cb3e5e2b16cdc6d1ba75c12dcafcf 100644 (file)
@@ -25,6 +25,7 @@ import com.google.common.collect.Sets;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
@@ -36,15 +37,12 @@ public class QueryOptions {
 
   public static final int DEFAULT_OFFSET = 0;
   public static final int DEFAULT_LIMIT = 10;
-  public static final boolean DEFAULT_ASCENDING = true;
 
   private int offset = DEFAULT_OFFSET;
   private int limit = DEFAULT_LIMIT;
-  private boolean ascending = DEFAULT_ASCENDING;
-  private String sortField;
   private Set<String> fieldsToReturn;
 
-  public QueryOptions(){
+  public QueryOptions() {
     fieldsToReturn = new HashSet<String>();
   }
 
@@ -64,6 +62,17 @@ public class QueryOptions {
     return this;
   }
 
+  /**
+   * Set offset and limit according to page approach
+   */
+  public QueryOptions setPage(int page, int pageSize) {
+    Preconditions.checkArgument(page > 0, "Page must be positive");
+    Preconditions.checkArgument(pageSize >= 0, "Page size must be positive or greater than 0");
+    this.offset = (page * pageSize) - pageSize;
+    this.limit = pageSize;
+    return this;
+  }
+
   /**
    * Limit on the number of results to return. Defaults to {@link #DEFAULT_LIMIT}.
    */
@@ -79,35 +88,13 @@ public class QueryOptions {
     return this;
   }
 
-  /**
-   * Is ascending sort ? Defaults to {@link #DEFAULT_ASCENDING}
-   */
-  public boolean isAscending() {
-    return ascending;
-  }
-
-  public QueryOptions setAscending(boolean ascending) {
-    this.ascending = ascending;
-    return this;
-  }
-
-  @CheckForNull
-  public String getSortField() {
-    return sortField;
-  }
-
-  public QueryOptions setSortField(@Nullable String sortField) {
-    this.sortField = sortField;
-    return this;
-  }
-
   @CheckForNull
   public Set<String> getFieldsToReturn() {
     return fieldsToReturn;
   }
 
-  public QueryOptions setFieldsToReturn(@Nullable Set<String> c) {
-    this.fieldsToReturn = c;
+  public QueryOptions setFieldsToReturn(@Nullable Collection<String> c) {
+    this.fieldsToReturn = (c == null ? null : Sets.newHashSet(c));
     return this;
   }
 
@@ -122,6 +109,15 @@ public class QueryOptions {
     return this;
   }
 
+  public QueryOptions addFieldsToReturn(String... c) {
+    if (fieldsToReturn == null) {
+      fieldsToReturn = Sets.newHashSet(c);
+    } else {
+      fieldsToReturn.addAll(Arrays.asList(c));
+    }
+    return this;
+  }
+
   public QueryOptions filterFieldsToReturn(final Set<String> keep) {
     if (fieldsToReturn == null) {
       fieldsToReturn = keep;
index f566c4d75c88563e4ddf1d741b42efa4e7b23363..b46bcd8134eae05b2e880c71a0af4d23a8d011c8 100644 (file)
@@ -126,7 +126,9 @@ public class WebServiceEngine implements ServerComponent, Startable {
       public String param(String key) {
         Param paramDef = action.param(key);
         if (paramDef == null) {
-          throw new BadRequestException(String.format("Parameter '%s' is undefined for action '%s'", key, action.key()));
+          String message = String.format("BUG - parameter '%s' is undefined for action '%s'", key, action.key());
+          LoggerFactory.getLogger(getClass()).error(message);
+          throw new IllegalStateException(message);
         }
         String value = StringUtils.defaultString(super.param(key), paramDef.defaultValue());
 
diff --git a/sonar-server/src/test/java/org/sonar/server/rule2/RuleMediumTest.java b/sonar-server/src/test/java/org/sonar/server/rule2/RuleMediumTest.java
deleted file mode 100644 (file)
index 6a18951..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * 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.rule2;
-
-import com.google.common.collect.Iterables;
-import com.sun.org.apache.xerces.internal.util.SynchronizedSymbolTable;
-import org.junit.After;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.rule.RuleStatus;
-import org.sonar.api.rule.Severity;
-import org.sonar.api.utils.DateUtils;
-import org.sonar.check.Cardinality;
-import org.sonar.core.persistence.DbSession;
-import org.sonar.core.persistence.MyBatis;
-import org.sonar.core.rule.RuleDto;
-import org.sonar.server.search.Hit;
-import org.sonar.server.search.QueryOptions;
-import org.sonar.server.search.Results;
-import org.sonar.server.tester.ServerTester;
-
-import java.util.Arrays;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class RuleMediumTest {
-
-  @ClassRule
-  public static ServerTester tester = new ServerTester();
-
-  @After
-  public void clear_data_store() {
-    tester.clearDataStores();
-  }
-
-  @Test
-  public void insert_in_db_and_index_in_es() {
-    // insert db
-    RuleKey ruleKey = RuleKey.of("javascript", "S001");
-    RuleDao dao = tester.get(RuleDao.class);
-    dao.insert(newRuleDto(ruleKey));
-
-    // verify that rule is persisted in db
-    RuleDto persistedDto = dao.selectByKey(ruleKey);
-    assertThat(persistedDto).isNotNull();
-    assertThat(persistedDto.getId()).isGreaterThanOrEqualTo(0);
-    assertThat(persistedDto.getRuleKey()).isEqualTo(ruleKey.rule());
-    assertThat(persistedDto.getLanguage()).isEqualTo("js");
-
-    // verify that rule is indexed in es
-    RuleIndex index = tester.get(RuleIndex.class);
-    index.refresh();
-
-    Hit hit = index.getByKey(ruleKey);
-    assertThat(hit).isNotNull();
-    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.REPOSITORY.key())).isEqualTo(ruleKey.repository());
-    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.KEY.key())).isEqualTo(ruleKey.rule());
-    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.LANGUAGE.key())).isEqualTo("js");
-    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.NAME.key())).isEqualTo("Rule S001");
-    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.DESCRIPTION.key())).isEqualTo("Description S001");
-    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.STATUS.key())).isEqualTo(RuleStatus.READY.toString());
-  }
-
-  @Test
-  public void search_rule_field_allowed(){
-    String lang = "java";
-
-    RuleDao dao = tester.get(RuleDao.class);
-    dao.insert(newRuleDto(RuleKey.of("javascript", "S001"))
-      .setLanguage(lang));
-
-    RuleService service = tester.get(RuleService.class).refresh();
-
-    RuleQuery query = service.newRuleQuery();
-
-    QueryOptions options = new QueryOptions();
-    options.getFieldsToReturn().add(RuleNormalizer.RuleField.LANGUAGE.key());
-
-    Results results = service.search(query, options);
-
-    assertThat(results.getTotal()).isEqualTo(1);
-    assertThat(results.getHits()).hasSize(1);
-    assertThat(Iterables.getFirst(results.getHits(), null).getFieldAsString("key")).isEqualTo("S001");
-    assertThat(Iterables.getFirst(results.getHits(), null)
-      .getFieldAsString(RuleNormalizer.RuleField.LANGUAGE.key()))
-      .isNotNull();
-    assertThat(Iterables.getFirst(results.getHits(), null)
-      .getFieldAsString(RuleNormalizer.RuleField.LANGUAGE.key()))
-      .isEqualTo(lang);
-  }
-
-  @Test
-  public void search_rule_no_field_selected() {
-    String lang = "java";
-
-    RuleDao dao = tester.get(RuleDao.class);
-    dao.insert(newRuleDto(RuleKey.of("javascript", "S001"))
-      .setLanguage(lang));
-
-    RuleService service = tester.get(RuleService.class).refresh();
-
-    RuleQuery query = service.newRuleQuery();
-
-    QueryOptions options = new QueryOptions();
-
-    Results results = service.search(query, options);
-
-    assertThat(results.getTotal()).isEqualTo(1);
-    assertThat(results.getHits()).hasSize(1);
-    assertThat(Iterables.getFirst(results.getHits(), null).getFieldAsString("key")).isEqualTo("S001");
-    assertThat(Iterables.getFirst(results.getHits(), null)
-      .getFieldAsString(RuleNormalizer.RuleField.LANGUAGE.key()))
-      .isNull();
-  }
-
-  @Test
-  public void search_rule_field_not_allowed(){
-    RuleDao dao = tester.get(RuleDao.class);
-    dao.insert(newRuleDto(RuleKey.of("javascript", "S001"))
-      .setConfigKey("I should not see this"));
-
-    RuleService service = tester.get(RuleService.class).refresh();
-
-    RuleQuery query = service.newRuleQuery();
-    QueryOptions options = new QueryOptions();
-    options.getFieldsToReturn().add(RuleNormalizer.RuleField.INTERNAL_KEY.key());
-
-    Results results = service.search(query, options);
-
-    assertThat(results.getTotal()).isEqualTo(1);
-    assertThat(results.getHits()).hasSize(1);
-    assertThat(Iterables.getFirst(results.getHits(), null).getFieldAsString("key")).isEqualTo("S001");
-    assertThat(Iterables.getFirst(results.getHits(), null)
-      .getFieldAsString(RuleNormalizer.RuleField.INTERNAL_KEY.key()))
-      .isNull();
-  }
-
-  @Test
-  public void search_rules_partial_match() throws InterruptedException {
-    RuleDao dao = tester.get(RuleDao.class);
-    dao.insert(newRuleDto(RuleKey.of("javascript", "S001"))
-    .setName("testing the partial match and matching of rule"));
-
-    RuleService service = tester.get(RuleService.class).refresh();
-
-    RuleQuery query = service.newRuleQuery().setQueryText("test");
-    Results results = service.search(query, new QueryOptions());
-
-    assertThat(results.getTotal()).isEqualTo(1);
-    assertThat(results.getHits()).hasSize(1);
-    assertThat(Iterables.getFirst(results.getHits(), null).getFieldAsString("key")).isEqualTo("S001");
-
-  }
-
-
-  @Test
-  public void search_rules_no_filters() throws InterruptedException {
-    RuleDao dao = tester.get(RuleDao.class);
-    dao.insert(newRuleDto(RuleKey.of("javascript", "S001")));
-
-    RuleService service = tester.get(RuleService.class).refresh();
-
-    RuleQuery query = service.newRuleQuery();
-    Results results = service.search(query, new QueryOptions());
-
-    assertThat(results.getTotal()).isEqualTo(1);
-    assertThat(results.getHits()).hasSize(1);
-    assertThat(Iterables.getFirst(results.getHits(), null).getFieldAsString("key")).isEqualTo("S001");
-
-  }
-
-  @Test
-  public void search_rules_by_repositories() throws InterruptedException {
-    RuleDao dao = tester.get(RuleDao.class);
-    dao.insert(newRuleDto(RuleKey.of("javascript", "S001")));
-    dao.insert(newRuleDto(RuleKey.of("java", "S002")));
-
-    RuleService service = tester.get(RuleService.class).refresh();
-
-    RuleQuery query = service.newRuleQuery().setRepositories(Arrays.asList("findbugs", "java"));
-    Results results = service.search(query, new QueryOptions());
-
-    assertThat(results.getTotal()).isEqualTo(1);
-    assertThat(results.getHits()).hasSize(1);
-    assertThat(Iterables.getFirst(results.getHits(), null).getFieldAsString("key")).isEqualTo("S002");
-  }
-
-  private RuleDto newRuleDto(RuleKey ruleKey) {
-    return new RuleDto()
-      .setRuleKey(ruleKey.rule())
-      .setRepositoryKey(ruleKey.repository())
-      .setName("Rule " + ruleKey.rule())
-      .setDescription("Description " + ruleKey.rule())
-      .setStatus(RuleStatus.READY.toString())
-      .setConfigKey("InternalKey" + ruleKey.rule())
-      .setSeverity(Severity.INFO)
-      .setCardinality(Cardinality.SINGLE)
-      .setLanguage("js")
-      .setRemediationFunction("linear")
-      .setDefaultRemediationFunction("linear_offset")
-      .setRemediationCoefficient("1h")
-      .setDefaultRemediationCoefficient("5d")
-      .setRemediationOffset("5min")
-      .setDefaultRemediationOffset("10h")
-      .setEffortToFixDescription(ruleKey.repository() + "." + ruleKey.rule() + ".effortToFix")
-      .setCreatedAt(DateUtils.parseDate("2013-12-16"))
-      .setUpdatedAt(DateUtils.parseDate("2013-12-17"));
-  }
-}
diff --git a/sonar-server/src/test/java/org/sonar/server/rule2/RuleSearchMediumTest.java b/sonar-server/src/test/java/org/sonar/server/rule2/RuleSearchMediumTest.java
new file mode 100644 (file)
index 0000000..98cbd55
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * 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.rule2;
+
+import com.google.common.collect.Iterables;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.check.Cardinality;
+import org.sonar.core.rule.RuleDto;
+import org.sonar.server.search.Hit;
+import org.sonar.server.search.QueryOptions;
+import org.sonar.server.search.Results;
+import org.sonar.server.tester.ServerTester;
+
+import java.util.Arrays;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class RuleSearchMediumTest {
+
+  @ClassRule
+  public static ServerTester tester = new ServerTester();
+
+  RuleDao dao = tester.get(RuleDao.class);
+  RuleIndex index = tester.get(RuleIndex.class);
+
+  @Before
+  public void clear_data_store() {
+    tester.clearDataStores();
+  }
+
+  @Test
+  @Ignore("TODO")
+  public void return_all_doc_fields_by_default() {
+    dao.insert(newRuleDto(RuleKey.of("javascript", "S001")));
+    index.refresh();
+
+    QueryOptions options = new QueryOptions();
+    Results results = index.search(new RuleQuery(), options);
+
+    assertThat(results.getHits()).hasSize(1);
+    Hit hit = Iterables.getFirst(results.getHits(), null);
+    assertThat(hit.getFields()).hasSize(RuleNormalizer.RuleField.values().length);
+  }
+
+  @Test
+  @Ignore("TODO permanent 'repo' is missing")
+  public void select_doc_fields_to_load() {
+    dao.insert(newRuleDto(RuleKey.of("javascript", "S001")));
+    index.refresh();
+
+    QueryOptions options = new QueryOptions();
+    options.addFieldsToReturn(RuleNormalizer.RuleField.LANGUAGE.key(), RuleNormalizer.RuleField.STATUS.key());
+    Results results = index.search(new RuleQuery(), options);
+
+    assertThat(results.getHits()).hasSize(1);
+    Hit hit = Iterables.getFirst(results.getHits(), null);
+    assertThat(hit.getFields()).hasSize(4);
+
+    // request fields
+    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.LANGUAGE.key())).isEqualTo("js");
+    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.STATUS.key())).isEqualTo(RuleStatus.READY.name());
+
+    // permanent fields
+    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.REPOSITORY.key())).isEqualTo("javascript");
+    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.KEY.key())).isEqualTo("S001");
+  }
+
+  @Test
+  public void search_by_name() throws InterruptedException {
+    dao.insert(newRuleDto(RuleKey.of("javascript", "S001"))
+      .setName("testing the partial match and matching of rule"));
+    index.refresh();
+
+    // substring
+    RuleQuery query = new RuleQuery().setQueryText("test");
+    assertThat(index.search(query, new QueryOptions()).getHits()).hasSize(1);
+
+    // substring
+    query = new RuleQuery().setQueryText("partial match");
+    assertThat(index.search(query, new QueryOptions()).getHits()).hasSize(1);
+
+    // case-insensitive
+    query = new RuleQuery().setQueryText("TESTING");
+    assertThat(index.search(query, new QueryOptions()).getHits()).hasSize(1);
+
+    // not found
+    query = new RuleQuery().setQueryText("not present");
+    assertThat(index.search(query, new QueryOptions()).getHits()).isEmpty();
+  }
+
+  @Test
+  @Ignore("TODO")
+  public void search_by_key_through_query_text() throws InterruptedException {
+    dao.insert(newRuleDto(RuleKey.of("javascript", "S001")));
+    dao.insert(newRuleDto(RuleKey.of("cobol", "S001")));
+    dao.insert(newRuleDto(RuleKey.of("php", "S002")));
+    index.refresh();
+
+    // key
+    RuleQuery query = new RuleQuery().setQueryText("S001");
+    assertThat(index.search(query, new QueryOptions()).getHits()).hasSize(2);
+
+    // partial key does not match
+    query = new RuleQuery().setQueryText("S00");
+    assertThat(index.search(query, new QueryOptions()).getHits()).isEmpty();
+
+    // repo:key -> nice-to-have !
+    query = new RuleQuery().setQueryText("javascript:S001");
+    assertThat(index.search(query, new QueryOptions()).getHits()).hasSize(1);
+  }
+
+  @Test
+  public void search_all_rules() throws InterruptedException {
+    dao.insert(newRuleDto(RuleKey.of("javascript", "S001")));
+    dao.insert(newRuleDto(RuleKey.of("java", "S002")));
+    index.refresh();
+
+    Results results = index.search(new RuleQuery(), new QueryOptions());
+
+    assertThat(results.getTotal()).isEqualTo(2);
+    assertThat(results.getHits()).hasSize(2);
+  }
+
+  @Test
+  public void search_rules_by_any_of_repositories() throws InterruptedException {
+    dao.insert(newRuleDto(RuleKey.of("findbugs", "S001")));
+    dao.insert(newRuleDto(RuleKey.of("pmd", "S002")));
+    index.refresh();
+    
+    RuleQuery query = new RuleQuery().setRepositories(Arrays.asList("checkstyle", "pmd"));
+    Results results = index.search(query, new QueryOptions());
+    assertThat(results.getHits()).hasSize(1);
+    assertThat(Iterables.getFirst(results.getHits(), null).getFieldAsString("key")).isEqualTo("S002");
+
+    // no results
+    query = new RuleQuery().setRepositories(Arrays.asList("checkstyle"));
+    assertThat(index.search(query, new QueryOptions()).getHits()).isEmpty();
+  }
+
+  private RuleDto newRuleDto(RuleKey ruleKey) {
+    return new RuleDto()
+      .setRuleKey(ruleKey.rule())
+      .setRepositoryKey(ruleKey.repository())
+      .setName("Rule " + ruleKey.rule())
+      .setDescription("Description " + ruleKey.rule())
+      .setStatus(RuleStatus.READY.toString())
+      .setConfigKey("InternalKey" + ruleKey.rule())
+      .setSeverity(Severity.INFO)
+      .setCardinality(Cardinality.SINGLE)
+      .setLanguage("js")
+      .setRemediationFunction("linear")
+      .setDefaultRemediationFunction("linear_offset")
+      .setRemediationCoefficient("1h")
+      .setDefaultRemediationCoefficient("5d")
+      .setRemediationOffset("5min")
+      .setDefaultRemediationOffset("10h")
+      .setEffortToFixDescription(ruleKey.repository() + "." + ruleKey.rule() + ".effortToFix")
+      .setCreatedAt(DateUtils.parseDate("2013-12-16"))
+      .setUpdatedAt(DateUtils.parseDate("2013-12-17"));
+  }
+}
diff --git a/sonar-server/src/test/java/org/sonar/server/rule2/RuleServiceMediumTest.java b/sonar-server/src/test/java/org/sonar/server/rule2/RuleServiceMediumTest.java
new file mode 100644 (file)
index 0000000..3bbbd91
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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.rule2;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.check.Cardinality;
+import org.sonar.core.rule.RuleDto;
+import org.sonar.server.search.Hit;
+import org.sonar.server.tester.ServerTester;
+
+import java.util.Collection;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class RuleServiceMediumTest {
+
+  @ClassRule
+  public static ServerTester tester = new ServerTester();
+
+  @Before
+  public void clear_data_store() {
+    tester.clearDataStores();
+  }
+
+  @Test
+  public void insert_in_db_and_index_in_es() {
+    // insert db
+    RuleKey ruleKey = RuleKey.of("javascript", "S001");
+    RuleDao dao = tester.get(RuleDao.class);
+    dao.insert(newRuleDto(ruleKey));
+
+    // verify that rule is persisted in db
+    RuleDto persistedDto = dao.selectByKey(ruleKey);
+    assertThat(persistedDto).isNotNull();
+    assertThat(persistedDto.getId()).isGreaterThanOrEqualTo(0);
+    assertThat(persistedDto.getRuleKey()).isEqualTo(ruleKey.rule());
+    assertThat(persistedDto.getLanguage()).isEqualTo("js");
+
+    // verify that rule is indexed in es
+    RuleIndex index = tester.get(RuleIndex.class);
+    index.refresh();
+
+    Hit hit = index.getByKey(ruleKey);
+    assertThat(hit).isNotNull();
+    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.REPOSITORY.key())).isEqualTo(ruleKey.repository());
+    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.KEY.key())).isEqualTo(ruleKey.rule());
+    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.LANGUAGE.key())).isEqualTo("js");
+    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.NAME.key())).isEqualTo("Rule S001");
+    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.DESCRIPTION.key())).isEqualTo("Description S001");
+    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.STATUS.key())).isEqualTo(RuleStatus.READY.toString());
+    assertThat(hit.getField(RuleNormalizer.RuleField.CREATED_AT.key())).isNotNull();
+    assertThat(hit.getField(RuleNormalizer.RuleField.UPDATED_AT.key())).isNotNull();
+    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.INTERNAL_KEY.key())).isEqualTo("InternalKeyS001");
+    assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.SEVERITY.key())).isEqualTo("INFO");
+//TODO    assertThat((Collection) hit.getField(RuleNormalizer.RuleField.SYSTEM_TAGS.key())).isEmpty();
+//TODO    assertThat((Collection) hit.getField(RuleNormalizer.RuleField.TAGS.key())).isEmpty();
+    assertThat((Boolean) hit.getField(RuleNormalizer.RuleField.TEMPLATE.key())).isFalse();
+  }
+
+  //TODO test delete, update, tags, params
+
+  private RuleDto newRuleDto(RuleKey ruleKey) {
+    return new RuleDto()
+      .setRuleKey(ruleKey.rule())
+      .setRepositoryKey(ruleKey.repository())
+      .setName("Rule " + ruleKey.rule())
+      .setDescription("Description " + ruleKey.rule())
+      .setStatus(RuleStatus.READY.toString())
+      .setConfigKey("InternalKey" + ruleKey.rule())
+      .setSeverity(Severity.INFO)
+      .setCardinality(Cardinality.SINGLE)
+      .setLanguage("js")
+      .setRemediationFunction("linear")
+      .setDefaultRemediationFunction("linear_offset")
+      .setRemediationCoefficient("1h")
+      .setDefaultRemediationCoefficient("5d")
+      .setRemediationOffset("5min")
+      .setDefaultRemediationOffset("10h")
+      .setEffortToFixDescription(ruleKey.repository() + "." + ruleKey.rule() + ".effortToFix")
+      .setCreatedAt(DateUtils.parseDate("2013-12-16"))
+      .setUpdatedAt(DateUtils.parseDate("2013-12-17"));
+  }
+}
index 4434f4705d61999b73f2b6284148fbc4c652bb12..299581ebd21d769f95999409f36667d6df8d1234 100644 (file)
@@ -164,7 +164,7 @@ public class WebServiceEngineTest {
     ServletResponse response = new ServletResponse();
     engine.execute(request, response, "api/system", "fail_with_undeclared_parameter");
 
-    assertThat(response.stream().outputAsString()).isEqualTo("{\"errors\":[{\"msg\":\"Parameter 'unknown' is undefined for action 'fail_with_undeclared_parameter'\"}]}");
+    assertThat(response.stream().outputAsString()).isEqualTo("{\"errors\":[{\"msg\":\"BUG - parameter 'unknown' is undefined for action 'fail_with_undeclared_parameter'\"}]}");
   }
 
   @Test