From: Guillaume Jambet Date: Fri, 13 Apr 2018 09:25:59 +0000 (+0200) Subject: SONAR-10577 return if an issue is from an external analyser in issue search X-Git-Tag: 7.5~1317 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=69cee931cd9c181019812107637d9686d8b13ef1;p=sonarqube.git SONAR-10577 return if an issue is from an external analyser in issue search --- diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java index a603618f441..0213e3106e5 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java @@ -91,6 +91,7 @@ public final class IssueDto implements Serializable { // joins private String ruleKey; private String ruleRepo; + private boolean isExternal; private String language; private String componentKey; private String moduleUuid; @@ -119,6 +120,7 @@ public final class IssueDto implements Serializable { .setAssignee(issue.assignee()) .setRuleId(ruleId) .setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule()) + .setExternal(issue.isFromExternalRuleEngine()) .setTags(issue.tags()) .setComponentUuid(issue.componentUuid()) .setComponentKey(issue.componentKey()) @@ -166,6 +168,7 @@ public final class IssueDto implements Serializable { .setIssueAttributes(KeyValueFormat.format(issue.attributes())) .setAuthorLogin(issue.authorLogin()) .setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule()) + .setExternal(issue.isFromExternalRuleEngine()) .setTags(issue.tags()) .setComponentUuid(issue.componentUuid()) .setComponentKey(issue.componentKey()) @@ -185,6 +188,7 @@ public final class IssueDto implements Serializable { public static IssueDto createFor(Project project, RuleDto rule) { return new IssueDto() .setProjectUuid(project.getUuid()) + .setExternal(rule.isExternal()) .setRuleId(rule.getId()) .setKee(Uuids.create()); } @@ -455,6 +459,7 @@ public final class IssueDto implements Serializable { this.ruleKey = rule.getRuleKey(); this.ruleRepo = rule.getRepositoryKey(); this.language = rule.getLanguage(); + this.isExternal = rule.isExternal(); return this; } @@ -480,6 +485,15 @@ public final class IssueDto implements Serializable { return this; } + public boolean isExternal() { + return isExternal; + } + + public IssueDto setExternal(boolean external) { + isExternal = external; + return this; + } + public String getComponentKey() { return componentKey; } @@ -719,6 +733,7 @@ public final class IssueDto implements Serializable { issue.setUpdateDate(longToDate(issueUpdateDate)); issue.setSelectedAt(selectedAt); issue.setLocations(parseLocations()); + issue.setFromExternalRuleEngine(isExternal); return issue; } } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml index a077a1e17ca..5f2e0f482cb 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml @@ -27,6 +27,7 @@ i.issue_close_date as issueCloseTime, i.created_at as createdAt, i.updated_at as updatedAt, + r.is_external as "isExternal", r.plugin_rule_key as ruleKey, r.plugin_name as ruleRepo, r.language as language, @@ -84,6 +85,7 @@ i.issue_close_date as "issueCloseDate", i.issue_creation_date as "issueCreationDate", i.issue_update_date as "issueUpdateDate", + r.is_external as "isExternal", r.plugin_name as "pluginName", r.plugin_rule_key as "pluginRuleKey", r.language, diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java index 9791628fc34..a96d88a9031 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java @@ -100,6 +100,7 @@ public class IssueDaoTest { assertThat(issue.getProjectKey()).isEqualTo(PROJECT_KEY); assertThat(issue.getLocations()).isNull(); assertThat(issue.parseLocations()).isNull(); + assertThat(issue.isExternal()).isTrue(); } @Test @@ -304,7 +305,7 @@ public class IssueDaoTest { } private void prepareTables() { - db.rules().insertRule(RULE); + db.rules().insertRule(RULE.setIsExternal(true)); OrganizationDto organizationDto = db.organizations().insert(); ComponentDto projectDto = db.components().insertPrivateProject(organizationDto, (t) -> t.setUuid(PROJECT_UUID).setDbKey(PROJECT_KEY)); db.components().insertComponent(newFileDto(projectDto).setUuid(FILE_UUID).setDbKey(FILE_KEY)); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDtoTest.java index c9ef120abde..cd323991d89 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDtoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDtoTest.java @@ -116,7 +116,7 @@ public class IssueDtoTest { public void set_rule() { IssueDto dto = new IssueDto() .setKee("100") - .setRule(new RuleDefinitionDto().setId(1).setRuleKey("AvoidCycle").setRepositoryKey("squid")) + .setRule(new RuleDefinitionDto().setId(1).setRuleKey("AvoidCycle").setRepositoryKey("squid").setIsExternal(true)) .setLanguage("xoo"); assertThat(dto.getRuleId()).isEqualTo(1); @@ -124,6 +124,7 @@ public class IssueDtoTest { assertThat(dto.getRule()).isEqualTo("AvoidCycle"); assertThat(dto.getRuleKey().toString()).isEqualTo("squid:AvoidCycle"); assertThat(dto.getLanguage()).isEqualTo("xoo"); + assertThat(dto.isExternal()).isEqualTo(true); } @Test diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleTesting.java b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleTesting.java index 77b70a3ecff..4a90d86466d 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleTesting.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleTesting.java @@ -41,12 +41,14 @@ import static java.util.Objects.requireNonNull; import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; import static org.apache.commons.lang.math.RandomUtils.nextInt; +import static org.sonar.api.rule.RuleKey.EXTERNAL_RULE_REPO_PREFIX; /** * Utility class for tests involving rules */ public class RuleTesting { + public static final RuleKey EXTERNAL_XOO = RuleKey.of(EXTERNAL_RULE_REPO_PREFIX + "xoo", "x1"); public static final RuleKey XOO_X1 = RuleKey.of("xoo", "x1"); public static final RuleKey XOO_X2 = RuleKey.of("xoo", "x2"); public static final RuleKey XOO_X3 = RuleKey.of("xoo", "x3"); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/commonrule/CommonRule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/commonrule/CommonRule.java index 0b56e4b21df..9f1a84bafaa 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/commonrule/CommonRule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/commonrule/CommonRule.java @@ -55,6 +55,7 @@ public abstract class CommonRule { issue.setSeverity(activeRule.get().getSeverity()); issue.setLine(null); issue.setChecksum(""); + issue.setFromExternalRuleEngine(false); } } return issue; diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java index 444b105017b..f61f8c12d1e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java @@ -21,9 +21,12 @@ package org.sonar.server.issue; import java.util.Collection; import java.util.Map; + +import com.google.common.base.Preconditions; import org.sonar.api.server.ServerSide; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.util.stream.MoreCollectors; +import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.issue.workflow.Transition; import org.sonar.server.user.UserSession; @@ -62,6 +65,9 @@ public class TransitionAction extends Action { } private boolean canExecuteTransition(DefaultIssue issue, String transitionKey) { + + checkArgument(!issue.isFromExternalRuleEngine(), "No transition allowed on issue from externally define rule"); + return transitionService.listTransitions(issue) .stream() .map(Transition::key) diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java index 3bd129ba174..893880956fb 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java @@ -162,8 +162,9 @@ public class SearchAction implements IssuesWsAction { new Change("6.3", "response field 'email' is renamed 'avatar'"), new Change("5.5", "response fields 'reporter' and 'actionPlan' are removed (drop of action plan and manual issue features)"), new Change("5.5", "parameters 'reporters', 'actionPlans' and 'planned' are dropped and therefore ignored (drop of action plan and manual issue features)"), - new Change("5.5", "response field 'debt' is renamed 'effort'")) - .setResponseExample(getClass().getResource("search-example.json")); + new Change("5.5", "response field 'debt' is renamed 'effort'"), + new Change("7.2", "response 'issue' now indicates if issue has been created by an externally defined rule engine and his name through the optional 'externalRuleEngine' field") + ).setResponseExample(getClass().getResource("search-example.json")); action.addPagingParams(100, MAX_LIMIT); action.createParam(Param.FACETS) diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java index ff6dea2e454..e55422e4b7d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java @@ -30,6 +30,7 @@ import java.util.Set; import javax.annotation.Nullable; import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; +import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.Duration; import org.sonar.api.utils.Durations; @@ -59,8 +60,10 @@ import org.sonarqube.ws.Issues.SearchWsResponse; import org.sonarqube.ws.Issues.Transitions; import org.sonarqube.ws.Issues.Users; +import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Strings.emptyToNull; import static com.google.common.base.Strings.nullToEmpty; +import static org.sonar.api.rule.RuleKey.EXTERNAL_RULE_REPO_PREFIX; import static org.sonar.core.util.Protobuf.setNullable; public class SearchResponseFormat { @@ -171,6 +174,9 @@ public class SearchResponseFormat { } } issueBuilder.setRule(dto.getRuleKey().toString()); + if (dto.isExternal()) { + issueBuilder.setExternalRuleEngine(engineNameFrom(dto.getRuleKey())); + } issueBuilder.setSeverity(Common.Severity.valueOf(dto.getSeverity())); setNullable(emptyToNull(dto.getAssignee()), issueBuilder::setAssignee); setNullable(emptyToNull(dto.getResolution()), issueBuilder::setResolution); @@ -192,6 +198,11 @@ public class SearchResponseFormat { setNullable(dto.getIssueCloseDate(), issueBuilder::setCloseDate, DateUtils::formatDateTime); } + private String engineNameFrom(RuleKey ruleKey) { + checkState(ruleKey.repository().startsWith(EXTERNAL_RULE_REPO_PREFIX)); + return ruleKey.repository().replace(EXTERNAL_RULE_REPO_PREFIX, ""); + } + private static void completeIssueLocations(IssueDto dto, Issue.Builder issueBuilder, SearchResponseData data) { DbIssues.Locations locations = dto.parseLocations(); if (locations == null) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ExternalRuleCreator.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ExternalRuleCreator.java index 6b4234af11e..4b8c27d0261 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ExternalRuleCreator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ExternalRuleCreator.java @@ -30,6 +30,7 @@ import org.sonar.server.computation.task.projectanalysis.issue.Rule; import org.sonar.server.computation.task.projectanalysis.issue.RuleImpl; import org.sonar.server.rule.index.RuleIndexer; +import static org.sonar.api.rule.RuleStatus.READY; import static org.sonar.db.rule.RuleDto.Scope.ALL; public class ExternalRuleCreator { @@ -59,6 +60,7 @@ public class ExternalRuleCreator { .setScope(ALL) .setStatus(RuleStatus.READY) .setSeverity(external.getSeverity()) + .setStatus(READY) .setCreatedAt(system2.now()) .setUpdatedAt(system2.now())); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueAssignerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueAssignerTest.java index 67a3613e930..5362e2e0fb1 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueAssignerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueAssignerTest.java @@ -213,7 +213,7 @@ public class IssueAssignerTest { "moduleUuid=,moduleUuidPath=,projectUuid=,projectKey=,ruleKey=,language=,severity=," + "manualSeverity=false,message=,line=2,gap=,effort=,status=,resolution=," + "assignee=,checksum=,attributes=,authorLogin=,comments=,tags=," + - "locations=,creationDate=,updateDate=,closeDate=,currentChange=,changes=,isNew=true,isCopied=false," + + "locations=,isFromExternalRuleEngine=false,creationDate=,updateDate=,closeDate=,currentChange=,changes=,isNew=true,isCopied=false," + "beingClosed=false,onDisabledRule=false,isChanged=false,sendNotifications=false,selectedAt=]"); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/TransitionActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/TransitionActionTest.java index 558684b3fd1..6ada52d8ace 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/TransitionActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/TransitionActionTest.java @@ -21,7 +21,9 @@ package org.sonar.server.issue; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; + import java.util.Date; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -42,6 +44,7 @@ import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.sonar.api.issue.Issue.STATUS_CLOSED; import static org.sonar.api.web.UserRole.ISSUE_ADMIN; import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonar.db.issue.IssueTesting.newDto; @@ -86,11 +89,11 @@ public class TransitionActionTest { @Test public void does_not_execute_if_transition_is_not_available() { loginAndAddProjectPermission("john", ISSUE_ADMIN); - issue.setStatus(Issue.STATUS_CLOSED); + issue.setStatus(STATUS_CLOSED); action.execute(ImmutableMap.of("transition", "reopen"), context); - assertThat(issue.status()).isEqualTo(Issue.STATUS_CLOSED); + assertThat(issue.status()).isEqualTo(STATUS_CLOSED); } @Test @@ -106,6 +109,21 @@ public class TransitionActionTest { action.verify(ImmutableMap.of("unknwown", "reopen"), Lists.newArrayList(), userSession); } + @Test + public void do_not_allow_transitions_for_issues_from_external_rule_engine() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("No transition allowed on issue from externally define rule"); + + loginAndAddProjectPermission("john", ISSUE_ADMIN); + + context.issue() + .setFromExternalRuleEngine(true) + .setStatus(STATUS_CLOSED); + + action.execute(ImmutableMap.of("transition", "close"), context); + + } + @Test public void should_support_all_issues() { assertThat(action.supports(new DefaultIssue().setResolution(null))).isTrue(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/BulkChangeActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/BulkChangeActionTest.java index b8575618c25..9b1aac04b67 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/BulkChangeActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/BulkChangeActionTest.java @@ -35,13 +35,18 @@ import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.System2; import org.sonar.db.DbClient; import org.sonar.db.DbTester; +import org.sonar.db.component.ComponentDbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.issue.IssueChangeDto; +import org.sonar.db.issue.IssueDbTester; import org.sonar.db.issue.IssueDto; import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.rule.RuleDbTester; +import org.sonar.db.rule.RuleDefinitionDto; import org.sonar.db.rule.RuleDto; import org.sonar.db.user.UserDto; import org.sonar.server.es.EsTester; +import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.UnauthorizedException; import org.sonar.server.issue.Action; import org.sonar.server.issue.IssueFieldsSetter; @@ -86,6 +91,7 @@ import static org.sonar.core.util.Protobuf.setNullable; import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonar.db.issue.IssueChangeDto.TYPE_COMMENT; import static org.sonar.db.issue.IssueTesting.newDto; +import static org.sonar.db.rule.RuleTesting.newRule; import static org.sonar.db.rule.RuleTesting.newRuleDto; public class BulkChangeActionTest { @@ -114,6 +120,9 @@ public class BulkChangeActionTest { private TestIssueChangePostProcessor issueChangePostProcessor = new TestIssueChangePostProcessor(); private List actions = new ArrayList<>(); + private RuleDbTester ruleDbTester = new RuleDbTester(db); + private IssueDbTester issueDbTester = new IssueDbTester(db); + private RuleDto rule; private OrganizationDto organization; private ComponentDto project; @@ -500,6 +509,39 @@ public class BulkChangeActionTest { .build()); } + @Test + public void fail_when_requesting_transition_on_issue_from_external_rules_engine(){ + + setUserProjectPermissions(USER, ISSUE_ADMIN); + UserDto userToAssign = db.users().insertUser("arthur"); + db.organizations().addMember(organization, user); + db.organizations().addMember(organization, userToAssign); + + RuleDefinitionDto rule = ruleDbTester.insert(newRule() + .setIsExternal(true)); + IssueDto issue1 = issueDbTester.insertIssue(newIssue() + .setStatus(STATUS_OPEN) + .setResolution(null) + .setRuleId(rule.getId()) + .setRuleKey(rule.getRuleKey(), rule.getRepositoryKey()) + .setAssignee(user.getLogin()) + .setType(BUG) + .setSeverity(MINOR)); + + IssueDto issue2 = issueDbTester.insertIssue(newIssue() + .setStatus(STATUS_OPEN) + .setResolution(null) + .setRuleId(rule.getId()) + .setRuleKey(rule.getRuleKey(), rule.getRepositoryKey()) + .setAssignee(user.getLogin()) + .setType(BUG) + .setSeverity(MAJOR)); + + BulkChangeWsResponse confirm = call(builder().setIssues(asList(issue1.getKey(), issue2.getKey())).setDoTransition("confirm").build()); + + assertThat(confirm.getFailures()).isEqualTo(2); + } + @Test public void fail_when_number_of_issues_is_more_than_500() { userSession.logIn("john"); @@ -591,6 +633,11 @@ public class BulkChangeActionTest { return newDto(rule, file, project).setStatus(STATUS_CLOSED).setResolution(RESOLUTION_FIXED); } + private IssueDto newIssue() { + RuleDto rule = ruleDbTester.insertRule(newRuleDto()); + return newDto(rule, file, project); + } + private void addActions() { actions.add(new org.sonar.server.issue.AssignAction(db.getDbClient(), issueFieldsSetter)); actions.add(new org.sonar.server.issue.SetSeverityAction(issueFieldsSetter, userSession)); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java index ffbe2089fa6..8a866b9d327 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java @@ -824,6 +824,16 @@ public class SearchActionTest { return rule; } + private RuleDto newExternalRule() { + RuleDto rule = RuleTesting.newDto(RuleTesting.EXTERNAL_XOO).setLanguage("xoo") + .setName("Rule name") + .setDescription("Rule desc") + .setIsExternal(true) + .setStatus(RuleStatus.READY); + db.rules().insert(rule.getDefinition()); + return rule; + } + private void indexPermissions() { permissionIndexer.indexOnStartup(permissionIndexer.getIndexTypes()); } diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/response_contains_all_fields_except_additional_fields.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/response_contains_all_fields_except_additional_fields.json index f7656e728d2..b008f9bff74 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/response_contains_all_fields_except_additional_fields.json +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/response_contains_all_fields_except_additional_fields.json @@ -3,7 +3,7 @@ { "organization": "my-org-2", "key": "82fd47d4-b650-4037-80bc-7b112bd4eac2", - "rule": "xoo:x1", + "rule": "external_xoo:x1", "severity": "MAJOR", "component": "FILE_KEY", "resolution": "FIXED", @@ -19,7 +19,8 @@ "owasp" ], "creationDate": "2014-09-04T01:00:00+0200", - "updateDate": "2017-12-04T00:00:00+0100" + "updateDate": "2017-12-04T00:00:00+0100", + "externalRuleEngine" : "xoo" } ] } diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java index 3192a5304bc..0add84d5a82 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java @@ -81,6 +81,8 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure. private Set tags = null; // temporarily an Object as long as DefaultIssue is used by sonar-batch private Object locations = null; + + private boolean isFromExternalRuleEngine; // FUNCTIONAL DATES private Date creationDate; private Date updateDate; @@ -239,6 +241,15 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure. return this; } + public boolean isFromExternalRuleEngine() { + return isFromExternalRuleEngine; + } + + public DefaultIssue setFromExternalRuleEngine(boolean fromExternalRuleEngine) { + isFromExternalRuleEngine = fromExternalRuleEngine; + return this; + } + @Override @CheckForNull public String message() { diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java index b436500c041..e302c0bd3e3 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java @@ -44,6 +44,7 @@ public class DefaultIssueBuilder implements Issuable.IssueBuilder { private String assignee; private RuleType type; private Map attributes; + private boolean isFromExternalRuleEngine; public DefaultIssueBuilder componentKey(String componentKey) { this.componentKey = componentKey; @@ -55,6 +56,11 @@ public class DefaultIssueBuilder implements Issuable.IssueBuilder { return this; } + public DefaultIssueBuilder fromExternalRuleEngine(boolean fromExternalRuleEngine) { + isFromExternalRuleEngine = fromExternalRuleEngine; + return this; + } + @Override public DefaultIssueBuilder ruleKey(RuleKey ruleKey) { this.ruleKey = ruleKey; @@ -164,6 +170,7 @@ public class DefaultIssueBuilder implements Issuable.IssueBuilder { issue.setCopied(false); issue.setBeingClosed(false); issue.setOnDisabledRule(false); + issue.setFromExternalRuleEngine(isFromExternalRuleEngine); return issue; } } diff --git a/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueBuilderTest.java b/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueBuilderTest.java index 76d3001eaa9..8c214f89873 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueBuilderTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueBuilderTest.java @@ -39,6 +39,7 @@ public class DefaultIssueBuilderTest { .line(123) .effortToFix(10000.0) .ruleKey(RuleKey.of("squid", "NullDereference")) + .fromExternalRuleEngine(false) .severity(Severity.CRITICAL) .attribute("JIRA", "FOO-123") .attribute("YOUTRACK", "YT-123") @@ -53,6 +54,7 @@ public class DefaultIssueBuilderTest { assertThat(issue.line()).isEqualTo(123); assertThat(issue.ruleKey().repository()).isEqualTo("squid"); assertThat(issue.ruleKey().rule()).isEqualTo("NullDereference"); + assertThat(issue.isFromExternalRuleEngine()).isFalse(); assertThat(issue.severity()).isEqualTo(Severity.CRITICAL); assertThat(issue.assignee()).isNull(); assertThat(issue.isNew()).isTrue(); diff --git a/sonar-ws/src/main/protobuf/ws-issues.proto b/sonar-ws/src/main/protobuf/ws-issues.proto index 93b99eb4a3c..e55bd9864db 100644 --- a/sonar-ws/src/main/protobuf/ws-issues.proto +++ b/sonar-ws/src/main/protobuf/ws-issues.proto @@ -157,6 +157,8 @@ message Issue { optional string organization = 29; optional string branch = 30; optional string pullRequest = 32; + + optional string externalRuleEngine = 33; } message Transitions {