]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5174 Add rule debt definitions into E/S and search rules by characteristic...
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 27 Mar 2014 17:39:28 +0000 (18:39 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 27 Mar 2014 17:39:37 +0000 (18:39 +0100)
sonar-server/src/main/java/org/sonar/server/debt/DebtModelService.java
sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java
sonar-server/src/main/java/org/sonar/server/rule/RubyRuleService.java
sonar-server/src/main/java/org/sonar/server/rule/RuleDocument.java
sonar-server/src/main/java/org/sonar/server/rule/RuleQuery.java
sonar-server/src/main/java/org/sonar/server/rule/RuleRegistry.java
sonar-server/src/main/java/org/sonar/server/rule/ws/RuleSearchWsHandler.java
sonar-server/src/main/resources/org/sonar/server/es/config/mappings/rule_mapping.json
sonar-server/src/test/java/org/sonar/server/rule/RuleRegistryTest.java
sonar-server/src/test/resources/org/sonar/server/qualityprofile/ESActiveRuleTest/shared/rule1.json
sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistryTest/shared/rule1.json

index 040f8468917bc124a397a061cf362a956e13d327..4a06db092849d84ba9ff9c93d1fe2c30222ead55 100644 (file)
@@ -75,8 +75,10 @@ public class DebtModelService implements DebtModel {
   }
 
   /**
-   * Disable characteristic and sub characteristic or only sub characteristic.
-   * Will also update every rules linked to sub characteristics by setting characteristic id to -1 and remove function, coefficient and offset.
+   * Delete a characteristic or a sub characteristic.
+   * <br/>
+   * If a characteristic is selected, all its sub characteristics will also be deleted.
+   * Every rules linked to sub characteristics will have their debt definitions reset.
    */
   public void delete(int characteristicId) {
     debtModelOperations.delete(characteristicId);
index 0bac168fceb11ec88472d03d700e7aa57acc3378..a8dcf16c09e95eef4e295b577bc8fc3b1e6ac8dd 100644 (file)
@@ -108,6 +108,9 @@ public class RegisterRules implements Startable {
       RulesDefinition.Context context = defLoader.load();
       Buffer buffer = new Buffer(system.now());
       List<CharacteristicDto> characteristicDtos = characteristicDao.selectEnabledCharacteristics();
+      for (CharacteristicDto characteristicDto : characteristicDtos) {
+        buffer.add(characteristicDto);
+      }
       selectRulesFromDb(buffer, sqlSession);
       enableRuleDefinitions(context, buffer, characteristicDtos, sqlSession);
       List<RuleDto> removedRules = processRemainingDbRules(buffer, sqlSession);
@@ -475,7 +478,7 @@ public class RegisterRules implements Startable {
   }
 
   private void index(Buffer buffer) {
-    ruleRegistry.bulkRegisterRules(buffer.rulesById.values(), buffer.paramsByRuleId, buffer.tagsByRuleId);
+    ruleRegistry.bulkRegisterRules(buffer.rulesById.values(), buffer.characteristicsById, buffer.paramsByRuleId, buffer.tagsByRuleId);
     esRuleTags.putAllTags(buffer.referenceTagsByTagValue.values());
   }
 
@@ -487,6 +490,7 @@ public class RegisterRules implements Startable {
     private Multimap<Integer, RuleParamDto> paramsByRuleId = ArrayListMultimap.create();
     private Multimap<Integer, RuleRuleTagDto> tagsByRuleId = ArrayListMultimap.create();
     private Map<String, RuleTagDto> referenceTagsByTagValue = Maps.newHashMap();
+    private Map<Integer, CharacteristicDto> characteristicsById = Maps.newHashMap();
 
     Buffer(long now) {
       this.now = new Date(now);
@@ -501,6 +505,10 @@ public class RegisterRules implements Startable {
       rulesByKey.put(RuleKey.of(rule.getRepositoryKey(), rule.getRuleKey()), rule);
     }
 
+    void add(CharacteristicDto characteristicDto) {
+      characteristicsById.put(characteristicDto.getId(), characteristicDto);
+    }
+
     void add(RuleTagDto tag) {
       referenceTagsByTagValue.put(tag.getTag(), tag);
     }
index e7bee3399d08cb156983bb7897266b6d99a04b8d..62de414ac06f2c79e1c9bd17c82356b008aa3289 100644 (file)
@@ -49,6 +49,9 @@ public class RubyRuleService implements ServerComponent, Startable {
     return ruleRegistry.findIds(params).toArray(new Integer[0]);
   }
 
+  /**
+   * No more used
+   */
   public void saveOrUpdate(int ruleId) {
     ruleRegistry.saveOrUpdate(ruleId);
   }
index 174dd12e136615b925f021d172bb8988537a3423..d5c8cffb597de8c1e5b009281f5f8192a7ba3df6 100644 (file)
@@ -22,20 +22,30 @@ package org.sonar.server.rule;
 
 public final class RuleDocument {
 
+  public static final String FIELD_ID = "id";
+  public static final String FIELD_REPOSITORY_KEY = "repositoryKey";
+  public static final String FIELD_KEY = "key";
+  public static final String FIELD_LANGUAGE = "language";
+  public static final String FIELD_NAME = "name";
+  public static final String FIELD_SEVERITY = "severity";
+  public static final String FIELD_DESCRIPTION = "description";
+  public static final String FIELD_TEMPLATE_ID = "templateId";
+  public static final String FIELD_STATUS = "status";
   public static final String FIELD_SYSTEM_TAGS = "systemTags";
   public static final String FIELD_ADMIN_TAGS = "adminTags";
   public static final String FIELD_PARAMS = "params";
   public static final String FIELD_UPDATED_AT = "updatedAt";
   public static final String FIELD_CREATED_AT = "createdAt";
-  public static final String FIELD_STATUS = "status";
-  public static final String FIELD_SEVERITY = "severity";
-  public static final String FIELD_REPOSITORY_KEY = "repositoryKey";
-  public static final String FIELD_TEMPLATE_ID = "templateId";
-  public static final String FIELD_DESCRIPTION = "description";
-  public static final String FIELD_NAME = "name";
-  public static final String FIELD_LANGUAGE = "language";
-  public static final String FIELD_KEY = "key";
-  public static final String FIELD_ID = "id";
+
+  public static final String FIELD_CHARACTERISTIC_ID = "characteristicId";
+  public static final String FIELD_CHARACTERISTIC_KEY = "characteristicKey";
+  public static final String FIELD_CHARACTERISTIC_NAME = "characteristicName";
+  public static final String FIELD_SUB_CHARACTERISTIC_ID = "subCharacteristicId";
+  public static final String FIELD_SUB_CHARACTERISTIC_KEY = "subCharacteristicKey";
+  public static final String FIELD_SUB_CHARACTERISTIC_NAME = "subCharacteristicName";
+  public static final String FIELD_REMEDIATION_FUNCTION = "remediationFunction";
+  public static final String FIELD_REMEDIATION_COEFFICIENT = "remediationCoefficient";
+  public static final String FIELD_REMEDIATION_OFFSET = "remediationOffset";
 
   public static final String FIELD_PARAM_KEY = "key";
   public static final String FIELD_PARAM_TYPE = "type";
index 200bcddd9239a94efc79b9b979f33a540032ee0e..1f11bfc92fc52f89c862ba65a515b826aee08507 100644 (file)
@@ -19,8 +19,9 @@
  */
 package org.sonar.server.rule;
 
-import org.sonar.server.paging.Paging;
+import com.google.common.base.Preconditions;
 
+import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
 /**
@@ -28,30 +29,52 @@ import javax.annotation.Nullable;
  */
 public class RuleQuery {
 
+  public static final int DEFAULT_PAGE_INDEX = 1;
   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;
+  private String characteristicKey;
+  private String subCharacteristicKey;
+
+  private int pageSize;
+  private int pageIndex;
+
+  private RuleQuery(Builder builder) {
+    this.key = builder.key;
+    this.query = builder.query;
+    this.subCharacteristicKey = builder.subCharacteristicKey;
+    this.characteristicKey = builder.characteristicKey;
+    this.pageSize = builder.pageSize;
+    this.pageIndex = builder.pageIndex;
   }
 
+  @CheckForNull
   public String key() {
-    return this.key;
+    return key;
   }
 
+  @CheckForNull
   public String query() {
-    return this.query;
+    return query;
   }
 
-  public Paging paging() {
-    return this.paging;
+  @CheckForNull
+  public String characteristicKey() {
+    return characteristicKey;
+  }
+
+  @CheckForNull
+  public String subCharacteristicKey() {
+    return subCharacteristicKey;
+  }
+
+  public int pageSize() {
+    return pageSize;
+  }
+
+  public int pageIndex() {
+    return pageIndex;
   }
 
   public static Builder builder() {
@@ -61,40 +84,60 @@ public class RuleQuery {
   public static class Builder {
 
     private String key;
-
     private String query;
+    private String characteristicKey;
+    private String subCharacteristicKey;
 
-    private int pageSize;
+    private Integer pageSize;
+    private Integer pageIndex;
 
-    private int page;
+    public Builder key(@Nullable String key) {
+      this.key = key;
+      return this;
+    }
 
-    private Builder() {
-      this.page = 0;
-      this.pageSize = DEFAULT_PAGE_SIZE;
+    public Builder searchQuery(@Nullable String query) {
+      this.query = query;
+      return this;
     }
 
-    public Builder withKey(String key) {
-      this.key = key;
+    public Builder characteristicKey(@Nullable String characteristicKey) {
+      this.characteristicKey = characteristicKey;
       return this;
     }
 
-    public Builder withSearchQuery(String query) {
-      this.query = query;
+    public Builder subCharacteristicKey(@Nullable String subCharacteristicKey) {
+      this.subCharacteristicKey = subCharacteristicKey;
       return this;
     }
 
-    public Builder withPageSize(int pageSize) {
+    public Builder pageSize(@Nullable Integer pageSize) {
       this.pageSize = pageSize;
       return this;
     }
 
-    public Builder withPage(int page) {
-      this.page = page;
+    public Builder pageIndex(@Nullable Integer page) {
+      this.pageIndex = page;
       return this;
     }
 
     public RuleQuery build() {
-      return new RuleQuery(key, query, Paging.create(pageSize, page));
+      initPageSize();
+      initPageIndex();
+      return new RuleQuery(this);
+    }
+
+    private void initPageSize() {
+      if (pageSize == null) {
+        pageSize = DEFAULT_PAGE_SIZE;
+      }
+    }
+
+    private void initPageIndex() {
+      if (pageIndex == null) {
+        pageIndex = DEFAULT_PAGE_INDEX;
+      }
+      Preconditions.checkArgument(pageIndex > 0, "Page index must be greater than 0 (got " + pageIndex + ")");
     }
   }
 }
index 6504ceee9da33e9000a9dfff43da60fabad96a8e..d0dfe152eedfcb97d9a835f1c397ecdf226d7e8c 100644 (file)
@@ -40,13 +40,16 @@ 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.core.technicaldebt.db.CharacteristicDto;
 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.Paging;
 import org.sonar.server.paging.PagingResult;
 
 import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
 
 import java.io.IOException;
 import java.util.Arrays;
@@ -55,6 +58,8 @@ import java.util.List;
 import java.util.Map;
 
 import static com.google.common.collect.Lists.newArrayList;
+import static org.elasticsearch.index.query.FilterBuilders.boolFilter;
+import static org.elasticsearch.index.query.FilterBuilders.termFilter;
 import static org.sonar.api.rules.Rule.STATUS_REMOVED;
 
 /**
@@ -81,8 +86,9 @@ public class RuleRegistry {
     searchIndex.addMappingFromClasspath(INDEX_RULES, TYPE_RULE, "/org/sonar/server/es/config/mappings/rule_mapping.json");
   }
 
-  public void bulkRegisterRules(Collection<RuleDto> rules, Multimap<Integer, RuleParamDto> paramsByRule, Multimap<Integer, RuleRuleTagDto> tagsByRule) {
-    String[] ids = bulkIndexRules(rules, paramsByRule, tagsByRule);
+  public void bulkRegisterRules(Collection<RuleDto> rules,Map<Integer, CharacteristicDto> characteristicByRule, Multimap<Integer, RuleParamDto> paramsByRule,
+                                Multimap<Integer, RuleRuleTagDto> tagsByRule) {
+    String[] ids = bulkIndexRules(rules, characteristicByRule, paramsByRule, tagsByRule);
     removeDeletedRules(ids);
   }
 
@@ -127,29 +133,35 @@ public class RuleRegistry {
   }
 
   public PagedResult<Rule> find(RuleQuery query) {
-    BoolFilterBuilder mainFilter = FilterBuilders.boolFilter().mustNot(FilterBuilders.termFilter(RuleDocument.FIELD_STATUS, STATUS_REMOVED));
+    BoolFilterBuilder mainFilter = boolFilter().mustNot(termFilter(RuleDocument.FIELD_STATUS, STATUS_REMOVED));
     if (StringUtils.isNotBlank(query.query())) {
       mainFilter.must(FilterBuilders.queryFilter(
-        QueryBuilders.multiMatchQuery(query.query(), RuleDocument.FIELD_NAME+".search", RuleDocument.FIELD_KEY).operator(Operator.AND)));
+        QueryBuilders.multiMatchQuery(query.query(), RuleDocument.FIELD_NAME + ".search", RuleDocument.FIELD_KEY).operator(Operator.AND)));
     }
+    if (query.characteristicKey() != null) {
+      mainFilter.must(termFilter(RuleDocument.FIELD_CHARACTERISTIC_KEY, query.characteristicKey()));
+    }
+    if (query.subCharacteristicKey() != null) {
+      mainFilter.must(termFilter(RuleDocument.FIELD_SUB_CHARACTERISTIC_KEY, query.subCharacteristicKey()));
+    }
+    Paging paging = Paging.create(query.pageSize(), query.pageIndex());
     SearchHits hits = searchIndex.executeRequest(
       searchIndex.client().prepareSearch(INDEX_RULES).setTypes(TYPE_RULE)
         .setPostFilter(mainFilter)
         .addSort(RuleDocument.FIELD_NAME, SortOrder.ASC)
-        .setSize(query.paging().pageSize())
-        .setFrom(query.paging().offset()));
+        .setSize(paging.pageSize())
+        .setFrom(paging.offset())
+    );
 
     Builder<Rule> rulesBuilder = ImmutableList.builder();
-    for (SearchHit hit: hits.hits()) {
+    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()));
+    return new PagedResult<Rule>(rulesBuilder.build(), PagingResult.create(paging.pageSize(), paging.pageIndex(), hits.getTotalHits()));
   }
 
   /**
    * Create or update definition of rule identified by <code>ruleId</code>
-   *
-   * @param ruleId
    */
   public void saveOrUpdate(int ruleId) {
     RuleDto rule = ruleDao.selectById(ruleId);
@@ -164,7 +176,7 @@ public class RuleRegistry {
 
   public void save(RuleDto rule, Collection<RuleParamDto> params, Collection<RuleRuleTagDto> tags) {
     try {
-      searchIndex.putSynchronous(INDEX_RULES, TYPE_RULE, Long.toString(rule.getId()), ruleDocument(rule, params, tags));
+      searchIndex.putSynchronous(INDEX_RULES, TYPE_RULE, Long.toString(rule.getId()), ruleDocument(rule, null, null, params, tags));
     } catch (IOException ioexception) {
       throw new IllegalStateException("Unable to index rule with id=" + rule.getId(), ioexception);
     }
@@ -173,11 +185,11 @@ public class RuleRegistry {
   @CheckForNull
   public Rule findByKey(RuleKey key) {
     final SearchHits hits = searchIndex.executeRequest(searchIndex.client().prepareSearch(INDEX_RULES).setTypes(TYPE_RULE)
-      .setPostFilter(FilterBuilders.boolFilter()
+      .setPostFilter(boolFilter()
         .must(
-          FilterBuilders.termFilter(RuleDocument.FIELD_REPOSITORY_KEY, key.repository()),
-          FilterBuilders.termFilter(RuleDocument.FIELD_KEY, key.rule())
-          )));
+          termFilter(RuleDocument.FIELD_REPOSITORY_KEY, key.repository()),
+          termFilter(RuleDocument.FIELD_KEY, key.rule())
+        )));
     if (hits.totalHits() == 0) {
       return null;
     } else {
@@ -185,7 +197,8 @@ public class RuleRegistry {
     }
   }
 
-  private String[] bulkIndexRules(Collection<RuleDto> rules, Multimap<Integer, RuleParamDto> paramsByRule, Multimap<Integer, RuleRuleTagDto> tagsByRule) {
+  private String[] bulkIndexRules(Collection<RuleDto> rules, Map<Integer, CharacteristicDto> characteristicsById, Multimap<Integer, RuleParamDto> paramsByRule,
+                                  Multimap<Integer, RuleRuleTagDto> tagsByRule) {
     try {
       String[] ids = new String[rules.size()];
       BytesStream[] docs = new BytesStream[rules.size()];
@@ -194,7 +207,10 @@ public class RuleRegistry {
       profiler.start("Build rules documents");
       for (RuleDto rule : rules) {
         ids[index] = rule.getId().toString();
-        docs[index] = ruleDocument(rule, paramsByRule.get(rule.getId()), tagsByRule.get(rule.getId()));
+        CharacteristicDto subCharacteristic = characteristicsById.get(rule.getSubCharacteristicId() != null ? rule.getSubCharacteristicId() : rule.getDefaultSubCharacteristicId());
+        CharacteristicDto characteristic = subCharacteristic != null ? characteristicsById.get(subCharacteristic.getParentId()) : null;
+        characteristicsById.get(rule.getSubCharacteristicId() != null ? rule.getSubCharacteristicId() : rule.getDefaultSubCharacteristicId());
+        docs[index] = ruleDocument(rule, characteristic, subCharacteristic, paramsByRule.get(rule.getId()), tagsByRule.get(rule.getId()));
         index++;
       }
       profiler.stop();
@@ -221,7 +237,8 @@ public class RuleRegistry {
     }
   }
 
-  private XContentBuilder ruleDocument(RuleDto rule, Collection<RuleParamDto> params, Collection<RuleRuleTagDto> tags) throws IOException {
+  private XContentBuilder ruleDocument(RuleDto rule, @Nullable CharacteristicDto characteristicDto, @Nullable CharacteristicDto subCharacteristicDto,
+                                       Collection<RuleParamDto> params, Collection<RuleRuleTagDto> tags) throws IOException {
     XContentBuilder document = XContentFactory.jsonBuilder()
       .startObject()
       .field(RuleDocument.FIELD_ID, rule.getId())
@@ -236,6 +253,19 @@ public class RuleRegistry {
       .field(RuleDocument.FIELD_CARDINALITY, rule.getCardinality())
       .field(RuleDocument.FIELD_CREATED_AT, rule.getCreatedAt())
       .field(RuleDocument.FIELD_UPDATED_AT, rule.getUpdatedAt());
+    if (characteristicDto != null && subCharacteristicDto != null) {
+      document
+        .field(RuleDocument.FIELD_CHARACTERISTIC_ID, characteristicDto.getId())
+        .field(RuleDocument.FIELD_CHARACTERISTIC_KEY, characteristicDto.getKey())
+        .field(RuleDocument.FIELD_CHARACTERISTIC_NAME, characteristicDto.getName())
+        .field(RuleDocument.FIELD_SUB_CHARACTERISTIC_ID, subCharacteristicDto.getId())
+        .field(RuleDocument.FIELD_SUB_CHARACTERISTIC_KEY, subCharacteristicDto.getKey())
+        .field(RuleDocument.FIELD_SUB_CHARACTERISTIC_NAME, subCharacteristicDto.getName())
+        .field(RuleDocument.FIELD_REMEDIATION_FUNCTION, rule.getRemediationFunction() != null ? rule.getRemediationFunction() : rule.getDefaultRemediationFunction())
+        .field(RuleDocument.FIELD_REMEDIATION_COEFFICIENT, rule.getRemediationCoefficient() != null ? rule.getRemediationCoefficient() : rule.getDefaultRemediationCoefficient())
+        .field(RuleDocument.FIELD_REMEDIATION_OFFSET, rule.getRemediationOffset() != null ? rule.getRemediationOffset() : rule.getDefaultRemediationOffset());
+    }
+
     if (rule.getNoteData() != null || rule.getNoteUserLogin() != null) {
       document.startObject(RuleDocument.FIELD_NOTE)
         .field(RuleDocument.FIELD_NOTE_DATA, rule.getNoteData())
@@ -256,10 +286,9 @@ public class RuleRegistry {
       }
       document.endArray();
     }
-
     List<String> systemTags = Lists.newArrayList();
     List<String> adminTags = Lists.newArrayList();
-    for (RuleRuleTagDto tag: tags) {
+    for (RuleRuleTagDto tag : tags) {
       if (tag.getType() == RuleTagType.SYSTEM) {
         systemTags.add(tag.getTag());
       } else {
index b1bb5800c33fc5633130b4166fd77217334e5df1..3a29c68aeed9da9a4a8098f65a3a843aa0d4c0ef 100644 (file)
@@ -58,9 +58,9 @@ public class RuleSearchWsHandler implements RequestHandler {
       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)
+          .searchQuery(ruleSearchParam)
+          .pageSize(pageSize)
+          .pageIndex(pageIndex)
           .build());
       foundRules = searchResult.results();
       hasMore = searchResult.paging().hasNextPage();
index d50c2deee9f56d800b8efef338600cbdddd82eaa..9cc4f36ade7686f4f2549ae669fd14daecf88585 100644 (file)
       "adminTags": {
         "type": "string",
         "index": "not_analyzed"
+      },
+      "characteristicId": {
+        "type": "integer",
+        "index": "not_analyzed"
+      },
+      "characteristicKey": {
+        "type": "string",
+        "index": "not_analyzed"
+      },
+      "characteristicName": {
+        "type": "string",
+        "index": "not_analyzed"
+      },
+      "subCharacteristicId": {
+        "type": "integer",
+        "index": "not_analyzed"
+      },
+      "subCharacteristicKey": {
+        "type": "string",
+        "index": "not_analyzed"
+      },
+      "subCharacteristicName": {
+        "type": "string",
+        "index": "not_analyzed"
+      },
+      "remediationFunction": {
+        "type": "string",
+        "index": "not_analyzed"
+      },
+      "remediationCoefficient": {
+        "type": "string",
+        "index": "not_analyzed"
+      },
+      "remediationOffset": {
+        "type": "string",
+        "index": "not_analyzed"
       }
     }
   }
index 8a160f2c6bb0a1bc485c40132140f652a10cb1a6..ed21787d8cb26b96b5972dcd557c8eff1ddf0910 100644 (file)
 package org.sonar.server.rule;
 
 import com.github.tlrx.elasticsearch.test.EsSetup;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.*;
 import org.apache.commons.io.IOUtils;
 import org.apache.ibatis.session.SqlSession;
 import org.elasticsearch.common.collect.Lists;
@@ -41,6 +38,7 @@ import org.sonar.api.rule.Severity;
 import org.sonar.core.persistence.MyBatis;
 import org.sonar.core.profiling.Profiling;
 import org.sonar.core.rule.*;
+import org.sonar.core.technicaldebt.db.CharacteristicDto;
 import org.sonar.server.es.ESIndex;
 import org.sonar.server.es.ESNode;
 import org.sonar.test.TestUtils;
@@ -49,6 +47,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import static com.google.common.collect.Maps.newHashMap;
 import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
@@ -95,6 +94,7 @@ public class RuleRegistryTest {
     esSetup.execute(
       EsSetup.index("rules", "rule", "1").withSource(testFileAsString("shared/rule1.json")),
       EsSetup.index("rules", "rule", "2").withSource(testFileAsString("shared/rule2.json")),
+      // rule 3 is removed
       EsSetup.index("rules", "rule", "3").withSource(testFileAsString("shared/rule3.json"))
     );
     esSetup.client().admin().cluster().prepareHealth(RuleRegistry.INDEX_RULES).setWaitForGreenStatus().execute().actionGet();
@@ -107,13 +107,13 @@ public class RuleRegistryTest {
   }
 
   @Test
-  public void should_register_mapping_at_startup() {
+  public void register_mapping_at_startup() {
     assertThat(esSetup.exists("rules")).isTrue();
     assertThat(esSetup.client().admin().indices().prepareTypesExists("rules").setTypes("rule").execute().actionGet().isExists()).isTrue();
   }
 
   @Test
-  public void should_find_rule_by_key() {
+  public void find_rule_by_key() {
     assertThat(registry.findByKey(RuleKey.of("unknown", "RuleWithParameters"))).isNull();
     assertThat(registry.findByKey(RuleKey.of("xoo", "unknown"))).isNull();
     final Rule rule = registry.findByKey(RuleKey.of("xoo", "RuleWithParameters"));
@@ -126,29 +126,29 @@ public class RuleRegistryTest {
   }
 
   @Test
-  public void should_filter_removed_rules() {
+  public void filter_removed_rules() {
     assertThat(registry.findIds(new HashMap<String, String>())).containsOnly(1, 2);
   }
 
   @Test
-  public void should_display_disabled_rule() {
+  public void display_disabled_rule() {
     assertThat(registry.findIds(ImmutableMap.of("status", "BETA|REMOVED"))).containsOnly(2, 3);
   }
 
   @Test
-  public void should_filter_on_name_or_key() throws Exception {
+  public void filter_on_name_or_key() throws Exception {
     assertThat(registry.findIds(ImmutableMap.of("nameOrKey", "parameters"))).containsOnly(1);
     assertThat(registry.findIds(ImmutableMap.of("nameOrKey", "issue"))).containsOnly(1, 2);
     assertThat(registry.findIds(ImmutableMap.of("nameOrKey", "issue line"))).containsOnly(2);
   }
 
   @Test
-  public void should_filter_on_key() throws Exception {
+  public void filter_on_key() throws Exception {
     assertThat(registry.findIds(ImmutableMap.of("key", "OneIssuePerLine"))).containsOnly(2);
   }
 
   @Test
-  public void should_filter_on_multiple_criteria() {
+  public void filter_on_multiple_criteria() {
     assertThat(registry.findIds(ImmutableMap.of("nameOrKey", "parameters", "key", "OneIssuePerLine"))).isEmpty();
     assertThat(registry.findIds(ImmutableMap.of("repositoryKey", "polop"))).isEmpty();
 
@@ -156,29 +156,29 @@ public class RuleRegistryTest {
   }
 
   @Test
-  public void should_filter_on_multiple_values() {
+  public void filter_on_multiple_values() {
     assertThat(registry.findIds(ImmutableMap.of("key", "RuleWithParameters|OneIssuePerLine"))).hasSize(2);
   }
 
   @Test(expected = IllegalArgumentException.class)
-  public void should_reject_leading_wildcard() {
+  public void reject_leading_wildcard() {
     registry.findIds(ImmutableMap.of("nameOrKey", "*ssue"));
   }
 
   @Test(expected = IllegalArgumentException.class)
-  public void should_wrap_parse_exceptions() {
+  public void wrap_parse_exceptions() {
     registry.findIds(ImmutableMap.of("nameOrKey", "\"'"));
   }
 
   @Test
-  public void should_remove_all_rules_when_ro_rule_registered() {
+  public void remove_all_rules_when_ro_rule_registered() {
     List<RuleDto> rules = Lists.newArrayList();
-    registry.bulkRegisterRules(rules, null, null);
+    registry.bulkRegisterRules(rules, null, null, null);
     assertThat(registry.findIds(new HashMap<String, String>())).hasSize(0);
   }
 
   @Test
-  public void should_index_all_rules() {
+  public void index_all_rules() {
     int ruleId1 = 3;
     RuleDto rule1 = new RuleDto();
     rule1.setRepositoryKey("repo");
@@ -187,6 +187,7 @@ public class RuleRegistryTest {
     rule1.setSeverity(Severity.MINOR);
     rule1.setNoteData("noteData");
     rule1.setNoteUserLogin("userLogin");
+
     int ruleId2 = 4;
     RuleDto rule2 = new RuleDto();
     rule2.setRepositoryKey("repo");
@@ -199,6 +200,7 @@ public class RuleRegistryTest {
     RuleParamDto paramRule2 = new RuleParamDto();
     paramRule2.setName("name");
     paramRule2.setRuleId(ruleId2);
+
     Multimap<Integer, RuleParamDto> params = ArrayListMultimap.create();
     params.put(ruleId2, paramRule2);
 
@@ -214,12 +216,13 @@ public class RuleRegistryTest {
     adminTagRule2.setRuleId(ruleId2);
     adminTagRule2.setTag("tag");
     adminTagRule2.setType(RuleTagType.ADMIN);
+
     Multimap<Integer, RuleRuleTagDto> tags = ArrayListMultimap.create();
     tags.put(ruleId2, systemTag1Rule2);
     tags.put(ruleId2, systemTag2Rule2);
     tags.put(ruleId2, adminTagRule2);
 
-    registry.bulkRegisterRules(rules, params, tags);
+    registry.bulkRegisterRules(rules, Maps.<Integer, CharacteristicDto>newHashMap(), params, tags);
     assertThat(registry.findIds(ImmutableMap.of("repositoryKey", "repo"))).hasSize(2);
 
     Map<String, Object> rule2Document = esSetup.client().prepareGet("rules", "rule", Integer.toString(ruleId2))
@@ -229,7 +232,7 @@ public class RuleRegistryTest {
   }
 
   @Test
-  public void should_index_and_reindex_single_rule() {
+  public void index_and_reindex_single_rule() {
     RuleDto rule = new RuleDto();
     rule.setRepositoryKey("repo");
     rule.setRuleKey("key");
@@ -245,7 +248,7 @@ public class RuleRegistryTest {
   }
 
   @Test
-  public void should_update_existing_rules_and_forget_deleted_rules() {
+  public void update_existing_rules_and_forget_deleted_rules() {
     int ruleId1 = 1;
     RuleDto rule1 = new RuleDto();
     rule1.setRepositoryKey("xoo");
@@ -265,7 +268,7 @@ public class RuleRegistryTest {
     when(ruleDao.selectNonManual(any(SqlSession.class))).thenReturn(rules);
     final Multimap<Integer, RuleParamDto> params = ArrayListMultimap.create();
     final Multimap<Integer, RuleRuleTagDto> tags = ArrayListMultimap.create();
-    registry.bulkRegisterRules(rules, params, tags);
+    registry.bulkRegisterRules(rules, Maps.<Integer, CharacteristicDto>newHashMap(), params, tags);
 
     assertThat(registry.findIds(ImmutableMap.of("repositoryKey", "xoo")))
       .hasSize(2)
@@ -274,19 +277,89 @@ public class RuleRegistryTest {
   }
 
   @Test
-  public void should_find_rules_by_name() {
+  public void find_rules_by_name() {
     // Removed rule should not appear
-    assertThat(registry.find(RuleQuery.builder().withPage(1).withPageSize(10).build()).results()).hasSize(2);
+    assertThat(registry.find(RuleQuery.builder().pageIndex(1).pageSize(10).build()).results()).hasSize(2);
 
     // Search is case insensitive
-    assertThat(registry.find(RuleQuery.builder().withPage(1).withPageSize(10).withSearchQuery("one issue per line").build()).results()).hasSize(1);
+    assertThat(registry.find(RuleQuery.builder().pageIndex(1).pageSize(10).searchQuery("one issue per line").build()).results()).hasSize(1);
 
     // Search is ngram based
-    assertThat(registry.find(RuleQuery.builder().withPage(1).withPageSize(10).withSearchQuery("with param").build()).results()).hasSize(1);
+    assertThat(registry.find(RuleQuery.builder().pageIndex(1).pageSize(10).searchQuery("with param").build()).results()).hasSize(1);
 
     // Search works also with key
-    assertThat(registry.find(RuleQuery.builder().withPage(1).withPageSize(10).withSearchQuery("OneIssuePerLine").build()).results()).hasSize(1);
+    assertThat(registry.find(RuleQuery.builder().pageIndex(1).pageSize(10).searchQuery("OneIssuePerLine").build()).results()).hasSize(1);
+  }
+
+  @Test
+  public void index_debt_definitions() {
+    Map<Integer, CharacteristicDto> characteristics = newHashMap();
+    characteristics.put(10, new CharacteristicDto().setId(10).setKey("REUSABILITY").setName("Reusability"));
+    characteristics.put(11, new CharacteristicDto().setId(11).setKey("MODULARITY").setName("Modularity").setParentId(10));
+
+    List<RuleDto> rules = ImmutableList.of(new RuleDto().setId(10).setRepositoryKey("repo").setRuleKey("key1").setSeverity(Severity.MINOR)
+      .setDefaultSubCharacteristicId(11).setDefaultRemediationFunction("LINEAR_OFFSET").setDefaultRemediationCoefficient("1h").setDefaultRemediationOffset("15min"));
+
+    registry.bulkRegisterRules(rules, characteristics, ArrayListMultimap.<Integer, RuleParamDto>create(), ArrayListMultimap.<Integer, RuleRuleTagDto>create());
+
+    Map<String, Object> ruleDocument = esSetup.client().prepareGet("rules", "rule", Integer.toString(10)).execute().actionGet().getSourceAsMap();
+    assertThat(ruleDocument.get(RuleDocument.FIELD_CHARACTERISTIC_ID)).isEqualTo(10);
+    assertThat(ruleDocument.get(RuleDocument.FIELD_CHARACTERISTIC_KEY)).isEqualTo("REUSABILITY");
+    assertThat(ruleDocument.get(RuleDocument.FIELD_CHARACTERISTIC_NAME)).isEqualTo("Reusability");
+    assertThat(ruleDocument.get(RuleDocument.FIELD_SUB_CHARACTERISTIC_ID)).isEqualTo(11);
+    assertThat(ruleDocument.get(RuleDocument.FIELD_SUB_CHARACTERISTIC_KEY)).isEqualTo("MODULARITY");
+    assertThat(ruleDocument.get(RuleDocument.FIELD_SUB_CHARACTERISTIC_NAME)).isEqualTo("Modularity");
+    assertThat(ruleDocument.get(RuleDocument.FIELD_REMEDIATION_FUNCTION)).isEqualTo("LINEAR_OFFSET");
+    assertThat(ruleDocument.get(RuleDocument.FIELD_REMEDIATION_COEFFICIENT)).isEqualTo("1h");
+    assertThat(ruleDocument.get(RuleDocument.FIELD_REMEDIATION_OFFSET)).isEqualTo("15min");
+  }
+
+  @Test
+  public void index_overridden_debt_definitions_if_both_default_and_overridden_values_exists() {
+    Map<Integer, CharacteristicDto> characteristics = newHashMap();
+    characteristics.put(10, new CharacteristicDto().setId(10).setKey("REUSABILITY").setName("Reusability"));
+    characteristics.put(11, new CharacteristicDto().setId(11).setKey("MODULARITY").setName("Modularity").setParentId(10));
+    characteristics.put(12, new CharacteristicDto().setId(12).setKey("PORTABILITY").setName("Portability"));
+    characteristics.put(13, new CharacteristicDto().setId(13).setKey("COMPILER").setName("Compiler").setParentId(12));
+
+    List<RuleDto> rules = ImmutableList.of(new RuleDto().setId(10).setRepositoryKey("repo").setRuleKey("key1").setSeverity(Severity.MINOR)
+      // default and overridden debt values are set
+      .setDefaultSubCharacteristicId(11).setDefaultRemediationFunction("LINEAR").setDefaultRemediationCoefficient("2h")
+      .setSubCharacteristicId(13).setRemediationFunction("LINEAR_OFFSET").setRemediationCoefficient("1h").setRemediationOffset("15min"));
+
+    registry.bulkRegisterRules(rules, characteristics, ArrayListMultimap.<Integer, RuleParamDto>create(), ArrayListMultimap.<Integer, RuleRuleTagDto>create());
+
+    Map<String, Object> ruleDocument = esSetup.client().prepareGet("rules", "rule", Integer.toString(10)).execute().actionGet().getSourceAsMap();
+    assertThat(ruleDocument.get(RuleDocument.FIELD_CHARACTERISTIC_ID)).isEqualTo(12);
+    assertThat(ruleDocument.get(RuleDocument.FIELD_CHARACTERISTIC_KEY)).isEqualTo("PORTABILITY");
+    assertThat(ruleDocument.get(RuleDocument.FIELD_CHARACTERISTIC_NAME)).isEqualTo("Portability");
+    assertThat(ruleDocument.get(RuleDocument.FIELD_SUB_CHARACTERISTIC_ID)).isEqualTo(13);
+    assertThat(ruleDocument.get(RuleDocument.FIELD_SUB_CHARACTERISTIC_KEY)).isEqualTo("COMPILER");
+    assertThat(ruleDocument.get(RuleDocument.FIELD_SUB_CHARACTERISTIC_NAME)).isEqualTo("Compiler");
+    assertThat(ruleDocument.get(RuleDocument.FIELD_REMEDIATION_FUNCTION)).isEqualTo("LINEAR_OFFSET");
+    assertThat(ruleDocument.get(RuleDocument.FIELD_REMEDIATION_COEFFICIENT)).isEqualTo("1h");
+    assertThat(ruleDocument.get(RuleDocument.FIELD_REMEDIATION_OFFSET)).isEqualTo("15min");
+  }
+
+  @Test
+  public void find_rules_by_characteristic_and_sub_characteristic() {
+    Map<Integer, CharacteristicDto> characteristics = newHashMap();
+    characteristics.put(10, new CharacteristicDto().setId(10).setKey("REUSABILITY").setName("Reusability"));
+    characteristics.put(11, new CharacteristicDto().setId(11).setKey("MODULARITY").setName("Modularity").setParentId(10));
+
+    List<RuleDto> rules = ImmutableList.of(new RuleDto().setId(10).setRepositoryKey("repo").setRuleKey("key1").setSeverity(Severity.MINOR)
+      .setDefaultSubCharacteristicId(11).setDefaultRemediationFunction("LINEAR").setDefaultRemediationCoefficient("2h"));
+
+    registry.bulkRegisterRules(rules, characteristics, ArrayListMultimap.<Integer, RuleParamDto>create(),
+      ArrayListMultimap.<Integer, RuleRuleTagDto>create());
+
+    assertThat(registry.find(RuleQuery.builder().subCharacteristicKey("MODULARITY").build()).results()).hasSize(1);
+    assertThat(registry.find(RuleQuery.builder().subCharacteristicKey("REUSABILITY").build()).results()).isEmpty();
+    assertThat(registry.find(RuleQuery.builder().subCharacteristicKey("Unknown").build()).results()).isEmpty();
 
+    assertThat(registry.find(RuleQuery.builder().characteristicKey("REUSABILITY").build()).results()).hasSize(1);
+    assertThat(registry.find(RuleQuery.builder().characteristicKey("MODULARITY").build()).results()).isEmpty();
+    assertThat(registry.find(RuleQuery.builder().characteristicKey("Unknown").build()).results()).isEmpty();
   }
 
   private String testFileAsString(String testFile) throws Exception {
index 38a50de3025de96c62b1cd24d66041106ec94fd8..1f8e10e7f72ac775a7bea99459487e442fc4ca34 100644 (file)
   "createdAt": "2013-10-28T13:07:26.329Z",
   "updatedAt": "2013-11-08T10:52:53.473Z",
   "params": [
-      {
-        "key": "string",
-        "type": "STRING",
-        "defaultValue": null,
-        "description": ""
-      },
-      {
-        "key": "text",
-        "type": "TEXT",
-        "defaultValue": null,
-        "description": ""
-      },
-      {
-        "key": "boolean",
-        "type": "BOOLEAN",
-        "defaultValue": null,
-        "description": ""
-      },
-      {
-        "key": "integer",
-        "type": "INTEGER",
-        "defaultValue": null,
-        "description": ""
-      },
-      {
-        "key": "float",
-        "type": "FLOAT",
-        "defaultValue": null,
-        "description": ""
-      }
-    ]
-  }
+    {
+      "key": "string",
+      "type": "STRING",
+      "defaultValue": null,
+      "description": ""
+    },
+    {
+      "key": "text",
+      "type": "TEXT",
+      "defaultValue": null,
+      "description": ""
+    },
+    {
+      "key": "boolean",
+      "type": "BOOLEAN",
+      "defaultValue": null,
+      "description": ""
+    },
+    {
+      "key": "integer",
+      "type": "INTEGER",
+      "defaultValue": null,
+      "description": ""
+    },
+    {
+      "key": "float",
+      "type": "FLOAT",
+      "defaultValue": null,
+      "description": ""
+    }
+  ]
 }
index 6d6e7da2957d560704a3432365c1def50a54734d..f7bc51f168b50123a49cd2d33f31dba9628f1a61 100644 (file)
   "systemTags": [ "has-params", "integration-tests" ],
   "adminTags": [ "keep-enabled" ],
   "params": [
-      {
-        "key": "string",
-        "type": "STRING",
-        "defaultValue": null,
-        "description": ""
-      },
-      {
-        "key": "text",
-        "type": "TEXT",
-        "defaultValue": null,
-        "description": ""
-      },
-      {
-        "key": "boolean",
-        "type": "BOOLEAN",
-        "defaultValue": null,
-        "description": ""
-      },
-      {
-        "key": "integer",
-        "type": "INTEGER",
-        "defaultValue": null,
-        "description": ""
-      },
-      {
-        "key": "float",
-        "type": "FLOAT",
-        "defaultValue": null,
-        "description": ""
-      }
-    ]
-  }
+    {
+      "key": "string",
+      "type": "STRING",
+      "defaultValue": null,
+      "description": ""
+    },
+    {
+      "key": "text",
+      "type": "TEXT",
+      "defaultValue": null,
+      "description": ""
+    },
+    {
+      "key": "boolean",
+      "type": "BOOLEAN",
+      "defaultValue": null,
+      "description": ""
+    },
+    {
+      "key": "integer",
+      "type": "INTEGER",
+      "defaultValue": null,
+      "description": ""
+    },
+    {
+      "key": "float",
+      "type": "FLOAT",
+      "defaultValue": null,
+      "description": ""
+    }
+  ],
+  "defaultCharacteristicId": 1,
+  "characteristicId": 2,
+  "defaultRemediationFunction": "LINEAR",
+  "remediationFunction": "LINEAR_OFFSET",
+  "defaultRemediationCoefficient": "2h",
+  "remediationCoefficient": "1h",
+  "defaultRemediationOffset": null,
+  "remediationOffset": "15min"
 }