Skip to content

Commit

Permalink
SONAR-11209 Allow sensors to provide ad hoc rule metadata for externa…
Browse files Browse the repository at this point in the history
…l issues
  • Loading branch information
henryju authored and SonarTech committed Sep 24, 2018
1 parent 326b303 commit cfba7fc
Show file tree
Hide file tree
Showing 53 changed files with 944 additions and 194 deletions.
Expand Up @@ -49,7 +49,7 @@
import org.sonar.xoo.rule.OneBugIssuePerLineSensor; import org.sonar.xoo.rule.OneBugIssuePerLineSensor;
import org.sonar.xoo.rule.OneDayDebtPerFileSensor; import org.sonar.xoo.rule.OneDayDebtPerFileSensor;
import org.sonar.xoo.rule.OneExternalIssuePerLineSensor; import org.sonar.xoo.rule.OneExternalIssuePerLineSensor;
import org.sonar.xoo.rule.OneExternalIssueWithDetailsPerLineSensor; import org.sonar.xoo.rule.OnePredefinedRuleExternalIssuePerLineSensor;
import org.sonar.xoo.rule.OneIssueOnDirPerFileSensor; import org.sonar.xoo.rule.OneIssueOnDirPerFileSensor;
import org.sonar.xoo.rule.OneIssuePerDirectorySensor; import org.sonar.xoo.rule.OneIssuePerDirectorySensor;
import org.sonar.xoo.rule.OneIssuePerFileSensor; import org.sonar.xoo.rule.OneIssuePerFileSensor;
Expand Down Expand Up @@ -168,7 +168,7 @@ public void define(Context context) {
if (context.getSonarQubeVersion().isGreaterThanOrEqual(Version.create(7, 2))) { if (context.getSonarQubeVersion().isGreaterThanOrEqual(Version.create(7, 2))) {
context.addExtensions( context.addExtensions(
OneExternalIssuePerLineSensor.class, OneExternalIssuePerLineSensor.class,
OneExternalIssueWithDetailsPerLineSensor.class, OnePredefinedRuleExternalIssuePerLineSensor.class,
SignificantCodeSensor.class); SignificantCodeSensor.class);
} }
if (context.getSonarQubeVersion().isGreaterThanOrEqual(Version.create(7, 3))) { if (context.getSonarQubeVersion().isGreaterThanOrEqual(Version.create(7, 3))) {
Expand Down
Expand Up @@ -22,54 +22,57 @@
import org.sonar.api.batch.fs.FilePredicates; import org.sonar.api.batch.fs.FilePredicates;
import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.InputFile.Type;
import org.sonar.api.batch.rule.Severity; import org.sonar.api.batch.rule.Severity;
import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.Sensor;
import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.batch.sensor.SensorDescriptor;
import org.sonar.api.batch.sensor.issue.NewExternalIssue; import org.sonar.api.batch.sensor.issue.NewExternalIssue;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.RuleType; import org.sonar.api.rules.RuleType;
import org.sonar.xoo.Xoo; import org.sonar.xoo.Xoo;
import org.sonar.xoo.Xoo2;


public class OneExternalIssuePerLineSensor implements Sensor { public class OneExternalIssuePerLineSensor implements Sensor {
public static final String RULE_KEY = "OneExternalIssuePerLine"; public static final String RULE_ID = "OneExternalIssuePerLine";
public static final String ENGINE_KEY = "XooEngine"; public static final String ENGINE_ID = "XooEngine";
public static final String SEVERITY = "MAJOR"; public static final String SEVERITY = "MAJOR";
public static final Long EFFORT = 10l; public static final Long EFFORT = 10l;
public static final RuleType TYPE = RuleType.BUG; public static final RuleType TYPE = RuleType.BUG;
public static final String ACTIVATE_EXTERNAL_ISSUES = "sonar.oneExternalIssuePerLine.activate"; public static final String ACTIVATE = "sonar.oneExternalIssuePerLine.activate";
public static final String REGISTER_AD_HOC_RULE = "sonar.oneExternalIssuePerLine.adhocRule";
private static final String NAME = "One External Issue Per Line"; private static final String NAME = "One External Issue Per Line";


@Override @Override
public void describe(SensorDescriptor descriptor) { public void describe(SensorDescriptor descriptor) {
descriptor descriptor
.name(NAME) .name(NAME)
.onlyOnLanguages(Xoo.KEY, Xoo2.KEY) .onlyOnLanguages(Xoo.KEY)
.onlyWhenConfiguration(c -> c.getBoolean(ACTIVATE_EXTERNAL_ISSUES).orElse(false)); .onlyWhenConfiguration(c -> c.getBoolean(ACTIVATE).orElse(false));
} }


@Override @Override
public void execute(SensorContext context) { public void execute(SensorContext context) {
analyse(context, Xoo.KEY, XooRulesDefinition.XOO_REPOSITORY);
analyse(context, Xoo2.KEY, XooRulesDefinition.XOO2_REPOSITORY);
}

private void analyse(SensorContext context, String language, String repo) {
FileSystem fs = context.fileSystem(); FileSystem fs = context.fileSystem();
FilePredicates p = fs.predicates(); FilePredicates p = fs.predicates();
for (InputFile file : fs.inputFiles(p.and(p.hasLanguages(language), p.hasType(Type.MAIN)))) { for (InputFile file : fs.inputFiles(p.and(p.hasLanguages(Xoo.KEY), p.hasType(InputFile.Type.MAIN)))) {
createIssues(file, context, repo); createIssues(file, context);
}
if (context.config().getBoolean(REGISTER_AD_HOC_RULE).orElse(false)) {
context.newAdHocRule()
.engineId(ENGINE_ID)
.ruleId(RULE_ID)
.name("An ad hoc rule")
.description("blah blah")
.severity(Severity.BLOCKER)
.type(RuleType.BUG)
.save();
} }
} }


private void createIssues(InputFile file, SensorContext context, String repo) { private static void createIssues(InputFile file, SensorContext context) {
RuleKey ruleKey = RuleKey.of(repo, RULE_KEY);
for (int line = 1; line <= file.lines(); line++) { for (int line = 1; line <= file.lines(); line++) {
NewExternalIssue newIssue = context.newExternalIssue(); NewExternalIssue newIssue = context.newExternalIssue();
newIssue newIssue
.forRule(ruleKey) .engineId(ENGINE_ID)
.ruleId(RULE_ID)
.at(newIssue.newLocation() .at(newIssue.newLocation()
.on(file) .on(file)
.at(file.selectLine(line)) .at(file.selectLine(line))
Expand Down
Expand Up @@ -28,52 +28,45 @@
import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.batch.sensor.SensorDescriptor;
import org.sonar.api.batch.sensor.issue.NewExternalIssue; import org.sonar.api.batch.sensor.issue.NewExternalIssue;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.RuleType; import org.sonar.api.rules.RuleType;
import org.sonar.xoo.Xoo; import org.sonar.xoo.Xoo;
import org.sonar.xoo.Xoo2;


public class OneExternalIssueWithDetailsPerLineSensor implements Sensor { public class OnePredefinedRuleExternalIssuePerLineSensor implements Sensor {
public static final String RULE_KEY = "OneExternalIssueWithDetailsPerLine"; public static final String RULE_ID = "OnePredefinedRuleExternalIssuePerLine";
public static final String ENGINE_KEY = "XooEngine"; public static final String ENGINE_ID = "XooEngine";
public static final String SEVERITY = "MAJOR"; public static final String SEVERITY = "MAJOR";
public static final Long EFFORT = 10l; public static final Long EFFORT = 10l;
public static final RuleType TYPE = RuleType.BUG; public static final RuleType TYPE = RuleType.BUG;
public static final String ACTIVATE_EXTERNAL_ISSUES = "sonar.oneExternalIssueWithDetailsPerLine.activate"; public static final String ACTIVATE = "sonar.onePredefinedRuleExternalIssuePerLine.activate";
private static final String NAME = "One External Issue Per Line"; private static final String NAME = "One External Issue Per Line With A Predefined Rule";


@Override @Override
public void describe(SensorDescriptor descriptor) { public void describe(SensorDescriptor descriptor) {
descriptor descriptor
.name(NAME) .name(NAME)
.onlyOnLanguages(Xoo.KEY, Xoo2.KEY) .onlyOnLanguages(Xoo.KEY)
.onlyWhenConfiguration(c -> c.getBoolean(ACTIVATE_EXTERNAL_ISSUES).orElse(false)); .onlyWhenConfiguration(c -> c.getBoolean(ACTIVATE).orElse(false));
} }


@Override @Override
public void execute(SensorContext context) { public void execute(SensorContext context) {
analyse(context, Xoo.KEY, XooRulesDefinition.XOO_REPOSITORY);
analyse(context, Xoo2.KEY, XooRulesDefinition.XOO2_REPOSITORY);
}

private void analyse(SensorContext context, String language, String repo) {
FileSystem fs = context.fileSystem(); FileSystem fs = context.fileSystem();
FilePredicates p = fs.predicates(); FilePredicates p = fs.predicates();
for (InputFile file : fs.inputFiles(p.and(p.hasLanguages(language), p.hasType(Type.MAIN)))) { for (InputFile file : fs.inputFiles(p.and(p.hasLanguages(Xoo.KEY), p.hasType(Type.MAIN)))) {
createIssues(file, context, repo); createIssues(file, context);
} }
} }


private void createIssues(InputFile file, SensorContext context, String repo) { private static void createIssues(InputFile file, SensorContext context) {
RuleKey ruleKey = RuleKey.of(repo, RULE_KEY);
for (int line = 1; line <= file.lines(); line++) { for (int line = 1; line <= file.lines(); line++) {
NewExternalIssue newIssue = context.newExternalIssue(); NewExternalIssue newIssue = context.newExternalIssue();
newIssue newIssue
.forRule(ruleKey) .engineId(ENGINE_ID)
.ruleId(RULE_ID)
.at(newIssue.newLocation() .at(newIssue.newLocation()
.on(file) .on(file)
.at(file.selectLine(line)) .at(file.selectLine(line))
.message("This issue is generated on each line and the rule contains details")) .message("This issue is generated on each line and the rule is predefined"))
.severity(Severity.valueOf(SEVERITY)) .severity(Severity.valueOf(SEVERITY))
.remediationEffortMinutes(EFFORT) .remediationEffortMinutes(EFFORT)
.type(TYPE) .type(TYPE)
Expand Down
Expand Up @@ -38,7 +38,6 @@ public class XooRulesDefinition implements RulesDefinition {


public static final String XOO_REPOSITORY = "xoo"; public static final String XOO_REPOSITORY = "xoo";
public static final String XOO2_REPOSITORY = "xoo2"; public static final String XOO2_REPOSITORY = "xoo2";
public static final String XOO_EXTERNAL_REPOSITORY = "xoo";


private static final String TEN_MIN = "10min"; private static final String TEN_MIN = "10min";


Expand Down Expand Up @@ -183,11 +182,11 @@ private void defineRulesXoo(Context context) {
} }


private static void defineRulesXooExternal(Context context) { private static void defineRulesXooExternal(Context context) {
NewRepository repo = context.createExternalRepository(XOO_EXTERNAL_REPOSITORY, Xoo.KEY).setName("XooExternal"); NewRepository repo = context.createExternalRepository(OneExternalIssuePerLineSensor.ENGINE_ID, Xoo.KEY).setName(OneExternalIssuePerLineSensor.ENGINE_ID);


repo.createRule(OneExternalIssueWithDetailsPerLineSensor.RULE_KEY) repo.createRule(OnePredefinedRuleExternalIssuePerLineSensor.RULE_ID)
.setSeverity(OneExternalIssueWithDetailsPerLineSensor.SEVERITY) .setSeverity(OnePredefinedRuleExternalIssuePerLineSensor.SEVERITY)
.setType(OneExternalIssueWithDetailsPerLineSensor.TYPE) .setType(OnePredefinedRuleExternalIssuePerLineSensor.TYPE)
.setScope(RuleScope.ALL) .setScope(RuleScope.ALL)
.setHtmlDescription("Generates one external issue in each line") .setHtmlDescription("Generates one external issue in each line")
.setName("One external issue per line"); .setName("One external issue per line");
Expand Down
Expand Up @@ -21,7 +21,6 @@


import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.sonar.api.SonarProduct;
import org.sonar.api.SonarQubeSide; import org.sonar.api.SonarQubeSide;
import org.sonar.api.internal.SonarRuntimeImpl; import org.sonar.api.internal.SonarRuntimeImpl;
import org.sonar.api.server.debt.DebtRemediationFunction; import org.sonar.api.server.debt.DebtRemediationFunction;
Expand Down Expand Up @@ -73,9 +72,9 @@ public void define_xoo_hotspot_rule() {


@Test @Test
public void define_xooExternal_rules() { public void define_xooExternal_rules() {
RulesDefinition.Repository repo = context.repository("external_xoo"); RulesDefinition.Repository repo = context.repository("external_XooEngine");
assertThat(repo).isNotNull(); assertThat(repo).isNotNull();
assertThat(repo.name()).isEqualTo("XooExternal"); assertThat(repo.name()).isEqualTo("XooEngine");
assertThat(repo.language()).isEqualTo("xoo"); assertThat(repo.language()).isEqualTo("xoo");
assertThat(repo.rules()).hasSize(1); assertThat(repo.rules()).hasSize(1);
} }
Expand Down
Expand Up @@ -23,7 +23,7 @@
import java.util.function.Supplier; import java.util.function.Supplier;
import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleKey;
import org.sonar.db.DbSession; import org.sonar.db.DbSession;
import org.sonar.server.rule.NewExternalRule; import org.sonar.server.rule.NewAddHocRule;


/** /**
* Repository of every rule in DB (including manual rules) whichever their status. * Repository of every rule in DB (including manual rules) whichever their status.
Expand All @@ -48,8 +48,8 @@ public interface RuleRepository {


Optional<Rule> findById(int id); Optional<Rule> findById(int id);


void insertNewExternalRuleIfAbsent(RuleKey ruleKey, Supplier<NewExternalRule> ruleSupplier); void addNewAddHocRuleIfAbsent(RuleKey ruleKey, Supplier<NewAddHocRule> ruleSupplier);


void persistNewExternalRules(DbSession dbSession); void persistNewAddHocRules(DbSession dbSession);


} }
Expand Up @@ -37,8 +37,8 @@
import org.sonar.db.rule.DeprecatedRuleKeyDto; import org.sonar.db.rule.DeprecatedRuleKeyDto;
import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleDto;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder; import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.server.rule.ExternalRuleCreator; import org.sonar.server.rule.AddHocRuleCreator;
import org.sonar.server.rule.NewExternalRule; import org.sonar.server.rule.NewAddHocRule;


import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
Expand All @@ -50,34 +50,34 @@ public class RuleRepositoryImpl implements RuleRepository {
@CheckForNull @CheckForNull
private Map<Integer, Rule> rulesById; private Map<Integer, Rule> rulesById;


private final ExternalRuleCreator creator; private final AddHocRuleCreator creator;
private final DbClient dbClient; private final DbClient dbClient;
private final AnalysisMetadataHolder analysisMetadataHolder; private final AnalysisMetadataHolder analysisMetadataHolder;


public RuleRepositoryImpl(ExternalRuleCreator creator, DbClient dbClient, AnalysisMetadataHolder analysisMetadataHolder) { public RuleRepositoryImpl(AddHocRuleCreator creator, DbClient dbClient, AnalysisMetadataHolder analysisMetadataHolder) {
this.creator = creator; this.creator = creator;
this.dbClient = dbClient; this.dbClient = dbClient;
this.analysisMetadataHolder = analysisMetadataHolder; this.analysisMetadataHolder = analysisMetadataHolder;
} }


public void insertNewExternalRuleIfAbsent(RuleKey ruleKey, Supplier<NewExternalRule> ruleSupplier) { public void addNewAddHocRuleIfAbsent(RuleKey ruleKey, Supplier<NewAddHocRule> ruleSupplier) {
ensureInitialized(); ensureInitialized();


if (!rulesByKey.containsKey(ruleKey)) { if (!rulesByKey.containsKey(ruleKey)) {
rulesByKey.computeIfAbsent(ruleKey, s -> new ExternalRuleWrapper(ruleSupplier.get())); rulesByKey.computeIfAbsent(ruleKey, s -> new AdHocRuleWrapper(ruleSupplier.get()));
} }
} }


@Override @Override
public void persistNewExternalRules(DbSession dbSession) { public void persistNewAddHocRules(DbSession dbSession) {
ensureInitialized(); ensureInitialized();


rulesByKey.values().stream() rulesByKey.values().stream()
.filter(ExternalRuleWrapper.class::isInstance) .filter(AdHocRuleWrapper.class::isInstance)
.forEach(extRule -> persistAndIndex(dbSession, (ExternalRuleWrapper) extRule)); .forEach(r -> persistAndIndex(dbSession, (AdHocRuleWrapper) r));
} }


private void persistAndIndex(DbSession dbSession, ExternalRuleWrapper external) { private void persistAndIndex(DbSession dbSession, AdHocRuleWrapper external) {
Rule rule = new RuleImpl(creator.persistAndIndex(dbSession, external.getDelegate())); Rule rule = new RuleImpl(creator.persistAndIndex(dbSession, external.getDelegate()));
rulesById.put(rule.getId(), rule); rulesById.put(rule.getId(), rule);
rulesByKey.put(external.getKey(), rule); rulesByKey.put(external.getKey(), rule);
Expand Down Expand Up @@ -145,15 +145,15 @@ private void loadRulesFromDb(DbSession dbSession) {
} }
} }


private static class ExternalRuleWrapper implements Rule { private static class AdHocRuleWrapper implements Rule {
private final NewExternalRule externalRule; private final NewAddHocRule addHocRule;


private ExternalRuleWrapper(NewExternalRule externalRule) { private AdHocRuleWrapper(NewAddHocRule addHocRule) {
this.externalRule = externalRule; this.addHocRule = addHocRule;
} }


public NewExternalRule getDelegate() { public NewAddHocRule getDelegate() {
return externalRule; return addHocRule;
} }


@Override @Override
Expand All @@ -163,12 +163,12 @@ public int getId() {


@Override @Override
public RuleKey getKey() { public RuleKey getKey() {
return externalRule.getKey(); return addHocRule.getKey();
} }


@Override @Override
public String getName() { public String getName() {
return externalRule.getName(); return addHocRule.getName();
} }


@Override @Override
Expand Down Expand Up @@ -201,7 +201,7 @@ public DebtRemediationFunction getRemediationFunction() {
@CheckForNull @CheckForNull
@Override @Override
public String getPluginKey() { public String getPluginKey() {
return externalRule.getPluginKey(); return addHocRule.getPluginKey();
} }
} }
} }

0 comments on commit cfba7fc

Please sign in to comment.