From f1fa4be793dcadbd6628150d24e441c2095e0a54 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Thu, 10 Jul 2014 07:27:01 +0200 Subject: [PATCH] SONAR-5007 return debt characteristic names in WS responses --- .../activity/ws/ActivitiesWebService.java | 2 +- .../server/activity/ws/ActivityMapping.java | 45 ++- .../server/activity/ws/SearchAction.java | 7 +- .../sonar/server/rule/ws/CreateAction.java | 5 +- .../org/sonar/server/rule/ws/RuleMapping.java | 262 +++++++++++------- .../sonar/server/rule/ws/SearchAction.java | 2 +- .../org/sonar/server/rule/ws/ShowAction.java | 3 +- .../sonar/server/rule/ws/UpdateAction.java | 5 +- .../sonar/server/search/ws/BaseMapping.java | 95 ++++--- .../sonar/server/rule/ws/RuleMappingTest.java | 18 +- .../rule/ws/RulesWebServiceMediumTest.java | 2 +- .../server/rule/ws/ShowActionMediumTest.java | 9 +- ...ith_default_and_overridden_debt_infos.json | 4 +- .../show_rule_with_default_debt_infos.json | 4 +- .../show_rule_with_overridden_debt_infos.json | 4 +- 15 files changed, 263 insertions(+), 204 deletions(-) diff --git a/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivitiesWebService.java b/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivitiesWebService.java index e6aa30ce395..925a0456d2b 100644 --- a/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivitiesWebService.java +++ b/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivitiesWebService.java @@ -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(); diff --git a/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivityMapping.java b/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivityMapping.java index ae2deb2f453..e2910b6c4ea 100644 --- a/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivityMapping.java +++ b/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivityMapping.java @@ -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 { 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(ActivityNormalizer.LogFields.DETAILS.field()) { + @Override + public void write(JsonWriter json, ActivityDoc activity, Object context) { + json.name("details").beginObject(); + for (Map.Entry detail : activity.details().entrySet()) { + json.prop(detail.getKey(), detail.getValue()); + } + json.endObject(); + } + }); } - private static class DetailField extends IndexField { - DetailField() { - super(ActivityNormalizer.LogFields.DETAILS.field()); - } - - @Override - public void write(JsonWriter json, Activity activity) { - json.name("details").beginObject(); - for (Map.Entry 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); } + } diff --git a/sonar-server/src/main/java/org/sonar/server/activity/ws/SearchAction.java b/sonar-server/src/main/java/org/sonar/server/activity/ws/SearchAction.java index a6c40219281..d221caad86e 100644 --- a/sonar-server/src/main/java/org/sonar/server/activity/ws/SearchAction.java +++ b/sonar-server/src/main/java/org/sonar/server/activity/ws/SearchAction.java @@ -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 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(); } diff --git a/sonar-server/src/main/java/org/sonar/server/rule/ws/CreateAction.java b/sonar-server/src/main/java/org/sonar/server/rule/ws/CreateAction.java index 58a2cf69cf0..fde8fd299c5 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/ws/CreateAction.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/ws/CreateAction.java @@ -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(); } } diff --git a/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java b/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java index 3f462bd4cba..ecb1f2233a3 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java @@ -19,157 +19,213 @@ */ 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 { + + 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(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() { + @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", "
"); + 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(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 { - 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(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 { - 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 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 { - 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 { + 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 { - private final MacroInterpreter macroInterpreter; - - private HtmlDescField(MacroInterpreter macroInterpreter) { - this.macroInterpreter = macroInterpreter; + private static class SubCharacteristicNameMapper extends IndexMapper { + 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", "
"); - 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 { + private static class OverriddenMapper implements Mapper { @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 { - @Override - public void write(JsonWriter json, Rule rule) { - // TODO set characteristic name - json.prop("debtSubCharName", rule.debtSubCharacteristicKey()); +class RuleMappingContext { + private final Map 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 { - @Override - public void write(JsonWriter json, Rule rule) { - json.prop("debtOverloaded", rule.debtOverloaded()); + void addAll(Collection coll) { + for (DebtCharacteristic c : coll) { + add(c); } } } diff --git a/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java b/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java index 06e3334574e..64137e07300 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java @@ -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 diff --git a/sonar-server/src/main/java/org/sonar/server/rule/ws/ShowAction.java b/sonar-server/src/main/java/org/sonar/server/rule/ws/ShowAction.java index 9bbc2018630..fb2d98994f6 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/ws/ShowAction.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/ws/ShowAction.java @@ -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); diff --git a/sonar-server/src/main/java/org/sonar/server/rule/ws/UpdateAction.java b/sonar-server/src/main/java/org/sonar/server/rule/ws/UpdateAction.java index 553491f93dc..a2246626b5f 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/ws/UpdateAction.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/ws/UpdateAction.java @@ -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(); } } diff --git a/sonar-server/src/main/java/org/sonar/server/search/ws/BaseMapping.java b/sonar-server/src/main/java/org/sonar/server/search/ws/BaseMapping.java index 0d7e44d56d8..69a73937c6a 100644 --- a/sonar-server/src/main/java/org/sonar/server/search/ws/BaseMapping.java +++ b/sonar-server/src/main/java/org/sonar/server/search/ws/BaseMapping.java @@ -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 implements ServerComponent { - private final Multimap indexFields = LinkedHashMultimap.create(); - private final Multimap fields = LinkedHashMultimap.create(); + private final Multimap indexFieldsByWsFields = LinkedHashMultimap.create(); + private final Multimap mappers = LinkedHashMultimap.create(); + /** + * All the WS supported fields + */ public Set supportedFields() { - return fields.keySet(); + return mappers.keySet(); } public QueryOptions newQueryOptions(SearchOptions options) { @@ -51,67 +54,73 @@ public abstract class BaseMapping implements ServerComponent { List 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 { - void write(JsonWriter json, D doc); + public static interface Mapper { + void write(JsonWriter json, DOC doc, CTX context); } - public abstract static class IndexField implements Field { + public abstract static class IndexMapper implements Mapper { 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 { + public static class IndexStringMapper extends IndexMapper { 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 { + public static class IndexBooleanMapper extends IndexMapper { 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 { + public static class IndexArrayMapper extends IndexMapper { 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 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 { + public static class IndexDatetimeMapper extends IndexMapper { 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)); diff --git a/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleMappingTest.java b/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleMappingTest.java index 0b0bd2bc162..d809c8582ca 100644 --- a/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleMappingTest.java +++ b/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleMappingTest.java @@ -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()); } } diff --git a/sonar-server/src/test/java/org/sonar/server/rule/ws/RulesWebServiceMediumTest.java b/sonar-server/src/test/java/org/sonar/server/rule/ws/RulesWebServiceMediumTest.java index 3534bf24c74..1fadc233225 100644 --- a/sonar-server/src/test/java/org/sonar/server/rule/ws/RulesWebServiceMediumTest.java +++ b/sonar-server/src/test/java/org/sonar/server/rule/ws/RulesWebServiceMediumTest.java @@ -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"); } diff --git a/sonar-server/src/test/java/org/sonar/server/rule/ws/ShowActionMediumTest.java b/sonar-server/src/test/java/org/sonar/server/rule/ws/ShowActionMediumTest.java index a9dcb383451..7e2185547e1 100644 --- a/sonar-server/src/test/java/org/sonar/server/rule/ws/ShowActionMediumTest.java +++ b/sonar-server/src/test/java/org/sonar/server/rule/ws/ShowActionMediumTest.java @@ -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); diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/ws/ShowActionMediumTest/show_rule_with_default_and_overridden_debt_infos.json b/sonar-server/src/test/resources/org/sonar/server/rule/ws/ShowActionMediumTest/show_rule_with_default_and_overridden_debt_infos.json index 7006001b0a5..2a2240862fa 100644 --- a/sonar-server/src/test/resources/org/sonar/server/rule/ws/ShowActionMediumTest/show_rule_with_default_and_overridden_debt_infos.json +++ b/sonar-server/src/test/resources/org/sonar/server/rule/ws/ShowActionMediumTest/show_rule_with_default_and_overridden_debt_infos.json @@ -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", diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/ws/ShowActionMediumTest/show_rule_with_default_debt_infos.json b/sonar-server/src/test/resources/org/sonar/server/rule/ws/ShowActionMediumTest/show_rule_with_default_debt_infos.json index 61d541adb1c..06381aae193 100644 --- a/sonar-server/src/test/resources/org/sonar/server/rule/ws/ShowActionMediumTest/show_rule_with_default_debt_infos.json +++ b/sonar-server/src/test/resources/org/sonar/server/rule/ws/ShowActionMediumTest/show_rule_with_default_debt_infos.json @@ -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", diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/ws/ShowActionMediumTest/show_rule_with_overridden_debt_infos.json b/sonar-server/src/test/resources/org/sonar/server/rule/ws/ShowActionMediumTest/show_rule_with_overridden_debt_infos.json index da095d9cfc6..7643a75c19e 100644 --- a/sonar-server/src/test/resources/org/sonar/server/rule/ws/ShowActionMediumTest/show_rule_with_overridden_debt_infos.json +++ b/sonar-server/src/test/resources/org/sonar/server/rule/ws/ShowActionMediumTest/show_rule_with_overridden_debt_infos.json @@ -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", -- 2.39.5