]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5007 return debt characteristic names in WS responses
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Thu, 10 Jul 2014 05:27:01 +0000 (07:27 +0200)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Thu, 10 Jul 2014 05:27:01 +0000 (07:27 +0200)
15 files changed:
sonar-server/src/main/java/org/sonar/server/activity/ws/ActivitiesWebService.java
sonar-server/src/main/java/org/sonar/server/activity/ws/ActivityMapping.java
sonar-server/src/main/java/org/sonar/server/activity/ws/SearchAction.java
sonar-server/src/main/java/org/sonar/server/rule/ws/CreateAction.java
sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java
sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java
sonar-server/src/main/java/org/sonar/server/rule/ws/ShowAction.java
sonar-server/src/main/java/org/sonar/server/rule/ws/UpdateAction.java
sonar-server/src/main/java/org/sonar/server/search/ws/BaseMapping.java
sonar-server/src/test/java/org/sonar/server/rule/ws/RuleMappingTest.java
sonar-server/src/test/java/org/sonar/server/rule/ws/RulesWebServiceMediumTest.java
sonar-server/src/test/java/org/sonar/server/rule/ws/ShowActionMediumTest.java
sonar-server/src/test/resources/org/sonar/server/rule/ws/ShowActionMediumTest/show_rule_with_default_and_overridden_debt_infos.json
sonar-server/src/test/resources/org/sonar/server/rule/ws/ShowActionMediumTest/show_rule_with_default_debt_infos.json
sonar-server/src/test/resources/org/sonar/server/rule/ws/ShowActionMediumTest/show_rule_with_overridden_debt_infos.json

index e6aa30ce395bf6059c17d4fcc72e50522949c5d1..925a0456d2ba796fef9cb1b24bff26a17c7b7abf 100644 (file)
@@ -35,7 +35,7 @@ public class ActivitiesWebService implements WebService {
   public void define(Context context) {
     NewController controller = context
       .createController(API_ENDPOINT)
-      .setDescription("Logs search and views");
+      .setDescription("Tracking of activities");
 
     search.define(controller);
     controller.done();
index ae2deb2f45305e832ea1b44091d561b5e36e3411..e2910b6c4eab6cdada4f137d77c66301d4ee084e 100644 (file)
@@ -22,40 +22,39 @@ package org.sonar.server.activity.ws;
 import org.sonar.api.resources.Languages;
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.core.activity.Activity;
+import org.sonar.server.activity.index.ActivityDoc;
 import org.sonar.server.activity.index.ActivityNormalizer;
 import org.sonar.server.search.ws.BaseMapping;
+import org.sonar.server.search.ws.SearchOptions;
 import org.sonar.server.text.MacroInterpreter;
 
 import java.util.Map;
 
 /**
- * Conversion between Log and WS JSON response
+ * Conversion between {@link org.sonar.server.activity.index.ActivityDoc} and WS JSON response
  */
-public class ActivityMapping extends BaseMapping {
-
+public class ActivityMapping extends BaseMapping<ActivityDoc, Object> {
 
   public ActivityMapping(Languages languages, MacroInterpreter macroInterpreter) {
-    super();
-    addIndexStringField("type", ActivityNormalizer.LogFields.TYPE.field());
-    addIndexStringField("action", ActivityNormalizer.LogFields.ACTION.field());
-    addIndexDatetimeField("createdAt", ActivityNormalizer.LogFields.CREATED_AT.field());
-    addIndexStringField("login", ActivityNormalizer.LogFields.LOGIN.field());
-    addIndexStringField("message", ActivityNormalizer.LogFields.MESSAGE.field());
-    addField("details", new DetailField());
+    map("type", ActivityNormalizer.LogFields.TYPE.field());
+    map("action", ActivityNormalizer.LogFields.ACTION.field());
+    mapDateTime("createdAt", ActivityNormalizer.LogFields.CREATED_AT.field());
+    map("login", ActivityNormalizer.LogFields.LOGIN.field());
+    map("message", ActivityNormalizer.LogFields.MESSAGE.field());
+    map("details", new IndexMapper<ActivityDoc, Object>(ActivityNormalizer.LogFields.DETAILS.field()) {
+      @Override
+      public void write(JsonWriter json, ActivityDoc activity, Object context) {
+        json.name("details").beginObject();
+        for (Map.Entry<String, String> detail : activity.details().entrySet()) {
+          json.prop(detail.getKey(), detail.getValue());
+        }
+        json.endObject();
+      }
+    });
   }
 
-  private static class DetailField extends IndexField<Activity> {
-    DetailField() {
-      super(ActivityNormalizer.LogFields.DETAILS.field());
-    }
-
-    @Override
-    public void write(JsonWriter json, Activity activity) {
-      json.name("details").beginObject();
-      for (Map.Entry<String, String> detail : activity.details().entrySet()) {
-        json.prop(detail.getKey(), detail.getValue());
-      }
-      json.endObject();
-    }
+  public void write(Activity activity, JsonWriter writer, SearchOptions options) {
+    doWrite((ActivityDoc)activity, null, writer, options);
   }
+
 }
index a6c40219281602f5826a310d15bcda4e8a7556b6..d221caad86eafd8da5e1734bbbd872ae2127e7e0 100644 (file)
@@ -27,7 +27,6 @@ import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.core.activity.Activity;
 import org.sonar.server.activity.ActivityService;
-import org.sonar.server.activity.index.ActivityDoc;
 import org.sonar.server.activity.index.ActivityQuery;
 import org.sonar.server.search.QueryOptions;
 import org.sonar.server.search.Result;
@@ -53,14 +52,14 @@ public class SearchAction implements RequestHandler {
   void define(WebService.NewController controller) {
     WebService.NewAction action = controller
       .createAction(SEARCH_ACTION)
-      .setDescription("Search for a logs")
+      .setDescription("Search for activities")
       .setSince("4.4")
       .setInternal(true)
       .setHandler(this);
 
     // Other parameters
     action.createParam(PARAM_TYPE)
-      .setDescription("Select types of log to search")
+      .setDescription("Types of activities to search")
       .setPossibleValues(Activity.Type.values())
       .setDefaultValue(StringUtils.join(Activity.Type.values(), ","));
 
@@ -87,7 +86,7 @@ public class SearchAction implements RequestHandler {
   private void writeLogs(Result<Activity> result, JsonWriter json, SearchOptions options) {
     json.name("logs").beginArray();
     for (Activity log : result.getHits()) {
-      mapping.write((ActivityDoc) log, json, options);
+      mapping.write(log, json, options);
     }
     json.endArray();
   }
index 58a2cf69cf06535a844447b87eb6853d44c1ad19..fde8fd299c5b4fe321e01e9cabdc60e4ec436e9a 100644 (file)
@@ -35,7 +35,6 @@ import org.sonar.server.rule.NewRule;
 import org.sonar.server.rule.ReactivationException;
 import org.sonar.server.rule.Rule;
 import org.sonar.server.rule.RuleService;
-import org.sonar.server.search.BaseDoc;
 
 import java.io.OutputStreamWriter;
 
@@ -158,7 +157,7 @@ public class CreateAction implements RequestHandler {
   private void writeResponse(Response response, RuleKey ruleKey) {
     Rule rule = service.getNonNullByKey(ruleKey);
     JsonWriter json = response.newJsonWriter().beginObject().name("rule");
-    mapping.write((BaseDoc) rule, json);
+    mapping.write(rule, json, null /* TODO replace by SearchOptions immutable constant */);
     json.endObject().close();
   }
 
@@ -169,7 +168,7 @@ public class CreateAction implements RequestHandler {
     stream.setStatus(409);
     stream.setMediaType(MimeTypes.JSON);
     JsonWriter json = JsonWriter.of(new OutputStreamWriter(stream.output())).beginObject().name("rule");
-    mapping.write((BaseDoc) rule, json);
+    mapping.write(rule, json, null /* TODO replace by SearchOptions immutable constant */);
     json.endObject().close();
   }
 }
index 3f462bd4cba2047987ea029ae6c4c32969d4a189..ecb1f2233a3bc742b3b98f005796c6a33b23ad55 100644 (file)
  */
 package org.sonar.server.rule.ws;
 
+import com.google.common.collect.Maps;
 import org.apache.commons.lang.StringEscapeUtils;
 import org.sonar.api.resources.Language;
 import org.sonar.api.resources.Languages;
+import org.sonar.api.server.debt.DebtCharacteristic;
+import org.sonar.api.server.debt.DebtModel;
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.markdown.Markdown;
 import org.sonar.server.rule.Rule;
 import org.sonar.server.rule.RuleParam;
+import org.sonar.server.rule.index.RuleDoc;
 import org.sonar.server.rule.index.RuleNormalizer;
 import org.sonar.server.search.ws.BaseMapping;
+import org.sonar.server.search.ws.SearchOptions;
 import org.sonar.server.text.MacroInterpreter;
 
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import java.util.Collection;
+import java.util.Map;
+
 /**
- * Conversion between RuleDoc and WS JSON response
+ * Conversion of {@link org.sonar.server.rule.index.RuleDoc} to WS JSON document
  */
-public class RuleMapping extends BaseMapping {
-
-  public RuleMapping(Languages languages, MacroInterpreter macroInterpreter) {
-    super();
-    addIndexStringField("repo", RuleNormalizer.RuleField.REPOSITORY.field());
-    addIndexStringField("name", RuleNormalizer.RuleField.NAME.field());
-    addIndexDatetimeField("createdAt", RuleNormalizer.RuleField.CREATED_AT.field());
-    addField("htmlDesc", new HtmlDescField(macroInterpreter));
-    addIndexStringField("severity", RuleNormalizer.RuleField.SEVERITY.field());
-    addIndexStringField("status", RuleNormalizer.RuleField.STATUS.field());
-    addIndexStringField("internalKey", RuleNormalizer.RuleField.INTERNAL_KEY.field());
-    addIndexBooleanField("isTemplate", RuleNormalizer.RuleField.IS_TEMPLATE.field());
-    addIndexStringField("templateKey", RuleNormalizer.RuleField.TEMPLATE_KEY.field());
-    addIndexArrayField("tags", RuleNormalizer.RuleField.TAGS.field());
-    addIndexArrayField("sysTags", RuleNormalizer.RuleField.SYSTEM_TAGS.field());
-    addField("defaultDebtChar", new IndexStringField("defaultDebtChar", RuleNormalizer.RuleField.DEFAULT_CHARACTERISTIC.field()));
-    addField("defaultDebtChar", new IndexStringField("defaultDebtSubChar", RuleNormalizer.RuleField.DEFAULT_SUB_CHARACTERISTIC.field()));
-    addField("debtChar", new IndexStringField("debtChar", RuleNormalizer.RuleField.CHARACTERISTIC.field(),
+public class RuleMapping extends BaseMapping<RuleDoc, RuleMappingContext> {
+
+  private final DebtModel debtModel;
+
+  public RuleMapping(final Languages languages, final MacroInterpreter macroInterpreter, final DebtModel debtModel) {
+    this.debtModel = debtModel;
+
+    mapBasicFields(languages);
+    mapDescriptionFields(macroInterpreter);
+    mapDebtFields();
+    mapParamFields();
+  }
+
+  private void mapBasicFields(final Languages languages) {
+    map("repo", RuleNormalizer.RuleField.REPOSITORY.field());
+    map("name", RuleNormalizer.RuleField.NAME.field());
+    mapDateTime("createdAt", RuleNormalizer.RuleField.CREATED_AT.field());
+    map("severity", RuleNormalizer.RuleField.SEVERITY.field());
+    map("status", RuleNormalizer.RuleField.STATUS.field());
+    map("internalKey", RuleNormalizer.RuleField.INTERNAL_KEY.field());
+    mapBoolean("isTemplate", RuleNormalizer.RuleField.IS_TEMPLATE.field());
+    map("templateKey", RuleNormalizer.RuleField.TEMPLATE_KEY.field());
+    mapArray("tags", RuleNormalizer.RuleField.TAGS.field());
+    mapArray("sysTags", RuleNormalizer.RuleField.SYSTEM_TAGS.field());
+    map("lang", RuleNormalizer.RuleField.LANGUAGE.field());
+    map("langName", new IndexMapper<RuleDoc, RuleMappingContext>(RuleNormalizer.RuleField.LANGUAGE.field()) {
+      @Override
+      public void write(JsonWriter json, RuleDoc rule, RuleMappingContext context) {
+        Language lang = languages.get(rule.language());
+        json.prop("langName", lang != null ? lang.getName() : null);
+      }
+    });
+  }
+
+  private void mapDescriptionFields(final MacroInterpreter macroInterpreter) {
+    map("htmlDesc", new Mapper<RuleDoc, RuleMappingContext>() {
+      @Override
+      public void write(JsonWriter json, RuleDoc rule, RuleMappingContext context) {
+        String html = rule.htmlDescription();
+        if (html != null) {
+          if (rule.isManual() || rule.templateKey() != null) {
+            String desc = StringEscapeUtils.escapeHtml(html);
+            desc = desc.replaceAll("\\n", "<br/>");
+            json.prop("htmlDesc", desc);
+          } else {
+            json.prop("htmlDesc", macroInterpreter.interpret(html));
+          }
+        }
+      }
+    });
+    map("noteLogin", RuleNormalizer.RuleField.NOTE_LOGIN.field());
+    map("mdNote", RuleNormalizer.RuleField.NOTE.field());
+    map("htmlNote", new IndexMapper<RuleDoc, RuleMappingContext>(RuleNormalizer.RuleField.NOTE.field()) {
+      @Override
+      public void write(JsonWriter json, RuleDoc rule, RuleMappingContext context) {
+        String markdownNote = rule.markdownNote();
+        if (markdownNote != null) {
+          json.prop("htmlNote", macroInterpreter.interpret(Markdown.convertToHtml(markdownNote)));
+        }
+      }
+    });
+  }
+
+  private void mapDebtFields() {
+    map("defaultDebtChar", new IndexStringMapper("defaultDebtChar", RuleNormalizer.RuleField.DEFAULT_CHARACTERISTIC.field()));
+    map("defaultDebtSubChar", new IndexStringMapper("defaultDebtSubChar", RuleNormalizer.RuleField.DEFAULT_SUB_CHARACTERISTIC.field()));
+    map("debtChar", new IndexStringMapper("debtChar", RuleNormalizer.RuleField.CHARACTERISTIC.field(),
       RuleNormalizer.RuleField.DEFAULT_CHARACTERISTIC.field()));
-    addField("debtChar", new IndexStringField("debtSubChar", RuleNormalizer.RuleField.SUB_CHARACTERISTIC.field(),
+    map("debtSubChar", new IndexStringMapper("debtSubChar", RuleNormalizer.RuleField.SUB_CHARACTERISTIC.field(),
       RuleNormalizer.RuleField.DEFAULT_SUB_CHARACTERISTIC.field()));
-    addField("debtRemFn", new IndexStringField("debtRemFnType", RuleNormalizer.RuleField.DEBT_FUNCTION_TYPE.field(),
+    map("debtCharName", new CharacteristicNameMapper());
+    map("debtSubCharName", new SubCharacteristicNameMapper());
+    map("debtRemFn", new IndexStringMapper("debtRemFnType", RuleNormalizer.RuleField.DEBT_FUNCTION_TYPE.field(),
       RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_TYPE.field()));
-    addField("debtRemFn", new IndexStringField("debtRemFnCoeff", RuleNormalizer.RuleField.DEBT_FUNCTION_COEFFICIENT.field(),
+    map("debtRemFn", new IndexStringMapper("debtRemFnCoeff", RuleNormalizer.RuleField.DEBT_FUNCTION_COEFFICIENT.field(),
       RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_COEFFICIENT.field()));
-    addField("debtRemFn", new IndexStringField("debtRemFnOffset", RuleNormalizer.RuleField.DEBT_FUNCTION_OFFSET.field(),
+    map("debtRemFn", new IndexStringMapper("debtRemFnOffset", RuleNormalizer.RuleField.DEBT_FUNCTION_OFFSET.field(),
       RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_OFFSET.field()));
-    addField("defaultDebtRemFn", new IndexStringField("defaultDebtRemFnType", RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_TYPE.field()));
-    addField("defaultDebtRemFn", new IndexStringField("defaultDebtRemFnCoeff", RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_COEFFICIENT.field()));
-    addField("defaultDebtRemFn", new IndexStringField("defaultDebtRemFnOffset", RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_OFFSET.field()));
-    addIndexStringField("effortToFixDescription", RuleNormalizer.RuleField.FIX_DESCRIPTION.field());
-    addIndexStringField("mdNote", RuleNormalizer.RuleField.NOTE.field());
-    addField("htmlNote", new HtmlNoteField(macroInterpreter));
-    addIndexStringField("noteLogin", RuleNormalizer.RuleField.NOTE_LOGIN.field());
-    addIndexStringField("lang", RuleNormalizer.RuleField.LANGUAGE.field());
-    addField("langName", new LangNameField(languages));
-    addField("debtCharName", new CharacteristicNameField());
-    addField("debtSubCharName", new SubCharacteristicNameField());
-    addField("debtOverloaded", new OverriddenField());
-    addField("params", new ParamsField());
+    map("defaultDebtRemFn", new IndexStringMapper("defaultDebtRemFnType", RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_TYPE.field()));
+    map("defaultDebtRemFn", new IndexStringMapper("defaultDebtRemFnCoeff", RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_COEFFICIENT.field()));
+    map("defaultDebtRemFn", new IndexStringMapper("defaultDebtRemFnOffset", RuleNormalizer.RuleField.DEFAULT_DEBT_FUNCTION_OFFSET.field()));
+    map("effortToFixDescription", RuleNormalizer.RuleField.FIX_DESCRIPTION.field());
+    map("debtOverloaded", new OverriddenMapper());
   }
 
-  private static class ParamsField extends IndexField<Rule> {
-    ParamsField() {
-      super(RuleNormalizer.RuleField.PARAMS.field());
-    }
-
-    @Override
-    public void write(JsonWriter json, Rule rule) {
-      json.name("params").beginArray();
-      for (RuleParam param : rule.params()) {
-        json
-          .beginObject()
-          .prop("key", param.key())
-          .prop("desc", param.description())
-          .prop("defaultValue", param.defaultValue())
-          .endObject();
+  private void mapParamFields() {
+    map("params", new IndexMapper<RuleDoc, RuleMappingContext>(RuleNormalizer.RuleField.PARAMS.field()) {
+      @Override
+      public void write(JsonWriter json, RuleDoc rule, RuleMappingContext context) {
+        json.name("params").beginArray();
+        for (RuleParam param : rule.params()) {
+          json
+            .beginObject()
+            .prop("key", param.key())
+            .prop("desc", param.description())
+            .prop("defaultValue", param.defaultValue())
+            .endObject();
+        }
+        json.endArray();
       }
-      json.endArray();
-    }
+    });
   }
 
-  private static class LangNameField extends IndexField<Rule> {
-    private final Languages languages;
-
-    private LangNameField(Languages languages) {
-      super(RuleNormalizer.RuleField.LANGUAGE.field());
-      this.languages = languages;
+  public void write(Rule rule, JsonWriter json, @Nullable SearchOptions options) {
+    RuleMappingContext context = new RuleMappingContext();
+    if (needDebtCharacteristicNames(options) && rule.debtCharacteristicKey() != null) {
+      // load debt characteristics if requested
+      context.add(debtModel.characteristicByKey(rule.debtCharacteristicKey()));
     }
+    if (needDebtSubCharacteristicNames(options) && rule.debtSubCharacteristicKey() != null) {
+      context.add(debtModel.characteristicByKey(rule.debtSubCharacteristicKey()));
+    }
+    doWrite((RuleDoc) rule, context, json, options);
+  }
 
-    @Override
-    public void write(JsonWriter json, Rule rule) {
-      String langKey = rule.language();
-      Language lang = languages.get(langKey);
-      json.prop("langName", lang != null ? lang.getName() : null);
+  public void write(Collection<Rule> rules, JsonWriter json, @Nullable SearchOptions options) {
+    if (!rules.isEmpty()) {
+      RuleMappingContext context = new RuleMappingContext();
+      if (needDebtCharacteristicNames(options) || needDebtSubCharacteristicNames(options)) {
+        // load all debt characteristics
+        context.addAll(debtModel.allCharacteristics());
+      }
+      for (Rule rule : rules) {
+        doWrite((RuleDoc) rule, context, json, options);
+      }
     }
   }
 
-  private static class HtmlNoteField extends IndexField<Rule> {
-    private final MacroInterpreter macroInterpreter;
+  private boolean needDebtCharacteristicNames(@Nullable SearchOptions options) {
+    return options == null || options.hasField("debtCharName");
+  }
+
+  private boolean needDebtSubCharacteristicNames(@Nullable SearchOptions options) {
+    return options == null || options.hasField("debtSubCharName");
+  }
 
-    private HtmlNoteField(MacroInterpreter macroInterpreter) {
-      super(RuleNormalizer.RuleField.NOTE.field());
-      this.macroInterpreter = macroInterpreter;
+  private static class CharacteristicNameMapper extends IndexMapper<RuleDoc, RuleMappingContext> {
+    private CharacteristicNameMapper() {
+      super(RuleNormalizer.RuleField.CHARACTERISTIC.field(), RuleNormalizer.RuleField.DEFAULT_CHARACTERISTIC.field());
     }
 
     @Override
-    public void write(JsonWriter json, Rule rule) {
-      String markdownNote = rule.markdownNote();
-      if (markdownNote != null) {
-        json.prop("htmlNote", macroInterpreter.interpret(Markdown.convertToHtml(markdownNote)));
-      }
+    public void write(JsonWriter json, RuleDoc rule, RuleMappingContext context) {
+      json.prop("debtCharName", context.debtCharacteristicName(rule.debtCharacteristicKey()));
     }
   }
 
-  private static class HtmlDescField implements Field<Rule> {
-    private final MacroInterpreter macroInterpreter;
-
-    private HtmlDescField(MacroInterpreter macroInterpreter) {
-      this.macroInterpreter = macroInterpreter;
+  private static class SubCharacteristicNameMapper extends IndexMapper<RuleDoc, RuleMappingContext> {
+    private SubCharacteristicNameMapper() {
+      super(RuleNormalizer.RuleField.SUB_CHARACTERISTIC.field(), RuleNormalizer.RuleField.DEFAULT_SUB_CHARACTERISTIC.field());
     }
 
     @Override
-    public void write(JsonWriter json, Rule rule) {
-      String html = rule.htmlDescription();
-      if (html != null) {
-        if (rule.isManual() || rule.templateKey() != null) {
-          String desc = StringEscapeUtils.escapeHtml(html);
-          desc = desc.replaceAll("\\n", "<br/>");
-          json.prop("htmlDesc", desc);
-        } else {
-          json.prop("htmlDesc", macroInterpreter.interpret(html));
-        }
-      }
+    public void write(JsonWriter json, RuleDoc rule, RuleMappingContext context) {
+      json.prop("debtSubCharName", context.debtCharacteristicName(rule.debtSubCharacteristicKey()));
     }
   }
 
-  private static class CharacteristicNameField implements Field<Rule> {
+  private static class OverriddenMapper implements Mapper<RuleDoc, RuleMappingContext> {
     @Override
-    public void write(JsonWriter json, Rule rule) {
-      // TODO set characteristic name
-      json.prop("debtCharName", rule.debtCharacteristicKey());
+    public void write(JsonWriter json, RuleDoc rule, RuleMappingContext context) {
+      json.prop("debtOverloaded", rule.debtOverloaded());
     }
   }
+}
 
-  private static class SubCharacteristicNameField implements Field<Rule> {
-    @Override
-    public void write(JsonWriter json, Rule rule) {
-      // TODO set characteristic name
-      json.prop("debtSubCharName", rule.debtSubCharacteristicKey());
+class RuleMappingContext {
+  private final Map<String, String> debtCharacteristicNamesByKey = Maps.newHashMap();
+
+  @CheckForNull
+  public String debtCharacteristicName(String key) {
+    return debtCharacteristicNamesByKey.get(key);
+  }
+
+  void add(@Nullable DebtCharacteristic c) {
+    if (c != null) {
+      debtCharacteristicNamesByKey.put(c.key(), c.name());
     }
   }
 
-  private static class OverriddenField implements Field<Rule> {
-    @Override
-    public void write(JsonWriter json, Rule rule) {
-      json.prop("debtOverloaded", rule.debtOverloaded());
+  void addAll(Collection<DebtCharacteristic> coll) {
+    for (DebtCharacteristic c : coll) {
+      add(c);
     }
   }
 }
index 06e3334574e2d13e24755f7fdffa559be87729cf..64137e0730019faa9ad5e306d065c7e67be7eddc 100644 (file)
@@ -109,7 +109,7 @@ public class SearchAction implements RequestHandler {
 
     action
       .createParam(PARAM_KEY)
-      .setDescription("Single or list of keys of rule to search for")
+      .setDescription("Key of rule to search for")
       .setExampleValue("squid:S001");
 
     action
index 9bbc2018630136cbf08f9638c3c13bdbb2caf6d8..fb2d98994f6d3fb6f8e097062ecbf8d4190214e3 100644 (file)
@@ -29,7 +29,6 @@ import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.rule.Rule;
 import org.sonar.server.rule.RuleService;
-import org.sonar.server.search.BaseDoc;
 
 /**
  * @since 4.4
@@ -78,7 +77,7 @@ public class ShowAction implements RequestHandler {
       throw new NotFoundException("Rule not found: " + key);
     }
     JsonWriter json = response.newJsonWriter().beginObject().name("rule");
-    mapping.write((BaseDoc) rule, json);
+    mapping.write(rule, json, null /* TODO replace by SearchOptions immutable constant */);
 
     if (request.mandatoryParamAsBoolean(PARAM_ACTIVES)) {
       activeRuleCompleter.completeShow(rule, json);
index 553491f93dc1eba3a84c5b569de8dd7b01c84350..a2246626b5fbd38ce10a14c233578b45ed3f9940 100644 (file)
@@ -37,7 +37,6 @@ import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.rule.Rule;
 import org.sonar.server.rule.RuleService;
 import org.sonar.server.rule.RuleUpdate;
-import org.sonar.server.search.BaseDoc;
 
 public class UpdateAction implements RequestHandler {
 
@@ -160,7 +159,7 @@ public class UpdateAction implements RequestHandler {
     return update;
   }
 
-  private RuleUpdate createRuleUpdate(RuleKey key){
+  private RuleUpdate createRuleUpdate(RuleKey key) {
     Rule rule = service.getByKey(key);
     if (rule == null) {
       throw new NotFoundException("This rule does not exists : " + key);
@@ -215,7 +214,7 @@ public class UpdateAction implements RequestHandler {
   private void writeResponse(Response response, RuleKey ruleKey) {
     Rule rule = service.getNonNullByKey(ruleKey);
     JsonWriter json = response.newJsonWriter().beginObject().name("rule");
-    mapping.write((BaseDoc) rule, json);
+    mapping.write(rule, json, null /* TODO replace by SearchOptions immutable constant */);
     json.endObject().close();
   }
 }
index 0d7e44d56d87f6a699787a79999ceab9815c45a3..69a73937c6a64957c194dd2c5868f846648c54ab 100644 (file)
@@ -34,15 +34,18 @@ import java.util.List;
 import java.util.Set;
 
 /**
- * Mapping of search documents (see BaseDoc) to WS JSON responses
+ * Mapping of search documents (see {@link org.sonar.server.search.BaseDoc}) to WS JSON responses
  */
-public abstract class BaseMapping implements ServerComponent {
+public abstract class BaseMapping<DOC extends BaseDoc, CTX> implements ServerComponent {
 
-  private final Multimap<String, String> indexFields = LinkedHashMultimap.create();
-  private final Multimap<String, BaseMapping.Field> fields = LinkedHashMultimap.create();
+  private final Multimap<String, String> indexFieldsByWsFields = LinkedHashMultimap.create();
+  private final Multimap<String, Mapper> mappers = LinkedHashMultimap.create();
 
+  /**
+   * All the WS supported fields
+   */
   public Set<String> supportedFields() {
-    return fields.keySet();
+    return mappers.keySet();
   }
 
   public QueryOptions newQueryOptions(SearchOptions options) {
@@ -51,67 +54,73 @@ public abstract class BaseMapping implements ServerComponent {
     List<String> optionFields = options.fields();
     if (optionFields != null) {
       for (String optionField : optionFields) {
-        result.addFieldsToReturn(this.indexFields.get(optionField));
+        result.addFieldsToReturn(indexFieldsByWsFields.get(optionField));
       }
     }
     return result;
   }
 
-  public void write(BaseDoc doc, JsonWriter json) {
-    write(doc, json, null);
+  /**
+   * Write all document fields
+   */
+  protected void doWrite(DOC doc, @Nullable CTX context, JsonWriter json) {
+    doWrite(doc, context, json, null);
   }
 
-  public void write(BaseDoc doc, JsonWriter json, @Nullable SearchOptions options) {
+  /**
+   * Write only requested document fields
+   */
+  protected void doWrite(DOC doc, @Nullable CTX context, JsonWriter json, @Nullable SearchOptions options) {
     json.beginObject();
     json.prop("key", doc.keyField());
     if (options == null || options.fields() == null) {
       // return all fields
-      for (BaseMapping.Field field : fields.values()) {
-        field.write(json, doc);
+      for (Mapper mapper : mappers.values()) {
+        mapper.write(json, doc, context);
       }
     } else {
       for (String optionField : options.fields()) {
-        for (BaseMapping.Field field : fields.get(optionField)) {
-          field.write(json, doc);
+        for (Mapper mapper : mappers.get(optionField)) {
+          mapper.write(json, doc, context);
         }
       }
     }
     json.endObject();
   }
 
-  protected BaseMapping addIndexStringField(String key, String indexKey) {
-    return addField(key, new IndexStringField(key, indexKey));
+  protected BaseMapping map(String key, String indexKey) {
+    return map(key, new IndexStringMapper(key, indexKey));
   }
 
-  protected BaseMapping addIndexBooleanField(String key, String indexKey) {
-    return addField(key, new IndexBooleanField(key, indexKey));
+  protected BaseMapping mapBoolean(String key, String indexKey) {
+    return map(key, new IndexBooleanMapper(key, indexKey));
   }
 
-  protected BaseMapping addIndexDatetimeField(String key, String indexKey) {
-    return addField(key, new IndexDatetimeField(key, indexKey));
+  protected BaseMapping mapDateTime(String key, String indexKey) {
+    return map(key, new IndexDatetimeMapper(key, indexKey));
   }
 
-  protected BaseMapping addIndexArrayField(String key, String indexKey) {
-    return addField(key, new IndexArrayField(key, indexKey));
+  protected BaseMapping mapArray(String key, String indexKey) {
+    return map(key, new IndexArrayMapper(key, indexKey));
   }
 
-  protected BaseMapping addField(String key, Field field) {
-    fields.put(key, field);
-    if (field instanceof IndexField) {
-      IndexField indexField = (IndexField) field;
-      indexFields.putAll(key, Arrays.asList(indexField.indexFields()));
+  protected BaseMapping map(String key, Mapper mapper) {
+    mappers.put(key, mapper);
+    if (mapper instanceof IndexMapper) {
+      IndexMapper indexField = (IndexMapper) mapper;
+      indexFieldsByWsFields.putAll(key, Arrays.asList(indexField.indexFields()));
     }
     return this;
   }
 
-  public static interface Field<D> {
-    void write(JsonWriter json, D doc);
+  public static interface Mapper<DOC extends BaseDoc, CTX> {
+    void write(JsonWriter json, DOC doc, CTX context);
   }
 
-  public abstract static class IndexField<D> implements Field<D> {
+  public abstract static class IndexMapper<DOC extends BaseDoc, CTX> implements Mapper<DOC, CTX> {
     protected final String[] indexFields;
 
-    protected IndexField(String... indexFields) {
+    protected IndexMapper(String... indexFields) {
       this.indexFields = indexFields;
     }
 
@@ -123,21 +132,21 @@ public abstract class BaseMapping implements ServerComponent {
   /**
    * String field
    */
-  public static class IndexStringField extends IndexField<BaseDoc> {
+  public static class IndexStringMapper<DOC extends BaseDoc, CTX> extends IndexMapper<DOC,CTX> {
     private final String key;
 
-    public IndexStringField(String key, String indexKey, String defaultIndexKey) {
+    public IndexStringMapper(String key, String indexKey, String defaultIndexKey) {
       super(indexKey, defaultIndexKey);
       this.key = key;
     }
 
-    public IndexStringField(String key, String indexKey) {
+    public IndexStringMapper(String key, String indexKey) {
       super(indexKey);
       this.key = key;
     }
 
     @Override
-    public void write(JsonWriter json, BaseDoc doc) {
+    public void write(JsonWriter json, DOC doc, CTX context) {
       Object val = doc.getNullableField(indexFields[0]);
       if (val == null && indexFields.length == 2) {
         // There is an alternative value
@@ -147,31 +156,31 @@ public abstract class BaseMapping implements ServerComponent {
     }
   }
 
-  public static class IndexBooleanField extends IndexField<BaseDoc> {
+  public static class IndexBooleanMapper<DOC extends BaseDoc, CTX> extends IndexMapper<DOC,CTX> {
     private final String key;
 
-    public IndexBooleanField(String key, String indexKey) {
+    public IndexBooleanMapper(String key, String indexKey) {
       super(indexKey);
       this.key = key;
     }
 
     @Override
-    public void write(JsonWriter json, BaseDoc doc) {
+    public void write(JsonWriter json, DOC doc, CTX context) {
       Boolean val = doc.getNullableField(indexFields[0]);
       json.prop(key, val != null ? val.booleanValue() : null);
     }
   }
 
-  public static class IndexArrayField extends IndexField<BaseDoc> {
+  public static class IndexArrayMapper<DOC extends BaseDoc, CTX> extends IndexMapper<DOC,CTX> {
     private final String key;
 
-    public IndexArrayField(String key, String indexKey) {
+    public IndexArrayMapper(String key, String indexKey) {
       super(indexKey);
       this.key = key;
     }
 
     @Override
-    public void write(JsonWriter json, BaseDoc doc) {
+    public void write(JsonWriter json, DOC doc, CTX context) {
       Iterable<String> values = doc.getNullableField(indexFields[0]);
       if (values != null) {
         json.name(key).beginArray().values(values).endArray();
@@ -179,16 +188,16 @@ public abstract class BaseMapping implements ServerComponent {
     }
   }
 
-  public static class IndexDatetimeField extends IndexField<BaseDoc> {
+  public static class IndexDatetimeMapper<DOC extends BaseDoc, CTX> extends IndexMapper<DOC,CTX> {
     private final String key;
 
-    public IndexDatetimeField(String key, String indexKey) {
+    public IndexDatetimeMapper(String key, String indexKey) {
       super(indexKey);
       this.key = key;
     }
 
     @Override
-    public void write(JsonWriter json, BaseDoc doc) {
+    public void write(JsonWriter json, DOC doc, CTX context) {
       String val = doc.getNullableField(indexFields[0]);
       if (val != null) {
         json.propDateTime(key, IndexUtils.parseDateTime(val));
index 0b0bd2bc1628dab93584da2a8411238e66f45ab2..d809c8582cab04944d36eb42f9ff3b511699c6a5 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.server.rule.ws;
 
 import org.junit.Test;
 import org.sonar.api.resources.Languages;
+import org.sonar.api.server.debt.DebtModel;
 import org.sonar.api.server.ws.internal.SimpleGetRequest;
 import org.sonar.server.rule.index.RuleNormalizer;
 import org.sonar.server.search.QueryOptions;
@@ -34,10 +35,11 @@ public class RuleMappingTest {
 
   Languages languages = new Languages();
   MacroInterpreter macroInterpreter = mock(MacroInterpreter.class);
+  DebtModel debtModel = mock(DebtModel.class);
 
   @Test
   public void toQueryOptions_load_all_fields() throws Exception {
-    RuleMapping mapping = new RuleMapping(languages, macroInterpreter);
+    RuleMapping mapping = new RuleMapping(languages, macroInterpreter, debtModel);
     SimpleGetRequest request = new SimpleGetRequest();
     request.setParam("p", "1");
     request.setParam("ps", "10");
@@ -48,7 +50,7 @@ public class RuleMappingTest {
 
   @Test
   public void toQueryOptions_load_only_few_simple_fields() throws Exception {
-    RuleMapping mapping = new RuleMapping(languages, macroInterpreter);
+    RuleMapping mapping = new RuleMapping(languages, macroInterpreter, debtModel);
     SimpleGetRequest request = new SimpleGetRequest();
     request.setParam("p", "1");
     request.setParam("ps", "10");
@@ -63,7 +65,7 @@ public class RuleMappingTest {
 
   @Test
   public void toQueryOptions_langName_requires_lang() throws Exception {
-    RuleMapping mapping = new RuleMapping(languages, macroInterpreter);
+    RuleMapping mapping = new RuleMapping(languages, macroInterpreter, debtModel);
     SimpleGetRequest request = new SimpleGetRequest();
     request.setParam("p", "1");
     request.setParam("ps", "10");
@@ -75,7 +77,7 @@ public class RuleMappingTest {
 
   @Test
   public void toQueryOptions_debt_requires_group_of_fields() throws Exception {
-    RuleMapping mapping = new RuleMapping(languages, macroInterpreter);
+    RuleMapping mapping = new RuleMapping(languages, macroInterpreter, debtModel);
     SimpleGetRequest request = new SimpleGetRequest();
     request.setParam("p", "1");
     request.setParam("ps", "10");
@@ -93,7 +95,7 @@ public class RuleMappingTest {
 
   @Test
   public void toQueryOptions_html_note_requires_markdown_note() throws Exception {
-    RuleMapping mapping = new RuleMapping(languages, macroInterpreter);
+    RuleMapping mapping = new RuleMapping(languages, macroInterpreter, debtModel);
     SimpleGetRequest request = new SimpleGetRequest();
     request.setParam("p", "1");
     request.setParam("ps", "10");
@@ -105,7 +107,7 @@ public class RuleMappingTest {
 
   @Test
   public void toQueryOptions_debt_characteristics() throws Exception {
-    RuleMapping mapping = new RuleMapping(languages, macroInterpreter);
+    RuleMapping mapping = new RuleMapping(languages, macroInterpreter, debtModel);
     SimpleGetRequest request = new SimpleGetRequest();
     request.setParam("p", "1");
     request.setParam("ps", "10");
@@ -114,8 +116,6 @@ public class RuleMappingTest {
 
     assertThat(queryOptions.getFieldsToReturn()).containsOnly(
       RuleNormalizer.RuleField.CHARACTERISTIC.field(),
-      RuleNormalizer.RuleField.SUB_CHARACTERISTIC.field(),
-      RuleNormalizer.RuleField.DEFAULT_CHARACTERISTIC.field(),
-      RuleNormalizer.RuleField.DEFAULT_SUB_CHARACTERISTIC.field());
+      RuleNormalizer.RuleField.DEFAULT_CHARACTERISTIC.field());
   }
 }
index 3534bf24c74cc20195a84b285e6687233ebe7fd8..1fadc2332259235b99259d1180921b2d3faa3359 100644 (file)
@@ -179,7 +179,7 @@ public class RulesWebServiceMediumTest {
 
     MockUserSession.set();
     WsTester.TestRequest request = tester.wsTester().newGetRequest(API_ENDPOINT, API_SEARCH_METHOD);
-    request.setParam(SearchOptions.PARAM_FIELDS, "debtRemFn,debtChar");
+    request.setParam(SearchOptions.PARAM_FIELDS, "debtRemFn,debtChar,debtCharName");
     WsTester.Result result = request.execute();
     result.assertJson(this.getClass(), "search_debt_rule.json");
   }
index a9dcb383451861ae53c4a2925c19679224ded529..7e2185547e19b0924891ca05396c0e0cc4e8b609 100644 (file)
@@ -102,7 +102,7 @@ public class ShowActionMediumTest {
       .setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN)
       .setLogin("me");
 
-    CharacteristicDto characteristicDto = new CharacteristicDto().setKey("API").setName("API").setEnabled(true);
+    CharacteristicDto characteristicDto = new CharacteristicDto().setKey("API").setName("Api").setEnabled(true);
     tester.get(CharacteristicDao.class).insert(characteristicDto, session);
     CharacteristicDto subCharacteristicDto = new CharacteristicDto().setKey("API_ABUSE").setName("API Abuse").setEnabled(true).setParentId(characteristicDto.getId());
     tester.get(CharacteristicDao.class).insert(subCharacteristicDto, session);
@@ -131,7 +131,6 @@ public class ShowActionMediumTest {
       .setParam("key", ruleDto.getKey().toString());
     WsTester.Result response = request.execute();
 
-    System.out.println("response = " + response.outputAsString());
     response.assertJson(getClass(), "show_rule_with_default_debt_infos.json", false);
   }
 
@@ -141,7 +140,7 @@ public class ShowActionMediumTest {
       .setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN)
       .setLogin("me");
 
-    CharacteristicDto characteristicDto = new CharacteristicDto().setKey("API").setName("API").setEnabled(true);
+    CharacteristicDto characteristicDto = new CharacteristicDto().setKey("API").setName("Api").setEnabled(true);
     tester.get(CharacteristicDao.class).insert(characteristicDto, session);
     CharacteristicDto subCharacteristicDto = new CharacteristicDto().setKey("API_ABUSE").setName("API Abuse").setEnabled(true).setParentId(characteristicDto.getId());
     tester.get(CharacteristicDao.class).insert(subCharacteristicDto, session);
@@ -177,12 +176,12 @@ public class ShowActionMediumTest {
       .setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN)
       .setLogin("me");
 
-    CharacteristicDto defaultCharacteristic = new CharacteristicDto().setKey("API").setName("API").setEnabled(true);
+    CharacteristicDto defaultCharacteristic = new CharacteristicDto().setKey("API").setName("Api").setEnabled(true);
     tester.get(CharacteristicDao.class).insert(defaultCharacteristic, session);
     CharacteristicDto defaultSubCharacteristic = new CharacteristicDto().setKey("API_ABUSE").setName("API Abuse").setEnabled(true).setParentId(defaultCharacteristic.getId());
     tester.get(CharacteristicDao.class).insert(defaultSubCharacteristic, session);
 
-    CharacteristicDto characteristic = new CharacteristicDto().setKey("OS").setName("OS").setEnabled(true);
+    CharacteristicDto characteristic = new CharacteristicDto().setKey("OS").setName("Os").setEnabled(true);
     tester.get(CharacteristicDao.class).insert(characteristic, session);
     CharacteristicDto subCharacteristic = new CharacteristicDto().setKey("OS_RELATED_PORTABILITY").setName("Portability").setEnabled(true).setParentId(characteristic.getId());
     tester.get(CharacteristicDao.class).insert(subCharacteristic, session);
index 7006001b0a56bbf147d96c5dc8df141426cfc2a9..2a2240862fa706177277cd7faab7e43b6eb6f302 100644 (file)
@@ -8,9 +8,9 @@
     "status": "BETA",
     "isTemplate": false,
     "debtChar": "OS",
-    "debtCharName": "OS",
+    "debtCharName": "Os",
     "debtSubChar": "OS_RELATED_PORTABILITY",
-    "debtSubCharName": "OS_RELATED_PORTABILITY",
+    "debtSubCharName": "Portability",
     "debtRemFnType": "LINEAR_OFFSET",
     "debtRemFnCoeff": "5d",
     "debtRemFnOffset": "10h",
index 61d541adb1ced78e97ec22f9b7919e20cfbf64d2..06381aae193bd18b8808557ad70207da4ba1da3b 100644 (file)
@@ -1,9 +1,9 @@
 {
   "rule": {
     "debtChar": "API",
-    "debtCharName": "API",
+    "debtCharName": "Api",
     "debtSubChar": "API_ABUSE",
-    "debtSubCharName": "API_ABUSE",
+    "debtSubCharName": "API Abuse",
     "debtRemFnType": "LINEAR_OFFSET",
     "debtRemFnCoeff": "5d",
     "debtRemFnOffset": "10h",
index da095d9cfc6f1a1886c8a3051419077110b3af3f..7643a75c19e43024a1731d394028da0086b38e03 100644 (file)
@@ -8,9 +8,9 @@
     "status": "BETA",
     "isTemplate": false,
     "debtChar": "API",
-    "debtCharName": "API",
+    "debtCharName": "Api",
     "debtSubChar": "API_ABUSE",
-    "debtSubCharName": "API_ABUSE",
+    "debtSubCharName": "API Abuse",
     "debtRemFnType": "LINEAR_OFFSET",
     "debtRemFnCoeff": "5d",
     "debtRemFnOffset": "10h",