aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-ce-task-projectanalysis
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2018-09-06 18:02:56 +0200
committerSonarTech <sonartech@sonarsource.com>2018-09-24 20:20:58 +0200
commit1cacbb1d2ed72d720058580746d678ba8da1b453 (patch)
tree3d08e44365ae38fb975239385d301fc64973f541 /server/sonar-ce-task-projectanalysis
parentcfba7fcb6500d8217bd81ecfcb8f47ec48ad55f2 (diff)
downloadsonarqube-1cacbb1d2ed72d720058580746d678ba8da1b453.tar.gz
sonarqube-1cacbb1d2ed72d720058580746d678ba8da1b453.zip
SONAR-11209 Store ad hoc rules coming from scanner in rules_metadata
Diffstat (limited to 'server/sonar-ce-task-projectanalysis')
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReader.java2
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderImpl.java7
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreator.java114
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRule.java128
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/Rule.java2
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleImpl.java13
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleRepository.java5
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryImpl.java46
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java50
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistAdHocRulesStep.java (renamed from server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistExternalRulesStep.java)8
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java2
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderRule.java11
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreatorTest.java180
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/DumbRule.java11
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRuleTest.java55
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryImplTest.java26
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryRule.java7
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistAdHocRulesStepTest.java (renamed from server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistExternalRulesStepTest.java)34
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistIssuesStepTest.java6
19 files changed, 612 insertions, 95 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReader.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReader.java
index f8bc0774d7a..a8e82f3d65f 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReader.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReader.java
@@ -31,6 +31,8 @@ public interface BatchReportReader {
CloseableIterator<ScannerReport.ActiveRule> readActiveRules();
+ CloseableIterator<ScannerReport.AdHocRule> readAdHocRules();
+
CloseableIterator<ScannerReport.Measure> readComponentMeasures(int componentRef);
@CheckForNull
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderImpl.java
index 680baaeca43..3c324d7a8ed 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderImpl.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderImpl.java
@@ -87,6 +87,13 @@ public class BatchReportReaderImpl implements BatchReportReader {
}
@Override
+ public CloseableIterator<ScannerReport.AdHocRule> readAdHocRules() {
+ ensureInitialized();
+ return delegate.readAdHocRules();
+ }
+
+
+ @Override
public CloseableIterator<ScannerReport.Measure> readComponentMeasures(int componentRef) {
ensureInitialized();
return delegate.readComponentMeasures(componentRef);
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreator.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreator.java
new file mode 100644
index 00000000000..432961896e1
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreator.java
@@ -0,0 +1,114 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.issue;
+
+import com.google.common.base.Preconditions;
+import java.util.Objects;
+import java.util.Optional;
+import org.sonar.api.rules.RuleType;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.rule.RuleDao;
+import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleMetadataDto;
+import org.sonar.server.rule.index.RuleIndexer;
+
+import static java.util.Objects.requireNonNull;
+import static org.sonar.api.rule.RuleStatus.READY;
+import static org.sonar.db.rule.RuleDto.Scope.ALL;
+
+public class AdHocRuleCreator {
+
+ private final DbClient dbClient;
+ private final System2 system2;
+ private final RuleIndexer ruleIndexer;
+
+ public AdHocRuleCreator(DbClient dbClient, System2 system2, RuleIndexer ruleIndexer) {
+ this.dbClient = dbClient;
+ this.system2 = system2;
+ this.ruleIndexer = ruleIndexer;
+ }
+
+ /**
+ * Persists a new add hoc rule in the DB and indexes it.
+ * @return the rule that was inserted in the DB, which <b>includes the generated ID</b>.
+ */
+ public RuleDto persistAndIndex(DbSession dbSession, NewAdHocRule adHoc, OrganizationDto organizationDto) {
+ RuleDao dao = dbClient.ruleDao();
+ Optional<RuleDto> existingRuleDtoOpt = dao.selectByKey(dbSession, organizationDto, adHoc.getKey());
+ RuleMetadataDto metadata;
+ long now = system2.now();
+ if (!existingRuleDtoOpt.isPresent()) {
+ RuleDefinitionDto dto = new RuleDefinitionDto()
+ .setRuleKey(adHoc.getKey())
+ .setIsExternal(true)
+ .setIsAdHoc(true)
+ .setName(adHoc.getEngineId() + ": " + adHoc.getRuleId())
+ .setScope(ALL)
+ .setStatus(READY)
+ .setCreatedAt(now)
+ .setUpdatedAt(now);
+ dao.insert(dbSession, dto);
+ metadata = new RuleMetadataDto()
+ .setRuleId(dto.getId())
+ .setOrganizationUuid(organizationDto.getUuid());
+ } else {
+ // No need to update the rule, only org specific metadata
+ RuleDto ruleDto = existingRuleDtoOpt.get();
+ Preconditions.checkState(ruleDto.isExternal() && ruleDto.isAdHoc());
+ metadata = ruleDto.getMetadata();
+ }
+
+ if (adHoc.hasDetails()) {
+ boolean changed = false;
+ if (!Objects.equals(metadata.getAdHocName(), adHoc.getName())) {
+ metadata.setAdHocName(adHoc.getName());
+ changed = true;
+ }
+ if (!Objects.equals(metadata.getAdHocDescription(), adHoc.getDescription())) {
+ metadata.setAdHocDescription(adHoc.getDescription());
+ changed = true;
+ }
+ if (!Objects.equals(metadata.getAdHocSeverity(), adHoc.getSeverity())) {
+ metadata.setAdHocSeverity(adHoc.getSeverity());
+ changed = true;
+ }
+ RuleType ruleType = requireNonNull(adHoc.getRuleType(), "Rule type should not be null");
+ if (!Objects.equals(metadata.getAdHocType(), ruleType.getDbConstant())) {
+ metadata.setAdHocType(ruleType);
+ changed = true;
+ }
+ if (changed) {
+ metadata.setUpdatedAt(now);
+ metadata.setCreatedAt(now);
+ dao.insertOrUpdate(dbSession, metadata);
+ }
+
+ }
+
+ RuleDto ruleDto = dao.selectOrFailByKey(dbSession, organizationDto, adHoc.getKey());
+ ruleIndexer.commitAndIndex(dbSession, ruleDto.getId());
+ return ruleDto;
+ }
+
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRule.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRule.java
new file mode 100644
index 00000000000..029430cf6c8
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRule.java
@@ -0,0 +1,128 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.issue;
+
+import com.google.common.base.Preconditions;
+import java.util.Objects;
+import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.RuleType;
+import org.sonar.scanner.protocol.Constants;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+import static org.apache.commons.lang.StringUtils.isNotBlank;
+import static org.apache.commons.lang.StringUtils.trimToNull;
+
+@Immutable
+public class NewAdHocRule {
+ private final RuleKey key;
+ private final String engineId;
+ private final String ruleId;
+ private final String name;
+ private final String description;
+ private final String severity;
+ private final RuleType ruleType;
+ private final boolean hasDetails;
+
+ public NewAdHocRule(ScannerReport.AdHocRule ruleFromScannerReport) {
+ Preconditions.checkArgument(isNotBlank(ruleFromScannerReport.getEngineId()), "'engine id' not expected to be null for an ad hoc rule");
+ Preconditions.checkArgument(isNotBlank(ruleFromScannerReport.getRuleId()), "'rule id' not expected to be null for an ad hoc rule");
+ Preconditions.checkArgument(isNotBlank(ruleFromScannerReport.getName()), "'name' not expected to be null for an ad hoc rule");
+ Preconditions.checkArgument(ruleFromScannerReport.getSeverity() != Constants.Severity.UNSET_SEVERITY , "'severity' not expected to be null for an ad hoc rule");
+ Preconditions.checkArgument(ruleFromScannerReport.getType() != ScannerReport.IssueType.UNSET, "'issue type' not expected to be null for an ad hoc rule");
+ this.key = RuleKey.of(RuleKey.EXTERNAL_RULE_REPO_PREFIX + ruleFromScannerReport.getEngineId(), ruleFromScannerReport.getRuleId());
+ this.engineId = ruleFromScannerReport.getEngineId();
+ this.ruleId = ruleFromScannerReport.getRuleId();
+ this.name = ruleFromScannerReport.getName();
+ this.description = trimToNull(ruleFromScannerReport.getDescription());
+ this.severity = ruleFromScannerReport.getSeverity().name();
+ this.ruleType = RuleType.valueOf(ruleFromScannerReport.getType().name());
+ this.hasDetails = true;
+ }
+
+ public NewAdHocRule(ScannerReport.ExternalIssue fromIssue) {
+ Preconditions.checkArgument(isNotBlank(fromIssue.getEngineId()), "'engine id' not expected to be null for an ad hoc rule");
+ Preconditions.checkArgument(isNotBlank(fromIssue.getRuleId()), "'rule id' not expected to be null for an ad hoc rule");
+ this.key = RuleKey.of(RuleKey.EXTERNAL_RULE_REPO_PREFIX + fromIssue.getEngineId(), fromIssue.getRuleId());
+ this.engineId = fromIssue.getEngineId();
+ this.ruleId = fromIssue.getRuleId();
+ this.name = null;
+ this.description = null;
+ this.severity = null;
+ this.ruleType = null;
+ this.hasDetails = false;
+ }
+
+ public RuleKey getKey() {
+ return key;
+ }
+
+ public String getEngineId() {
+ return engineId;
+ }
+
+ public String getRuleId() {
+ return ruleId;
+ }
+
+ @CheckForNull
+ public String getName() {
+ return name;
+ }
+
+ @CheckForNull
+ public String getDescription() {
+ return description;
+ }
+
+ @CheckForNull
+ public String getSeverity() {
+ return severity;
+ }
+
+ @CheckForNull
+ public RuleType getRuleType() {
+ return ruleType;
+ }
+
+ public boolean hasDetails() {
+ return hasDetails;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ NewAdHocRule that = (NewAdHocRule) o;
+ return Objects.equals(key, that.key);
+ }
+
+ @Override
+ public int hashCode() {
+ return key.hashCode();
+ }
+
+
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/Rule.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/Rule.java
index 6cba8ab28e4..eb52047cdfc 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/Rule.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/Rule.java
@@ -44,6 +44,8 @@ public interface Rule {
boolean isExternal();
+ boolean isAdHoc();
+
/**
* Get all tags, whatever system or user tags.
*/
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleImpl.java
index 43b0d294995..d81ab7930ff 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleImpl.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleImpl.java
@@ -44,7 +44,8 @@ public class RuleImpl implements Rule {
private final DebtRemediationFunction remediationFunction;
private final RuleType type;
private final String pluginKey;
- private final boolean external;
+ private final boolean isExternal;
+ private final boolean isAdHoc;
public RuleImpl(RuleDto dto) {
this.id = dto.getId();
@@ -55,7 +56,8 @@ public class RuleImpl implements Rule {
this.remediationFunction = effectiveRemediationFunction(dto);
this.type = RuleType.valueOfNullable(dto.getType());
this.pluginKey = dto.getPluginKey();
- this.external = dto.isExternal();
+ this.isExternal = dto.isExternal();
+ this.isAdHoc = dto.isAdHoc();
}
@Override
@@ -142,7 +144,12 @@ public class RuleImpl implements Rule {
}
@Override
+ public boolean isAdHoc() {
+ return isAdHoc;
+ }
+
+ @Override
public boolean isExternal() {
- return external;
+ return isExternal;
}
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleRepository.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleRepository.java
index a780920f733..7104dcbce99 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleRepository.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleRepository.java
@@ -23,7 +23,6 @@ import java.util.Optional;
import java.util.function.Supplier;
import org.sonar.api.rule.RuleKey;
import org.sonar.db.DbSession;
-import org.sonar.server.rule.NewAddHocRule;
/**
* Repository of every rule in DB (including manual rules) whichever their status.
@@ -48,8 +47,8 @@ public interface RuleRepository {
Optional<Rule> findById(int id);
- void addNewAddHocRuleIfAbsent(RuleKey ruleKey, Supplier<NewAddHocRule> ruleSupplier);
+ void addOrUpdateAddHocRuleIfNeeded(RuleKey ruleKey, Supplier<NewAdHocRule> ruleSupplier);
- void persistNewAddHocRules(DbSession dbSession);
+ void saveOrUpdateAddHocRules(DbSession dbSession);
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryImpl.java
index 7a99e846c1c..e91923c267f 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryImpl.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryImpl.java
@@ -31,14 +31,12 @@ import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rules.RuleType;
import org.sonar.api.server.debt.DebtRemediationFunction;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.rule.DeprecatedRuleKeyDto;
import org.sonar.db.rule.RuleDto;
-import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
-import org.sonar.server.rule.AddHocRuleCreator;
-import org.sonar.server.rule.NewAddHocRule;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
@@ -50,37 +48,40 @@ public class RuleRepositoryImpl implements RuleRepository {
@CheckForNull
private Map<Integer, Rule> rulesById;
- private final AddHocRuleCreator creator;
+ private final AdHocRuleCreator creator;
private final DbClient dbClient;
private final AnalysisMetadataHolder analysisMetadataHolder;
- public RuleRepositoryImpl(AddHocRuleCreator creator, DbClient dbClient, AnalysisMetadataHolder analysisMetadataHolder) {
+ private Map<RuleKey, NewAdHocRule> adHocRulesPersist = new HashMap<>();
+
+ public RuleRepositoryImpl(AdHocRuleCreator creator, DbClient dbClient, AnalysisMetadataHolder analysisMetadataHolder) {
this.creator = creator;
this.dbClient = dbClient;
this.analysisMetadataHolder = analysisMetadataHolder;
}
- public void addNewAddHocRuleIfAbsent(RuleKey ruleKey, Supplier<NewAddHocRule> ruleSupplier) {
+ public void addOrUpdateAddHocRuleIfNeeded(RuleKey ruleKey, Supplier<NewAdHocRule> ruleSupplier) {
ensureInitialized();
- if (!rulesByKey.containsKey(ruleKey)) {
- rulesByKey.computeIfAbsent(ruleKey, s -> new AdHocRuleWrapper(ruleSupplier.get()));
+ Rule existingRule = rulesByKey.get(ruleKey);
+ if (existingRule == null || (existingRule.isAdHoc() && !adHocRulesPersist.containsKey(ruleKey))) {
+ NewAdHocRule newAdHocRule = ruleSupplier.get();
+ adHocRulesPersist.put(ruleKey, newAdHocRule);
+ rulesByKey.put(ruleKey, new AdHocRuleWrapper(newAdHocRule));
}
}
@Override
- public void persistNewAddHocRules(DbSession dbSession) {
+ public void saveOrUpdateAddHocRules(DbSession dbSession) {
ensureInitialized();
- rulesByKey.values().stream()
- .filter(AdHocRuleWrapper.class::isInstance)
- .forEach(r -> persistAndIndex(dbSession, (AdHocRuleWrapper) r));
+ adHocRulesPersist.values().forEach(r -> persistAndIndex(dbSession, r));
}
- private void persistAndIndex(DbSession dbSession, AdHocRuleWrapper external) {
- Rule rule = new RuleImpl(creator.persistAndIndex(dbSession, external.getDelegate()));
+ private void persistAndIndex(DbSession dbSession, NewAdHocRule adHocRule) {
+ Rule rule = new RuleImpl(creator.persistAndIndex(dbSession, adHocRule, analysisMetadataHolder.getOrganization().toDto()));
rulesById.put(rule.getId(), rule);
- rulesByKey.put(external.getKey(), rule);
+ rulesByKey.put(adHocRule.getKey(), rule);
}
@Override
@@ -146,19 +147,19 @@ public class RuleRepositoryImpl implements RuleRepository {
}
private static class AdHocRuleWrapper implements Rule {
- private final NewAddHocRule addHocRule;
+ private final NewAdHocRule addHocRule;
- private AdHocRuleWrapper(NewAddHocRule addHocRule) {
+ private AdHocRuleWrapper(NewAdHocRule addHocRule) {
this.addHocRule = addHocRule;
}
- public NewAddHocRule getDelegate() {
+ public NewAdHocRule getDelegate() {
return addHocRule;
}
@Override
public int getId() {
- return 0;
+ throw new UnsupportedOperationException("Rule is not persisted, can't know the id");
}
@Override
@@ -188,6 +189,11 @@ public class RuleRepositoryImpl implements RuleRepository {
}
@Override
+ public boolean isAdHoc() {
+ return true;
+ }
+
+ @Override
public Set<String> getTags() {
return Collections.emptySet();
}
@@ -201,7 +207,7 @@ public class RuleRepositoryImpl implements RuleRepository {
@CheckForNull
@Override
public String getPluginKey() {
- return addHocRule.getPluginKey();
+ return null;
}
}
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java
index 767627d6a79..0ee7f7e1e49 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java
@@ -21,14 +21,23 @@ package org.sonar.ce.task.projectanalysis.issue;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
+import javax.annotation.Nullable;
import org.sonar.api.issue.Issue;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.log.Loggers;
+import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
+import org.sonar.ce.task.projectanalysis.issue.commonrule.CommonRuleEngine;
+import org.sonar.ce.task.projectanalysis.issue.filter.IssueFilter;
+import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolder;
+import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepository;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.tracking.Input;
import org.sonar.core.issue.tracking.LazyInput;
@@ -39,14 +48,7 @@ import org.sonar.db.protobuf.DbIssues;
import org.sonar.scanner.protocol.Constants.Severity;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.protocol.output.ScannerReport.IssueType;
-import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
-import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
-import org.sonar.ce.task.projectanalysis.issue.commonrule.CommonRuleEngine;
-import org.sonar.ce.task.projectanalysis.issue.filter.IssueFilter;
-import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolder;
-import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepository;
import org.sonar.server.rule.CommonRuleKeys;
-import org.sonar.server.rule.NewAddHocRule;
import static org.apache.commons.lang.StringUtils.isNotEmpty;
@@ -120,12 +122,20 @@ public class TrackerRawInputFactory {
}
}
- try (CloseableIterator<ScannerReport.ExternalIssue> reportIssues = reportReader.readComponentExternalIssues(component.getReportAttributes().getRef())) {
+ Map<RuleKey, ScannerReport.AdHocRule> adHocRuleMap = new HashMap<>();
+ try (CloseableIterator<ScannerReport.AdHocRule> reportAdHocRule = reportReader.readAdHocRules()) {
+ while (reportAdHocRule.hasNext()) {
+ ScannerReport.AdHocRule adHocRule = reportAdHocRule.next();
+ adHocRuleMap.put(RuleKey.of(RuleKey.EXTERNAL_RULE_REPO_PREFIX + adHocRule.getEngineId(), adHocRule.getRuleId()), adHocRule);
+ }
+ }
+
+ try (CloseableIterator<ScannerReport.ExternalIssue> reportExternalIssues = reportReader.readComponentExternalIssues(component.getReportAttributes().getRef())) {
// optimization - do not load line hashes if there are no issues -> getLineHashSequence() is executed
// as late as possible
- while (reportIssues.hasNext()) {
- ScannerReport.ExternalIssue reportExternalIssue = reportIssues.next();
- result.add(toExternalIssue(getLineHashSequence(), reportExternalIssue));
+ while (reportExternalIssues.hasNext()) {
+ ScannerReport.ExternalIssue reportExternalIssue = reportExternalIssues.next();
+ result.add(toExternalIssue(getLineHashSequence(), reportExternalIssue, adHocRuleMap));
}
}
@@ -181,11 +191,12 @@ public class TrackerRawInputFactory {
return issue;
}
- private DefaultIssue toExternalIssue(LineHashSequence lineHashSeq, ScannerReport.ExternalIssue reportExternalIssue) {
+ private DefaultIssue toExternalIssue(LineHashSequence lineHashSeq, ScannerReport.ExternalIssue reportExternalIssue, Map<RuleKey, ScannerReport.AdHocRule> adHocRuleMap) {
DefaultIssue issue = new DefaultIssue();
init(issue);
- issue.setRuleKey(RuleKey.of(RuleKey.EXTERNAL_RULE_REPO_PREFIX + reportExternalIssue.getEngineId(), reportExternalIssue.getRuleId()));
+ RuleKey ruleKey = RuleKey.of(RuleKey.EXTERNAL_RULE_REPO_PREFIX + reportExternalIssue.getEngineId(), reportExternalIssue.getRuleId());
+ issue.setRuleKey(ruleKey);
if (reportExternalIssue.hasTextRange()) {
int startLine = reportExternalIssue.getTextRange().getStartLine();
issue.setLine(startLine);
@@ -217,16 +228,15 @@ public class TrackerRawInputFactory {
issue.setLocations(dbLocationsBuilder.build());
issue.setType(toRuleType(reportExternalIssue.getType()));
- ruleRepository.addNewAddHocRuleIfAbsent(issue.getRuleKey(), () -> toAdHocRule(reportExternalIssue));
+ ruleRepository.addOrUpdateAddHocRuleIfNeeded(ruleKey, () -> toAdHocRule(reportExternalIssue, adHocRuleMap.get(issue.ruleKey())));
return issue;
}
- private NewAddHocRule toAdHocRule(ScannerReport.ExternalIssue reportIssue) {
- NewAddHocRule.Builder builder = new NewAddHocRule.Builder()
- .setName(reportIssue.getEngineId() + " " + reportIssue.getRuleId())
- .setKey(RuleKey.of(RuleKey.EXTERNAL_RULE_REPO_PREFIX + reportIssue.getEngineId(), reportIssue.getRuleId()));
-
- return builder.build();
+ private NewAdHocRule toAdHocRule(ScannerReport.ExternalIssue reportIssue, @Nullable ScannerReport.AdHocRule adHocRule) {
+ if (adHocRule != null) {
+ return new NewAdHocRule(adHocRule);
+ }
+ return new NewAdHocRule(reportIssue);
}
private RuleType toRuleType(IssueType type) {
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistExternalRulesStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistAdHocRulesStep.java
index 28c871d75fa..4c6e4ef565f 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistExternalRulesStep.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistAdHocRulesStep.java
@@ -24,12 +24,12 @@ import org.sonar.ce.task.step.ComputationStep;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
-public class PersistExternalRulesStep implements ComputationStep {
+public class PersistAdHocRulesStep implements ComputationStep {
private final DbClient dbClient;
private final RuleRepository ruleRepository;
- public PersistExternalRulesStep(DbClient dbClient, RuleRepository ruleRepository) {
+ public PersistAdHocRulesStep(DbClient dbClient, RuleRepository ruleRepository) {
this.dbClient = dbClient;
this.ruleRepository = ruleRepository;
}
@@ -37,14 +37,14 @@ public class PersistExternalRulesStep implements ComputationStep {
@Override
public void execute(ComputationStep.Context context) {
try (DbSession dbSession = dbClient.openSession(false)) {
- ruleRepository.persistNewAddHocRules(dbSession);
+ ruleRepository.saveOrUpdateAddHocRules(dbSession);
}
}
@Override
public String getDescription() {
- return "Persist new externally defined Rules";
+ return "Persist new ad hoc Rules";
}
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java
index 631acb001b2..47b209e3589 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java
@@ -94,7 +94,7 @@ public class ReportComputationSteps extends AbstractComputationSteps {
PersistAnalysisPropertiesStep.class,
PersistMeasuresStep.class,
PersistLiveMeasuresStep.class,
- PersistExternalRulesStep.class,
+ PersistAdHocRulesStep.class,
PersistIssuesStep.class,
PersistProjectLinksStep.class,
PersistEventsStep.class,
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderRule.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderRule.java
index 0572a9b7a3c..06a0b1e1594 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderRule.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderRule.java
@@ -46,6 +46,7 @@ public class BatchReportReaderRule implements TestRule, BatchReportReader {
private Map<Integer, ScannerReport.Component> components = new HashMap<>();
private Map<Integer, List<ScannerReport.Issue>> issues = new HashMap<>();
private Map<Integer, List<ScannerReport.ExternalIssue>> externalIssues = new HashMap<>();
+ private List<ScannerReport.AdHocRule> adHocRules = new ArrayList<>();
private Map<Integer, List<ScannerReport.Duplication>> duplications = new HashMap<>();
private Map<Integer, List<ScannerReport.CpdTextBlock>> duplicationBlocks = new HashMap<>();
private Map<Integer, List<ScannerReport.Symbol>> symbols = new HashMap<>();
@@ -179,6 +180,16 @@ public class BatchReportReaderRule implements TestRule, BatchReportReader {
return closeableIterator(externalIssues.get(componentRef));
}
+ @Override
+ public CloseableIterator<ScannerReport.AdHocRule> readAdHocRules() {
+ return closeableIterator(adHocRules);
+ }
+
+ public BatchReportReaderRule putAdHocRules(List<ScannerReport.AdHocRule> adHocRules) {
+ this.adHocRules = adHocRules;
+ return this;
+ }
+
public BatchReportReaderRule putIssues(int componentRef, List<ScannerReport.Issue> issues) {
this.issues.put(componentRef, issues);
return this;
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreatorTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreatorTest.java
new file mode 100644
index 00000000000..295cf953716
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreatorTest.java
@@ -0,0 +1,180 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.issue;
+
+import org.junit.Test;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.rules.RuleType;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleMetadataDto;
+import org.sonar.scanner.protocol.Constants;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.rule.index.RuleIndexer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AdHocRuleCreatorTest {
+
+ @org.junit.Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+ @org.junit.Rule
+ public EsTester es = EsTester.create();
+
+ private RuleIndexer indexer = new RuleIndexer(es.client(), db.getDbClient());
+ private AdHocRuleCreator underTest = new AdHocRuleCreator(db.getDbClient(), System2.INSTANCE, indexer);
+ private DbSession dbSession = db.getSession();
+
+ @Test
+ public void create_ad_hoc_rule_from_issue() {
+ OrganizationDto organization = db.organizations().insert();
+ NewAdHocRule addHocRule = new NewAdHocRule(ScannerReport.ExternalIssue.newBuilder().setEngineId("eslint").setRuleId("no-cond-assign").build());
+
+ RuleDto rule = underTest.persistAndIndex(dbSession, addHocRule, organization);
+
+ assertThat(rule).isNotNull();
+ assertThat(rule.isExternal()).isTrue();
+ assertThat(rule.isAdHoc()).isTrue();
+ assertThat(rule.getId()).isGreaterThan(0);
+ assertThat(rule.getKey()).isEqualTo(RuleKey.of("external_eslint", "no-cond-assign"));
+ assertThat(rule.getName()).isEqualTo("eslint: no-cond-assign");
+ assertThat(rule.getDescription()).isNull();
+ assertThat(rule.getSeverity()).isNull();
+ assertThat(rule.getType()).isEqualTo(0);
+ assertThat(rule.getMetadata().getAdHocName()).isNull();
+ assertThat(rule.getMetadata().getAdHocDescription()).isNull();
+ assertThat(rule.getMetadata().getAdHocSeverity()).isNull();
+ assertThat(rule.getMetadata().getAdHocType()).isNull();
+ }
+
+ @Test
+ public void create_ad_hoc_rule_from_scanner_report() {
+ OrganizationDto organization = db.organizations().insert();
+ NewAdHocRule addHocRule = new NewAdHocRule(ScannerReport.AdHocRule.newBuilder()
+ .setEngineId("eslint")
+ .setRuleId("no-cond-assign")
+ .setName("No condition assigned")
+ .setDescription("A description")
+ .setSeverity(Constants.Severity.BLOCKER)
+ .setType(ScannerReport.IssueType.BUG)
+ .build());
+
+ RuleDto rule = underTest.persistAndIndex(dbSession, addHocRule, organization);
+
+ assertThat(rule).isNotNull();
+ assertThat(rule.isExternal()).isTrue();
+ assertThat(rule.isAdHoc()).isTrue();
+ assertThat(rule.getId()).isGreaterThan(0);
+ assertThat(rule.getKey()).isEqualTo(RuleKey.of("external_eslint", "no-cond-assign"));
+ assertThat(rule.getName()).isEqualTo("eslint: no-cond-assign");
+ assertThat(rule.getDescription()).isNull();
+ assertThat(rule.getSeverity()).isNull();
+ assertThat(rule.getType()).isEqualTo(0);
+ assertThat(rule.getMetadata().getAdHocName()).isEqualTo("No condition assigned");
+ assertThat(rule.getMetadata().getAdHocDescription()).isEqualTo("A description");
+ assertThat(rule.getMetadata().getAdHocSeverity()).isEqualTo(Severity.BLOCKER);
+ assertThat(rule.getMetadata().getAdHocType()).isEqualTo(RuleType.BUG.getDbConstant());
+ }
+
+ @Test
+ public void update_metadata_only() {
+ OrganizationDto organization = db.organizations().insert();
+ NewAdHocRule addHocRule = new NewAdHocRule(ScannerReport.AdHocRule.newBuilder()
+ .setEngineId("eslint")
+ .setRuleId("no-cond-assign")
+ .setName("No condition assigned")
+ .setDescription("A description")
+ .setSeverity(Constants.Severity.BLOCKER)
+ .setType(ScannerReport.IssueType.BUG)
+ .build());
+ RuleDto rule = underTest.persistAndIndex(dbSession, addHocRule, organization);
+ long creationDate = rule.getCreatedAt();
+ NewAdHocRule addHocRuleUpdated = new NewAdHocRule(ScannerReport.AdHocRule.newBuilder()
+ .setEngineId("eslint")
+ .setRuleId("no-cond-assign")
+ .setName("No condition assigned updated")
+ .setDescription("A description updated")
+ .setSeverity(Constants.Severity.CRITICAL)
+ .setType(ScannerReport.IssueType.CODE_SMELL)
+ .build());
+
+ RuleDto ruleUpdated = underTest.persistAndIndex(dbSession, addHocRuleUpdated, organization);
+
+ assertThat(ruleUpdated).isNotNull();
+ assertThat(ruleUpdated.isExternal()).isTrue();
+ assertThat(ruleUpdated.isAdHoc()).isTrue();
+ assertThat(ruleUpdated.getId()).isGreaterThan(0);
+ assertThat(ruleUpdated.getKey()).isEqualTo(RuleKey.of("external_eslint", "no-cond-assign"));
+ assertThat(ruleUpdated.getName()).isEqualTo("eslint: no-cond-assign");
+ assertThat(ruleUpdated.getDescription()).isNull();
+ assertThat(ruleUpdated.getSeverity()).isNull();
+ assertThat(ruleUpdated.getType()).isEqualTo(0);
+ assertThat(ruleUpdated.getMetadata().getAdHocName()).isEqualTo("No condition assigned updated");
+ assertThat(ruleUpdated.getMetadata().getAdHocDescription()).isEqualTo("A description updated");
+ assertThat(ruleUpdated.getMetadata().getAdHocSeverity()).isEqualTo(Severity.CRITICAL);
+ assertThat(ruleUpdated.getMetadata().getAdHocType()).isEqualTo(RuleType.CODE_SMELL.getDbConstant());
+
+ assertThat(ruleUpdated.getDefinition().getCreatedAt()).isEqualTo(creationDate);
+ assertThat(ruleUpdated.getMetadata().getCreatedAt()).isEqualTo(creationDate);
+ assertThat(ruleUpdated.getMetadata().getUpdatedAt()).isGreaterThan(creationDate);
+ }
+
+ @Test
+ public void does_not_update_rule_when_no_change() {
+ OrganizationDto organization = db.organizations().insert();
+ RuleDefinitionDto rule = db.rules().insert(r -> r.setRepositoryKey("external_eslint").setIsExternal(true).setIsAdHoc(true));
+ RuleMetadataDto ruleMetadata = db.rules().insertOrUpdateMetadata(rule, organization);
+
+ RuleDto ruleUpdated = underTest.persistAndIndex(dbSession, new NewAdHocRule(ScannerReport.AdHocRule.newBuilder()
+ .setEngineId("eslint")
+ .setRuleId(rule.getKey().rule())
+ .setName(ruleMetadata.getAdHocName())
+ .setDescription(ruleMetadata.getAdHocDescription())
+ .setSeverity(Constants.Severity.valueOf(ruleMetadata.getAdHocSeverity()))
+ .setType(ScannerReport.IssueType.forNumber(ruleMetadata.getAdHocType()))
+ .build()),
+ organization);
+
+ assertThat(ruleUpdated).isNotNull();
+ assertThat(ruleUpdated.isExternal()).isTrue();
+ assertThat(ruleUpdated.isAdHoc()).isTrue();
+ assertThat(ruleUpdated.getKey()).isEqualTo(rule.getKey());
+ assertThat(ruleUpdated.getName()).isEqualTo(rule.getName());
+ assertThat(ruleUpdated.getDescription()).isEqualTo(rule.getDescription());
+ assertThat(ruleUpdated.getSeverity()).isEqualTo(rule.getSeverity());
+ assertThat(ruleUpdated.getType()).isEqualTo(rule.getType());
+ assertThat(ruleUpdated.getDefinition().getCreatedAt()).isEqualTo(rule.getCreatedAt());
+ assertThat(ruleUpdated.getDefinition().getUpdatedAt()).isEqualTo(rule.getUpdatedAt());
+
+ assertThat(ruleUpdated.getMetadata().getAdHocName()).isEqualTo(ruleMetadata.getAdHocName());
+ assertThat(ruleUpdated.getMetadata().getAdHocDescription()).isEqualTo(ruleMetadata.getAdHocDescription());
+ assertThat(ruleUpdated.getMetadata().getAdHocSeverity()).isEqualTo(ruleMetadata.getAdHocSeverity());
+ assertThat(ruleUpdated.getMetadata().getAdHocType()).isEqualTo(ruleMetadata.getAdHocType());
+ assertThat(ruleUpdated.getMetadata().getCreatedAt()).isEqualTo(rule.getCreatedAt());
+ assertThat(ruleUpdated.getMetadata().getUpdatedAt()).isEqualTo(rule.getUpdatedAt());
+ }
+
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/DumbRule.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/DumbRule.java
index 058cbf3ae66..9acde59120f 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/DumbRule.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/DumbRule.java
@@ -39,6 +39,7 @@ public class DumbRule implements Rule {
private DebtRemediationFunction function;
private String pluginKey;
private boolean isExternal;
+ private boolean isAdHoc;
public DumbRule(RuleKey key) {
this.key = key;
@@ -90,6 +91,11 @@ public class DumbRule implements Rule {
return isExternal;
}
+ @Override
+ public boolean isAdHoc() {
+ return isAdHoc;
+ }
+
public DumbRule setId(Integer id) {
this.id = id;
return this;
@@ -130,4 +136,9 @@ public class DumbRule implements Rule {
return this;
}
+ public DumbRule setIsAdHoc(boolean isAdHoc) {
+ this.isAdHoc = isAdHoc;
+ return this;
+ }
+
}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRuleTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRuleTest.java
new file mode 100644
index 00000000000..982d4d2a894
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewAdHocRuleTest.java
@@ -0,0 +1,55 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.issue;
+
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class NewAdHocRuleTest {
+ @org.junit.Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ @Test
+ public void fail_if_engine_id_is_not_set() {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("'engine id' not expected to be null for an ad hoc rule");
+
+ new NewAdHocRule(ScannerReport.ExternalIssue.newBuilder().build());
+ }
+
+ @Test
+ public void test_equals_and_hashcode() {
+ NewAdHocRule adHocRule1 = new NewAdHocRule(ScannerReport.ExternalIssue.newBuilder().setEngineId("eslint").setRuleId("no-cond-assign").build());
+ NewAdHocRule adHocRule2 = new NewAdHocRule(ScannerReport.ExternalIssue.newBuilder().setEngineId("eslint").setRuleId("no-cond-assign").build());
+ NewAdHocRule anotherAdHocRule = new NewAdHocRule(ScannerReport.ExternalIssue.newBuilder().setEngineId("eslint").setRuleId("another").build());
+
+ assertThat(adHocRule1).isEqualTo(adHocRule1);
+ assertThat(adHocRule1).isEqualTo(adHocRule2);
+ assertThat(adHocRule1).isNotEqualTo(null);
+ assertThat(adHocRule1).isNotEqualTo(anotherAdHocRule);
+
+ assertThat(adHocRule1.hashCode()).isEqualTo(adHocRule1.hashCode());
+ assertThat(adHocRule1.hashCode()).isEqualTo(adHocRule2.hashCode());
+ assertThat(adHocRule1.hashCode()).isNotEqualTo(anotherAdHocRule.hashCode());
+ }
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryImplTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryImplTest.java
index c359b6919d1..44111ca1b0f 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryImplTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryImplTest.java
@@ -37,8 +37,7 @@ import org.sonar.db.rule.DeprecatedRuleKeyDto;
import org.sonar.db.rule.RuleDao;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.rule.RuleDto;
-import org.sonar.server.rule.AddHocRuleCreator;
-import org.sonar.server.rule.NewAddHocRule;
+import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.server.rule.index.RuleIndexer;
import static org.assertj.core.api.Assertions.assertThat;
@@ -78,8 +77,8 @@ public class RuleRepositoryImplTest {
private RuleDao ruleDao = mock(RuleDao.class);
private RuleIndexer ruleIndexer = mock(RuleIndexer.class);
- private AddHocRuleCreator addHocRuleCreator = new AddHocRuleCreator(db.getDbClient(), System2.INSTANCE, ruleIndexer);
- private RuleRepositoryImpl underTest = new RuleRepositoryImpl(addHocRuleCreator, dbClient, analysisMetadataHolder);
+ private AdHocRuleCreator adHocRuleCreator = new AdHocRuleCreator(db.getDbClient(), System2.INSTANCE, ruleIndexer);
+ private RuleRepositoryImpl underTest = new RuleRepositoryImpl(adHocRuleCreator, dbClient, analysisMetadataHolder);
@Before
public void setUp() throws Exception {
@@ -268,15 +267,11 @@ public class RuleRepositoryImplTest {
@Test
public void accept_new_externally_defined_Rules() {
- RuleKey ruleKey = RuleKey.of("eslint", "no-cond-assign");
+ RuleKey ruleKey = RuleKey.of("external_eslint", "no-cond-assign");
- underTest.addNewAddHocRuleIfAbsent(ruleKey, () -> new NewAddHocRule.Builder()
- .setKey(ruleKey)
- .setPluginKey("eslint")
- .build());
+ underTest.addOrUpdateAddHocRuleIfNeeded(ruleKey, () -> new NewAdHocRule(ScannerReport.ExternalIssue.newBuilder().setEngineId("eslint").setRuleId("no-cond-assign").build()));
assertThat(underTest.getByKey(ruleKey)).isNotNull();
- assertThat(underTest.getByKey(ruleKey).getPluginKey()).isEqualTo("eslint");
assertThat(underTest.getByKey(ruleKey).getType()).isNull();
RuleDao ruleDao = dbClient.ruleDao();
@@ -286,15 +281,12 @@ public class RuleRepositoryImplTest {
@Test
public void persist_new_externally_defined_Rules() {
- underTest = new RuleRepositoryImpl(addHocRuleCreator, db.getDbClient(), analysisMetadataHolder);
+ underTest = new RuleRepositoryImpl(adHocRuleCreator, db.getDbClient(), analysisMetadataHolder);
- RuleKey ruleKey = RuleKey.of("eslint", "no-cond-assign");
- underTest.addNewAddHocRuleIfAbsent(ruleKey, () -> new NewAddHocRule.Builder()
- .setKey(ruleKey)
- .setPluginKey("eslint")
- .build());
+ RuleKey ruleKey = RuleKey.of("external_eslint", "no-cond-assign");
+ underTest.addOrUpdateAddHocRuleIfNeeded(ruleKey, () -> new NewAdHocRule(ScannerReport.ExternalIssue.newBuilder().setEngineId("eslint").setRuleId("no-cond-assign").build()));
- underTest.persistNewAddHocRules(db.getSession());
+ underTest.saveOrUpdateAddHocRules(db.getSession());
db.commit();
Optional<RuleDefinitionDto> ruleDefinitionDto = db.getDbClient().ruleDao().selectDefinitionByKey(db.getSession(), ruleKey);
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryRule.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryRule.java
index ee875f21287..a676e006511 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryRule.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryRule.java
@@ -26,7 +26,6 @@ import java.util.function.Supplier;
import org.junit.rules.ExternalResource;
import org.sonar.api.rule.RuleKey;
import org.sonar.db.DbSession;
-import org.sonar.server.rule.NewAddHocRule;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
@@ -35,7 +34,7 @@ public class RuleRepositoryRule extends ExternalResource implements RuleReposito
private final Map<RuleKey, Rule> rulesByKey = new HashMap<>();
private final Map<Integer, Rule> rulesById = new HashMap<>();
- private final Map<RuleKey, NewAddHocRule> newExternalRulesById = new HashMap<>();
+ private final Map<RuleKey, NewAdHocRule> newExternalRulesById = new HashMap<>();
@Override
protected void after() {
@@ -68,7 +67,7 @@ public class RuleRepositoryRule extends ExternalResource implements RuleReposito
}
@Override
- public void persistNewAddHocRules(DbSession dbSession) {
+ public void saveOrUpdateAddHocRules(DbSession dbSession) {
throw new UnsupportedOperationException();
}
@@ -87,7 +86,7 @@ public class RuleRepositoryRule extends ExternalResource implements RuleReposito
}
@Override
- public void addNewAddHocRuleIfAbsent(RuleKey ruleKey, Supplier<NewAddHocRule> ruleSupplier) {
+ public void addOrUpdateAddHocRuleIfNeeded(RuleKey ruleKey, Supplier<NewAdHocRule> ruleSupplier) {
newExternalRulesById.computeIfAbsent(ruleKey, k -> ruleSupplier.get());
}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistExternalRulesStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistAdHocRulesStepTest.java
index 2832628b193..98ccf035b7a 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistExternalRulesStepTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistAdHocRulesStepTest.java
@@ -26,6 +26,8 @@ import org.junit.Test;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.System2;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
+import org.sonar.ce.task.projectanalysis.issue.AdHocRuleCreator;
+import org.sonar.ce.task.projectanalysis.issue.NewAdHocRule;
import org.sonar.ce.task.projectanalysis.issue.RuleRepositoryImpl;
import org.sonar.ce.task.step.ComputationStep;
import org.sonar.ce.task.step.TestComputationStepContext;
@@ -33,15 +35,14 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.rule.RuleDao;
import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.server.es.EsTester;
-import org.sonar.server.rule.AddHocRuleCreator;
-import org.sonar.server.rule.NewAddHocRule;
import org.sonar.server.rule.index.RuleIndexDefinition;
import org.sonar.server.rule.index.RuleIndexer;
import static org.assertj.core.api.Assertions.assertThat;
-public class PersistExternalRulesStepTest extends BaseStepTest {
+public class PersistAdHocRulesStepTest extends BaseStepTest {
@Rule
public DbTester db = DbTester.create(System2.INSTANCE);
@@ -59,7 +60,7 @@ public class PersistExternalRulesStepTest extends BaseStepTest {
public EsTester es = EsTester.create();
private RuleIndexer indexer = new RuleIndexer(es.client(), dbClient);
- private AddHocRuleCreator addHocRuleCreator = new AddHocRuleCreator(dbClient, System2.INSTANCE, indexer);
+ private AdHocRuleCreator adHocRuleCreator = new AdHocRuleCreator(dbClient, System2.INSTANCE, indexer);
@Override
protected ComputationStep step() {
@@ -68,19 +69,15 @@ public class PersistExternalRulesStepTest extends BaseStepTest {
@Before
public void setup() {
- ruleRepository = new RuleRepositoryImpl(addHocRuleCreator, dbClient, analysisMetadataHolder);
- underTest = new PersistExternalRulesStep(dbClient, ruleRepository);
+ ruleRepository = new RuleRepositoryImpl(adHocRuleCreator, dbClient, analysisMetadataHolder);
+ underTest = new PersistAdHocRulesStep(dbClient, ruleRepository);
}
@Test
- public void persist_and_index_new_external_rules() {
+ public void persist_and_index_new_ad_hoc_rules() {
- RuleKey ruleKey = RuleKey.of("eslint", "no-cond-assign");
- ruleRepository.addNewAddHocRuleIfAbsent(ruleKey, () -> new NewAddHocRule.Builder()
- .setKey(ruleKey)
- .setPluginKey("eslint")
- .setName("eslint:no-cond-assign")
- .build());
+ RuleKey ruleKey = RuleKey.of("external_eslint", "no-cond-assign");
+ ruleRepository.addOrUpdateAddHocRuleIfNeeded(ruleKey, () -> new NewAdHocRule(ScannerReport.ExternalIssue.newBuilder().setEngineId("eslint").setRuleId("no-cond-assign").build()));
underTest.execute(new TestComputationStepContext());
@@ -90,12 +87,12 @@ public class PersistExternalRulesStepTest extends BaseStepTest {
RuleDefinitionDto reloaded = ruleDefinitionDtoOptional.get();
assertThat(reloaded.getRuleKey()).isEqualTo("no-cond-assign");
- assertThat(reloaded.getRepositoryKey()).isEqualTo("eslint");
+ assertThat(reloaded.getRepositoryKey()).isEqualTo("external_eslint");
assertThat(reloaded.isExternal()).isTrue();
+ assertThat(reloaded.isAdHoc()).isTrue();
assertThat(reloaded.getType()).isEqualTo(0);
assertThat(reloaded.getSeverity()).isNull();
- assertThat(reloaded.getName()).isEqualTo("eslint:no-cond-assign");
- assertThat(reloaded.getPluginKey()).isEqualTo("eslint");
+ assertThat(reloaded.getName()).isEqualTo("eslint: no-cond-assign");
assertThat(es.countDocuments(RuleIndexDefinition.INDEX_TYPE_RULE)).isEqualTo(1l);
assertThat(es.getDocuments(RuleIndexDefinition.INDEX_TYPE_RULE).iterator().next().getId()).isEqualTo(Integer.toString(reloaded.getId()));
@@ -105,10 +102,7 @@ public class PersistExternalRulesStepTest extends BaseStepTest {
public void do_not_persist_existing_external_rules() {
RuleKey ruleKey = RuleKey.of("eslint", "no-cond-assign");
db.rules().insert(ruleKey, r -> r.setIsExternal(true));
- ruleRepository.addNewAddHocRuleIfAbsent(ruleKey, () -> new NewAddHocRule.Builder()
- .setKey(ruleKey)
- .setPluginKey("eslint")
- .build());
+ ruleRepository.addOrUpdateAddHocRuleIfNeeded(ruleKey, () -> new NewAdHocRule(ScannerReport.ExternalIssue.newBuilder().setEngineId("eslint").setRuleId("no-cond-assign").build()));
underTest.execute(new TestComputationStepContext());
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistIssuesStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistIssuesStepTest.java
index e5cdb6f6a12..b4f9e66e558 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistIssuesStepTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistIssuesStepTest.java
@@ -32,6 +32,7 @@ import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.System2;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
+import org.sonar.ce.task.projectanalysis.issue.AdHocRuleCreator;
import org.sonar.ce.task.projectanalysis.issue.IssueCache;
import org.sonar.ce.task.projectanalysis.issue.RuleRepositoryImpl;
import org.sonar.ce.task.projectanalysis.issue.UpdateConflictResolver;
@@ -53,7 +54,6 @@ import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.rule.RuleTesting;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.server.issue.IssueStorage;
-import org.sonar.server.rule.AddHocRuleCreator;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
@@ -85,7 +85,7 @@ public class PersistIssuesStepTest extends BaseStepTest {
private IssueCache issueCache;
private ComputationStep underTest;
- private AddHocRuleCreator addHocRuleCreator = mock(AddHocRuleCreator.class);
+ private AdHocRuleCreator adHocRuleCreator = mock(AdHocRuleCreator.class);
@Override
protected ComputationStep step() {
@@ -99,7 +99,7 @@ public class PersistIssuesStepTest extends BaseStepTest {
when(system2.now()).thenReturn(NOW);
reportReader.setMetadata(ScannerReport.Metadata.getDefaultInstance());
- underTest = new PersistIssuesStep(dbClient, system2, new UpdateConflictResolver(), new RuleRepositoryImpl(addHocRuleCreator, dbClient, analysisMetadataHolder), issueCache,
+ underTest = new PersistIssuesStep(dbClient, system2, new UpdateConflictResolver(), new RuleRepositoryImpl(adHocRuleCreator, dbClient, analysisMetadataHolder), issueCache,
new IssueStorage());
}