}
public void scrollNonClosedByComponentUuidExcludingExternals(DbSession dbSession, String componentUuid, ResultHandler<IssueDto> handler) {
- mapper(dbSession).scrollNonClosedByComponentUuid(componentUuid, handler);
+ mapper(dbSession).scrollNonClosedByComponentUuidExcludingExternals(componentUuid, handler);
}
public void scrollNonClosedByModuleOrProjectExcludingExternals(DbSession dbSession, ComponentDto module, ResultHandler<IssueDto> handler) {
int updateIfBeforeSelectedDate(IssueDto issue);
void scrollNonClosedByComponentUuid(@Param("componentUuid") String componentUuid, ResultHandler<IssueDto> handler);
+
+ void scrollNonClosedByComponentUuidExcludingExternals(@Param("componentUuid") String componentUuid, ResultHandler<IssueDto> handler);
void scrollNonClosedByModuleOrProject(
@Param("projectUuid") String projectUuid,
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import java.util.Set;
+import javax.annotation.CheckForNull;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rules.RuleType;
return updatedAt;
}
+ @CheckForNull
public RuleType getTypeAsRuleType() {
- return RuleType.valueOf(type);
+ return RuleType.valueOfNullable(type);
}
public String getSeverityAsString() {
</select>
<select id="scrollNonClosedByComponentUuid" parameterType="String" resultType="Issue" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
+ select
+ <include refid="issueColumns"/>
+ from issues i
+ inner join rules r on r.id=i.rule_id
+ inner join projects p on p.uuid=i.component_uuid
+ inner join projects root on root.uuid=i.project_uuid
+ where
+ i.component_uuid = #{componentUuid,jdbcType=VARCHAR} and
+ i.status <> 'CLOSED'
+ </select>
+
+ <select id="scrollNonClosedByComponentUuidExcludingExternals" parameterType="String" resultType="Issue" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
select
<include refid="issueColumns"/>
from issues i
*/
package org.sonar.server.platform.db.migration.version.v72;
+import java.sql.SQLException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.db.CoreDbTester;
-import java.sql.SQLException;
-
import static java.sql.Types.BOOLEAN;
-import static java.sql.Types.VARCHAR;
import static org.junit.rules.ExpectedException.none;
import static org.sonar.db.CoreDbTester.createForSchema;
import java.util.Collections;
import java.util.Set;
import javax.annotation.concurrent.Immutable;
-import org.apache.commons.lang.StringUtils;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rules.RuleType;
@Immutable
public class NewExternalRule implements Rule {
private final RuleKey key;
- private final String severity;
+ private final String name;
private final RuleType type;
private final String pluginKey;
private NewExternalRule(Builder builder) {
this.key = checkNotNull(builder.key, "key");
- this.severity = checkNotEmpty(builder.severity, "severity");
this.type = checkNotNull(builder.type, "type");
this.pluginKey = builder.pluginKey;
- }
-
- private static String checkNotEmpty(String str, String name) {
- if (StringUtils.isEmpty(str)) {
- throw new IllegalStateException("'" + name + "' not expected to be empty for an external rule");
- }
- return str;
+ this.name = builder.name;
}
private static <T> T checkNotNull(T obj, String name) {
return obj;
}
- public String getSeverity() {
- return severity;
- }
-
@Override
public int getId() {
return 0;
@Override
public String getName() {
- return key.toString();
+ return name;
}
@Override
private String severity;
private RuleType type;
private String pluginKey;
+ private String name;
public Builder setKey(RuleKey key) {
this.key = key;
return this;
}
- public Builder setSeverity(String severity) {
- this.severity = StringUtils.trimToNull(severity);
+ public Builder setName(String name) {
+ this.name = name;
return this;
}
return type;
}
+ public String name() {
+ return name;
+ }
+
public NewExternalRule build() {
return new NewExternalRule(this);
}
RuleStatus getStatus();
+ @CheckForNull
RuleType getType();
-
+
boolean isExternal();
/**
this.status = dto.getStatus();
this.tags = union(dto.getSystemTags(), dto.getTags());
this.remediationFunction = effectiveRemediationFunction(dto);
- this.type = RuleType.valueOf(dto.getType());
+ this.type = RuleType.valueOfNullable(dto.getType());
this.pluginKey = dto.getPluginKey();
this.external = dto.isExternal();
}
import static org.apache.commons.lang.StringUtils.isNotEmpty;
public class TrackerRawInputFactory {
-
+ private static final long DEFAULT_EXTERNAL_ISSUE_EFFORT = 0l;
private final TreeRootHolder treeRootHolder;
private final BatchReportReader reportReader;
private final SourceLinesRepository sourceLinesRepository;
dbLocationsBuilder.addFlow(dbFlowBuilder);
}
}
+ issue.setFromExternalRuleEngine(false);
issue.setLocations(dbLocationsBuilder.build());
return issue;
}
if (reportIssue.getSeverity() != Severity.UNSET_SEVERITY) {
issue.setSeverity(reportIssue.getSeverity().name());
}
- if (reportIssue.getEffort() != 0) {
- issue.setEffort(Duration.create(reportIssue.getEffort()));
- }
+ issue.setEffort(Duration.create(reportIssue.getEffort() != 0 ? reportIssue.getEffort() : DEFAULT_EXTERNAL_ISSUE_EFFORT));
DbIssues.Locations.Builder dbLocationsBuilder = DbIssues.Locations.newBuilder();
if (reportIssue.hasTextRange()) {
dbLocationsBuilder.setTextRange(convertTextRange(reportIssue.getTextRange()));
dbLocationsBuilder.addFlow(dbFlowBuilder);
}
}
+ issue.setFromExternalRuleEngine(true);
issue.setLocations(dbLocationsBuilder.build());
issue.setType(toRuleType(reportIssue.getType()));
private NewExternalRule toExternalRule(ScannerReport.ExternalIssue reportIssue) {
NewExternalRule.Builder builder = new NewExternalRule.Builder()
.setType(toRuleType(reportIssue.getType()))
- .setKey(RuleKey.of(RuleKey.EXTERNAL_RULE_REPO_PREFIX + reportIssue.getRuleRepository(), reportIssue.getRuleKey()))
- .setPluginKey(reportIssue.getRuleRepository());
+ .setName(RuleKey.of(reportIssue.getRuleRepository(), reportIssue.getRuleKey()).toString())
+ .setKey(RuleKey.of(RuleKey.EXTERNAL_RULE_REPO_PREFIX + reportIssue.getRuleRepository(), reportIssue.getRuleKey()));
if (reportIssue.getSeverity() != Severity.UNSET_SEVERITY) {
builder.setSeverity(reportIssue.getSeverity().name());
*/
package org.sonar.server.rule;
-import org.sonar.api.rule.RuleStatus;
import org.sonar.api.utils.System2;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
.setPluginKey(external.getPluginKey())
.setIsExternal(external.isExternal())
.setName(external.getName())
- .setType(external.getType())
.setScope(ALL)
- .setStatus(RuleStatus.READY)
- .setSeverity(external.getSeverity())
.setStatus(READY)
.setCreatedAt(system2.now())
.setUpdatedAt(system2.now()));
return this;
}
+ @CheckForNull
public RuleType type() {
- return RuleType.valueOf(getField(RuleIndexDefinition.FIELD_RULE_TYPE));
+ return RuleType.valueOfNullable(getNullableField(RuleIndexDefinition.FIELD_RULE_TYPE));
}
- public RuleDoc setType(RuleType ruleType) {
- setField(RuleIndexDefinition.FIELD_RULE_TYPE, ruleType.name());
+ public RuleDoc setType(@Nullable RuleType ruleType) {
+ setField(RuleIndexDefinition.FIELD_RULE_TYPE, ruleType == null ? null : ruleType.name());
return this;
}
*/
public RuleQuery createRuleSearchQuery(DbSession dbSession, Request request) {
RuleQuery query = createRuleQuery(dbSession, request);
- query.setIsExternal(request.paramAsBoolean(PARAM_IS_EXTERNAL));
+ query.setIsExternal(request.mandatoryParamAsBoolean(PARAM_IS_EXTERNAL));
return query;
}
// Rule-specific search parameters
defineGenericRuleSearchParameters(action);
-
+ defineIsExternalParam(action);
+ }
+
+ static void defineIsExternalParam(WebService.NewAction action) {
action
- .createParam(PARAM_IS_EXTERNAL)
- .setDescription("Filter external engine rules")
- .setBooleanPossibleValues()
- .setSince("7.2");
+ .createParam(PARAM_IS_EXTERNAL)
+ .setDescription("Filter external engine rules")
+ .setDefaultValue(false)
+ .setBooleanPossibleValues()
+ .setSince("7.2");
}
public static void defineGenericRuleSearchParameters(WebService.NewAction action) {
return closeableIterator(externalIssues.get(componentRef));
}
- public BatchReportReaderRule putIssues(int componentRef, List<ScannerReport.Issue> issue) {
- this.issues.put(componentRef, issue);
+ public BatchReportReaderRule putIssues(int componentRef, List<ScannerReport.Issue> issues) {
+ this.issues.put(componentRef, issues);
+ return this;
+ }
+
+ public BatchReportReaderRule putExternalIssues(int componentRef, List<ScannerReport.ExternalIssue> externalIssues) {
+ this.externalIssues.put(componentRef, externalIssues);
return this;
}
NewExternalRule.Builder builder = new NewExternalRule.Builder()
.setKey(RuleKey.of("repo", "rule"))
.setPluginKey("repo")
+ .setName("name")
.setSeverity("MAJOR")
.setType(RuleType.BUG);
assertThat(builder.severity()).isEqualTo("MAJOR");
assertThat(builder.type()).isEqualTo(RuleType.BUG);
+ assertThat(builder.name()).isEqualTo("name");
NewExternalRule rule = builder.build();
- assertThat(rule.getName()).isEqualTo("repo:rule");
+ assertThat(rule.getName()).isEqualTo("name");
assertThat(rule.getPluginKey()).isEqualTo("repo");
assertThat(rule.getSeverity()).isEqualTo("MAJOR");
assertThat(rule.getType()).isEqualTo(RuleType.BUG);
import org.sonar.api.issue.Issue;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
+import org.sonar.api.utils.Duration;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.tracking.Input;
import org.sonar.scanner.protocol.Constants;
@Rule
public SourceLinesRepositoryRule fileSourceRepository = new SourceLinesRepositoryRule();
-
+
@Rule
public RuleRepositoryRule ruleRepository = new RuleRepositoryRule();
assertThat(issue.gap()).isEqualTo(3.14);
assertThat(issue.message()).isEqualTo("the message");
+ // fields set by compute engine
+ assertThat(issue.checksum()).isEqualTo(input.getLineHashSequence().getHashForLine(2));
+ assertThat(issue.tags()).isEmpty();
+ assertInitializedIssue(issue);
+ assertThat(issue.debt()).isNull();
+ }
+
+ @Test
+ public void load_external_issues_from_report() {
+ fileSourceRepository.addLines(FILE_REF, "line 1;", "line 2;");
+ ScannerReport.ExternalIssue reportIssue = ScannerReport.ExternalIssue.newBuilder()
+ .setTextRange(TextRange.newBuilder().setStartLine(2).build())
+ .setMsg("the message")
+ .setRuleRepository("eslint")
+ .setRuleKey("S001")
+ .setSeverity(Constants.Severity.BLOCKER)
+ .setEffort(20l)
+ .build();
+ reportReader.putExternalIssues(FILE.getReportAttributes().getRef(), asList(reportIssue));
+ Input<DefaultIssue> input = underTest.create(FILE);
+
+ Collection<DefaultIssue> issues = input.getIssues();
+ assertThat(issues).hasSize(1);
+ DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
+
+ // fields set by analysis report
+ assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("external_eslint", "S001"));
+ assertThat(issue.severity()).isEqualTo(Severity.BLOCKER);
+ assertThat(issue.line()).isEqualTo(2);
+ assertThat(issue.effort()).isEqualTo(Duration.create(20l));
+ assertThat(issue.message()).isEqualTo("the message");
+
+ // fields set by compute engine
+ assertThat(issue.checksum()).isEqualTo(input.getLineHashSequence().getHashForLine(2));
+ assertThat(issue.tags()).isEmpty();
+ assertInitializedIssue(issue);
+ }
+
+ @Test
+ public void load_external_issues_from_report_with_default_effort() {
+ fileSourceRepository.addLines(FILE_REF, "line 1;", "line 2;");
+ ScannerReport.ExternalIssue reportIssue = ScannerReport.ExternalIssue.newBuilder()
+ .setTextRange(TextRange.newBuilder().setStartLine(2).build())
+ .setMsg("the message")
+ .setRuleRepository("eslint")
+ .setRuleKey("S001")
+ .setSeverity(Constants.Severity.BLOCKER)
+ .build();
+ reportReader.putExternalIssues(FILE.getReportAttributes().getRef(), asList(reportIssue));
+ Input<DefaultIssue> input = underTest.create(FILE);
+
+ Collection<DefaultIssue> issues = input.getIssues();
+ assertThat(issues).hasSize(1);
+ DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
+
+ // fields set by analysis report
+ assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("external_eslint", "S001"));
+ assertThat(issue.severity()).isEqualTo(Severity.BLOCKER);
+ assertThat(issue.line()).isEqualTo(2);
+ assertThat(issue.effort()).isEqualTo(Duration.create(0l));
+ assertThat(issue.message()).isEqualTo("the message");
+
// fields set by compute engine
assertThat(issue.checksum()).isEqualTo(input.getLineHashSequence().getHashForLine(2));
assertThat(issue.tags()).isEmpty();
assertThat(issue.status()).isEqualTo(Issue.STATUS_OPEN);
assertThat(issue.key()).isNull();
assertThat(issue.authorLogin()).isNull();
- assertThat(issue.debt()).isNull();
}
}
.setKey(ruleKey)
.setPluginKey("eslint")
.setSeverity(BLOCKER)
+ .setName("eslint:no-cond-assign")
.setType(BUG)
.build());
import org.sonar.server.rule.index.RuleIndexer;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.api.rule.Severity.BLOCKER;
import static org.sonar.api.rules.RuleType.BUG;
public class ExternalRuleCreatorTest {
NewExternalRule externalRule = new NewExternalRule.Builder()
.setKey(ruleKey)
.setPluginKey("eslint")
- .setSeverity(BLOCKER)
.setType(BUG)
+ .setName("name")
.build();
Rule rule1 = underTest.persistAndIndex(dbSession, externalRule);
assertThat(rule1.getId()).isGreaterThan(0);
assertThat(rule1.getKey()).isEqualTo(ruleKey);
assertThat(rule1.getPluginKey()).isEqualTo("eslint");
- assertThat(rule1.getName()).isEqualTo(ruleKey.toString());
- assertThat(rule1.getType()).isEqualTo(BUG);
-
+ assertThat(rule1.getName()).isEqualTo("name");
+ assertThat(rule1.getType()).isNull();
}
}
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
+import org.sonar.api.server.ws.internal.SimpleGetRequest;
import org.sonar.api.utils.System2;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY;
import static org.sonar.db.qualityprofile.ActiveRuleDto.INHERITED;
import static org.sonar.db.qualityprofile.ActiveRuleDto.OVERRIDES;
-import static org.sonar.server.rule.ws.SearchAction.defineGenericRuleSearchParameters;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVATION;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVE_SEVERITIES;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_AVAILABLE_SINCE;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_COMPARE_TO_PROFILE;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_INHERITANCE;
-import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_IS_TEMPLATE;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_IS_EXTERNAL;
+import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_IS_TEMPLATE;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_LANGUAGES;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ORGANIZATION;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_QPROFILE;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_TAGS;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_TEMPLATE_KEY;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_TYPES;
+import static org.sonar.server.rule.ws.SearchAction.defineGenericRuleSearchParameters;
public class RuleQueryFactoryTest {
assertThat(result.getCompareToQProfile()).isNull();
}
+ @Test
+ public void create_rule_search_query() {
+ QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization);
+ QProfileDto compareToQualityProfile = dbTester.qualityProfiles().insert(organization);
+
+ RuleQuery result = executeRuleSearchQuery(
+ PARAM_RULE_KEY, "ruleKey",
+
+ PARAM_ACTIVATION, "true",
+ PARAM_ACTIVE_SEVERITIES, "MINOR,MAJOR",
+ PARAM_AVAILABLE_SINCE, "2016-01-01",
+ PARAM_INHERITANCE, "INHERITED,OVERRIDES",
+ PARAM_IS_TEMPLATE, "true",
+ PARAM_IS_EXTERNAL, "false",
+ PARAM_LANGUAGES, "java,js",
+ TEXT_QUERY, "S001",
+ PARAM_ORGANIZATION, organization.getKey(),
+ PARAM_QPROFILE, qualityProfile.getKee(),
+ PARAM_COMPARE_TO_PROFILE, compareToQualityProfile.getKee(),
+ PARAM_REPOSITORIES, "pmd,checkstyle",
+ PARAM_SEVERITIES, "MINOR,CRITICAL",
+ PARAM_STATUSES, "DEPRECATED,READY",
+ PARAM_TAGS, "tag1,tag2",
+ PARAM_TEMPLATE_KEY, "architectural",
+ PARAM_TYPES, "CODE_SMELL,BUG",
+
+ SORT, "updatedAt",
+ ASCENDING, "false");
+
+ assertResult(result, qualityProfile, compareToQualityProfile);
+ assertThat(result.isExternal()).isFalse();
+ }
+
+ @Test
+ public void is_external_is_mandatory_for_rule_search_query() {
+ dbTester.qualityProfiles().insert(organization);
+ dbTester.qualityProfiles().insert(organization);
+ Request request = new SimpleGetRequest();
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'is_external' parameter is missing");
+ underTest.createRuleSearchQuery(dbTester.getSession(), request);
+ }
+
@Test
public void create_query() {
QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization);
PARAM_ACTIVE_SEVERITIES, "MINOR,MAJOR",
PARAM_AVAILABLE_SINCE, "2016-01-01",
PARAM_INHERITANCE, "INHERITED,OVERRIDES",
- PARAM_IS_TEMPLATE, "true",
+ PARAM_IS_TEMPLATE, "true",
PARAM_IS_EXTERNAL, "true",
PARAM_LANGUAGES, "java,js",
TEXT_QUERY, "S001",
SORT, "updatedAt",
ASCENDING, "false");
+ assertResult(result, qualityProfile, compareToQualityProfile);
+ assertThat(result.isExternal()).isNull();
+ }
+
+ private void assertResult(RuleQuery result, QProfileDto qualityProfile, QProfileDto compareToQualityProfile) {
assertThat(result.getKey()).isEqualTo("ruleKey");
assertThat(result.getActivation()).isTrue();
assertThat(result.getTags()).containsOnly("tag1", "tag2");
assertThat(result.templateKey()).isEqualTo("architectural");
assertThat(result.getTypes()).containsOnly(BUG, CODE_SMELL);
-
assertThat(result.getSortField()).isEqualTo("updatedAt");
}
QProfileDto compareToQualityProfile = dbTester.qualityProfiles().insert(otherOrganization);
expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("The specified quality profile '" + compareToQualityProfile.getKee() + "' is not part of the specified organization '" + organization.getKey() + "'");
+ expectedException
+ .expectMessage("The specified quality profile '" + compareToQualityProfile.getKee() + "' is not part of the specified organization '" + organization.getKey() + "'");
execute(PARAM_QPROFILE, qualityProfile.getKee(),
PARAM_COMPARE_TO_PROFILE, compareToQualityProfile.getKee(),
return fakeAction.getRuleQuery();
}
+ private RuleQuery executeRuleSearchQuery(String... paramsKeyAndValue) {
+ SimpleGetRequest request = new SimpleGetRequest();
+ for (int i = 0; i < paramsKeyAndValue.length; i += 2) {
+ request.setParam(paramsKeyAndValue[i], paramsKeyAndValue[i + 1]);
+ }
+
+ return underTest.createRuleSearchQuery(dbTester.getSession(), request);
+ }
+
private class FakeAction implements WsAction {
private final RuleQueryFactory ruleQueryFactory;
import java.util.LinkedHashSet;
import java.util.Set;
+import javax.annotation.CheckForNull;
import static java.lang.String.format;
import static java.util.Arrays.stream;
}
throw new IllegalArgumentException(format("Unsupported type value : %d", dbConstant));
}
+
+ @CheckForNull
+ public static RuleType valueOfNullable(int dbConstant) {
+ // iterating the array is fast-enough as size is small. No need for a map.
+ for (RuleType type : values()) {
+ if (type.getDbConstant() == dbConstant) {
+ return type;
+ }
+ }
+ if (dbConstant == 0) {
+ return null;
+ }
+ throw new IllegalArgumentException(format("Unsupported type value : %d", dbConstant));
+ }
}