import org.sonar.core.metric.DefaultMetricFinder;
import org.sonar.core.notification.DefaultNotificationManager;
import org.sonar.core.permission.PermissionFacade;
-import org.sonar.core.persistence.DaoUtils;
-import org.sonar.core.persistence.DatabaseVersion;
-import org.sonar.core.persistence.DefaultDatabase;
-import org.sonar.core.persistence.MyBatis;
-import org.sonar.core.persistence.PreviewDatabaseFactory;
-import org.sonar.core.persistence.SemaphoreUpdater;
-import org.sonar.core.persistence.SemaphoresImpl;
+import org.sonar.core.persistence.*;
import org.sonar.core.preview.PreviewCache;
import org.sonar.core.profiling.Profiling;
import org.sonar.core.purge.PurgeProfiler;
import org.sonar.server.db.EmbeddedDatabaseFactory;
import org.sonar.server.db.migrations.DatabaseMigrations;
import org.sonar.server.db.migrations.DatabaseMigrator;
-import org.sonar.server.debt.DebtCharacteristicsXMLImporter;
-import org.sonar.server.debt.DebtModelBackup;
-import org.sonar.server.debt.DebtModelLookup;
-import org.sonar.server.debt.DebtModelOperations;
-import org.sonar.server.debt.DebtModelPluginRepository;
-import org.sonar.server.debt.DebtModelService;
-import org.sonar.server.debt.DebtModelXMLExporter;
-import org.sonar.server.debt.DebtRulesXMLImporter;
+import org.sonar.server.debt.*;
import org.sonar.server.duplication.ws.DuplicationsParser;
import org.sonar.server.duplication.ws.DuplicationsWriter;
import org.sonar.server.duplication.ws.DuplicationsWs;
-import org.sonar.server.issue.ActionService;
-import org.sonar.server.issue.AssignAction;
-import org.sonar.server.issue.CommentAction;
-import org.sonar.server.issue.DefaultIssueFinder;
-import org.sonar.server.issue.InternalRubyIssueService;
-import org.sonar.server.issue.IssueBulkChangeService;
-import org.sonar.server.issue.IssueChangelogFormatter;
-import org.sonar.server.issue.IssueChangelogService;
-import org.sonar.server.issue.IssueCommentService;
-import org.sonar.server.issue.IssueService;
-import org.sonar.server.issue.IssueStatsFinder;
-import org.sonar.server.issue.PlanAction;
-import org.sonar.server.issue.PublicRubyIssueService;
-import org.sonar.server.issue.ServerIssueStorage;
-import org.sonar.server.issue.SetSeverityAction;
-import org.sonar.server.issue.TransitionAction;
+import org.sonar.server.issue.*;
import org.sonar.server.issue.actionplan.ActionPlanService;
import org.sonar.server.issue.actionplan.ActionPlanWs;
import org.sonar.server.issue.filter.IssueFilterService;
import org.sonar.server.platform.ws.RestartHandler;
import org.sonar.server.platform.ws.ServerWs;
import org.sonar.server.platform.ws.SystemWs;
-import org.sonar.server.plugins.BatchWs;
-import org.sonar.server.plugins.InstalledPluginReferentialFactory;
-import org.sonar.server.plugins.PluginDownloader;
-import org.sonar.server.plugins.ServerExtensionInstaller;
-import org.sonar.server.plugins.ServerPluginJarInstaller;
-import org.sonar.server.plugins.ServerPluginJarsInstaller;
-import org.sonar.server.plugins.ServerPluginRepository;
-import org.sonar.server.plugins.UpdateCenterClient;
-import org.sonar.server.plugins.UpdateCenterMatrixFactory;
+import org.sonar.server.plugins.*;
import org.sonar.server.qualitygate.QgateProjectFinder;
import org.sonar.server.qualitygate.QualityGates;
import org.sonar.server.qualitygate.RegisterQualityGates;
-import org.sonar.server.qualitygate.ws.QGatesAppAction;
-import org.sonar.server.qualitygate.ws.QGatesCopyAction;
-import org.sonar.server.qualitygate.ws.QGatesCreateAction;
-import org.sonar.server.qualitygate.ws.QGatesCreateConditionAction;
-import org.sonar.server.qualitygate.ws.QGatesDeleteConditionAction;
-import org.sonar.server.qualitygate.ws.QGatesDeselectAction;
-import org.sonar.server.qualitygate.ws.QGatesDestroyAction;
-import org.sonar.server.qualitygate.ws.QGatesListAction;
-import org.sonar.server.qualitygate.ws.QGatesRenameAction;
-import org.sonar.server.qualitygate.ws.QGatesSearchAction;
-import org.sonar.server.qualitygate.ws.QGatesSelectAction;
-import org.sonar.server.qualitygate.ws.QGatesSetAsDefaultAction;
-import org.sonar.server.qualitygate.ws.QGatesShowAction;
-import org.sonar.server.qualitygate.ws.QGatesUnsetDefaultAction;
-import org.sonar.server.qualitygate.ws.QGatesUpdateConditionAction;
-import org.sonar.server.qualitygate.ws.QGatesWs;
-import org.sonar.server.qualityprofile.BuiltInProfiles;
-import org.sonar.server.qualityprofile.QProfileBackuper;
-import org.sonar.server.qualityprofile.QProfileCopier;
-import org.sonar.server.qualityprofile.QProfileExporters;
-import org.sonar.server.qualityprofile.QProfileFactory;
-import org.sonar.server.qualityprofile.QProfileLoader;
-import org.sonar.server.qualityprofile.QProfileLookup;
-import org.sonar.server.qualityprofile.QProfileProjectLookup;
-import org.sonar.server.qualityprofile.QProfileProjectOperations;
-import org.sonar.server.qualityprofile.QProfileRepositoryExporter;
-import org.sonar.server.qualityprofile.QProfileReset;
-import org.sonar.server.qualityprofile.QProfileService;
-import org.sonar.server.qualityprofile.QProfiles;
-import org.sonar.server.qualityprofile.RegisterQualityProfiles;
-import org.sonar.server.qualityprofile.RuleActivator;
-import org.sonar.server.qualityprofile.RuleActivatorContextFactory;
+import org.sonar.server.qualitygate.ws.*;
+import org.sonar.server.qualityprofile.*;
import org.sonar.server.qualityprofile.db.ActiveRuleDao;
import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer;
-import org.sonar.server.qualityprofile.ws.BulkRuleActivationActions;
-import org.sonar.server.qualityprofile.ws.ProfilesWs;
-import org.sonar.server.qualityprofile.ws.QProfileRestoreBuiltInAction;
-import org.sonar.server.qualityprofile.ws.QProfilesWs;
-import org.sonar.server.qualityprofile.ws.RuleActivationActions;
-import org.sonar.server.rule.DefaultRuleFinder;
-import org.sonar.server.rule.DeprecatedRulesDefinition;
-import org.sonar.server.rule.RegisterRules;
-import org.sonar.server.rule.RubyRuleService;
-import org.sonar.server.rule.RuleCreator;
-import org.sonar.server.rule.RuleDefinitionsLoader;
-import org.sonar.server.rule.RuleDeleter;
-import org.sonar.server.rule.RuleOperations;
-import org.sonar.server.rule.RuleRepositories;
-import org.sonar.server.rule.RuleService;
-import org.sonar.server.rule.RuleUpdater;
+import org.sonar.server.qualityprofile.ws.*;
+import org.sonar.server.rule.*;
import org.sonar.server.rule.db.RuleDao;
import org.sonar.server.rule.index.RuleIndex;
import org.sonar.server.rule.index.RuleNormalizer;
-import org.sonar.server.rule.ws.ActiveRuleCompleter;
-import org.sonar.server.rule.ws.AppAction;
-import org.sonar.server.rule.ws.DeleteAction;
-import org.sonar.server.rule.ws.RuleMapping;
-import org.sonar.server.rule.ws.RulesWebService;
-import org.sonar.server.rule.ws.SearchAction;
-import org.sonar.server.rule.ws.TagsAction;
-import org.sonar.server.rule.ws.UpdateAction;
-import org.sonar.server.search.ESNode;
-import org.sonar.server.search.IndexClient;
-import org.sonar.server.search.IndexQueue;
-import org.sonar.server.search.IndexQueueWorker;
-import org.sonar.server.search.IndexSynchronizer;
+import org.sonar.server.rule.ws.*;
+import org.sonar.server.search.*;
import org.sonar.server.source.CodeColorizers;
import org.sonar.server.source.DeprecatedSourceDecorator;
import org.sonar.server.source.HtmlSourceDecorator;
import org.sonar.server.source.ws.ScmWriter;
import org.sonar.server.source.ws.ShowAction;
import org.sonar.server.source.ws.SourcesWs;
-import org.sonar.server.startup.CleanPreviewAnalysisCache;
-import org.sonar.server.startup.CopyRequirementsFromCharacteristicsToRules;
-import org.sonar.server.startup.GeneratePluginIndex;
-import org.sonar.server.startup.GwtPublisher;
-import org.sonar.server.startup.JdbcDriverDeployer;
-import org.sonar.server.startup.LogServerId;
-import org.sonar.server.startup.RegisterDashboards;
-import org.sonar.server.startup.RegisterDebtModel;
-import org.sonar.server.startup.RegisterMetrics;
-import org.sonar.server.startup.RegisterNewMeasureFilters;
-import org.sonar.server.startup.RegisterPermissionTemplates;
-import org.sonar.server.startup.RegisterServletFilters;
-import org.sonar.server.startup.RenameDeprecatedPropertyKeys;
-import org.sonar.server.startup.ServerMetadataPersister;
+import org.sonar.server.startup.*;
import org.sonar.server.test.CoverageService;
-import org.sonar.server.test.ws.CoverageShowAction;
-import org.sonar.server.test.ws.CoverageWs;
-import org.sonar.server.test.ws.TestsCoveredFilesAction;
-import org.sonar.server.test.ws.TestsShowAction;
-import org.sonar.server.test.ws.TestsTestCasesAction;
-import org.sonar.server.test.ws.TestsWs;
+import org.sonar.server.test.ws.*;
import org.sonar.server.text.MacroInterpreter;
import org.sonar.server.text.RubyTextService;
import org.sonar.server.ui.JRubyI18n;
import org.sonar.server.ui.PageDecorations;
import org.sonar.server.ui.Views;
import org.sonar.server.updatecenter.ws.UpdateCenterWs;
-import org.sonar.server.user.DefaultUserService;
-import org.sonar.server.user.DoPrivileged;
-import org.sonar.server.user.GroupMembershipFinder;
-import org.sonar.server.user.GroupMembershipService;
-import org.sonar.server.user.NewUserNotifier;
-import org.sonar.server.user.SecurityRealmFactory;
+import org.sonar.server.user.*;
import org.sonar.server.user.ws.UsersWs;
-import org.sonar.server.util.BooleanTypeValidation;
-import org.sonar.server.util.FloatTypeValidation;
-import org.sonar.server.util.IntegerTypeValidation;
-import org.sonar.server.util.StringListTypeValidation;
-import org.sonar.server.util.StringTypeValidation;
-import org.sonar.server.util.TextTypeValidation;
-import org.sonar.server.util.TypeValidations;
+import org.sonar.server.util.*;
import org.sonar.server.ws.ListingWs;
import org.sonar.server.ws.WebServiceEngine;
MeasureFilterDao.class,
ActivityDao.class,
+ // Text
+ MacroInterpreter.class,
+
// Elasticsearch
ESNode.class,
RuleNormalizer.class,
pico.addSingleton(org.sonar.server.duplication.ws.ShowAction.class);
// text
- pico.addSingleton(MacroInterpreter.class);
pico.addSingleton(RubyTextService.class);
// Notifications
private String ruleKey;
private RuleKey templateKey;
- private String name, htmlDescription, severity;
+ private String name, htmlDescription, markdownDescription, severity;
private RuleStatus status;
private final Map<String, String> parameters = Maps.newHashMap();
return this;
}
+ @CheckForNull
+ public String markdownDescription() {
+ return markdownDescription;
+ }
+
+ public NewRule setMarkdownDescription(@Nullable String markdownDescription) {
+ this.markdownDescription = markdownDescription;
+ return this;
+ }
+
@CheckForNull
public String severity() {
return severity;
import org.sonar.core.qualityprofile.db.ActiveRuleDto;
import org.sonar.core.qualityprofile.db.ActiveRuleParamDto;
import org.sonar.core.rule.RuleDto;
+import org.sonar.core.rule.RuleDto.Format;
import org.sonar.core.rule.RuleParamDto;
import org.sonar.core.technicaldebt.db.CharacteristicDao;
import org.sonar.core.technicaldebt.db.CharacteristicDto;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import static com.google.common.collect.Lists.newArrayList;
RuleDto ruleDto = RuleDto.createFor(RuleKey.of(ruleDef.repository().key(), ruleDef.key()))
.setIsTemplate(ruleDef.template())
.setConfigKey(ruleDef.internalKey())
- .setDescription(ruleDef.htmlDescription())
.setLanguage(ruleDef.repository().language())
.setName(ruleDef.name())
.setSeverity(ruleDef.severity())
.setStatus(ruleDef.status())
.setEffortToFixDescription(ruleDef.effortToFixDescription())
.setSystemTags(ruleDef.tags());
+ if (ruleDef.htmlDescription() != null) {
+ ruleDto.setDescription(ruleDef.htmlDescription());
+ ruleDto.setDescriptionFormat(Format.HTML);
+ } else {
+ ruleDto.setDescription(ruleDef.markdownDescription());
+ ruleDto.setDescriptionFormat(Format.MARKDOWN);
+ }
dbClient.ruleDao().insert(session, ruleDto);
return ruleDto;
dto.setName(def.name());
changed = true;
}
- if (!StringUtils.equals(dto.getDescription(), def.htmlDescription())) {
- dto.setDescription(def.htmlDescription());
- changed = true;
- }
+ changed = mergeDescription(def, dto);
if (!dto.getSystemTags().containsAll(def.tags())) {
dto.setSystemTags(def.tags());
changed = true;
return changed;
}
+ private boolean mergeDescription(RulesDefinition.Rule def, RuleDto dto) {
+ boolean changed = false;
+ if (def.htmlDescription() != null && !StringUtils.equals(dto.getDescription(), def.htmlDescription())) {
+ dto.setDescription(def.htmlDescription());
+ dto.setDescriptionFormat(Format.HTML);
+ changed = true;
+ } else if (def.markdownDescription() != null && !StringUtils.equals(dto.getDescription(), def.markdownDescription())) {
+ dto.setDescription(def.markdownDescription());
+ dto.setDescriptionFormat(Format.MARKDOWN);
+ changed = true;
+ }
+ return changed;
+ }
+
private boolean mergeDebtDefinitions(RulesDefinition.Rule def, RuleDto dto, @Nullable CharacteristicDto subCharacteristic) {
// Debt definitions are set to null if the sub-characteristic and the remediation function are null
DebtRemediationFunction debtRemediationFunction = subCharacteristic != null ? def.debtRemediationFunction() : null;
import org.sonar.api.server.debt.DebtRemediationFunction;
import javax.annotation.CheckForNull;
+
import java.util.Date;
import java.util.List;
String htmlDescription();
+ String markdownDescription();
+
String effortToFixDescription();
/**
import org.sonar.api.rule.Severity;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.rule.RuleDto;
+import org.sonar.core.rule.RuleDto.Format;
import org.sonar.core.rule.RuleParamDto;
import org.sonar.server.db.DbClient;
import org.sonar.server.rule.index.RuleDoc;
}
private static void validateDescription(NewRule newRule){
- if (Strings.isNullOrEmpty(newRule.htmlDescription())) {
+ if (Strings.isNullOrEmpty(newRule.htmlDescription()) && Strings.isNullOrEmpty(newRule.markdownDescription())) {
throw new IllegalArgumentException("The description is missing");
}
}
.setTemplateId(templateRuleDto.getId())
.setConfigKey(templateRuleDto.getConfigKey())
.setName(newRule.name())
- .setDescription(newRule.htmlDescription())
+ .setDescription(newRule.markdownDescription())
+ .setDescriptionFormat(Format.MARKDOWN)
.setSeverity(newRule.severity())
.setStatus(newRule.status())
.setLanguage(templateRuleDto.getLanguage())
private RuleKey createManualRule(RuleKey ruleKey, NewRule newRule, DbSession dbSession){
RuleDto ruleDto = RuleDto.createFor(ruleKey)
.setName(newRule.name())
- .setDescription(newRule.htmlDescription())
+ .setDescription(newRule.markdownDescription())
+ .setDescriptionFormat(Format.MARKDOWN)
.setSeverity(newRule.severity())
.setStatus(RuleStatus.READY);
dbClient.ruleDao().insert(dbSession, ruleDto);
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
return getNullableField(RuleNormalizer.RuleField.HTML_DESCRIPTION.field());
}
+ @Override
+ public String markdownDescription() {
+ return getNullableField(RuleNormalizer.RuleField.MARKDOWN_DESCRIPTION.field());
+ }
+
@Override
@CheckForNull
public String effortToFixDescription() {
import org.sonar.core.rule.RuleDto;
import org.sonar.core.rule.RuleParamDto;
import org.sonar.core.technicaldebt.db.CharacteristicDto;
+import org.sonar.markdown.Markdown;
import org.sonar.server.db.DbClient;
import org.sonar.server.search.BaseNormalizer;
import org.sonar.server.search.IndexDefinition;
import org.sonar.server.search.IndexField;
import org.sonar.server.search.Indexable;
import org.sonar.server.search.es.ListUpdate;
+import org.sonar.server.text.MacroInterpreter;
import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
public class RuleNormalizer extends BaseNormalizer<RuleDto, RuleKey> {
+ private final MacroInterpreter macroInterpreter;
+
public static final class RuleParamField extends Indexable {
public static final IndexField NAME = add(IndexField.Type.STRING, "name");
public static final IndexField CREATED_AT = addSortable(IndexField.Type.DATE, "createdAt");
public static final IndexField UPDATED_AT = addSortable(IndexField.Type.DATE, UPDATED_AT_FIELD);
public static final IndexField HTML_DESCRIPTION = addSearchable(IndexField.Type.TEXT, "htmlDesc");
+ public static final IndexField MARKDOWN_DESCRIPTION = add(IndexField.Type.TEXT, "mdDesc");
public static final IndexField SEVERITY = add(IndexField.Type.STRING, "severity");
public static final IndexField STATUS = add(IndexField.Type.STRING, "status");
public static final IndexField FIX_DESCRIPTION = add(IndexField.Type.STRING, "effortToFix");
}
}
- public RuleNormalizer(DbClient db) {
+ public RuleNormalizer(DbClient db, MacroInterpreter macroInterpreter) {
super(IndexDefinition.RULE, db);
+ this.macroInterpreter = macroInterpreter;
}
@Override
update.put(RuleField.NAME.field(), rule.getName());
update.put(RuleField.CREATED_AT.field(), rule.getCreatedAt());
update.put(RuleField.UPDATED_AT.field(), rule.getUpdatedAt());
- update.put(RuleField.HTML_DESCRIPTION.field(), rule.getDescription());
+
+ if (RuleDto.Format.HTML.equals(rule.getDescriptionFormat())) {
+ update.put(RuleField.HTML_DESCRIPTION.field(), rule.getDescription());
+ update.put(RuleField.MARKDOWN_DESCRIPTION.field(), null);
+ } else {
+ update.put(RuleField.HTML_DESCRIPTION.field(), rule.getDescription() == null ? null : Markdown.convertToHtml(rule.getDescription()));
+ update.put(RuleField.MARKDOWN_DESCRIPTION.field(), rule.getDescription());
+ }
+
update.put(RuleField.FIX_DESCRIPTION.field(), rule.getEffortToFixDescription());
update.put(RuleField.SEVERITY.field(), rule.getSeverityString());
if (!Strings.isNullOrEmpty(customKey)) {
NewRule newRule = NewRule.createForCustomRule(customKey, RuleKey.parse(request.mandatoryParam(PARAM_TEMPLATE_KEY)))
.setName(request.mandatoryParam(PARAM_NAME))
- .setHtmlDescription(request.mandatoryParam(PARAM_DESCRIPTION))
+ .setMarkdownDescription(request.mandatoryParam(PARAM_DESCRIPTION))
.setSeverity(request.mandatoryParam(PARAM_SEVERITY))
.setStatus(RuleStatus.valueOf(request.mandatoryParam(PARAM_STATUS)))
.setPreventReactivation(request.paramAsBoolean(PARAM_PREVENT_REACTIVATION));
if (!Strings.isNullOrEmpty(manualKey)) {
NewRule newRule = NewRule.createForManualRule(manualKey)
.setName(request.mandatoryParam(PARAM_NAME))
- .setHtmlDescription(request.mandatoryParam(PARAM_DESCRIPTION))
+ .setMarkdownDescription(request.mandatoryParam(PARAM_DESCRIPTION))
.setSeverity(request.param(PARAM_SEVERITY))
.setPreventReactivation(request.paramAsBoolean(PARAM_PREVENT_REACTIVATION));
writeResponse(response, service.create(newRule));
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 javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+
import java.util.Collection;
import java.util.Map;
map("htmlDesc", new Mapper<RuleDoc, RuleMappingContext>() {
@Override
public void write(JsonWriter json, RuleDoc rule, RuleMappingContext context) {
- String html = rule.htmlDescription();
- if (html != null) {
- if (rule.isManual() || rule.templateKey() != null) {
- String desc = StringEscapeUtils.escapeHtml(html);
- desc = desc.replaceAll("\\n", "<br/>");
- json.prop("htmlDesc", desc);
- } else {
- json.prop("htmlDesc", macroInterpreter.interpret(html));
- }
+ if (rule.markdownDescription() != null) {
+ json.prop("htmlDesc", macroInterpreter.interpret(Markdown.convertToHtml(rule.markdownDescription())));
+ } else {
+ json.prop("htmlDesc", macroInterpreter.interpret(rule.htmlDescription()));
}
}
});
+ map("mdDesc", RuleNormalizer.RuleField.MARKDOWN_DESCRIPTION.field());
map("noteLogin", RuleNormalizer.RuleField.NOTE_LOGIN.field());
map("mdNote", RuleNormalizer.RuleField.NOTE.field());
map("htmlNote", new IndexMapper<RuleDoc, RuleMappingContext>(RuleNormalizer.RuleField.NOTE.field()) {
import org.sonar.api.server.debt.DebtRemediationFunction;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.rule.RuleDto;
+import org.sonar.core.rule.RuleDto.Format;
import org.sonar.core.rule.RuleParamDto;
import org.sonar.server.db.DbClient;
import org.sonar.server.rule.db.RuleDao;
// Create custom rule
NewRule newRule = NewRule.createForCustomRule("CUSTOM_RULE", templateRule.getKey())
.setName("My custom")
- .setHtmlDescription("Some description")
+ .setMarkdownDescription("Some description")
.setSeverity(Severity.MAJOR)
.setStatus(RuleStatus.READY)
.setParameters(ImmutableMap.of("regex", "a.*"));
.setStatus(RuleStatus.REMOVED)
.setName("Old name")
.setDescription("Old description")
+ .setDescriptionFormat(Format.MARKDOWN)
.setSeverity(Severity.INFO));
dao.addRuleParam(dbSession, rule, dao.findRuleParamsByRuleKey(dbSession, templateRule.getKey()).get(0).setDefaultValue("a.*"));
dbSession.commit();
// Create custom rule with same key, but with different values
NewRule newRule = NewRule.createForCustomRule(key, templateRule.getKey())
.setName("New name")
- .setHtmlDescription("New description")
+ .setMarkdownDescription("New description")
.setSeverity(Severity.MAJOR)
.setStatus(RuleStatus.READY)
.setParameters(ImmutableMap.of("regex", "c.*"));
// These values should be the same than before
assertThat(result.name()).isEqualTo("Old name");
- assertThat(result.htmlDescription()).isEqualTo("Old description");
+ assertThat(result.markdownDescription()).isEqualTo("Old description");
assertThat(result.severity()).isEqualTo(Severity.INFO);
assertThat(result.param("regex").defaultValue()).isEqualTo("a.*");
public void create_manual_rule() throws Exception {
NewRule newRule = NewRule.createForManualRule("MANUAL_RULE")
.setName("My manual")
- .setHtmlDescription("Some description");
+ .setMarkdownDescription("Some description");
RuleKey ruleKey = creator.create(newRule);
dbSession.clearCache();
assertThat(rule).isNotNull();
assertThat(rule.key()).isEqualTo(RuleKey.of("manual", "MANUAL_RULE"));
assertThat(rule.name()).isEqualTo("My manual");
- assertThat(rule.htmlDescription()).isEqualTo("Some description");
+ assertThat(rule.markdownDescription()).isEqualTo("Some description");
assertThat(rule.severity()).isNull();
assertThat(rule.status()).isEqualTo(RuleStatus.READY);
assertThat(rule.language()).isNull();
public void create_manual_rule_with_severity() throws Exception {
NewRule newRule = NewRule.createForManualRule("MANUAL_RULE")
.setName("My manual")
- .setHtmlDescription("Some description")
+ .setMarkdownDescription("Some description")
.setSeverity(Severity.BLOCKER);
RuleKey ruleKey = creator.create(newRule);
assertThat(rule).isNotNull();
assertThat(rule.key()).isEqualTo(RuleKey.of("manual", "MANUAL_RULE"));
assertThat(rule.name()).isEqualTo("My manual");
- assertThat(rule.htmlDescription()).isEqualTo("Some description");
+ assertThat(rule.markdownDescription()).isEqualTo("Some description");
assertThat(rule.severity()).isEqualTo(Severity.BLOCKER);
assertThat(rule.status()).isEqualTo(RuleStatus.READY);
assertThat(rule.language()).isNull();
// Create a rule with the same key and with another name, description and severity
NewRule newRule = NewRule.createForManualRule(key)
.setName("New name")
- .setHtmlDescription("New description");
+ .setMarkdownDescription("New description");
RuleKey ruleKey = creator.create(newRule);
dbSession.clearCache();
// Name, description and severity should be the same than before
assertThat(result.name()).isEqualTo("Old name");
- assertThat(result.htmlDescription()).isEqualTo("Old description");
+ assertThat(result.markdownDescription()).isEqualTo("Old description");
assertThat(result.severity()).isEqualTo(Severity.INFO);
// Check that the id is the same
Rule rule = service.getByKey(manualRule.getKey());
assertThat(rule).isNotNull();
- assertThat(rule.htmlDescription()).isEqualTo("<div>Manual rule desc</div>");
+ assertThat(rule.htmlDescription()).isEqualTo("<div>Manual rule desc</div>");
}
@Test
import org.sonar.api.rule.Severity;
import org.sonar.api.server.debt.DebtRemediationFunction;
import org.sonar.core.rule.RuleDto;
+import org.sonar.core.rule.RuleDto.Format;
import java.util.Date;
.setRepositoryKey(ruleKey.repository())
.setName("Rule " + ruleKey.rule())
.setDescription("Description " + ruleKey.rule())
+ .setDescriptionFormat(Format.HTML)
.setStatus(RuleStatus.READY)
.setConfigKey("InternalKey" + ruleKey.rule())
.setSeverity(Severity.INFO)
import org.sonar.core.persistence.AbstractDaoTestCase;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.rule.RuleDto;
+import org.sonar.core.rule.RuleDto.Format;
import org.sonar.core.rule.RuleParamDto;
import java.util.List;
assertThat(ruleDto.getId()).isEqualTo(1);
assertThat(ruleDto.getName()).isEqualTo("Avoid Null");
assertThat(ruleDto.getDescription()).isEqualTo("Should avoid NULL");
- assertThat(ruleDto.getDescriptionFormat()).isEqualTo("HTML");
+ assertThat(ruleDto.getDescriptionFormat()).isEqualTo(Format.HTML);
assertThat(ruleDto.getStatus()).isEqualTo(RuleStatus.READY);
assertThat(ruleDto.getRepositoryKey()).isEqualTo("checkstyle");
assertThat(ruleDto.getNoteData()).isEqualTo("Rule note with accents \u00e9\u00e8\u00e0");
assertThat(ruleDto.getId()).isEqualTo(1);
assertThat(ruleDto.getName()).isEqualTo("Avoid Null");
assertThat(ruleDto.getDescription()).isEqualTo("Should avoid NULL");
- assertThat(ruleDto.getDescriptionFormat()).isEqualTo("HTML");
+ assertThat(ruleDto.getDescriptionFormat()).isEqualTo(Format.HTML);
assertThat(ruleDto.getStatus()).isEqualTo(RuleStatus.READY);
assertThat(ruleDto.getRepositoryKey()).isEqualTo("checkstyle");
assertThat(ruleDto.getNoteData()).isEqualTo("Rule note with accents \u00e9\u00e8\u00e0");
assertThat(ruleDto.getId()).isEqualTo(2);
assertThat(ruleDto.getName()).isEqualTo("Avoid Null");
assertThat(ruleDto.getDescription()).isEqualTo("Should avoid NULL");
- assertThat(ruleDto.getDescriptionFormat()).isEqualTo("HTML");
+ assertThat(ruleDto.getDescriptionFormat()).isEqualTo(Format.HTML);
assertThat(ruleDto.getStatus()).isEqualTo(RuleStatus.READY);
assertThat(ruleDto.getRepositoryKey()).isEqualTo("checkstyle");
}
.setRepositoryKey("plugin")
.setName("new name")
.setDescription("new description")
- .setDescriptionFormat("MARKDOWN")
+ .setDescriptionFormat(Format.MARKDOWN)
.setStatus(RuleStatus.DEPRECATED)
.setConfigKey("NewConfigKey")
.setSeverity(Severity.INFO)
.setRepositoryKey("plugin")
.setName("new name")
.setDescription("new description")
- .setDescriptionFormat("MARKDOWN")
+ .setDescriptionFormat(Format.MARKDOWN)
.setStatus(RuleStatus.DEPRECATED)
.setConfigKey("NewConfigKey")
.setSeverity(Severity.INFO)
.setRepositoryKey("plugin")
.setName("new name")
.setDescription("new description")
- .setDescriptionFormat("HTML")
+ .setDescriptionFormat(Format.HTML)
.setStatus(RuleStatus.DEPRECATED)
.setConfigKey("NewConfigKey")
.setSeverity(Severity.INFO)
.setRepositoryKey("plugin2")
.setName("new name2")
.setDescription("new description2")
- .setDescriptionFormat("MARKDOWN")
+ .setDescriptionFormat(Format.MARKDOWN)
.setStatus(RuleStatus.BETA)
.setConfigKey("NewConfigKey2")
.setSeverity(Severity.MAJOR)
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.rule.RuleDto;
+import org.sonar.core.rule.RuleDto.Format;
import org.sonar.core.rule.RuleParamDto;
import org.sonar.core.technicaldebt.db.CharacteristicDao;
import org.sonar.core.technicaldebt.db.CharacteristicDto;
RuleTesting.newDto(RuleKey.of("java", "S001"))
.setName("Rule S001")
.setDescription("Rule S001 <b>description</b>")
+ .setDescriptionFormat(Format.HTML)
.setSeverity(Severity.MINOR)
.setStatus(RuleStatus.BETA)
.setConfigKey("InternalKeyS001")
RuleTesting.newDto(RuleKey.of("java", "S001"))
.setName("Rule S001")
.setDescription("Rule S001 <b>description</b>")
+ .setDescriptionFormat(Format.HTML)
.setSeverity(Severity.MINOR)
.setStatus(RuleStatus.BETA)
.setConfigKey("InternalKeyS001")
.setName("My custom")
.setSeverity(Severity.MINOR)
.setStatus(RuleStatus.READY)
- .setHtmlDescription("<div>line1\nline2</div>");
+ .setMarkdownDescription("<div>line1\nline2</div>");
RuleKey customRuleKey = ruleService.create(customRule);
session.clearCache();
NewRule manualRule = NewRule.createForManualRule("MY_MANUAL")
.setName("My manual")
.setSeverity(Severity.MINOR)
- .setHtmlDescription("<div>line1\nline2</div>");
+ .setMarkdownDescription("<div>line1\nline2</div>");
RuleKey customRuleKey = ruleService.create(manualRule);
session.clearCache();
public static final Integer DISABLED_CHARACTERISTIC_ID = -1;
+ public enum Format {
+ HTML, MARKDOWN
+ }
+
private Integer id;
private String repositoryKey;
private String ruleKey;
private String description;
- private String descriptionFormat;
+ private Format descriptionFormat;
private RuleStatus status;
private String name;
private String configKey;
return this;
}
- public String getDescriptionFormat() {
+ public Format getDescriptionFormat() {
return descriptionFormat;
}
- public RuleDto setDescriptionFormat(String descriptionFormat) {
+ public RuleDto setDescriptionFormat(Format descriptionFormat) {
this.descriptionFormat = descriptionFormat;
return this;
}
assertThat(ruleDto.getId()).isEqualTo(1);
assertThat(ruleDto.getName()).isEqualTo("Avoid Null");
assertThat(ruleDto.getDescription()).isEqualTo("Should avoid NULL");
- assertThat(ruleDto.getDescriptionFormat()).isEqualTo("HTML");
+ assertThat(ruleDto.getDescriptionFormat()).isEqualTo(RuleDto.Format.HTML);
assertThat(ruleDto.getStatus()).isEqualTo(RuleStatus.READY);
assertThat(ruleDto.getRepositoryKey()).isEqualTo("checkstyle");
assertThat(ruleDto.getNoteData()).isEqualTo("Rule note with accents \u00e9\u00e8\u00e0");
package org.sonar.api.server.rule;
import com.google.common.base.Strings;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import com.google.common.collect.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;
class NewRule {
private final String repoKey, key;
- private String name, htmlDescription, internalKey, severity = Severity.MAJOR;
+ private String name, htmlDescription, markdownDescription, internalKey, severity = Severity.MAJOR;
private boolean template;
private RuleStatus status = RuleStatus.defaultStatus();
private String debtSubCharacteristic;
}
public NewRule setHtmlDescription(@Nullable String s) {
+ if (markdownDescription != null) {
+ throw new IllegalStateException(String.format("Rule '%s' already has a Markdown description", this));
+ }
this.htmlDescription = StringUtils.trimToNull(s);
return this;
}
return this;
}
+ public NewRule setMarkdownDescription(@Nullable String s) {
+ if (htmlDescription != null) {
+ throw new IllegalStateException(String.format("Rule '%s' already has an HTML description", this));
+ }
+ this.markdownDescription = StringUtils.trimToNull(s);
+ return this;
+ }
+
+ /**
+ * Load description from a file available in classpath. Example : <code>setMarkdownDescription(getClass().getResource("/myrepo/Rule1234.md")</code>
+ */
+ public NewRule setMarkdownDescription(@Nullable URL classpathUrl) {
+ if (classpathUrl != null) {
+ try {
+ setMarkdownDescription(IOUtils.toString(classpathUrl));
+ } catch (IOException e) {
+ throw new IllegalStateException("Fail to read: " + classpathUrl, e);
+ }
+ } else {
+ this.markdownDescription = null;
+ }
+ return this;
+ }
+
/**
* Default value is {@link org.sonar.api.rule.RuleStatus#READY}. The value
* {@link org.sonar.api.rule.RuleStatus#REMOVED} is not accepted and raises an
@Immutable
class Rule {
private final Repository repository;
- private final String repoKey, key, name, htmlDescription, internalKey, severity;
+ private final String repoKey, key, name, htmlDescription, markdownDescription, internalKey, severity;
private final boolean template;
private final String debtSubCharacteristic;
private final DebtRemediationFunction debtRemediationFunction;
this.key = newRule.key;
this.name = newRule.name;
this.htmlDescription = newRule.htmlDescription;
+ this.markdownDescription = newRule.markdownDescription;
this.internalKey = newRule.internalKey;
this.severity = newRule.severity;
this.template = newRule.template;
return htmlDescription;
}
+ @CheckForNull
+ public String markdownDescription() {
+ return markdownDescription;
+ }
+
public boolean template() {
return template;
}