]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5007 improve management of active rules in api/rules/search and api/rules/show
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Thu, 22 May 2014 21:07:49 +0000 (23:07 +0200)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Thu, 22 May 2014 21:10:30 +0000 (23:10 +0200)
16 files changed:
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java
sonar-plugin-api/src/test/java/org/sonar/api/server/ws/WebServiceTest.java
sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRuleService.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleNormalizer.java
sonar-server/src/main/java/org/sonar/server/rule2/RuleService.java
sonar-server/src/main/java/org/sonar/server/rule2/index/RuleIndex.java
sonar-server/src/main/java/org/sonar/server/rule2/index/RuleQuery.java
sonar-server/src/main/java/org/sonar/server/rule2/index/RuleResult.java
sonar-server/src/main/java/org/sonar/server/rule2/ws/ActiveRuleCompleter.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/rule2/ws/SearchAction.java
sonar-server/src/main/java/org/sonar/server/rule2/ws/ShowAction.java
sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java
sonar-server/src/test/java/org/sonar/server/rule2/RuleServiceMediumTest.java
sonar-server/src/test/java/org/sonar/server/rule2/index/RuleIndexMediumTest.java

index 5e464a4200f530e5d867320756c3914a418994c4..d656213f30e1f41ec07e58ea8d4c9266ee40643b 100644 (file)
@@ -32,10 +32,13 @@ import org.sonar.api.ServerExtension;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.Immutable;
-
 import java.io.IOException;
 import java.net.URL;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * Defines a web service. Note that contrary to the deprecated {@link org.sonar.api.web.Webservice}
@@ -457,8 +460,8 @@ public interface WebService extends ServerExtension {
     /**
      * @since 4.4
      */
-    public NewParam setExampleValue(@Nullable String s) {
-      this.exampleValue = s;
+    public NewParam setExampleValue(@Nullable Object s) {
+      this.exampleValue = (s != null ? s.toString() : null);
       return this;
     }
 
@@ -500,8 +503,8 @@ public interface WebService extends ServerExtension {
     /**
      * @since 4.4
      */
-    public NewParam setDefaultValue(@Nullable String s) {
-      this.defaultValue = s;
+    public NewParam setDefaultValue(@Nullable Object o) {
+      this.defaultValue = (o != null ? o.toString() : null);
       return this;
     }
 
index 4dab17f0641a263586203e969b0270612816b147..aed3c7d78c637f12d19020ad53c597c546b03d2a 100644 (file)
@@ -21,9 +21,11 @@ package org.sonar.api.server.ws;
 
 import org.apache.commons.lang.StringUtils;
 import org.junit.Test;
+import org.sonar.api.rule.RuleStatus;
 
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.Collection;
 
 import static org.fest.assertions.Assertions.assertThat;
 import static org.fest.assertions.Fail.fail;
@@ -274,6 +276,58 @@ public class WebServiceTest {
     assertThat(action.param("severity").possibleValues()).containsOnly("INFO", "MAJOR", "BLOCKER");
   }
 
+  @Test
+  public void param_metadata_as_objects() {
+    new WebService() {
+      @Override
+      public void define(Context context) {
+        NewController newController = context.createController("api/rule");
+        NewAction create = newController.createAction("create").setHandler(mock(RequestHandler.class));
+        create.createParam("status")
+          .setDefaultValue(RuleStatus.BETA)
+          .setPossibleValues(RuleStatus.BETA, RuleStatus.READY)
+          .setExampleValue(RuleStatus.BETA);
+        create.createParam("max")
+          .setDefaultValue(11)
+          .setPossibleValues(11, 13, 17)
+          .setExampleValue(17);
+        newController.done();
+      }
+    }.define(context);
+
+    WebService.Action action = context.controller("api/rule").action("create");
+    assertThat(action.param("status").defaultValue()).isEqualTo("BETA");
+    assertThat(action.param("status").possibleValues()).containsOnly("BETA", "READY");
+    assertThat(action.param("status").exampleValue()).isEqualTo("BETA");
+    assertThat(action.param("max").defaultValue()).isEqualTo("11");
+    assertThat(action.param("max").possibleValues()).containsOnly("11", "13", "17");
+    assertThat(action.param("max").exampleValue()).isEqualTo("17");
+  }
+
+  @Test
+  public void param_null_metadata() {
+    new WebService() {
+      @Override
+      public void define(Context context) {
+        NewController newController = context.createController("api/rule");
+        NewAction create = newController.createAction("create").setHandler(mock(RequestHandler.class));
+        create.createParam("status")
+          .setDefaultValue(null)
+          .setPossibleValues((Collection) null)
+          .setExampleValue(null);
+        create.createParam("max")
+          .setPossibleValues((String[]) null);
+        newController.done();
+      }
+    }.define(context);
+
+    WebService.Action action = context.controller("api/rule").action("create");
+    assertThat(action.param("status").defaultValue()).isNull();
+    assertThat(action.param("status").possibleValues()).isNull();
+    assertThat(action.param("status").exampleValue()).isNull();
+    assertThat(action.param("max").possibleValues()).isNull();
+  }
+
   @Test
   public void fail_if_duplicated_action_parameters() {
     try {
index 68df4edaf9e2714cefd2dc00db322487ccaeab79..4bd0d16d9b9dd6fd62e262547237f2bb8a4884bb 100644 (file)
@@ -334,6 +334,7 @@ class ServerComponents {
     pico.addSingleton(SetTagsAction.class);
     pico.addSingleton(SetNoteAction.class);
     pico.addSingleton(RuleMapping.class);
+    pico.addSingleton(ActiveRuleCompleter.class);
     pico.addSingleton(org.sonar.server.rule2.ws.AppAction.class);
 
     // measure
index 2801a9a784363b3f9e0f09e045b3599db1d5dc27..d5330cbbdab308047a0c5771711bae320e0f66bc 100644 (file)
@@ -31,6 +31,7 @@ import org.sonar.core.preview.PreviewCache;
 import org.sonar.core.qualityprofile.db.ActiveRuleDto;
 import org.sonar.core.qualityprofile.db.ActiveRuleKey;
 import org.sonar.core.qualityprofile.db.ActiveRuleParamDto;
+import org.sonar.core.qualityprofile.db.QualityProfileKey;
 import org.sonar.core.rule.RuleParamDto;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
@@ -39,6 +40,7 @@ import org.sonar.server.search.IndexClient;
 import org.sonar.server.user.UserSession;
 import org.sonar.server.util.TypeValidations;
 
+import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import java.util.Collection;
 import java.util.List;
@@ -64,6 +66,15 @@ public class ActiveRuleService implements ServerComponent {
     this.previewCache = previewCache;
   }
 
+  @CheckForNull
+  public ActiveRule getByKey(ActiveRuleKey key) {
+    return index.get(ActiveRuleIndex.class).getByKey(key);
+  }
+
+  public List<ActiveRule> findByRuleKey(RuleKey key){
+    return index.get(ActiveRuleIndex.class).findByRule(key);
+  }
+
   /**
    * Activate a rule on a Quality profile. Update configuration (severity/parameters) if the rule is already
    * activated.
@@ -192,8 +203,4 @@ public class ActiveRuleService implements ServerComponent {
       }
     }
   }
-
-  public List<ActiveRule> findByRuleKey(RuleKey ruleKey){
-    return index.get(ActiveRuleIndex.class).findByRule(ruleKey);
-  }
 }
index f8752e5ee63f5139d39d5ef89723d8854a9468da..8a598e448dea4d3b24eef429a2024ec2441d24ba 100644 (file)
@@ -52,7 +52,6 @@ import org.sonar.server.es.ESNode;
 import org.sonar.server.qualityprofile.ActiveRule;
 import org.sonar.server.rule2.index.RuleIndexDefinition;
 import org.sonar.server.search.BaseIndex;
-import org.sonar.server.search.QueryOptions;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -120,7 +119,7 @@ public class ActiveRuleIndex extends BaseIndex<ActiveRule, ActiveRuleDto, Active
   }
 
   @Override
-  public ActiveRule toDoc(Map<String,Object> fields, QueryOptions options) {
+  public ActiveRule toDoc(Map<String, Object> fields) {
     return new ActiveRuleDoc(fields);
   }
 
@@ -140,7 +139,7 @@ public class ActiveRuleIndex extends BaseIndex<ActiveRule, ActiveRuleDto, Active
 
     List<ActiveRule> activeRules = new ArrayList<ActiveRule>();
     for (SearchHit hit : response.getHits()) {
-      activeRules.add(toDoc(hit.getSource(), QueryOptions.DEFAULT));
+      activeRules.add(toDoc(hit.getSource()));
     }
 
     return activeRules;
@@ -156,6 +155,6 @@ public class ActiveRuleIndex extends BaseIndex<ActiveRule, ActiveRuleDto, Active
       .setType(this.getIndexType())
       .setIndex(this.getIndexName())
       .setId(ActiveRuleKey.of(qualityProfileKey, ruleKey).toString())
-      .get().getSource(), QueryOptions.DEFAULT);
+      .get().getSource());
   }
 }
index de1592669b1181c6eb0ce0ae9c57dce941e69d84..3dabe998307d51fe3913474c7fa5e692d751c18b 100644 (file)
@@ -25,7 +25,6 @@ import org.sonar.core.persistence.DbSession;
 import org.sonar.core.qualityprofile.db.ActiveRuleDto;
 import org.sonar.core.qualityprofile.db.ActiveRuleKey;
 import org.sonar.core.qualityprofile.db.ActiveRuleParamDto;
-import org.sonar.core.qualityprofile.db.QualityProfileDao;
 import org.sonar.core.qualityprofile.db.QualityProfileDto;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.search.BaseNormalizer;
index 340ae3e07a6187f21f51f79bcca1b4ca3bb0476a..fa8c8fafcfde86e199ddac610e7349ea8eaeadcd 100644 (file)
@@ -24,11 +24,8 @@ import org.sonar.api.ServerComponent;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.core.persistence.DbSession;
-import org.sonar.core.qualityprofile.db.QualityProfileKey;
 import org.sonar.core.rule.RuleDto;
 import org.sonar.server.db.DbClient;
-import org.sonar.server.qualityprofile.ActiveRule;
-import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
 import org.sonar.server.rule2.index.RuleIndex;
 import org.sonar.server.rule2.index.RuleNormalizer;
 import org.sonar.server.rule2.index.RuleQuery;
@@ -39,7 +36,6 @@ import org.sonar.server.user.UserSession;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import java.util.Date;
-import java.util.List;
 import java.util.Set;
 
 /**
@@ -48,12 +44,10 @@ import java.util.Set;
 public class RuleService implements ServerComponent {
 
   private final RuleIndex index;
-  private final ActiveRuleIndex activeRuleIndex;
   private final DbClient db;
 
-  public RuleService(ActiveRuleIndex activeRuleIndex, RuleIndex index, DbClient db) {
+  public RuleService(RuleIndex index, DbClient db) {
     this.index = index;
-    this.activeRuleIndex = activeRuleIndex;
     this.db = db;
   }
 
@@ -67,30 +61,7 @@ public class RuleService implements ServerComponent {
   }
 
   public RuleResult search(RuleQuery query, QueryOptions options) {
-    RuleResult result = index.search(query, options);
-
-    /** Check for activation */
-    if (query.getActivation() != null && !query.getActivation().isEmpty()) {
-      if (query.getActivation().equalsIgnoreCase("true")) {
-        for (Rule rule : result.getHits()) {
-          if(query.getQProfileKey() == null){
-            throw new IllegalStateException("\"activation=true\" requires a profile key!");
-          }
-          QualityProfileKey qualityProfileKey =  QualityProfileKey.parse(query.getQProfileKey());
-          result.getActiveRules().put(rule.key().toString(),
-            activeRuleIndex.getByRuleKeyAndProfileKey(rule.key(),qualityProfileKey));
-        }
-      } else if (query.getActivation().equalsIgnoreCase("all")) {
-        for (Rule rule : result.getHits()) {
-          List<ActiveRule> activeRules = activeRuleIndex.findByRule(rule.key());
-          for (ActiveRule activeRule : activeRules) {
-            result.getActiveRules().put(rule.key().toString(), activeRule);
-          }
-        }
-      }
-    }
-
-    return result;
+    return index.search(query, options);
   }
 
   /**
@@ -104,11 +75,10 @@ public class RuleService implements ServerComponent {
   /**
    * Set tags for rule.
    *
-   * @param ruleKey  the required key
-   * @param tags     Set of tags. <code>null</code> to remove all tags.
+   * @param ruleKey the required key
+   * @param tags    Set of tags. <code>null</code> to remove all tags.
    */
   public void setTags(RuleKey ruleKey, Set<String> tags) {
-
     checkAdminPermission(UserSession.get());
 
     DbSession dbSession = db.openSession(false);
index 886bf2ec8757a9431f22e2c2726dbd91caeec528..e39e2fe8c4959bc92d2f85cafe336a88063a27e5 100644 (file)
@@ -114,7 +114,8 @@ public class RuleIndex extends BaseIndex<Rule, RuleDto, RuleKey> {
 
     addMatchField(mapping, RuleNormalizer.RuleField.REPOSITORY.key(), "string");
     addMatchField(mapping, RuleNormalizer.RuleField.SEVERITY.key(), "string");
-    addMatchField(mapping, RuleNormalizer.RuleField.STATUS.key(), "string");;
+    addMatchField(mapping, RuleNormalizer.RuleField.STATUS.key(), "string");
+    ;
     addMatchField(mapping, RuleNormalizer.RuleField.LANGUAGE.key(), "string");
 
     mapping.startObject(RuleNormalizer.RuleField.CHARACTERISTIC.key())
@@ -293,27 +294,32 @@ public class RuleIndex extends BaseIndex<Rule, RuleDto, RuleKey> {
     }
 
     /** Implementation of activation query */
-    if (query.getActivation() != null && !query.getActivation().isEmpty()) {
-      if (query.getActivation().equals("false")) {
-        /** these are inactive rules */
-        fb.mustNot(FilterBuilders.hasChildFilter(new ActiveRuleIndexDefinition().getIndexType(),
-          QueryBuilders.matchAllQuery()));
-      } else if (query.getActivation().equals("all")) {
-        /** these are active rules */
+    if (query.getActivation() == Boolean.TRUE) {
+      if (query.getQProfileKey() == null) {
+        // the rules that are activated at least once
         fb.must(FilterBuilders.hasChildFilter(new ActiveRuleIndexDefinition().getIndexType(),
           QueryBuilders.matchAllQuery()));
-      } else if (query.getActivation().equals("true")) {
-        /** these are active rules for a given profile*/
-        if (query.getQProfileKey() == null || query.getQProfileKey().isEmpty()) {
-          throw new IllegalStateException("qProfile is required when \"activation=true\"");
-        }
+      } else {
+        // the rules that are activated on this profile
         fb.must(FilterBuilders.hasChildFilter(new ActiveRuleIndexDefinition().getIndexType(),
           QueryBuilders.termQuery(ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY.key(),
             query.getQProfileKey())
         ));
       }
-    }
+    } else if (query.getActivation() == Boolean.FALSE) {
+      if (query.getQProfileKey() == null) {
+        // the rules that are never activated, on any profile
+        fb.mustNot(FilterBuilders.hasChildFilter(new ActiveRuleIndexDefinition().getIndexType(),
+          QueryBuilders.matchAllQuery()));
 
+      } else {
+        // the rules that are not activated on this profile
+        fb.mustNot(FilterBuilders.hasChildFilter(new ActiveRuleIndexDefinition().getIndexType(),
+          QueryBuilders.termQuery(ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY.key(),
+            query.getQProfileKey())
+        ));
+      }
+    }
 
     if ((query.getLanguages() != null && !query.getLanguages().isEmpty()) ||
       (query.getRepositories() != null && !query.getRepositories().isEmpty()) ||
@@ -322,7 +328,7 @@ public class RuleIndex extends BaseIndex<Rule, RuleDto, RuleKey> {
       (query.getStatuses() != null && !query.getStatuses().isEmpty()) ||
       (query.getKey() != null && !query.getKey().isEmpty()) ||
       (query.getDebtCharacteristics() != null && !query.getDebtCharacteristics().isEmpty()) ||
-      (query.getActivation() != null && !query.getActivation().isEmpty())) {
+      (query.getActivation() != null)) {
       return fb;
     } else {
       return FilterBuilders.matchAllFilter();
@@ -331,7 +337,7 @@ public class RuleIndex extends BaseIndex<Rule, RuleDto, RuleKey> {
 
   protected void setFacets(SearchRequestBuilder query) {
 
-          /* the Lang facet */
+    /* the Lang facet */
     query.addAggregation(AggregationBuilders
       .terms("Languages")
       .field(RuleNormalizer.RuleField.LANGUAGE.key())
@@ -371,7 +377,7 @@ public class RuleIndex extends BaseIndex<Rule, RuleDto, RuleKey> {
 
 
   @Override
-  protected Rule toDoc(Map<String, Object> fields, QueryOptions options) {
+  protected Rule toDoc(Map<String, Object> fields) {
     Preconditions.checkArgument(fields != null, "Cannot construct Rule with null response!!!");
     return new RuleDoc(fields);
   }
@@ -391,9 +397,9 @@ public class RuleIndex extends BaseIndex<Rule, RuleDto, RuleKey> {
 
     SearchResponse esResponse = request.get();
 
-    Terms aggregation = (Terms) esResponse.getAggregations().get(key);
+    Terms aggregation = esResponse.getAggregations().get(key);
 
-    for (Terms.Bucket value : aggregation.getBuckets()){
+    for (Terms.Bucket value : aggregation.getBuckets()) {
       tags.add(value.getKey());
     }
     return tags;
index 722bf243072f8c57d06f06b6d741ab1ea33de4d6..a70a328b56958f0c571053d45536ead9f9906d69 100644 (file)
@@ -70,16 +70,18 @@ public class RuleQuery {
   private Collection<String> severities;
   private Collection<RuleStatus> statuses;
   private Collection<String> tags;
+  private Collection<String> allOfTags;
   private Collection<String> debtCharacteristics;
   private Boolean hasDebtCharacteristic;
   private SortField sortField;
   private boolean ascendingSort = true;
-  private String activation;
+  private Boolean activation;
   private String qProfileKey;
 
 
   /**
    * TODO should not be public
+   *
    * @see org.sonar.server.rule2.RuleService#newRuleQuery()
    */
   public RuleQuery() {
@@ -95,13 +97,13 @@ public class RuleQuery {
     return this;
   }
 
-  public RuleQuery setActivation(String activation) {
+  public RuleQuery setActivation(@Nullable Boolean activation) {
     this.activation = activation;
     return this;
   }
 
   @CheckForNull
-  public String getActivation(){
+  public Boolean getActivation() {
     return this.activation;
   }
 
@@ -180,6 +182,16 @@ public class RuleQuery {
     return this;
   }
 
+  @CheckForNull
+  public Collection<String> getAllOfTags() {
+    return allOfTags;
+  }
+
+  public RuleQuery setAllOfTags(@Nullable Collection<String> tags) {
+    this.allOfTags = tags;
+    return this;
+  }
+
   @CheckForNull
   public Collection<String> getDebtCharacteristics() {
     return debtCharacteristics;
@@ -195,8 +207,8 @@ public class RuleQuery {
     return hasDebtCharacteristic;
   }
 
-  public RuleQuery setHasDebtCharacteristic(@Nullable Boolean hasDebtCharacteristic) {
-    this.hasDebtCharacteristic = hasDebtCharacteristic;
+  public RuleQuery setHasDebtCharacteristic(@Nullable Boolean b) {
+    this.hasDebtCharacteristic = b;
     return this;
   }
 
index d96551f649e15bd9ff055edd4d53e804f604a800..30f394c32438ff6dcf8e3a8cf782267ce48319be 100644 (file)
  */
 package org.sonar.server.rule2.index;
 
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Multimap;
 import org.elasticsearch.action.search.SearchResponse;
-import org.sonar.server.qualityprofile.ActiveRule;
 import org.sonar.server.rule2.Rule;
 import org.sonar.server.search.Result;
 
@@ -31,11 +28,8 @@ import java.util.Map;
 
 public class RuleResult extends Result<Rule> {
 
-  private Multimap<String,ActiveRule> activeRules;
-
   public RuleResult(SearchResponse response) {
     super(response);
-    activeRules = ArrayListMultimap.create();
   }
 
   @Override
@@ -46,8 +40,4 @@ public class RuleResult extends Result<Rule> {
   public Collection<Rule> getRules() {
     return super.getHits();
   }
-
-  public  Multimap<String,ActiveRule> getActiveRules() {
-    return this.activeRules;
-  }
 }
diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/ws/ActiveRuleCompleter.java b/sonar-server/src/main/java/org/sonar/server/rule2/ws/ActiveRuleCompleter.java
new file mode 100644 (file)
index 0000000..1aa41ca
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * 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.ws;
+
+import org.sonar.api.ServerComponent;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.core.qualityprofile.db.ActiveRuleKey;
+import org.sonar.core.qualityprofile.db.QualityProfileKey;
+import org.sonar.server.qualityprofile.ActiveRule;
+import org.sonar.server.qualityprofile.ActiveRuleService;
+import org.sonar.server.rule2.Rule;
+import org.sonar.server.rule2.index.RuleQuery;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Add details about active rules to api/rules/search and api/rules/show
+ * web services.
+ */
+public class ActiveRuleCompleter implements ServerComponent {
+  private final ActiveRuleService service;
+
+  public ActiveRuleCompleter(ActiveRuleService service) {
+    this.service = service;
+  }
+
+  void completeSearch(RuleQuery query, Collection<Rule> rules, JsonWriter json) {
+    json.name("actives").beginObject();
+
+    if (query.getQProfileKey() != null) {
+      // Load details of active rules on the selected profile
+      QualityProfileKey profileKey = QualityProfileKey.parse(query.getQProfileKey());
+      for (Rule rule : rules) {
+        ActiveRule activeRule = service.getByKey(ActiveRuleKey.of(profileKey, rule.key()));
+        if (activeRule != null) {
+          writeActiveRules(rule.key(), Arrays.asList(activeRule), json);
+        }
+      }
+    } else {
+      // Load details of all active rules
+      for (Rule rule : rules) {
+        writeActiveRules(rule.key(), service.findByRuleKey(rule.key()), json);
+      }
+    }
+    json.endObject();
+  }
+
+  void completeShow(Rule rule, JsonWriter json) {
+    json.name("actives").beginArray();
+    for (ActiveRule activeRule : service.findByRuleKey(rule.key())) {
+      writeActiveRule(activeRule, json);
+    }
+    json.endArray();
+  }
+
+  private void writeActiveRules(RuleKey ruleKey, Collection<ActiveRule> activeRules, JsonWriter json) {
+    if (!activeRules.isEmpty()) {
+      json.name(ruleKey.toString());
+      json.beginArray();
+      for (ActiveRule activeRule : activeRules) {
+        writeActiveRule(activeRule, json);
+      }
+      json.endArray();
+    }
+  }
+
+  private void writeActiveRule(ActiveRule activeRule, JsonWriter json) {
+    json
+      .beginObject()
+      .prop("qProfile", activeRule.key().qProfile().toString())
+      .prop("inherit", activeRule.inheritance().toString())
+      .prop("severity", activeRule.severity());
+    if (activeRule.parentKey() != null) {
+      json.prop("parent", activeRule.parentKey().toString());
+    }
+    json.name("params").beginArray();
+    for (Map.Entry<String, String> param : activeRule.params().entrySet()) {
+      json
+        .beginObject()
+        .prop("key", param.getKey())
+        .prop("value", param.getValue())
+        .endObject();
+    }
+    json.endArray().endObject();
+  }
+}
index 3db1c619ca401c2bf02aa919a6ce16c69cce8e9e..13f755c6ee6e9f53e59d35344cf1cf829f273045 100644 (file)
@@ -28,7 +28,6 @@ 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;
-import org.sonar.server.qualityprofile.ActiveRule;
 import org.sonar.server.rule2.Rule;
 import org.sonar.server.rule2.RuleService;
 import org.sonar.server.rule2.index.RuleDoc;
@@ -47,23 +46,25 @@ import java.util.Map;
  */
 public class SearchAction implements RequestHandler {
 
-  private static final String PARAM_REPOSITORIES = "repositories";
-  private static final String PARAM_ACTIVATION = "activation";
-  private static final String PARAM_QPROFILE = "qprofile";
-  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 = "debt_characteristics";
-  private static final String PARAM_HAS_DEBT_CHARACTERISTIC = "has_debt_characteristic";
-  private static final String PARAM_TAGS = "tags";
-  private static final String PARAM_ALL_OF_TAGS = "all_of_tags";
-  private static final String PARAM_FACETS = "facets";
-
-  private final RuleService service;
+  public static final String PARAM_REPOSITORIES = "repositories";
+  public static final String PARAM_ACTIVATION = "activation";
+  public static final String PARAM_QPROFILE = "qprofile";
+  public static final String PARAM_SEVERITIES = "severities";
+  public static final String PARAM_STATUSES = "statuses";
+  public static final String PARAM_LANGUAGES = "languages";
+  public static final String PARAM_DEBT_CHARACTERISTICS = "debt_characteristics";
+  public static final String PARAM_HAS_DEBT_CHARACTERISTIC = "has_debt_characteristic";
+  public static final String PARAM_TAGS = "tags";
+  public static final String PARAM_ALL_OF_TAGS = "all_of_tags";
+  public static final String PARAM_FACETS = "facets";
+
+  private final RuleService ruleService;
+  private final ActiveRuleCompleter activeRuleCompleter;
   private final RuleMapping mapping;
 
-  public SearchAction(RuleService service, RuleMapping mapping) {
-    this.service = service;
+  public SearchAction(RuleService service, ActiveRuleCompleter activeRuleCompleter, RuleMapping mapping) {
+    this.ruleService = service;
+    this.activeRuleCompleter = activeRuleCompleter;
     this.mapping = mapping;
   }
 
@@ -97,7 +98,7 @@ public class SearchAction implements RequestHandler {
     action
       .createParam(SearchOptions.PARAM_TEXT_QUERY)
       .setDescription("UTF-8 search query")
-      .setExampleValue("null pointer");
+      .setExampleValue("xpath");
 
     action
       .createParam(PARAM_REPOSITORIES)
@@ -119,7 +120,7 @@ public class SearchAction implements RequestHandler {
       .createParam(PARAM_STATUSES)
       .setDescription("Comma-separated list of status codes")
       .setPossibleValues(RuleStatus.values())
-      .setExampleValue(RuleStatus.READY.toString());
+      .setExampleValue(RuleStatus.READY);
 
     action
       .createParam(PARAM_DEBT_CHARACTERISTICS)
@@ -142,15 +143,15 @@ public class SearchAction implements RequestHandler {
       .setExampleValue("security,java8");
 
     action
-      .createParam(PARAM_QPROFILE)
-      .setDescription("Key of Quality profile")
-      .setExampleValue("java:Sonar way");
+      .createParam(PARAM_ACTIVATION)
+      .setDescription("TODO")
+      .setBooleanPossibleValues();
 
     action
-      .createParam(PARAM_ACTIVATION)
-      .setDescription("Used only if 'qprofile' is set")
-      .setExampleValue("java:Sonar way")
-      .setPossibleValues("false", "true", "all");
+      .createParam(PARAM_QPROFILE)
+      .setDescription("Key of Quality profile to filter on. Used only if the parameter '" +
+        PARAM_ACTIVATION + "' is set.")
+      .setExampleValue("java:Sonar way");
 
     // TODO limit the fields to sort on + document possible values + default value ?
     action
@@ -162,7 +163,7 @@ public class SearchAction implements RequestHandler {
       .createParam(SearchOptions.PARAM_ASCENDING)
       .setDescription("Ascending sort")
       .setBooleanPossibleValues()
-      .setDefaultValue("true");
+      .setDefaultValue(true);
   }
 
   @Override
@@ -172,23 +173,22 @@ public class SearchAction implements RequestHandler {
     QueryOptions queryOptions = mapping.newQueryOptions(searchOptions);
     queryOptions.setFacet(request.mandatoryParamAsBoolean(PARAM_FACETS));
 
-    RuleResult results = service.search(query, queryOptions);
+    RuleResult results = ruleService.search(query, queryOptions);
 
     JsonWriter json = response.newJsonWriter().beginObject();
     searchOptions.writeStatistics(json, results);
     writeRules(results, json, searchOptions);
     if (searchOptions.hasField("actives")) {
-      writeActiveRules(results, json);
+      activeRuleCompleter.completeSearch(query, results.getRules(), json);
     }
     if (queryOptions.isFacet()) {
-
       writeFacets(results, json);
     }
     json.endObject().close();
   }
 
   private RuleQuery createRuleQuery(Request request) {
-    RuleQuery query = service.newRuleQuery();
+    RuleQuery query = ruleService.newRuleQuery();
     query.setQueryText(request.param(SearchOptions.PARAM_TEXT_QUERY));
     query.setSeverities(request.paramAsStrings(PARAM_SEVERITIES));
     query.setRepositories(request.paramAsStrings(PARAM_REPOSITORIES));
@@ -196,11 +196,12 @@ public class SearchAction implements RequestHandler {
     query.setLanguages(request.paramAsStrings(PARAM_LANGUAGES));
     query.setDebtCharacteristics(request.paramAsStrings(PARAM_DEBT_CHARACTERISTICS));
     query.setHasDebtCharacteristic(request.paramAsBoolean(PARAM_HAS_DEBT_CHARACTERISTIC));
-    query.setActivation(request.param(PARAM_ACTIVATION));
+    query.setActivation(request.paramAsBoolean(PARAM_ACTIVATION));
     query.setQProfileKey(request.param(PARAM_QPROFILE));
     query.setSortField(RuleQuery.SortField.valueOfOrNull(request.param(SearchOptions.PARAM_SORT)));
     query.setAscendingSort(request.mandatoryParamAsBoolean(SearchOptions.PARAM_ASCENDING));
     query.setTags(request.paramAsStrings(PARAM_TAGS));
+    query.setAllOfTags(request.paramAsStrings(PARAM_ALL_OF_TAGS));
     return query;
   }
 
@@ -212,58 +213,19 @@ public class SearchAction implements RequestHandler {
     json.endArray();
   }
 
-  private void writeActiveRules(RuleResult result, JsonWriter json) {
-    json.name("actives").beginObject();
-    for (Map.Entry<String, Collection<ActiveRule>> entry : result.getActiveRules().asMap().entrySet()) {
-      // rule key
-      json.name(entry.getKey());
-      json.beginArray();
-      for (ActiveRule activeRule : entry.getValue()) {
-        writeActiveRule(json, activeRule);
-      }
-      json.endArray();
-    }
-    json.endObject();
-  }
-
-  /**
-   * This method is static and package protected because it's used by {@link org.sonar.server.rule2.ws.ShowAction}
-   */
-  static void writeActiveRule(JsonWriter json, ActiveRule activeRule) {
-    json
-      .beginObject()
-      .prop("qProfile", activeRule.key().qProfile().toString())
-      .prop("inherit", activeRule.inheritance().toString())
-      .prop("severity", activeRule.severity());
-    if (activeRule.parentKey() != null) {
-      json.prop("parent", activeRule.parentKey().toString());
-    }
-    json.name("params").beginArray();
-    for (Map.Entry<String, String> param : activeRule.params().entrySet()) {
-      json
-        .beginObject()
-        .prop("key", param.getKey())
-        .prop("value", param.getValue())
-        .endObject();
-    }
-    json.endArray().endObject();
-  }
-
   private void writeFacets(RuleResult results, JsonWriter json) {
     json.name("facets").beginArray();
     for (Map.Entry<String, Collection<FacetValue>> facet : results.getFacets().entrySet()) {
-      System.out.println("facet = " + facet);
       json.beginObject();
       json.prop("name", facet.getKey());
       json.name("values").beginArray();
       for (FacetValue facetValue : facet.getValue()) {
         json.beginObject();
         json.prop("val", facetValue.getKey());
-        json.prop("count", (Integer) facetValue.getValue());
+        json.prop("count", facetValue.getValue());
         json.endObject();
       }
-      json.endArray();
-      json.endObject();
+      json.endArray().endObject();
     }
     json.endArray();
   }
index a19a4c0fb9aca45670b982b90013e05815c6075b..a517549dddfa052faf884362d1d1c55603381f13 100644 (file)
@@ -27,30 +27,26 @@ import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.server.exceptions.NotFoundException;
-import org.sonar.server.qualityprofile.ActiveRule;
-import org.sonar.server.qualityprofile.ActiveRuleService;
 import org.sonar.server.rule2.Rule;
 import org.sonar.server.rule2.RuleService;
 import org.sonar.server.search.BaseDoc;
 
-import java.util.List;
-
 /**
  * @since 4.4
  */
 public class ShowAction implements RequestHandler {
 
-  private static final String PARAM_KEY = "key";
-  private static final String PARAM_ACTIVATION = "activation";
+  public static final String PARAM_KEY = "key";
+  public static final String PARAM_ACTIVES = "actives";
 
   private final RuleService service;
   private final RuleMapping mapping;
-  private final ActiveRuleService activeRuleService;
+  private final ActiveRuleCompleter activeRuleCompleter;
 
-  public ShowAction(RuleService service, ActiveRuleService activeRuleService, RuleMapping mapping) {
+  public ShowAction(RuleService service, ActiveRuleCompleter activeRuleCompleter, RuleMapping mapping) {
     this.service = service;
     this.mapping = mapping;
-    this.activeRuleService = activeRuleService;
+    this.activeRuleCompleter = activeRuleCompleter;
   }
 
   void define(WebService.NewController controller) {
@@ -68,10 +64,10 @@ public class ShowAction implements RequestHandler {
       .setExampleValue("javascript:EmptyBlock");
 
     action
-      .createParam(PARAM_ACTIVATION)
-      .setDescription("Show rule's activations for all profiles (ActiveRules)")
-      .setDefaultValue("true")
-      .setBooleanPossibleValues();
+      .createParam(PARAM_ACTIVES)
+      .setDescription("Show rule's activations for all profiles (\"active rules\")")
+      .setBooleanPossibleValues()
+      .setDefaultValue(false);
   }
 
   @Override
@@ -84,19 +80,10 @@ public class ShowAction implements RequestHandler {
     JsonWriter json = response.newJsonWriter().beginObject().name("rule");
     mapping.write((BaseDoc) rule, json);
 
-    if (request.mandatoryParamAsBoolean(PARAM_ACTIVATION)) {
-      writeActiveRules(activeRuleService.findByRuleKey(key), json);
+    if (request.mandatoryParamAsBoolean(PARAM_ACTIVES)) {
+      activeRuleCompleter.completeShow(rule, json);
     }
 
     json.endObject().close();
   }
-
-  void writeActiveRules(List<ActiveRule> activeRules, JsonWriter json) {
-    json.name("actives").beginArray();
-    for (ActiveRule activeRule : activeRules) {
-      SearchAction.writeActiveRule(json, activeRule);
-    }
-    json.endArray();
-
-  }
 }
index 19a7d3f4995adb75ee2d46fdca9a83d641a5bdbb..36133dd3d27beee5bfa80a095e9f774161f98ff2 100644 (file)
@@ -151,7 +151,7 @@ public abstract class BaseIndex<D, E extends Dto<K>, K extends Serializable>
 
   /* Base CRUD methods */
 
-  protected abstract D toDoc(Map<String,Object> fields, QueryOptions options);
+  protected abstract D toDoc(Map<String,Object> fields);
 
   public D getByKey(K key) {
     GetResponse response = getClient().prepareGet()
@@ -161,7 +161,7 @@ public abstract class BaseIndex<D, E extends Dto<K>, K extends Serializable>
       .setRouting(this.getKeyValue(key))
       .get();
     if (response.isExists()) {
-      return toDoc(response.getSource(), QueryOptions.DEFAULT);
+      return toDoc(response.getSource());
     }
     return null;
   }
index cf691a4a28140d19704bf515c5f10f5b897bb83e..94b8c26308451500833bf0a5a32221228c13f059 100644 (file)
@@ -42,10 +42,7 @@ import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.qualityprofile.persistence.ActiveRuleDao;
 import org.sonar.server.rule2.index.RuleIndex;
 import org.sonar.server.rule2.index.RuleNormalizer;
-import org.sonar.server.rule2.index.RuleQuery;
-import org.sonar.server.rule2.index.RuleResult;
 import org.sonar.server.rule2.persistence.RuleDao;
-import org.sonar.server.search.QueryOptions;
 import org.sonar.server.tester.ServerTester;
 import org.sonar.server.user.MockUserSession;
 
@@ -85,7 +82,8 @@ public class RuleServiceMediumTest {
     dao.insert(newRuleDto(rule1)
         .setTags(Sets.newHashSet("security"))
         .setSystemTags(Collections.<String>emptySet()),
-      dbSession);
+      dbSession
+    );
 
     RuleKey rule2 = RuleKey.of("java", "S001");
     dao.insert(newRuleDto(rule2)
@@ -169,8 +167,9 @@ public class RuleServiceMediumTest {
     RuleKey rule1 = RuleKey.of("javascript", "S001");
     dao.insert(newRuleDto(rule1)
         .setTags(Sets.newHashSet("security"))
-        .setSystemTags(Sets.newHashSet("java-coding","stephane.gamard@sonarsource.com")),
-      dbSession);
+        .setSystemTags(Sets.newHashSet("java-coding", "stephane.gamard@sonarsource.com")),
+      dbSession
+    );
 
     RuleKey rule2 = RuleKey.of("java", "S001");
     dao.insert(newRuleDto(rule2)
@@ -179,10 +178,9 @@ public class RuleServiceMediumTest {
     dbSession.commit();
 
 
-
     Set<String> tags = index.terms(RuleNormalizer.RuleField._TAGS.key());
-    assertThat(tags).containsOnly("java-coding","security",
-      "stephane.gamard@sonarsource.com","mytag");
+    assertThat(tags).containsOnly("java-coding", "security",
+      "stephane.gamard@sonarsource.com", "mytag");
 
     tags = index.terms(RuleNormalizer.RuleField.SYSTEM_TAGS.key());
     assertThat(tags).containsOnly("java-coding",
@@ -194,8 +192,8 @@ public class RuleServiceMediumTest {
   public void test_search_activation_on_rules() throws InterruptedException {
 
     // 1. Create in DB
-    QualityProfileDto qprofile1 = QualityProfileDto.createFor("profile1","java");
-    QualityProfileDto qprofile2 = QualityProfileDto.createFor("profile2","java");
+    QualityProfileDto qprofile1 = QualityProfileDto.createFor("profile1", "java");
+    QualityProfileDto qprofile2 = QualityProfileDto.createFor("profile2", "java");
     tester.get(QualityProfileDao.class).insert(qprofile1, dbSession);
     tester.get(QualityProfileDao.class).insert(qprofile2, dbSession);
 
@@ -217,7 +215,6 @@ public class RuleServiceMediumTest {
     dbSession.commit();
 
 
-
     // 2. test in DB
     assertThat(tester.get(RuleDao.class).findAll(dbSession)).hasSize(2);
     assertThat(tester.get(ActiveRuleDao.class).findByRule(rule1, dbSession)).hasSize(1);
@@ -225,23 +222,24 @@ public class RuleServiceMediumTest {
 
 
     // 3. Test for ALL activations
-    RuleQuery query = new RuleQuery()
-      .setActivation("all");
-    RuleResult result = service.search(query, new QueryOptions());
-    assertThat(result.getActiveRules().values()).hasSize(3);
-
-    // 4. Test for NO active rules
-    query = new RuleQuery()
-      .setActivation("false");
-    result = service.search(query, new QueryOptions());
-    assertThat(result.getActiveRules().values()).hasSize(0);
-
-    // 4. Test for  active rules of QProfile
-    query = new RuleQuery()
-      .setActivation("true")
-      .setQProfileKey(qprofile1.getKey().toString());
-    result = service.search(query, new QueryOptions());
-    assertThat(result.getActiveRules().values()).hasSize(2);
+    // TODO
+//    RuleQuery query = new RuleQuery()
+//      .setActivation("all");
+//    RuleResult result = service.search(query, new QueryOptions());
+//    assertThat(result.getActiveRules().values()).hasSize(3);
+//
+//    // 4. Test for NO active rules
+//    query = new RuleQuery()
+//      .setActivation("false");
+//    result = service.search(query, new QueryOptions());
+//    assertThat(result.getActiveRules().values()).hasSize(0);
+//
+//    // 4. Test for  active rules of QProfile
+//    query = new RuleQuery()
+//      .setActivation("true")
+//      .setQProfileKey(qprofile1.getKey().toString());
+//    result = service.search(query, new QueryOptions());
+//    assertThat(result.getActiveRules().values()).hasSize(2);
   }
 
   private RuleDto newRuleDto(RuleKey ruleKey) {
index cbafce55bc5fce328f98ac96c8722b6157235eba..4031b64d78a9ca6f042818567cbb36f74bf5f76a 100644 (file)
@@ -474,27 +474,24 @@ public class RuleIndexMediumTest {
     RuleResult result;
 
     // 1. get all active rules.
-    result = index.search(new RuleQuery().setActivation("all"),
+    result = index.search(new RuleQuery().setActivation(true),
       new QueryOptions());
     assertThat(result.getHits()).hasSize(2);
 
     // 2. get all inactive rules.
-    result = index.search(new RuleQuery().setActivation("false"),
+    result = index.search(new RuleQuery().setActivation(false),
       new QueryOptions());
     assertThat(result.getHits()).hasSize(1);
     assertThat(result.getHits().get(0).name()).isEqualTo(rule3.getName());
 
-    // 3. get all active rules missing profile.
-    try {
-      index.search(new RuleQuery().setActivation("true"),
+    // 3. get all rules not active on profile
+    index.search(new RuleQuery().setActivation(false).setQProfileKey(qualityProfileDto2.getKey().toString()),
         new QueryOptions());
-      fail();
-    } catch (IllegalStateException e) {
-      assertThat(e).hasMessage("qProfile is required when \"activation=true\"");
-    }
+    // TODO
+    assertThat(result.getRules()).hasSize(1);
 
-    // 4. get all active rules. for qualityProfileDto2
-    result = index.search(new RuleQuery().setActivation("true")
+    // 4. get all active rules on profile
+    result = index.search(new RuleQuery().setActivation(true)
         .setQProfileKey(qualityProfileDto2.getKey().toString()),
       new QueryOptions()
     );