From e7b45f77f155d73d1563c1c06e0b91b871ea65c8 Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Wed, 16 Sep 2015 15:10:22 +0200 Subject: [PATCH] SONAR-6822 WS rules/search from json to protobuf --- .../exceptions/BadRequestException.java | 13 +- .../server/exceptions/ForbiddenException.java | 6 +- .../server/exceptions/NotFoundException.java | 8 +- .../exceptions/UnauthorizedException.java | 8 +- .../main/java/org/sonar/server/rule/Rule.java | 2 + .../org/sonar/server/rule/index/RuleDoc.java | 2 + .../server/rule/ws/ActiveRuleCompleter.java | 123 +- .../sonar/server/rule/ws/CreateAction.java | 49 +- .../org/sonar/server/rule/ws/RuleMapping.java | 422 +- .../sonar/server/rule/ws/SearchAction.java | 72 +- .../org/sonar/server/rule/ws/ShowAction.java | 20 +- .../sonar/server/rule/ws/UpdateAction.java | 17 +- .../sonar/server/search/ws/BaseMapping.java | 14 +- .../search_2_rules_fields.json | 9 +- .../sonar/db/component/ComponentMapper.xml | 2 +- .../org/sonarqube/ws/QualityProfiles.java | 422 +- .../main/gen-java/org/sonarqube/ws/Rules.java | 16120 +++++++++++++++- .../main/protobuf/ws-qualityprofiles.proto | 4 +- sonar-ws/src/main/protobuf/ws-rules.proto | 116 + 19 files changed, 16914 insertions(+), 515 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/exceptions/BadRequestException.java b/server/sonar-server/src/main/java/org/sonar/server/exceptions/BadRequestException.java index 22f224b3d6f..f6241cd93a4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/exceptions/BadRequestException.java +++ b/server/sonar-server/src/main/java/org/sonar/server/exceptions/BadRequestException.java @@ -20,36 +20,35 @@ package org.sonar.server.exceptions; import com.google.common.base.Objects; +import java.util.List; import org.sonar.api.utils.ValidationMessages; -import java.util.List; +import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; /** * Request is not valid and can not be processed. */ public class BadRequestException extends ServerException { - private static final int BAD_REQUEST = 400; - private final Errors errors; public BadRequestException(String l10nKey, Object... l10nParams) { - super(BAD_REQUEST); + super(HTTP_BAD_REQUEST); this.errors = new Errors().add(Message.of(l10nKey, l10nParams)); } public BadRequestException(List messages) { - super(BAD_REQUEST); + super(HTTP_BAD_REQUEST); this.errors = new Errors().add(messages); } public BadRequestException(Errors e) { - super(BAD_REQUEST); + super(HTTP_BAD_REQUEST); this.errors = e; } public BadRequestException(ValidationMessages validationMessages) { - super(BAD_REQUEST); + super(HTTP_BAD_REQUEST); this.errors = new Errors(); for (String s : validationMessages.getErrors()) { errors.add(Message.of(s)); diff --git a/server/sonar-server/src/main/java/org/sonar/server/exceptions/ForbiddenException.java b/server/sonar-server/src/main/java/org/sonar/server/exceptions/ForbiddenException.java index 0ac360c29ad..1aef994d703 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/exceptions/ForbiddenException.java +++ b/server/sonar-server/src/main/java/org/sonar/server/exceptions/ForbiddenException.java @@ -21,14 +21,14 @@ package org.sonar.server.exceptions; import com.google.common.base.Preconditions; +import static java.net.HttpURLConnection.HTTP_FORBIDDEN; + /** * Permission denied. User does not have the required permissions. */ public class ForbiddenException extends ServerException { - private static final int FORBIDDEN = 403; - public ForbiddenException(String message) { - super(FORBIDDEN, Preconditions.checkNotNull(message)); + super(HTTP_FORBIDDEN, Preconditions.checkNotNull(message)); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/exceptions/NotFoundException.java b/server/sonar-server/src/main/java/org/sonar/server/exceptions/NotFoundException.java index a7d090d1ce1..5e46f4372a0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/exceptions/NotFoundException.java +++ b/server/sonar-server/src/main/java/org/sonar/server/exceptions/NotFoundException.java @@ -19,15 +19,15 @@ */ package org.sonar.server.exceptions; -public class NotFoundException extends ServerException { +import static java.net.HttpURLConnection.HTTP_NOT_FOUND; - private static final int NOT_FOUND = 404; +public class NotFoundException extends ServerException { public NotFoundException() { - super(NOT_FOUND); + super(HTTP_NOT_FOUND); } public NotFoundException(String message) { - super(NOT_FOUND, message); + super(HTTP_NOT_FOUND, message); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/exceptions/UnauthorizedException.java b/server/sonar-server/src/main/java/org/sonar/server/exceptions/UnauthorizedException.java index 9d42120a344..2ecaf5cb7bd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/exceptions/UnauthorizedException.java +++ b/server/sonar-server/src/main/java/org/sonar/server/exceptions/UnauthorizedException.java @@ -19,18 +19,18 @@ */ package org.sonar.server.exceptions; +import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; + /** * User needs to be authenticated. HTTP request is generally redirected to login form. */ public class UnauthorizedException extends ServerException { - private static final int UNAUTHORIZED = 401; - public UnauthorizedException() { - super(UNAUTHORIZED); + super(HTTP_UNAUTHORIZED); } public UnauthorizedException(String message) { - super(UNAUTHORIZED, message); + super(HTTP_UNAUTHORIZED, message); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/Rule.java b/server/sonar-server/src/main/java/org/sonar/server/rule/Rule.java index 076e6c6dd57..fe82d772fb8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/Rule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/Rule.java @@ -39,8 +39,10 @@ public interface Rule { String name(); + @CheckForNull String htmlDescription(); + @CheckForNull String markdownDescription(); String effortToFixDescription(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDoc.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDoc.java index 66d60ec4325..34f106a5165 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDoc.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDoc.java @@ -88,11 +88,13 @@ public class RuleDoc extends BaseDoc implements Rule { } @Override + @CheckForNull public String htmlDescription() { return getNullableField(RuleNormalizer.RuleField.HTML_DESCRIPTION.field()); } @Override + @CheckForNull public String markdownDescription() { return getNullableField(RuleNormalizer.RuleField.MARKDOWN_DESCRIPTION.field()); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/ActiveRuleCompleter.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/ActiveRuleCompleter.java index bfb1f8efc99..e221b5e16cc 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/ActiveRuleCompleter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/ActiveRuleCompleter.java @@ -20,26 +20,27 @@ package org.sonar.server.rule.ws; import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import org.sonar.api.server.ServerSide; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import javax.annotation.CheckForNull; import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.ServerSide; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; -import org.sonar.api.utils.text.JsonWriter; import org.sonar.db.qualityprofile.ActiveRuleKey; import org.sonar.db.qualityprofile.QualityProfileDto; import org.sonar.server.qualityprofile.ActiveRule; import org.sonar.server.qualityprofile.QProfileLoader; import org.sonar.server.rule.Rule; import org.sonar.server.rule.index.RuleQuery; +import org.sonarqube.ws.Rules; +import org.sonarqube.ws.Rules.SearchResponse; +import org.sonarqube.ws.Rules.ShowResponse; -import javax.annotation.CheckForNull; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; +import static com.google.common.collect.Sets.newHashSet; /** * Add details about active rules to api/rules/search and api/rules/show @@ -58,80 +59,74 @@ public class ActiveRuleCompleter { this.languages = languages; } - void completeSearch(RuleQuery query, Collection rules, JsonWriter json) { - Collection harvestedProfileKeys = writeActiveRules(json, query, rules); - - writeProfiles(json, harvestedProfileKeys); + void completeSearch(RuleQuery query, Collection rules, SearchResponse.Builder searchResponse) { + Collection harvestedProfileKeys = writeActiveRules(searchResponse, query, rules); + searchResponse.setQProfiles(buildQProfiles(harvestedProfileKeys)); } - private Collection writeActiveRules(JsonWriter json, RuleQuery query, Collection rules) { - Collection qProfileKeys = Sets.newHashSet(); + private Collection writeActiveRules(SearchResponse.Builder response, RuleQuery query, Collection rules) { + Collection qProfileKeys = newHashSet(); + Rules.Actives.Builder activesBuilder = response.getActivesBuilder(); - json.name("actives").beginObject(); String profileKey = query.getQProfileKey(); if (profileKey != null) { // Load details of active rules on the selected profile for (Rule rule : rules) { ActiveRule activeRule = loader.getActiveRule(ActiveRuleKey.of(profileKey, rule.key())); if (activeRule != null) { - qProfileKeys = writeActiveRules(rule.key(), Arrays.asList(activeRule), json); + qProfileKeys = writeActiveRules(rule.key(), Arrays.asList(activeRule), activesBuilder); } } } else { // Load details of all active rules for (Rule rule : rules) { - qProfileKeys = writeActiveRules(rule.key(), loader.findActiveRulesByRule(rule.key()), json); + qProfileKeys = writeActiveRules(rule.key(), loader.findActiveRulesByRule(rule.key()), activesBuilder); } } - json.endObject(); + response.setActives(activesBuilder); return qProfileKeys; } - void completeShow(Rule rule, JsonWriter json) { - json.name("actives").beginArray(); + void completeShow(Rule rule, ShowResponse.Builder response) { for (ActiveRule activeRule : loader.findActiveRulesByRule(rule.key())) { - writeActiveRule(activeRule, json); + response.addActives(buildActiveRuleResponse(activeRule)); } - json.endArray(); } - private Collection writeActiveRules(RuleKey ruleKey, Collection activeRules, JsonWriter json) { - Collection qProfileKeys = Sets.newHashSet(); - if (!activeRules.isEmpty()) { - json.name(ruleKey.toString()); - json.beginArray(); - for (ActiveRule activeRule : activeRules) { - qProfileKeys.add(writeActiveRule(activeRule, json)); - } - json.endArray(); + private Collection writeActiveRules(RuleKey ruleKey, Collection activeRules, Rules.Actives.Builder activesBuilder) { + Collection qProfileKeys = newHashSet(); + Rules.ActiveList.Builder activeRulesListResponse = Rules.ActiveList.newBuilder(); + for (ActiveRule activeRule : activeRules) { + activeRulesListResponse.addActiveList(buildActiveRuleResponse(activeRule)); + qProfileKeys.add(activeRule.key().qProfile()); } + activesBuilder + .getMutableActives() + .put(ruleKey.toString(), activeRulesListResponse.build()); return qProfileKeys; } - private String writeActiveRule(ActiveRule activeRule, JsonWriter json) { - json - .beginObject() - .prop("qProfile", activeRule.key().qProfile()) - .prop("inherit", activeRule.inheritance().toString()) - .prop("severity", activeRule.severity()); + private Rules.Active buildActiveRuleResponse(ActiveRule activeRule) { + Rules.Active.Builder activeRuleResponse = Rules.Active.newBuilder(); + activeRuleResponse.setQProfile(activeRule.key().qProfile()); + activeRuleResponse.setInherit(activeRule.inheritance().toString()); + activeRuleResponse.setSeverity(activeRule.severity()); ActiveRuleKey parentKey = activeRule.parentKey(); if (parentKey != null) { - json.prop("parent", parentKey.toString()); + activeRuleResponse.setParent(parentKey.toString()); } - json.name("params").beginArray(); + Rules.Active.Param.Builder paramBuilder = Rules.Active.Param.newBuilder(); for (Map.Entry param : activeRule.params().entrySet()) { - json - .beginObject() - .prop("key", param.getKey()) - .prop("value", param.getValue()) - .endObject(); + activeRuleResponse.addParams(paramBuilder.clear() + .setKey(param.getKey()) + .setValue(param.getValue())); } - json.endArray().endObject(); - return activeRule.key().qProfile(); + + return activeRuleResponse.build(); } - private void writeProfiles(JsonWriter json, Collection harvestedProfileKeys) { + private Rules.QProfiles.Builder buildQProfiles(Collection harvestedProfileKeys) { Map qProfilesByKey = Maps.newHashMap(); for (String qProfileKey : harvestedProfileKeys) { if (!qProfilesByKey.containsKey(qProfileKey)) { @@ -147,11 +142,14 @@ public class ActiveRuleCompleter { } } } - json.name("qProfiles").beginObject(); + + Rules.QProfiles.Builder qProfilesResponse = Rules.QProfiles.newBuilder(); + Map qProfilesMapResponse = qProfilesResponse.getMutableQProfiles(); for (QualityProfileDto profile : qProfilesByKey.values()) { - writeProfile(json, profile); + writeProfile(qProfilesMapResponse, profile); } - json.endObject(); + + return qProfilesResponse; } @CheckForNull @@ -159,15 +157,22 @@ public class ActiveRuleCompleter { return loader.getByKey(qProfileKey); } - private void writeProfile(JsonWriter json, QualityProfileDto profile) { - Language language = languages.get(profile.getLanguage()); - String langName = language == null ? profile.getLanguage() : language.getName(); - json.name(profile.getKey()).beginObject() - .prop("name", profile.getName()) - .prop("lang", profile.getLanguage()) - .prop("langName", langName) - .prop("parent", profile.getParentKee()) - .endObject(); + private void writeProfile(Map profilesResponse, QualityProfileDto profile) { + Rules.QProfile.Builder profileResponse = Rules.QProfile.newBuilder(); + if (profile.getName() != null) { + profileResponse.setName(profile.getName()); + } + if (profile.getLanguage() != null) { + profileResponse.setLang(profile.getLanguage()); + Language language = languages.get(profile.getLanguage()); + String langName = language == null ? profile.getLanguage() : language.getName(); + profileResponse.setLangName(langName); + } + if (profile.getParentKee() != null) { + profileResponse.setParent(profile.getParentKee()); + } + + profilesResponse.put(profile.getKey(), profileResponse.build()); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/CreateAction.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/CreateAction.java index c97c0e06cfb..01605ebbb12 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/CreateAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/CreateAction.java @@ -19,7 +19,6 @@ */ package org.sonar.server.rule.ws; -import com.google.common.base.Strings; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleStatus; import org.sonar.api.rule.Severity; @@ -27,16 +26,16 @@ import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.KeyValueFormat; -import org.sonar.api.utils.text.JsonWriter; import org.sonar.server.exceptions.BadRequestException; -import org.sonar.server.plugins.MimeTypes; 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.sonarqube.ws.Rules; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; +import static com.google.common.base.Strings.isNullOrEmpty; +import static java.net.HttpURLConnection.HTTP_CONFLICT; +import static org.sonar.server.ws.WsUtils.writeProtobuf; /** * @since 4.4 @@ -120,15 +119,15 @@ public class CreateAction implements RulesWsAction { } @Override - public void handle(Request request, Response response) { + public void handle(Request request, Response response) throws Exception { String customKey = request.param(PARAM_CUSTOM_KEY); String manualKey = request.param(PARAM_MANUAL_KEY); - if (Strings.isNullOrEmpty(customKey) && Strings.isNullOrEmpty(manualKey)) { + if (isNullOrEmpty(customKey) && isNullOrEmpty(manualKey)) { throw new BadRequestException(String.format("Either '%s' or '%s' parameters should be set", PARAM_CUSTOM_KEY, PARAM_MANUAL_KEY)); } try { - if (!Strings.isNullOrEmpty(customKey)) { + if (!isNullOrEmpty(customKey)) { NewRule newRule = NewRule.createForCustomRule(customKey, RuleKey.parse(request.mandatoryParam(PARAM_TEMPLATE_KEY))) .setName(request.mandatoryParam(PARAM_NAME)) .setMarkdownDescription(request.mandatoryParam(PARAM_DESCRIPTION)) @@ -136,40 +135,42 @@ public class CreateAction implements RulesWsAction { .setStatus(RuleStatus.valueOf(request.mandatoryParam(PARAM_STATUS))) .setPreventReactivation(request.mandatoryParamAsBoolean(PARAM_PREVENT_REACTIVATION)); String params = request.param(PARAMS); - if (!Strings.isNullOrEmpty(params)) { + if (!isNullOrEmpty(params)) { newRule.setParameters(KeyValueFormat.parse(params)); } - writeResponse(response, service.create(newRule)); + writeResponse(request, response, service.create(newRule)); } - if (!Strings.isNullOrEmpty(manualKey)) { + if (!isNullOrEmpty(manualKey)) { NewRule newRule = NewRule.createForManualRule(manualKey) .setName(request.mandatoryParam(PARAM_NAME)) .setMarkdownDescription(request.mandatoryParam(PARAM_DESCRIPTION)) .setSeverity(request.param(PARAM_SEVERITY)) .setPreventReactivation(request.mandatoryParamAsBoolean(PARAM_PREVENT_REACTIVATION)); - writeResponse(response, service.create(newRule)); + writeResponse(request, response, service.create(newRule)); } } catch (ReactivationException e) { - write409(response, e.ruleKey()); + write409(request, response, e.ruleKey()); } } - private void writeResponse(Response response, RuleKey ruleKey) { + private void writeResponse(Request request, Response response, RuleKey ruleKey) throws Exception { Rule rule = service.getNonNullByKey(ruleKey); - JsonWriter json = response.newJsonWriter().beginObject().name("rule"); - mapping.write(rule, json, null /* TODO replace by SearchOptions immutable constant */); - json.endObject().close(); + Rules.CreateResponse createResponse = Rules.CreateResponse.newBuilder() + .setRule(mapping.buildRuleResponse(rule, null /* TODO replace by SearchOptions immutable constant */)) + .build(); + + writeProtobuf(createResponse, request, response); } - private void write409(Response response, RuleKey ruleKey) { + private void write409(Request request, Response response, RuleKey ruleKey) throws Exception { Rule rule = service.getNonNullByKey(ruleKey); - Response.Stream stream = response.stream(); - stream.setStatus(409); - stream.setMediaType(MimeTypes.JSON); - JsonWriter json = JsonWriter.of(new OutputStreamWriter(stream.output(), StandardCharsets.UTF_8)).beginObject().name("rule"); - mapping.write(rule, json, null /* TODO replace by SearchOptions immutable constant */); - json.endObject().close(); + response.stream().setStatus(HTTP_CONFLICT); + Rules.CreateResponse createResponse = Rules.CreateResponse.newBuilder() + .setRule(mapping.buildRuleResponse(rule, null /* TODO replace by SearchOptions immutable constant */)) + .build(); + + writeProtobuf(createResponse, request, response); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java index 95a9e3be87c..ffb9911fa38 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java @@ -19,27 +19,35 @@ */ package org.sonar.server.rule.ws; +import com.google.common.base.Function; import com.google.common.collect.Maps; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; 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.server.debt.DebtRemediationFunction; 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.IndexField; import org.sonar.server.search.QueryContext; import org.sonar.server.search.ws.BaseMapping; import org.sonar.server.text.MacroInterpreter; - -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - -import java.util.Collection; -import java.util.Map; import org.sonar.server.user.UserSession; +import org.sonarqube.ws.Common; +import org.sonarqube.ws.Rules; + +import static com.google.common.collect.FluentIterable.from; +import static org.sonar.api.utils.DateUtils.formatDateTime; /** * Conversion of {@link org.sonar.server.rule.index.RuleDoc} to WS JSON document @@ -47,18 +55,22 @@ import org.sonar.server.user.UserSession; public class RuleMapping extends BaseMapping { private final DebtModel debtModel; + private final Languages languages; + private final MacroInterpreter macroInterpreter; public RuleMapping(final Languages languages, final MacroInterpreter macroInterpreter, final DebtModel debtModel, UserSession userSession) { super(userSession); this.debtModel = debtModel; + this.languages = languages; + this.macroInterpreter = macroInterpreter; - mapBasicFields(languages); - mapDescriptionFields(macroInterpreter); + mapBasicFields(); + mapDescriptionFields(); mapDebtFields(); mapParamFields(); } - private void mapBasicFields(final Languages languages) { + private void mapBasicFields() { map("repo", RuleNormalizer.RuleField.REPOSITORY.field()); map("name", RuleNormalizer.RuleField.NAME.field()); mapDateTime("createdAt", RuleNormalizer.RuleField.CREATED_AT.field()); @@ -70,195 +82,357 @@ public class RuleMapping extends BaseMapping { 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); - } - }); + map("langName", RuleNormalizer.RuleField.LANGUAGE.field()); } - private void mapDescriptionFields(final MacroInterpreter macroInterpreter) { - map("htmlDesc", new IndexMapper(RuleNormalizer.RuleField.MARKDOWN_DESCRIPTION.field(), RuleNormalizer.RuleField.HTML_DESCRIPTION.field()) { - @Override - public void write(JsonWriter json, RuleDoc rule, RuleMappingContext context) { - if (rule.markdownDescription() != null) { - json.prop("htmlDesc", macroInterpreter.interpret(Markdown.convertToHtml(rule.markdownDescription()))); - } else { - json.prop("htmlDesc", macroInterpreter.interpret(rule.htmlDescription())); - } - } - }); + private void mapDescriptionFields() { + map("htmlDesc", RuleNormalizer.RuleField.HTML_DESCRIPTION.field()); map("mdDesc", RuleNormalizer.RuleField.MARKDOWN_DESCRIPTION.field()); 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))); - } - } - }); + map("htmlNote", RuleNormalizer.RuleField.NOTE.field()); } 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", RuleNormalizer.RuleField.CHARACTERISTIC.field()); + map("debtSubChar", RuleNormalizer.RuleField.SUB_CHARACTERISTIC.field()); - map("debtChar", new CharacteristicMapper()); - map("debtSubChar", new SubCharacteristicMapper()); - - map("debtCharName", new CharacteristicNameMapper()); - map("debtSubCharName", new SubCharacteristicNameMapper()); + map("debtCharName", RuleNormalizer.RuleField.CHARACTERISTIC.field()); + map("debtSubCharName", RuleNormalizer.RuleField.SUB_CHARACTERISTIC.field()); 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()); + map("debtOverloaded", new SimpleMapper( + RuleNormalizer.RuleField.CHARACTERISTIC_OVERLOADED.field(), + RuleNormalizer.RuleField.SUB_CHARACTERISTIC_OVERLOADED.field(), + RuleNormalizer.RuleField.DEBT_FUNCTION_TYPE_OVERLOADED.field())); map("debtRemFn", new IndexStringMapper("debtRemFnType", RuleNormalizer.RuleField.DEBT_FUNCTION_TYPE.field())); map("debtRemFn", new IndexStringMapper("debtRemFnCoeff", RuleNormalizer.RuleField.DEBT_FUNCTION_COEFFICIENT.field())); map("debtRemFn", new IndexStringMapper("debtRemFnOffset", RuleNormalizer.RuleField.DEBT_FUNCTION_OFFSET.field())); } - public static class EffectiveDebtRemFn extends IndexStringMapper { + private void mapParamFields() { + map("params", RuleNormalizer.RuleField.PARAMS.field()); + } + + public Rules.Rule buildRuleResponse(Rule ruleDoc, @Nullable QueryContext queryContext) { + Rules.Rule.Builder ruleResponse = Rules.Rule.newBuilder(); + + RuleMappingContext context = new RuleMappingContext(); + Set fieldsToReturn = fieldsToReturn(queryContext); + + ruleResponse.setKey(ruleDoc.key().toString()); + setRepository(ruleResponse, ruleDoc, fieldsToReturn); + setDebtCharacteristicNames(ruleResponse, ruleDoc, queryContext, context); + setDebtSubCharacteristicNames(ruleResponse, ruleDoc, queryContext, context); + setName(ruleResponse, ruleDoc, fieldsToReturn); + setStatus(ruleResponse, ruleDoc, fieldsToReturn); + setTags(ruleResponse, ruleDoc, fieldsToReturn); + setSysTags(ruleResponse, ruleDoc, fieldsToReturn); + setParams(ruleResponse, ruleDoc, fieldsToReturn); + setCreatedAt(ruleResponse, ruleDoc, fieldsToReturn); + setDescriptionFields(ruleResponse, ruleDoc, fieldsToReturn); + setSeverity(ruleResponse, ruleDoc, fieldsToReturn); + setInternalKey(ruleResponse, ruleDoc, fieldsToReturn); + setLanguage(ruleResponse, ruleDoc, fieldsToReturn); + setIsTemplate(ruleResponse, ruleDoc, fieldsToReturn); + setTemplateKey(ruleResponse, ruleDoc, fieldsToReturn); + setDebtRemediationFunctionFields(ruleResponse, ruleDoc, fieldsToReturn); + setDefaultDebtRemediationFunctionFields(ruleResponse, ruleDoc, fieldsToReturn); + setIsDebtOverloaded(ruleResponse, ruleDoc, fieldsToReturn); + setDefaultDebtChar(ruleResponse, ruleDoc, fieldsToReturn); + setDefaultDebtSubChar(ruleResponse, ruleDoc, fieldsToReturn); + setEffortToFixDescription(ruleResponse, ruleDoc, fieldsToReturn); + + return ruleResponse.build(); + } - public EffectiveDebtRemFn(String key, String indexKey, String defaultIndexKey) { - super(key, indexKey, defaultIndexKey); + private static void setRepository(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, RuleNormalizer.RuleField.REPOSITORY)) { + ruleResponse.setRepo(ruleDoc.key().repository()); } + } - @Override - public void write(JsonWriter json, RuleDoc doc, RuleMappingContext context) { - if(doc.debtOverloaded()){ - Object val = doc.getNullableField(indexFields[0]); - json.prop(key, val != null ? val.toString() : null); - } else { - super.write(json,doc,context); + private static void setEffortToFixDescription(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, RuleNormalizer.RuleField.FIX_DESCRIPTION) && ruleDoc.effortToFixDescription() != null) { + ruleResponse.setEffortToFixDescription(ruleDoc.effortToFixDescription()); + } + } + + private static void setDefaultDebtSubChar(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, "defaultDebtSubChar") && ruleDoc.defaultDebtSubCharacteristicKey() != null) { + ruleResponse.setDefaultDebtSubChar(ruleDoc.defaultDebtSubCharacteristicKey()); + } + } + + private static void setDefaultDebtChar(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, "defaultDebtChar") && ruleDoc.defaultDebtCharacteristicKey() != null) { + ruleResponse.setDefaultDebtChar(ruleDoc.defaultDebtCharacteristicKey()); + } + } + + private static void setIsDebtOverloaded(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, "debtOverloaded")) { + ruleResponse.setDebtOverloaded(ruleToOverloaded(ruleDoc)); + } + } + + private static void setDefaultDebtRemediationFunctionFields(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, "defaultDebtRemFn")) { + DebtRemediationFunction defaultDebtRemediationFunction = ruleDoc.defaultDebtRemediationFunction(); + if (defaultDebtRemediationFunction != null) { + if (defaultDebtRemediationFunction.coefficient() != null) { + ruleResponse.setDefaultDebtRemFnCoeff(defaultDebtRemediationFunction.coefficient()); + } + if (defaultDebtRemediationFunction.offset() != null) { + ruleResponse.setDefaultDebtRemFnOffset(defaultDebtRemediationFunction.offset()); + } + if (defaultDebtRemediationFunction.type() != null) { + ruleResponse.setDefaultDebtRemFnType(defaultDebtRemediationFunction.type().name()); + } } } } - 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("htmlDesc", param.description() == null ? null : Markdown.convertToHtml(param.description())) - .prop("type", param.type().type()) - .prop("defaultValue", param.defaultValue()) - .endObject(); + private static void setDebtRemediationFunctionFields(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, "debtRemFn")) { + DebtRemediationFunction debtRemediationFunction = ruleDoc.debtRemediationFunction(); + if (debtRemediationFunction != null) { + if (debtRemediationFunction.type() != null) { + ruleResponse.setDebtRemFnType(debtRemediationFunction.type().name()); + } + if (debtRemediationFunction.coefficient() != null) { + ruleResponse.setDebtRemFnCoeff(debtRemediationFunction.coefficient()); + } + if (debtRemediationFunction.offset() != null) { + ruleResponse.setDebtRemFnOffset(debtRemediationFunction.offset()); } - json.endArray(); } - }); + } } - public void write(Rule rule, JsonWriter json, @Nullable QueryContext queryContext) { - RuleMappingContext context = new RuleMappingContext(); + private void setDebtCharacteristicNames(Rules.Rule.Builder ruleResponse, Rule ruleDoc, @Nullable QueryContext queryContext, RuleMappingContext context) { if (needDebtCharacteristicNames(queryContext)) { - String debtCharacteristicKey = rule.debtCharacteristicKey(); + String debtCharacteristicKey = ruleDoc.debtCharacteristicKey(); if (debtCharacteristicKey != null) { // load debt characteristics if requested context.add(debtModel.characteristicByKey(debtCharacteristicKey)); + buildCharacteristicRuleResponse(ruleResponse, ruleDoc, context); } } + } + + private void setDebtSubCharacteristicNames(Rules.Rule.Builder ruleResponse, Rule ruleDoc, @Nullable QueryContext queryContext, RuleMappingContext context) { if (needDebtSubCharacteristicNames(queryContext)) { - String debtSubCharacteristicKey = rule.debtSubCharacteristicKey(); + String debtSubCharacteristicKey = ruleDoc.debtSubCharacteristicKey(); if (debtSubCharacteristicKey != null) { context.add(debtModel.characteristicByKey(debtSubCharacteristicKey)); + buildDebtSubCharacteristicRuleResponse(ruleResponse, ruleDoc, context); } } - doWrite((RuleDoc) rule, context, json, queryContext); } - public void write(Collection rules, JsonWriter json, @Nullable QueryContext queryContext) { - if (!rules.isEmpty()) { - RuleMappingContext context = new RuleMappingContext(); - if (needDebtCharacteristicNames(queryContext) || needDebtSubCharacteristicNames(queryContext)) { - // load all debt characteristics - context.addAll(debtModel.allCharacteristics()); - } - for (Rule rule : rules) { - doWrite((RuleDoc) rule, context, json, queryContext); - } + private static Set fieldsToReturn(@Nullable QueryContext queryContext) { + return queryContext == null ? Collections.emptySet() : queryContext.getFieldsToReturn(); + } + + private static void setName(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, RuleNormalizer.RuleField.NAME) && ruleDoc.name() != null) { + ruleResponse.setName(ruleDoc.name()); } } - private boolean needDebtCharacteristicNames(@Nullable QueryContext context) { - return context == null || context.getFieldsToReturn().contains("debtCharName"); + private static void setStatus(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, RuleNormalizer.RuleField.STATUS) && ruleDoc.status() != null) { + ruleResponse.setStatus(Common.RuleStatus.valueOf(ruleDoc.status().toString())); + } } - private boolean needDebtSubCharacteristicNames(@Nullable QueryContext context) { - return context == null || context.getFieldsToReturn().contains("debtSubCharName"); + private static void setTags(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, RuleNormalizer.RuleField.TAGS)) { + ruleResponse.getTagsBuilder().addAllTags(ruleDoc.tags()); + } } - private static class CharacteristicMapper extends IndexMapper { - private CharacteristicMapper() { - super(RuleNormalizer.RuleField.CHARACTERISTIC.field()); + private static void setSysTags(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, RuleNormalizer.RuleField.SYSTEM_TAGS)) { + ruleResponse.getSysTagsBuilder().addAllSysTags(ruleDoc.systemTags()); } + } - @Override - public void write(JsonWriter json, RuleDoc rule, RuleMappingContext context) { - String debtCharacteristicKey = rule.debtCharacteristicKey(); - if (debtCharacteristicKey != null && !DebtCharacteristic.NONE.equals(debtCharacteristicKey)) { - json.prop("debtChar", debtCharacteristicKey); + private static void setParams(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, RuleNormalizer.RuleField.PARAMS)) { + ruleResponse.getParamsBuilder().addAllParams(from(ruleDoc.params()) + .transform(RuleParamToResponseRuleParam.INSTANCE) + .toList()); + } + } + + private static void setCreatedAt(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, RuleNormalizer.RuleField.CREATED_AT) && ruleDoc.createdAt() != null) { + ruleResponse.setCreatedAt(formatDateTime(ruleDoc.createdAt())); + } + } + + private void setDescriptionFields(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, RuleNormalizer.RuleField.HTML_DESCRIPTION)) { + if (ruleDoc.markdownDescription() != null) { + ruleResponse.setHtmlDesc(macroInterpreter.interpret(Markdown.convertToHtml(ruleDoc.markdownDescription()))); + } else if (ruleDoc.htmlDescription() != null) { + ruleResponse.setHtmlDesc(macroInterpreter.interpret(ruleDoc.htmlDescription())); } } + if (shouldReturnField(fieldsToReturn, "htmlNote") && ruleDoc.markdownNote() != null) { + ruleResponse.setHtmlNote(macroInterpreter.interpret(Markdown.convertToHtml(ruleDoc.markdownNote()))); + } + if (shouldReturnField(fieldsToReturn, "mdNote") && ruleDoc.markdownNote() != null) { + ruleResponse.setMdNote(ruleDoc.markdownNote()); + } + if (shouldReturnField(fieldsToReturn, RuleNormalizer.RuleField.MARKDOWN_DESCRIPTION) && ruleDoc.markdownDescription() != null) { + ruleResponse.setMdDesc(ruleDoc.markdownDescription()); + } + if (shouldReturnField(fieldsToReturn, RuleNormalizer.RuleField.NOTE_LOGIN) && ruleDoc.noteLogin() != null) { + ruleResponse.setNoteLogin(ruleDoc.noteLogin()); + } } - private static class SubCharacteristicMapper extends IndexMapper { - private SubCharacteristicMapper() { - super(RuleNormalizer.RuleField.SUB_CHARACTERISTIC.field()); + private static void setSeverity(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, RuleNormalizer.RuleField.SEVERITY) && ruleDoc.severity() != null) { + ruleResponse.setSeverity(ruleDoc.severity()); } + } - @Override - public void write(JsonWriter json, RuleDoc rule, RuleMappingContext context) { - String debtSubCharacteristicKey = rule.debtSubCharacteristicKey(); - if (debtSubCharacteristicKey != null && !DebtCharacteristic.NONE.equals(debtSubCharacteristicKey)) { - json.prop("debtSubChar", debtSubCharacteristicKey); + private static void setInternalKey(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, RuleNormalizer.RuleField.INTERNAL_KEY) && ruleDoc.internalKey() != null) { + ruleResponse.setInternalKey(ruleDoc.internalKey()); + } + } + + private void setLanguage(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, RuleNormalizer.RuleField.LANGUAGE) && ruleDoc.language() != null) { + ruleResponse.setLang(ruleDoc.language()); + Language language = languages.get(ruleDoc.language()); + if (language != null) { + ruleResponse.setLangName(language.getName()); } } } - private static class CharacteristicNameMapper extends IndexMapper { - private CharacteristicNameMapper() { - super(RuleNormalizer.RuleField.CHARACTERISTIC.field()); + private static void setIsTemplate(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, RuleNormalizer.RuleField.IS_TEMPLATE)) { + ruleResponse.setIsTemplate(ruleDoc.isTemplate()); } + } - @Override - public void write(JsonWriter json, RuleDoc rule, RuleMappingContext context) { - json.prop("debtCharName", context.debtCharacteristicName(rule.debtCharacteristicKey())); + private static void setTemplateKey(Rules.Rule.Builder ruleResponse, Rule ruleDoc, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, RuleNormalizer.RuleField.TEMPLATE_KEY) && ruleDoc.templateKey() != null) { + ruleResponse.setTemplateKey(ruleDoc.templateKey().toString()); } } - private static class SubCharacteristicNameMapper extends IndexMapper { - private SubCharacteristicNameMapper() { - super(RuleNormalizer.RuleField.SUB_CHARACTERISTIC.field()); + private static boolean shouldReturnField(Set fieldsToReturn, IndexField field) { + return fieldsToReturn.isEmpty() || fieldsToReturn.contains(field.field()); + } + + private static boolean shouldReturnField(Set fieldsToReturn, String fieldName) { + return fieldsToReturn.isEmpty() || fieldsToReturn.contains(fieldName); + } + + private void buildCharacteristicRuleResponse(Rules.Rule.Builder ruleResponse, Rule ruleDoc, RuleMappingContext context) { + String ruleCharacteristic = ruleToCharacteristic(ruleDoc); + if (ruleCharacteristic != null) { + ruleResponse.setDebtChar(ruleCharacteristic); + String ruleCharacteristicName = ruleToCharacteristicName(ruleDoc, context); + if (ruleCharacteristicName != null) { + ruleResponse.setDebtCharName(ruleCharacteristicName); + } } + } - @Override - public void write(JsonWriter json, RuleDoc rule, RuleMappingContext context) { - json.prop("debtSubCharName", context.debtCharacteristicName(rule.debtSubCharacteristicKey())); + private void buildDebtSubCharacteristicRuleResponse(Rules.Rule.Builder ruleResponse, Rule ruleDoc, RuleMappingContext context) { + String ruleSubCharacteristic = ruleToSubCharacteristic(ruleDoc); + if (ruleSubCharacteristic != null) { + ruleResponse.setDebtSubChar(ruleSubCharacteristic); + String ruleSubCharacteristicName = ruleToSubCharacteristicName(ruleDoc, context); + if (ruleSubCharacteristicName != null) { + ruleResponse.setDebtSubCharName(ruleSubCharacteristicName); + } + } + } + + private boolean needDebtCharacteristicNames(@Nullable QueryContext context) { + return context == null || context.getFieldsToReturn().contains("debtCharName"); + } + + private boolean needDebtSubCharacteristicNames(@Nullable QueryContext context) { + return context == null || context.getFieldsToReturn().contains("debtSubCharName"); + } + + @CheckForNull + private static String ruleToCharacteristic(Rule rule) { + String debtCharacteristicKey = rule.debtCharacteristicKey(); + if (debtCharacteristicKey != null && !DebtCharacteristic.NONE.equals(debtCharacteristicKey)) { + return debtCharacteristicKey; } + return null; + } + + @CheckForNull + private static String ruleToSubCharacteristic(Rule rule) { + String debtSubCharacteristicKey = rule.debtSubCharacteristicKey(); + if (debtSubCharacteristicKey != null && !DebtCharacteristic.NONE.equals(debtSubCharacteristicKey)) { + return debtSubCharacteristicKey; + } + + return null; + } + + private static String ruleToCharacteristicName(Rule rule, RuleMappingContext context) { + return context.debtCharacteristicName(rule.debtCharacteristicKey()); + } + + private static String ruleToSubCharacteristicName(Rule rule, RuleMappingContext context) { + return context.debtCharacteristicName(rule.debtSubCharacteristicKey()); + } + + private static boolean ruleToOverloaded(Rule rule) { + return rule.debtOverloaded(); } - private static class OverriddenMapper extends IndexMapper { - private OverriddenMapper() { - super(RuleNormalizer.RuleField.CHARACTERISTIC_OVERLOADED.field(), - RuleNormalizer.RuleField.SUB_CHARACTERISTIC_OVERLOADED.field(), - RuleNormalizer.RuleField.DEBT_FUNCTION_TYPE_OVERLOADED.field()); + private static class SimpleMapper extends IndexMapper { + private SimpleMapper(String... fields) { + super(fields); } + @Override - public void write(JsonWriter json, RuleDoc rule, RuleMappingContext context) { - json.prop("debtOverloaded", rule.debtOverloaded()); + public void write(JsonWriter json, RuleDoc doc, RuleMappingContext context) { + // do not do anything + } + } + + private enum RuleParamToResponseRuleParam implements Function { + INSTANCE; + + @Override + public Rules.Rule.Param apply(@Nonnull RuleParam param) { + Rules.Rule.Param.Builder paramResponse = Rules.Rule.Param.newBuilder(); + paramResponse.setKey(param.key()); + if (param.description() != null) { + paramResponse.setHtmlDesc(Markdown.convertToHtml(param.description())); + } + if (param.defaultValue() != null) { + paramResponse.setDefaultValue(param.defaultValue()); + } + if (param.type() != null) { + paramResponse.setType(param.type().type()); + } + + return paramResponse.build(); } } } @@ -276,10 +450,4 @@ class RuleMappingContext { debtCharacteristicNamesByKey.put(c.key(), c.name()); } } - - void addAll(Collection coll) { - for (DebtCharacteristic c : coll) { - add(c); - } - } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java index 28fb88e75c4..00439eb0f9b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java @@ -41,7 +41,6 @@ import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService.Param; -import org.sonar.api.utils.text.JsonWriter; import org.sonar.db.qualityprofile.QualityProfileDto; import org.sonar.server.qualityprofile.ActiveRule; import org.sonar.server.rule.Rule; @@ -54,6 +53,10 @@ import org.sonar.server.search.QueryContext; import org.sonar.server.search.Result; import org.sonar.server.search.ws.SearchOptions; import org.sonar.server.user.UserSession; +import org.sonarqube.ws.Common; +import org.sonarqube.ws.Rules.SearchResponse; + +import static org.sonar.server.ws.WsUtils.writeProtobuf; /** * @since 4.4 @@ -124,19 +127,24 @@ public class SearchAction implements RulesWsAction { RuleQuery query = doQuery(request); Result result = doSearch(query, context); - JsonWriter json = response.newJsonWriter().beginObject(); - writeStatistics(json, result, context); - doContextResponse(request, result, json); + SearchResponse responseBuilder = buildResponse(request, context, result); + writeProtobuf(responseBuilder, request, response); + } + + private SearchResponse buildResponse(Request request, QueryContext context, Result result) { + SearchResponse.Builder responseBuilder = SearchResponse.newBuilder(); + writeStatistics(responseBuilder, result, context); + doContextResponse(request, result, responseBuilder); if (context.isFacet()) { - writeFacets(request, context, result, json); + writeFacets(responseBuilder, request, context, result); } - json.endObject().close(); + return responseBuilder.build(); } - protected void writeStatistics(JsonWriter json, Result searchResult, QueryContext context) { - json.prop("total", searchResult.getTotal()); - json.prop(Param.PAGE, context.getPage()); - json.prop(Param.PAGE_SIZE, context.getLimit()); + protected void writeStatistics(SearchResponse.Builder response, Result searchResult, QueryContext context) { + response.setTotal(searchResult.getTotal()); + response.setP(context.getPage()); + response.setPs(context.getLimit()); } protected void doDefinition(WebService.NewAction action) { @@ -299,12 +307,10 @@ public class SearchAction implements RulesWsAction { return query; } - private void writeRules(Result result, JsonWriter json, QueryContext context) { - json.name("rules").beginArray(); + private void writeRules(SearchResponse.Builder response, Result result, QueryContext context) { for (Rule rule : result.getHits()) { - mapping.write(rule, json, context); + response.addRules(mapping.buildRuleResponse(rule, context)); } - json.endArray(); } protected QueryContext getQueryContext(Request request) { @@ -355,12 +361,12 @@ public class SearchAction implements RulesWsAction { return plainQuery; } - protected void doContextResponse(Request request, Result result, JsonWriter json) { + protected void doContextResponse(Request request, Result result, SearchResponse.Builder response) { // TODO Get rid of this horrible hack: fields on request are not the same as fields for ES search ! 2/2 QueryContext contextForResponse = loadCommonContext(request); - writeRules(result, json, contextForResponse); + writeRules(response, result, contextForResponse); if (contextForResponse.getFieldsToReturn().contains("actives")) { - activeRuleCompleter.completeSearch(doQuery(request), result.getHits(), json); + activeRuleCompleter.completeSearch(doQuery(request), result.getHits(), response); } } @@ -370,7 +376,7 @@ public class SearchAction implements RulesWsAction { return builder.add("actives").build(); } - protected void writeFacets(Request request, QueryContext context, Result results, JsonWriter json) { + protected void writeFacets(SearchResponse.Builder response, Request request, QueryContext context, Result results) { addMandatoryFacetValues(results, RuleIndex.FACET_DEBT_CHARACTERISTICS, request.paramAsStrings(PARAM_DEBT_CHARACTERISTICS)); addMandatoryFacetValues(results, RuleIndex.FACET_LANGUAGES, request.paramAsStrings(PARAM_LANGUAGES)); addMandatoryFacetValues(results, RuleIndex.FACET_REPOSITORIES, request.paramAsStrings(PARAM_REPOSITORIES)); @@ -381,25 +387,23 @@ public class SearchAction implements RulesWsAction { mergeNoneAndEmptyBucketOnCharacteristics(results); - json.name("facets").beginArray(); + Common.Facet.Builder facet = Common.Facet.newBuilder(); + Common.FacetValue.Builder value = Common.FacetValue.newBuilder(); for (String facetName : context.facets()) { - json.beginObject(); - json.prop("property", facetName); - json.name("values").beginArray(); + facet.clear().setProperty(facetName); if (results.getFacets().containsKey(facetName)) { Set itemsFromFacets = Sets.newHashSet(); for (FacetValue facetValue : results.getFacets().get(facetName)) { itemsFromFacets.add(facetValue.getKey()); - json.beginObject(); - json.prop("val", facetValue.getKey()); - json.prop("count", facetValue.getValue()); - json.endObject(); + facet.addValues(value + .clear() + .setVal(facetValue.getKey()) + .setCount(facetValue.getValue())); } - addZeroFacetsForSelectedItems(request, facetName, itemsFromFacets, json); + addZeroFacetsForSelectedItems(facet, request, facetName, itemsFromFacets); } - json.endArray().endObject(); + response.getFacetsBuilder().addFacets(facet); } - json.endArray(); } protected void mergeNoneAndEmptyBucketOnCharacteristics(Result results) { @@ -424,15 +428,15 @@ public class SearchAction implements RulesWsAction { } } - private static void addZeroFacetsForSelectedItems(Request request, String facetName, Set itemsFromFacets, JsonWriter json) { + private static void addZeroFacetsForSelectedItems(Common.Facet.Builder facet, Request request, String facetName, Set itemsFromFacets) { List requestParams = request.paramAsStrings(facetName); if (requestParams != null) { + Common.FacetValue.Builder value = Common.FacetValue.newBuilder(); for (String param : requestParams) { if (!itemsFromFacets.contains(param)) { - json.beginObject(); - json.prop("val", param); - json.prop("count", 0); - json.endObject(); + facet.addValues(value.clear() + .setVal(param) + .setCount(0L)); } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/ShowAction.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/ShowAction.java index b767d93972a..d942ec7e337 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/ShowAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/ShowAction.java @@ -24,10 +24,12 @@ import org.sonar.api.rule.RuleKey; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; -import org.sonar.api.utils.text.JsonWriter; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.rule.Rule; import org.sonar.server.rule.RuleService; +import org.sonarqube.ws.Rules.ShowResponse; + +import static org.sonar.server.ws.WsUtils.writeProtobuf; /** * @since 4.4 @@ -70,19 +72,25 @@ public class ShowAction implements RulesWsAction { } @Override - public void handle(Request request, Response response) { + public void handle(Request request, Response response) throws Exception { RuleKey key = RuleKey.parse(request.mandatoryParam(PARAM_KEY)); Rule rule = service.getByKey(key); if (rule == null) { throw new NotFoundException("Rule not found: " + key); } - JsonWriter json = response.newJsonWriter().beginObject().name("rule"); - mapping.write(rule, json, null /* TODO replace by SearchOptions immutable constant */); + + ShowResponse showResponse = buildResponse(request, rule); + writeProtobuf(showResponse, request, response); + } + + private ShowResponse buildResponse(Request request, Rule rule) { + ShowResponse.Builder responseBuilder = ShowResponse.newBuilder(); + responseBuilder.setRule(mapping.buildRuleResponse(rule, null /* TODO replace by SearchOptions immutable constant */)); if (request.mandatoryParamAsBoolean(PARAM_ACTIVES)) { - activeRuleCompleter.completeShow(rule, json); + activeRuleCompleter.completeShow(rule, responseBuilder); } - json.endObject().close(); + return responseBuilder.build(); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/UpdateAction.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/UpdateAction.java index dad8c61eac5..efe567d80c6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/UpdateAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/UpdateAction.java @@ -31,11 +31,13 @@ import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.KeyValueFormat; -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.rule.RuleUpdate; +import org.sonarqube.ws.Rules.UpdateResponse; + +import static org.sonar.server.ws.WsUtils.writeProtobuf; public class UpdateAction implements RulesWsAction { @@ -126,7 +128,9 @@ public class UpdateAction implements RulesWsAction { public void handle(Request request, Response response) throws Exception { RuleUpdate update = readRequest(request); service.update(update); - writeResponse(response, update.getRuleKey()); + UpdateResponse updateResponse = buildResponse(update.getRuleKey()); + + writeProtobuf(updateResponse, request, response); } private RuleUpdate readRequest(Request request) { @@ -211,10 +215,11 @@ public class UpdateAction implements RulesWsAction { } } - private void writeResponse(Response response, RuleKey ruleKey) { + private UpdateResponse buildResponse(RuleKey ruleKey) { Rule rule = service.getNonNullByKey(ruleKey); - JsonWriter json = response.newJsonWriter().beginObject().name("rule"); - mapping.write(rule, json, null /* TODO replace by SearchOptions immutable constant */); - json.endObject().close(); + UpdateResponse.Builder responseBuilder = UpdateResponse.newBuilder(); + responseBuilder.setRule(mapping.buildRuleResponse(rule, null /* TODO replace by SearchOptions immutable constant */)); + + return responseBuilder.build(); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/ws/BaseMapping.java b/server/sonar-server/src/main/java/org/sonar/server/search/ws/BaseMapping.java index fd501c432a1..ba286ea5755 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/ws/BaseMapping.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/ws/BaseMapping.java @@ -67,13 +67,6 @@ public abstract class BaseMapping { return result; } - /** - * Write all document fields - */ - protected void doWrite(DOC doc, @Nullable CTX context, JsonWriter json) { - doWrite(doc, context, json, null); - } - /** * Write only requested document fields */ @@ -120,7 +113,7 @@ public abstract class BaseMapping { return this; } - public static interface Mapper { + public interface Mapper { void write(JsonWriter json, DOC doc, CTX context); } @@ -142,11 +135,6 @@ public abstract class BaseMapping { public static class IndexStringMapper extends IndexMapper { protected final String key; - public IndexStringMapper(String key, String indexKey, String defaultIndexKey) { - super(indexKey, defaultIndexKey); - this.key = key; - } - public IndexStringMapper(String key, String indexKey) { super(indexKey); this.key = key; diff --git a/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWsMediumTest/search_2_rules_fields.json b/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWsMediumTest/search_2_rules_fields.json index 6be06553d52..0dcbe93c49f 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWsMediumTest/search_2_rules_fields.json +++ b/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWsMediumTest/search_2_rules_fields.json @@ -1,5 +1,7 @@ { - "total": 2, "p": 1, "ps": 100, + "total": 2, + "p": 1, + "ps": 100, "rules": [ { "key": "xoo:x2", @@ -10,6 +12,7 @@ { "key": "xoo:x1", "name": "Rule x1", - "htmlDesc": "Description x1", + "htmlDesc": "Description x1" } - ]} + ] +} diff --git a/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml b/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml index a8c533f317b..8087982763e 100644 --- a/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml +++ b/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml @@ -184,7 +184,7 @@