@@ -57,7 +57,7 @@ public class PushEventFactory { | |||
this.flowGenerator = flowGenerator; | |||
} | |||
public Optional<PushEventDto> raiseEventOnIssue(DefaultIssue currentIssue) { | |||
public Optional<PushEventDto> raiseEventOnIssue(String projectUuid, DefaultIssue currentIssue) { | |||
var currentIssueComponentUuid = currentIssue.componentUuid(); | |||
if (!taintChecker.isTaintVulnerability(currentIssue) || currentIssueComponentUuid == null) { | |||
return Optional.empty(); | |||
@@ -65,10 +65,10 @@ public class PushEventFactory { | |||
var component = treeRootHolder.getComponentByUuid(Objects.requireNonNull(currentIssue.componentUuid())); | |||
if (currentIssue.isNew() || currentIssue.isCopied() || isReopened(currentIssue)) { | |||
return Optional.of(raiseTaintVulnerabilityRaisedEvent(component, currentIssue)); | |||
return Optional.of(raiseTaintVulnerabilityRaisedEvent(projectUuid, component, currentIssue)); | |||
} | |||
if (currentIssue.isBeingClosed()) { | |||
return Optional.of(raiseTaintVulnerabilityClosedEvent(currentIssue)); | |||
return Optional.of(raiseTaintVulnerabilityClosedEvent(projectUuid, currentIssue)); | |||
} | |||
return Optional.empty(); | |||
} | |||
@@ -82,11 +82,12 @@ public class PushEventFactory { | |||
return status != null && status.toString().equals("CLOSED|OPEN"); | |||
} | |||
private PushEventDto raiseTaintVulnerabilityRaisedEvent(Component component, DefaultIssue issue) { | |||
private PushEventDto raiseTaintVulnerabilityRaisedEvent(String projectUuid, Component component, DefaultIssue issue) { | |||
TaintVulnerabilityRaised event = prepareEvent(component, issue); | |||
return new PushEventDto() | |||
.setName("TaintVulnerabilityRaised") | |||
.setProjectUuid(treeRootHolder.getRoot().getUuid()) | |||
.setProjectUuid(projectUuid) | |||
.setLanguage(issue.language()) | |||
.setPayload(serializeEvent(event)); | |||
} | |||
@@ -116,11 +117,12 @@ public class PushEventFactory { | |||
return event; | |||
} | |||
private PushEventDto raiseTaintVulnerabilityClosedEvent(DefaultIssue issue) { | |||
private static PushEventDto raiseTaintVulnerabilityClosedEvent(String projectUuid, DefaultIssue issue) { | |||
TaintVulnerabilityClosed event = new TaintVulnerabilityClosed(issue.key(), issue.projectKey()); | |||
return new PushEventDto() | |||
.setName("TaintVulnerabilityClosed") | |||
.setProjectUuid(treeRootHolder.getRoot().getUuid()) | |||
.setProjectUuid(projectUuid) | |||
.setLanguage(issue.language()) | |||
.setPayload(serializeEvent(event)); | |||
} | |||
@@ -22,6 +22,7 @@ package org.sonar.ce.task.projectanalysis.step; | |||
import java.util.Optional; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.ce.task.projectanalysis.component.TreeRootHolder; | |||
import org.sonar.ce.task.projectanalysis.issue.ProtoIssueCache; | |||
import org.sonar.ce.task.projectanalysis.pushevent.PushEventFactory; | |||
import org.sonar.ce.task.step.ComputationStep; | |||
@@ -38,13 +39,16 @@ public class PersistPushEventsStep implements ComputationStep { | |||
private final DbClient dbClient; | |||
private final ProtoIssueCache protoIssueCache; | |||
private final PushEventFactory pushEventFactory; | |||
private final TreeRootHolder treeRootHolder; | |||
public PersistPushEventsStep(DbClient dbClient, | |||
ProtoIssueCache protoIssueCache, | |||
PushEventFactory pushEventFactory) { | |||
PushEventFactory pushEventFactory, | |||
TreeRootHolder treeRootHolder) { | |||
this.dbClient = dbClient; | |||
this.protoIssueCache = protoIssueCache; | |||
this.pushEventFactory = pushEventFactory; | |||
this.treeRootHolder = treeRootHolder; | |||
} | |||
@Override | |||
@@ -52,10 +56,11 @@ public class PersistPushEventsStep implements ComputationStep { | |||
try (DbSession dbSession = dbClient.openSession(true); | |||
CloseableIterator<DefaultIssue> issues = protoIssueCache.traverse()) { | |||
int batchCounter = 0; | |||
var projectUuid = getProjectUuid(dbSession); | |||
while (issues.hasNext()) { | |||
DefaultIssue currentIssue = issues.next(); | |||
Optional<PushEventDto> raisedEvent = pushEventFactory.raiseEventOnIssue(currentIssue); | |||
Optional<PushEventDto> raisedEvent = pushEventFactory.raiseEventOnIssue(projectUuid, currentIssue); | |||
if (raisedEvent.isEmpty()) { | |||
continue; | |||
@@ -72,6 +77,14 @@ public class PersistPushEventsStep implements ComputationStep { | |||
} | |||
} | |||
private String getProjectUuid(DbSession dbSession) { | |||
var branch = dbClient.branchDao().selectByUuid(dbSession, treeRootHolder.getRoot().getUuid()); | |||
if (branch.isEmpty()) { | |||
return treeRootHolder.getRoot().getUuid(); | |||
} | |||
return branch.get().getProjectUuid(); | |||
} | |||
private static int flushIfNeeded(DbSession dbSession, int batchCounter) { | |||
if (batchCounter > MAX_BATCH_SIZE) { | |||
flushSession(dbSession); |
@@ -24,7 +24,6 @@ import java.util.List; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.mockito.ArgumentMatcher; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rules.RuleType; | |||
import org.sonar.api.utils.DateUtils; | |||
@@ -38,7 +37,6 @@ import org.sonar.core.issue.DefaultIssue; | |||
import org.sonar.core.issue.FieldDiffs; | |||
import org.sonar.db.protobuf.DbCommons; | |||
import org.sonar.db.protobuf.DbIssues; | |||
import org.sonar.db.pushevent.PushEventDto; | |||
import org.sonar.server.issue.TaintChecker; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
@@ -71,11 +69,13 @@ public class PushEventFactoryTest { | |||
DefaultIssue defaultIssue = createDefaultIssue() | |||
.setNew(true); | |||
assertThat(underTest.raiseEventOnIssue(defaultIssue)) | |||
assertThat(underTest.raiseEventOnIssue("some-project-uuid", defaultIssue)) | |||
.isNotEmpty() | |||
.hasValueSatisfying(pushEventDto -> { | |||
assertThat(pushEventDto.getName()).isEqualTo("TaintVulnerabilityRaised"); | |||
assertThat(pushEventDto.getPayload()).isNotNull(); | |||
assertThat(pushEventDto.getLanguage()).isEqualTo("java"); | |||
assertThat(pushEventDto.getProjectUuid()).isEqualTo("some-project-uuid"); | |||
}); | |||
} | |||
@@ -88,7 +88,7 @@ public class PushEventFactoryTest { | |||
.setCopied(false) | |||
.setCurrentChange(new FieldDiffs().setDiff("status", "CLOSED", "OPEN")); | |||
assertThat(underTest.raiseEventOnIssue(defaultIssue)) | |||
assertThat(underTest.raiseEventOnIssue("some-project-uuid", defaultIssue)) | |||
.isNotEmpty() | |||
.hasValueSatisfying(pushEventDto -> { | |||
assertThat(pushEventDto.getName()).isEqualTo("TaintVulnerabilityRaised"); | |||
@@ -99,12 +99,12 @@ public class PushEventFactoryTest { | |||
@Test | |||
public void skip_event_if_taint_vulnerability_status_change() { | |||
DefaultIssue defaultIssue = createDefaultIssue() | |||
.setChanged(true) | |||
.setNew(false) | |||
.setCopied(false) | |||
.setCurrentChange(new FieldDiffs().setDiff("status", "OPEN", "FIXED")); | |||
.setChanged(true) | |||
.setNew(false) | |||
.setCopied(false) | |||
.setCurrentChange(new FieldDiffs().setDiff("status", "OPEN", "FIXED")); | |||
assertThat(underTest.raiseEventOnIssue(defaultIssue)).isEmpty(); | |||
assertThat(underTest.raiseEventOnIssue("some-project-uuid", defaultIssue)).isEmpty(); | |||
} | |||
@Test | |||
@@ -112,7 +112,7 @@ public class PushEventFactoryTest { | |||
DefaultIssue defaultIssue = createDefaultIssue() | |||
.setCopied(true); | |||
assertThat(underTest.raiseEventOnIssue(defaultIssue)) | |||
assertThat(underTest.raiseEventOnIssue("some-project-uuid", defaultIssue)) | |||
.isNotEmpty() | |||
.hasValueSatisfying(pushEventDto -> { | |||
assertThat(pushEventDto.getName()).isEqualTo("TaintVulnerabilityRaised"); | |||
@@ -128,7 +128,7 @@ public class PushEventFactoryTest { | |||
.setCopied(false) | |||
.setBeingClosed(true); | |||
assertThat(underTest.raiseEventOnIssue(defaultIssue)) | |||
assertThat(underTest.raiseEventOnIssue("some-project-uuid", defaultIssue)) | |||
.isNotEmpty() | |||
.hasValueSatisfying(pushEventDto -> { | |||
assertThat(pushEventDto.getName()).isEqualTo("TaintVulnerabilityClosed"); | |||
@@ -147,7 +147,7 @@ public class PushEventFactoryTest { | |||
.setCreationDate(DateUtils.parseDate("2022-01-01")) | |||
.setRuleKey(RuleKey.of("javasecurity", "S123")); | |||
assertThat(underTest.raiseEventOnIssue(defaultIssue)).isEmpty(); | |||
assertThat(underTest.raiseEventOnIssue("some-project-uuid", defaultIssue)).isEmpty(); | |||
} | |||
@Test | |||
@@ -160,7 +160,7 @@ public class PushEventFactoryTest { | |||
when(taintChecker.isTaintVulnerability(any())).thenReturn(false); | |||
assertThat(underTest.raiseEventOnIssue(defaultIssue)).isEmpty(); | |||
assertThat(underTest.raiseEventOnIssue("some-project-uuid", defaultIssue)).isEmpty(); | |||
defaultIssue = new DefaultIssue() | |||
.setComponentUuid("issue-component-uuid") | |||
@@ -170,7 +170,7 @@ public class PushEventFactoryTest { | |||
.setType(RuleType.VULNERABILITY) | |||
.setRuleKey(RuleKey.of("weirdrepo", "S123")); | |||
assertThat(underTest.raiseEventOnIssue(defaultIssue)).isEmpty(); | |||
assertThat(underTest.raiseEventOnIssue("some-project-uuid", defaultIssue)).isEmpty(); | |||
} | |||
@Test | |||
@@ -183,7 +183,7 @@ public class PushEventFactoryTest { | |||
when(taintChecker.isTaintVulnerability(any())).thenReturn(false); | |||
assertThat(underTest.raiseEventOnIssue(defaultIssue)).isEmpty(); | |||
assertThat(underTest.raiseEventOnIssue("some-project-uuid", defaultIssue)).isEmpty(); | |||
} | |||
@Test | |||
@@ -196,7 +196,7 @@ public class PushEventFactoryTest { | |||
when(taintChecker.isTaintVulnerability(any())).thenReturn(false); | |||
assertThat(underTest.raiseEventOnIssue(defaultIssue)).isEmpty(); | |||
assertThat(underTest.raiseEventOnIssue("some-project-uuid", defaultIssue)).isEmpty(); | |||
} | |||
private void buildComponentTree() { | |||
@@ -215,6 +215,7 @@ public class PushEventFactoryTest { | |||
return new DefaultIssue() | |||
.setComponentUuid("issue-component-uuid") | |||
.setType(RuleType.VULNERABILITY) | |||
.setLanguage("java") | |||
.setCreationDate(new Date()) | |||
.setLocations(DbIssues.Locations.newBuilder() | |||
.addFlow(DbIssues.Flow.newBuilder() | |||
@@ -230,22 +231,4 @@ public class PushEventFactoryTest { | |||
.setRuleKey(RuleKey.of("javasecurity", "S123")); | |||
} | |||
private static class PushEventMatcher implements ArgumentMatcher<PushEventDto> { | |||
private final PushEventDto left; | |||
static PushEventMatcher eq(PushEventDto left) { | |||
return new PushEventMatcher(left); | |||
} | |||
private PushEventMatcher(PushEventDto left) { | |||
this.left = left; | |||
} | |||
@Override | |||
public boolean matches(PushEventDto right) { | |||
return left.getName().equals(right.getName()); | |||
} | |||
} | |||
} |
@@ -31,6 +31,9 @@ import org.sonar.api.impl.utils.TestSystem2; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rules.RuleType; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.ce.task.projectanalysis.component.Component; | |||
import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolderRule; | |||
import org.sonar.ce.task.projectanalysis.component.ReportComponent; | |||
import org.sonar.ce.task.projectanalysis.issue.ProtoIssueCache; | |||
import org.sonar.ce.task.projectanalysis.pushevent.PushEventFactory; | |||
import org.sonar.ce.task.step.ComputationStep; | |||
@@ -42,6 +45,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.assertThatCode; | |||
import static org.mockito.ArgumentMatchers.any; | |||
import static org.mockito.ArgumentMatchers.anyString; | |||
import static org.mockito.ArgumentMatchers.eq; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
@@ -55,14 +60,16 @@ public class PersistPushEventsStepTest { | |||
public final PushEventFactory pushEventFactory = mock(PushEventFactory.class); | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Rule | |||
public MutableTreeRootHolderRule treeRootHolder = new MutableTreeRootHolderRule(); | |||
private ProtoIssueCache protoIssueCache; | |||
private PersistPushEventsStep underTest; | |||
@Before | |||
public void before() throws IOException { | |||
protoIssueCache = new ProtoIssueCache(temp.newFile(), System2.INSTANCE); | |||
underTest = new PersistPushEventsStep(db.getDbClient(), protoIssueCache, pushEventFactory); | |||
buildComponentTree(); | |||
underTest = new PersistPushEventsStep(db.getDbClient(), protoIssueCache, pushEventFactory, treeRootHolder); | |||
} | |||
@Test | |||
@@ -83,7 +90,7 @@ public class PersistPushEventsStepTest { | |||
createIssue("key1").setType(RuleType.VULNERABILITY)) | |||
.close(); | |||
when(pushEventFactory.raiseEventOnIssue(any())).thenThrow(new RuntimeException("I have a bad feelings about this")); | |||
when(pushEventFactory.raiseEventOnIssue(any(), any())).thenThrow(new RuntimeException("I have a bad feelings about this")); | |||
assertThatCode(() -> underTest.execute(mock(ComputationStep.Context.class))) | |||
.doesNotThrowAnyException(); | |||
@@ -125,7 +132,30 @@ public class PersistPushEventsStepTest { | |||
.setComponentKey("ck2")) | |||
.close(); | |||
when(pushEventFactory.raiseEventOnIssue(any(DefaultIssue.class))).thenReturn( | |||
when(pushEventFactory.raiseEventOnIssue(eq("uuid_1"), any(DefaultIssue.class))).thenReturn( | |||
Optional.of(createPushEvent()), | |||
Optional.of(createPushEvent())); | |||
underTest.execute(mock(ComputationStep.Context.class)); | |||
assertThat(db.countSql(db.getSession(), "SELECT count(uuid) FROM push_events")).isEqualTo(2); | |||
} | |||
@Test | |||
public void store_push_events_for_branch() { | |||
var project = db.components().insertPrivateProject(); | |||
db.components().insertProjectBranch(project, b -> b.setUuid("uuid_1")); | |||
protoIssueCache.newAppender() | |||
.append(createIssue("key1").setType(RuleType.VULNERABILITY) | |||
.setComponentUuid("cu1") | |||
.setComponentKey("ck1")) | |||
.append(createIssue("key2").setType(RuleType.VULNERABILITY) | |||
.setComponentUuid("cu2") | |||
.setComponentKey("ck2")) | |||
.close(); | |||
when(pushEventFactory.raiseEventOnIssue(eq(project.uuid()), any(DefaultIssue.class))).thenReturn( | |||
Optional.of(createPushEvent()), | |||
Optional.of(createPushEvent())); | |||
@@ -144,7 +174,7 @@ public class PersistPushEventsStepTest { | |||
.setComponentUuid("cu" + value) | |||
.setComponentKey("ck" + value); | |||
appender.append(defaultIssue); | |||
when(pushEventFactory.raiseEventOnIssue(defaultIssue)).thenReturn(Optional.of(createPushEvent())); | |||
when(pushEventFactory.raiseEventOnIssue(anyString(), eq(defaultIssue))).thenReturn(Optional.of(createPushEvent())); | |||
}); | |||
appender.close(); | |||
@@ -169,4 +199,16 @@ public class PersistPushEventsStepTest { | |||
return new PushEventDto().setProjectUuid("project-uuid").setName("event").setPayload("test".getBytes(UTF_8)); | |||
} | |||
private void buildComponentTree() { | |||
treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1) | |||
.setUuid("uuid_1") | |||
.addChildren(ReportComponent.builder(Component.Type.FILE, 2) | |||
.setUuid("issue-component-uuid") | |||
.build()) | |||
.addChildren(ReportComponent.builder(Component.Type.FILE, 3) | |||
.setUuid("location-component-uuid") | |||
.build()) | |||
.build()); | |||
} | |||
} |