Skip to content

Commit

Permalink
SONAR-10577 return if an issue is from an external analyser in issue …
Browse files Browse the repository at this point in the history
…search
  • Loading branch information
Guillaume Jambet authored and SonarTech committed Apr 26, 2018
1 parent 4754c43 commit 69cee93
Show file tree
Hide file tree
Showing 19 changed files with 149 additions and 9 deletions.
15 changes: 15 additions & 0 deletions server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -119,6 +120,7 @@ public static IssueDto toDtoForComputationInsert(DefaultIssue issue, int ruleId,
.setAssignee(issue.assignee())
.setRuleId(ruleId)
.setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule())
.setExternal(issue.isFromExternalRuleEngine())
.setTags(issue.tags())
.setComponentUuid(issue.componentUuid())
.setComponentKey(issue.componentKey())
Expand Down Expand Up @@ -166,6 +168,7 @@ public static IssueDto toDtoForUpdate(DefaultIssue issue, long now) {
.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())
Expand All @@ -185,6 +188,7 @@ public static IssueDto toDtoForUpdate(DefaultIssue issue, long now) {
public static IssueDto createFor(Project project, RuleDto rule) {
return new IssueDto()
.setProjectUuid(project.getUuid())
.setExternal(rule.isExternal())
.setRuleId(rule.getId())
.setKee(Uuids.create());
}
Expand Down Expand Up @@ -455,6 +459,7 @@ public IssueDto setRule(RuleDefinitionDto rule) {
this.ruleKey = rule.getRuleKey();
this.ruleRepo = rule.getRepositoryKey();
this.language = rule.getLanguage();
this.isExternal = rule.isExternal();
return this;
}

Expand All @@ -480,6 +485,15 @@ public IssueDto setLanguage(String language) {
return this;
}

public boolean isExternal() {
return isExternal;
}

public IssueDto setExternal(boolean external) {
isExternal = external;
return this;
}

public String getComponentKey() {
return componentKey;
}
Expand Down Expand Up @@ -719,6 +733,7 @@ public DefaultIssue toDefaultIssue() {
issue.setUpdateDate(longToDate(issueUpdateDate));
issue.setSelectedAt(selectedAt);
issue.setLocations(parseLocations());
issue.setFromExternalRuleEngine(isExternal);
return issue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public void selectByKeyOrFail() {
assertThat(issue.getProjectKey()).isEqualTo(PROJECT_KEY);
assertThat(issue.getLocations()).isNull();
assertThat(issue.parseLocations()).isNull();
assertThat(issue.isExternal()).isTrue();
}

@Test
Expand Down Expand Up @@ -304,7 +305,7 @@ private static IssueDto newIssueDto(String key) {
}

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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,15 @@ public void set_issue_fields() {
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);
assertThat(dto.getRuleRepo()).isEqualTo("squid");
assertThat(dto.getRule()).isEqualTo("AvoidCycle");
assertThat(dto.getRuleKey().toString()).isEqualTo("squid:AvoidCycle");
assertThat(dto.getLanguage()).isEqualTo("xoo");
assertThat(dto.isExternal()).isEqualTo(true);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@
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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public DefaultIssue processFile(Component file, String fileLanguage) {
issue.setSeverity(activeRule.get().getSeverity());
issue.setLine(null);
issue.setChecksum("");
issue.setFromExternalRuleEngine(false);
}
}
return issue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@

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;

Expand Down Expand Up @@ -62,6 +65,9 @@ public boolean shouldRefreshMeasures() {
}

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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,9 @@ public void define(WebService.NewController controller) {
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
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;
Expand Down Expand Up @@ -59,8 +60,10 @@
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 {
Expand Down Expand Up @@ -171,6 +174,9 @@ private void formatIssue(Issue.Builder issueBuilder, IssueDto dto, SearchRespons
}
}
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);
Expand All @@ -192,6 +198,11 @@ private void formatIssue(Issue.Builder issueBuilder, IssueDto dto, SearchRespons
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
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 {
Expand Down Expand Up @@ -59,6 +60,7 @@ public Rule persistAndIndex(DbSession dbSession, NewExternalRule external) {
.setScope(ALL)
.setStatus(RuleStatus.READY)
.setSeverity(external.getSeverity())
.setStatus(READY)
.setCreatedAt(system2.now())
.setUpdatedAt(system2.now()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public void display_warning_when_line_is_above_max_size() {
"moduleUuid=<null>,moduleUuidPath=<null>,projectUuid=<null>,projectKey=<null>,ruleKey=<null>,language=<null>,severity=<null>," +
"manualSeverity=false,message=<null>,line=2,gap=<null>,effort=<null>,status=<null>,resolution=<null>," +
"assignee=<null>,checksum=<null>,attributes=<null>,authorLogin=<null>,comments=<null>,tags=<null>," +
"locations=<null>,creationDate=<null>,updateDate=<null>,closeDate=<null>,currentChange=<null>,changes=<null>,isNew=true,isCopied=false," +
"locations=<null>,isFromExternalRuleEngine=false,creationDate=<null>,updateDate=<null>,closeDate=<null>,currentChange=<null>,changes=<null>,isNew=true,isCopied=false," +
"beingClosed=false,onDisabledRule=false,isChanged=false,sendNotifications=false,selectedAt=<null>]");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@

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;
Expand All @@ -42,6 +44,7 @@
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;
Expand Down Expand Up @@ -86,11 +89,11 @@ public void execute() {
@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
Expand All @@ -106,6 +109,21 @@ public void fail_to_verify_when_parameter_not_found() {
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,18 @@
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;
Expand Down Expand Up @@ -86,6 +91,7 @@
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 {
Expand Down Expand Up @@ -114,6 +120,9 @@ public class BulkChangeActionTest {
private TestIssueChangePostProcessor issueChangePostProcessor = new TestIssueChangePostProcessor();
private List<Action> actions = new ArrayList<>();

private RuleDbTester ruleDbTester = new RuleDbTester(db);
private IssueDbTester issueDbTester = new IssueDbTester(db);

private RuleDto rule;
private OrganizationDto organization;
private ComponentDto project;
Expand Down Expand Up @@ -500,6 +509,39 @@ public void fail_when_only_comment_action() {
.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");
Expand Down Expand Up @@ -591,6 +633,11 @@ private IssueDto newResolvedIssue() {
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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,16 @@ private RuleDto newRule() {
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());
}
Expand Down
Loading

0 comments on commit 69cee93

Please sign in to comment.