]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-17501 Send push events to SonarLint for branches
authorJacek <jacek.poreda@sonarsource.com>
Mon, 24 Oct 2022 13:03:40 +0000 (15:03 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 26 Oct 2022 20:03:10 +0000 (20:03 +0000)
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventFactory.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistPushEventsStep.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventFactoryTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistPushEventsStepTest.java

index 18a8a17b8fbb93df61e521b4f0d5bd518f6e91f7..30b1221456226b919979f55d8f9752e15f33a478 100644 (file)
@@ -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));
   }
 
index 8c302b0f4b2343e14e6357eed3eccfcc2184aa87..722af961aeffcedca537f0eb4dd40d3d62b4e5a0 100644 (file)
@@ -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);
index 65548362402adba4d119ffbb31436a2c49a8c513..1b4a04e3d5d87a6ce30d1377801361cfc6fc5637 100644 (file)
@@ -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());
-    }
-  }
-
 }
index ab54b01d72da3f223bdff2c82f36ca16c3973fd9..c2495c8cb67d2a63e6720905d5006f66826d172b 100644 (file)
@@ -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());
+  }
+
 }