]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-17210 - Generate the SARIF report
authorAurelien Poscia <aurelien.poscia@sonarsource.com>
Fri, 19 Aug 2022 14:30:53 +0000 (16:30 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 30 Aug 2022 20:03:14 +0000 (20:03 +0000)
16 files changed:
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/Rule.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleImpl.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleRepositoryImpl.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/Flow.java [new file with mode: 0644]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/FlowGenerator.java [new file with mode: 0644]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/Location.java [new file with mode: 0644]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/TextRange.java [new file with mode: 0644]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Flow.java [deleted file]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Location.java [deleted file]
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/pushevent/TaintVulnerabilityRaised.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TextRange.java [deleted file]
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/DumbRule.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/locations/flow/FlowGeneratorTest.java [new file with mode: 0644]
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventFactoryTest.java

index a90a512cd8a334cde8e9f53f6fdbe5ba96cdfb0b..674b1c3f9c4d73e31d7b468d9120f13ed2ea3858 100644 (file)
@@ -55,10 +55,10 @@ import org.sonar.ce.task.projectanalysis.filesystem.ComputationTempFolderProvide
 import org.sonar.ce.task.projectanalysis.issue.BaseIssuesLoader;
 import org.sonar.ce.task.projectanalysis.issue.CloseIssuesOnRemovedComponentsVisitor;
 import org.sonar.ce.task.projectanalysis.issue.ClosedIssuesInputFactory;
-import org.sonar.ce.task.projectanalysis.issue.ComputeLocationHashesVisitor;
 import org.sonar.ce.task.projectanalysis.issue.ComponentIssuesLoader;
 import org.sonar.ce.task.projectanalysis.issue.ComponentIssuesRepositoryImpl;
 import org.sonar.ce.task.projectanalysis.issue.ComponentsWithUnprocessedIssues;
+import org.sonar.ce.task.projectanalysis.issue.ComputeLocationHashesVisitor;
 import org.sonar.ce.task.projectanalysis.issue.DebtCalculator;
 import org.sonar.ce.task.projectanalysis.issue.DefaultAssignee;
 import org.sonar.ce.task.projectanalysis.issue.EffortAggregator;
@@ -105,6 +105,7 @@ import org.sonar.ce.task.projectanalysis.issue.commonrule.SkippedTestRule;
 import org.sonar.ce.task.projectanalysis.issue.commonrule.TestErrorRule;
 import org.sonar.ce.task.projectanalysis.issue.filter.IssueFilter;
 import org.sonar.ce.task.projectanalysis.language.LanguageRepositoryImpl;
+import org.sonar.ce.task.projectanalysis.locations.flow.FlowGenerator;
 import org.sonar.ce.task.projectanalysis.measure.MeasureComputersHolderImpl;
 import org.sonar.ce.task.projectanalysis.measure.MeasureComputersVisitor;
 import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryImpl;
@@ -256,6 +257,7 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop
       ComponentIssuesRepositoryImpl.class,
       IssueFilter.class,
 
+      FlowGenerator.class,
       // push events
       PushEventFactory.class,
 
index 6d70363bb948e488acd906adc794f4d469be6bb5..a6cf2028e908f533e16ce614fdfb13d710bc5fc2 100644 (file)
@@ -58,4 +58,8 @@ public interface Rule {
 
   @CheckForNull
   String getPluginKey();
+
+  String getDefaultRuleDescription();
+
+  String getSeverity();
 }
index f8600a0f1d3ff715e02daf668ff60055aa48e5c7..ee29fda5c08e14107af9ac5e202ba1361adb90d7 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.ce.task.projectanalysis.issue;
 
 import com.google.common.base.MoreObjects;
+import java.util.Optional;
 import java.util.Set;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
@@ -29,6 +30,7 @@ import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.server.debt.DebtRemediationFunction;
 import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction;
+import org.sonar.db.rule.RuleDescriptionSectionDto;
 import org.sonar.db.rule.RuleDto;
 
 import static com.google.common.collect.Sets.union;
@@ -47,6 +49,8 @@ public class RuleImpl implements Rule {
   private final String pluginKey;
   private final boolean isExternal;
   private final boolean isAdHoc;
+  private final String defaultRuleDescription;
+  private final String severity;
 
   public RuleImpl(RuleDto dto) {
     this.uuid = dto.getUuid();
@@ -60,6 +64,14 @@ public class RuleImpl implements Rule {
     this.pluginKey = dto.getPluginKey();
     this.isExternal = dto.isExternal();
     this.isAdHoc = dto.isAdHoc();
+    this.defaultRuleDescription = getNonNullDefaultRuleDescription(dto);
+    this.severity = Optional.ofNullable(dto.getSeverityString()).orElse(dto.getAdHocSeverity());
+  }
+
+  private static String getNonNullDefaultRuleDescription(RuleDto dto) {
+    return Optional.ofNullable(dto.getDefaultRuleDescriptionSection())
+      .map(RuleDescriptionSectionDto::getContent)
+      .orElse("");
   }
 
   @Override
@@ -109,6 +121,15 @@ public class RuleImpl implements Rule {
     return pluginKey;
   }
 
+  public String getDefaultRuleDescription() {
+    return defaultRuleDescription;
+  }
+
+  @Override
+  public String getSeverity() {
+    return severity;
+  }
+
   @Override
   public boolean equals(@Nullable Object o) {
     if (this == o) {
index dcf299a5295bc06e9ea082f7a9f4c87a9ba0645d..4ada1d7bc2d5dccb9da07a6b4f13f04f759d8c0b 100644 (file)
@@ -211,5 +211,15 @@ public class RuleRepositoryImpl implements RuleRepository {
     public String getPluginKey() {
       return null;
     }
+
+    @Override
+    public String getDefaultRuleDescription() {
+      return addHocRule.getDescription();
+    }
+
+    @Override
+    public String getSeverity() {
+      return addHocRule.getSeverity();
+    }
   }
 }
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/Flow.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/Flow.java
new file mode 100644 (file)
index 0000000..2b7e844
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.locations.flow;
+
+import java.util.List;
+
+public class Flow {
+  private List<Location> locations;
+
+  public Flow() {
+    // nothing to do
+  }
+
+  public List<Location> getLocations() {
+    return locations;
+  }
+
+  public void setLocations(List<Location> locations) {
+    this.locations = locations;
+  }
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/FlowGenerator.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/FlowGenerator.java
new file mode 100644 (file)
index 0000000..be25776
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.locations.flow;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
+import org.sonar.db.protobuf.DbCommons;
+import org.sonar.db.protobuf.DbIssues;
+
+import static java.util.stream.Collectors.toCollection;
+
+public class FlowGenerator {
+  private final TreeRootHolder treeRootHolder;
+
+  public FlowGenerator(TreeRootHolder treeRootHolder) {
+    this.treeRootHolder = treeRootHolder;
+  }
+
+  public List<Flow> convertFlows(String componentName, @Nullable DbIssues.Locations issueLocations) {
+    if (issueLocations == null) {
+      return Collections.emptyList();
+    }
+    return issueLocations.getFlowList().stream()
+      .map(sourceFlow -> toFlow(componentName, sourceFlow))
+      .collect(Collectors.toCollection(LinkedList::new));
+  }
+
+  private Flow toFlow(String componentName, DbIssues.Flow sourceFlow) {
+    Flow flow = new Flow();
+    List<Location> locations = getFlowLocations(componentName, sourceFlow);
+    flow.setLocations(locations);
+    return flow;
+  }
+
+  private List<Location> getFlowLocations(String componentName, DbIssues.Flow sourceFlow) {
+    return sourceFlow.getLocationList().stream()
+      .map(sourceLocation -> toLocation(componentName, sourceLocation))
+      .collect(toCollection(LinkedList::new));
+  }
+
+  private Location toLocation(String componentName, DbIssues.Location sourceLocation) {
+    Location location = new Location();
+    Component locationComponent = treeRootHolder.getComponentByUuid(sourceLocation.getComponentId());
+    String filePath = Optional.ofNullable(locationComponent).map(Component::getName).orElse(componentName);
+    location.setFilePath(filePath);
+    location.setMessage(sourceLocation.getMsg());
+
+    TextRange textRange = getTextRange(sourceLocation.getTextRange(), sourceLocation.getChecksum());
+    location.setTextRange(textRange);
+    return location;
+  }
+
+  private static TextRange getTextRange(DbCommons.TextRange source, String checksum) {
+    TextRange textRange = new TextRange();
+    textRange.setStartLine(source.getStartLine());
+    textRange.setStartLineOffset(source.getStartOffset());
+    textRange.setEndLine(source.getEndLine());
+    textRange.setEndLineOffset(source.getEndOffset());
+    textRange.setHash(checksum);
+    return textRange;
+  }
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/Location.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/Location.java
new file mode 100644 (file)
index 0000000..bedae21
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.locations.flow;
+
+public class Location {
+  private String filePath;
+  private String message;
+  private TextRange textRange;
+
+  public Location() {
+    // nothing to do
+  }
+
+  public String getFilePath() {
+    return filePath;
+  }
+
+  public void setFilePath(String filePath) {
+    this.filePath = filePath;
+  }
+
+  public String getMessage() {
+    return message;
+  }
+
+  public void setMessage(String message) {
+    this.message = message;
+  }
+
+  public TextRange getTextRange() {
+    return textRange;
+  }
+
+  public void setTextRange(TextRange textRange) {
+    this.textRange = textRange;
+  }
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/TextRange.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/locations/flow/TextRange.java
new file mode 100644 (file)
index 0000000..16e336c
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.locations.flow;
+
+import java.util.Objects;
+
+public class TextRange {
+  private int startLine;
+  private int startLineOffset;
+  private int endLine;
+  private int endLineOffset;
+  private String hash;
+
+  public TextRange() {
+    // nothing to do
+  }
+
+  public int getStartLine() {
+    return startLine;
+  }
+
+  public void setStartLine(int startLine) {
+    this.startLine = startLine;
+  }
+
+  public int getStartLineOffset() {
+    return startLineOffset;
+  }
+
+  public void setStartLineOffset(int startLineOffset) {
+    this.startLineOffset = startLineOffset;
+  }
+
+  public int getEndLine() {
+    return endLine;
+  }
+
+  public void setEndLine(int endLine) {
+    this.endLine = endLine;
+  }
+
+  public int getEndLineOffset() {
+    return endLineOffset;
+  }
+
+  public void setEndLineOffset(int endLineOffset) {
+    this.endLineOffset = endLineOffset;
+  }
+
+  public String getHash() {
+    return hash;
+  }
+
+  public void setHash(String hash) {
+    this.hash = hash;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    TextRange textRange = (TextRange) o;
+    return getStartLine() == textRange.getStartLine() && getStartLineOffset() == textRange.getStartLineOffset() && getEndLine() == textRange.getEndLine()
+      && getEndLineOffset() == textRange.getEndLineOffset() && Objects.equals(getHash(), textRange.getHash());
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(getStartLine(), getStartLineOffset(), getEndLine(), getEndLineOffset(), getHash());
+  }
+
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Flow.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Flow.java
deleted file mode 100644 (file)
index 75671df..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.pushevent;
-
-import java.util.List;
-
-public class Flow {
-  private List<Location> locations;
-
-  public Flow() {
-    // nothing to do
-  }
-
-  public List<Location> getLocations() {
-    return locations;
-  }
-
-  public void setLocations(List<Location> locations) {
-    this.locations = locations;
-  }
-}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Location.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Location.java
deleted file mode 100644 (file)
index 016dbfa..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.pushevent;
-
-public class Location {
-  private String filePath;
-  private String message;
-  private TextRange textRange;
-
-  public Location() {
-    // nothing to do
-  }
-
-  public String getFilePath() {
-    return filePath;
-  }
-
-  public void setFilePath(String filePath) {
-    this.filePath = filePath;
-  }
-
-  public String getMessage() {
-    return message;
-  }
-
-  public void setMessage(String message) {
-    this.message = message;
-  }
-
-  public TextRange getTextRange() {
-    return textRange;
-  }
-
-  public void setTextRange(TextRange textRange) {
-    this.textRange = textRange;
-  }
-}
index 35e689780821a51b9460564d4eb698a861dfe7e7..18a8a17b8fbb93df61e521b4f0d5bd518f6e91f7 100644 (file)
@@ -21,8 +21,6 @@ package org.sonar.ce.task.projectanalysis.pushevent;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
-import java.util.LinkedList;
-import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import org.jetbrains.annotations.NotNull;
@@ -30,6 +28,9 @@ import org.sonar.api.ce.ComputeEngineSide;
 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
 import org.sonar.ce.task.projectanalysis.component.Component;
 import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
+import org.sonar.ce.task.projectanalysis.locations.flow.FlowGenerator;
+import org.sonar.ce.task.projectanalysis.locations.flow.Location;
+import org.sonar.ce.task.projectanalysis.locations.flow.TextRange;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.db.protobuf.DbCommons;
 import org.sonar.db.protobuf.DbIssues;
@@ -38,7 +39,6 @@ import org.sonar.server.issue.TaintChecker;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Objects.requireNonNull;
-import static java.util.Objects.requireNonNullElse;
 
 @ComputeEngineSide
 public class PushEventFactory {
@@ -47,12 +47,14 @@ public class PushEventFactory {
   private final TreeRootHolder treeRootHolder;
   private final AnalysisMetadataHolder analysisMetadataHolder;
   private final TaintChecker taintChecker;
+  private final FlowGenerator flowGenerator;
 
   public PushEventFactory(TreeRootHolder treeRootHolder,
-    AnalysisMetadataHolder analysisMetadataHolder, TaintChecker taintChecker) {
+    AnalysisMetadataHolder analysisMetadataHolder, TaintChecker taintChecker, FlowGenerator flowGenerator) {
     this.treeRootHolder = treeRootHolder;
     this.analysisMetadataHolder = analysisMetadataHolder;
     this.taintChecker = taintChecker;
+    this.flowGenerator = flowGenerator;
   }
 
   public Optional<PushEventDto> raiseEventOnIssue(DefaultIssue currentIssue) {
@@ -110,25 +112,7 @@ public class PushEventFactory {
     mainLocation.setTextRange(mainLocationTextRange);
     event.setMainLocation(mainLocation);
 
-    List<Flow> flows = new LinkedList<>();
-    for (DbIssues.Flow sourceFlow : issueLocations.getFlowList()) {
-      Flow flow = new Flow();
-      List<Location> locations = new LinkedList<>();
-      for (DbIssues.Location sourceLocation : sourceFlow.getLocationList()) {
-        Location location = new Location();
-        var locationComponent = treeRootHolder.getComponentByUuid(sourceLocation.getComponentId());
-        location.setFilePath(requireNonNullElse(locationComponent, component).getName());
-        location.setMessage(sourceLocation.getMsg());
-
-        TextRange textRange = getTextRange(sourceLocation.getTextRange(), sourceLocation.getChecksum());
-        location.setTextRange(textRange);
-
-        locations.add(location);
-      }
-      flow.setLocations(locations);
-      flows.add(flow);
-    }
-    event.setFlows(flows);
+    event.setFlows(flowGenerator.convertFlows(component.getName(), issueLocations));
     return event;
   }
 
index e9ab37d368f0db994410c82f7a06c0ce71d3b82e..a3ddd139ef6486d38dde0685b947b0d9b81e419c 100644 (file)
@@ -20,6 +20,8 @@
 package org.sonar.ce.task.projectanalysis.pushevent;
 
 import java.util.List;
+import org.sonar.ce.task.projectanalysis.locations.flow.Flow;
+import org.sonar.ce.task.projectanalysis.locations.flow.Location;
 
 public class TaintVulnerabilityRaised {
 
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TextRange.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TextRange.java
deleted file mode 100644 (file)
index ce7ebc5..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.pushevent;
-
-public class TextRange {
-  private int startLine;
-  private int startLineOffset;
-  private int endLine;
-  private int endLineOffset;
-  private String hash;
-
-  public TextRange() {
-    // nothing to do
-  }
-
-  public int getStartLine() {
-    return startLine;
-  }
-
-  public void setStartLine(int startLine) {
-    this.startLine = startLine;
-  }
-
-  public int getStartLineOffset() {
-    return startLineOffset;
-  }
-
-  public void setStartLineOffset(int startLineOffset) {
-    this.startLineOffset = startLineOffset;
-  }
-
-  public int getEndLine() {
-    return endLine;
-  }
-
-  public void setEndLine(int endLine) {
-    this.endLine = endLine;
-  }
-
-  public int getEndLineOffset() {
-    return endLineOffset;
-  }
-
-  public void setEndLineOffset(int endLineOffset) {
-    this.endLineOffset = endLineOffset;
-  }
-
-  public String getHash() {
-    return hash;
-  }
-
-  public void setHash(String hash) {
-    this.hash = hash;
-  }
-}
index 00a7e960a58ddc3e856c9da0ce8d674d86dd9697..7ce05f22a7c1cabff15f2445da80d3eb3f00faf2 100644 (file)
@@ -95,6 +95,16 @@ public class DumbRule implements Rule {
     return pluginKey;
   }
 
+  @Override
+  public String getDefaultRuleDescription() {
+    return null;
+  }
+
+  @Override
+  public String getSeverity() {
+    return null;
+  }
+
   @Override
   public boolean isExternal() {
     return isExternal;
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/locations/flow/FlowGeneratorTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/locations/flow/FlowGeneratorTest.java
new file mode 100644 (file)
index 0000000..ddc2795
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.locations.flow;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang.math.RandomUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
+import org.sonar.db.protobuf.DbCommons;
+import org.sonar.db.protobuf.DbIssues;
+
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class FlowGeneratorTest {
+
+  private static final String COMPONENT_NAME = "test_comp";
+
+  @Mock
+  private TreeRootHolder treeRootHolder;
+
+  @InjectMocks
+  private FlowGenerator flowGenerator;
+
+  @Test
+  public void convertFlows_withNullDbLocations_returnsEmptyList() {
+    assertThat(flowGenerator.convertFlows(COMPONENT_NAME, null)).isEmpty();
+  }
+
+  @Test
+  public void convertFlows_withEmptyDbLocations_returnsEmptyList() {
+    DbIssues.Locations issueLocations = DbIssues.Locations.newBuilder().build();
+    assertThat(flowGenerator.convertFlows(COMPONENT_NAME, issueLocations)).isEmpty();
+  }
+
+  @Test
+  public void convertFlows_withSingleDbLocations_returnsCorrectFlow() {
+    DbIssues.Location location = createDbLocation("comp_id_1");
+    DbIssues.Locations issueLocations = DbIssues.Locations.newBuilder()
+      .addFlow(createFlow(location))
+      .build();
+
+    List<Flow> flows = flowGenerator.convertFlows(COMPONENT_NAME, issueLocations);
+
+    assertThat(flows).hasSize(1);
+    Flow singleFlow = flows.iterator().next();
+
+    assertThat(singleFlow.getLocations()).hasSize(1);
+    Location singleLocation = singleFlow.getLocations().iterator().next();
+
+    assertLocationMatches(singleLocation, location);
+  }
+
+  @Test
+  public void convertFlows_with2FlowsSingleDbLocations_returnsCorrectFlow() {
+    DbIssues.Location location1 = createDbLocation("comp_id_1");
+    DbIssues.Location location2 = createDbLocation("comp_id_2");
+    DbIssues.Locations issueLocations = DbIssues.Locations.newBuilder()
+      .addFlow(createFlow(location1))
+      .addFlow(createFlow(location2))
+      .build();
+
+    List<Flow> flows = flowGenerator.convertFlows(COMPONENT_NAME, issueLocations);
+
+    assertThat(flows).hasSize(2).extracting(f -> f.getLocations().size()).containsExactly(1, 1);
+    Map<String, DbIssues.Location> toDbLocation = Map.of(
+      "file_path_" + location1.getComponentId(), location1,
+      "file_path_" + location2.getComponentId(), location2);
+    flows.stream()
+      .map(actualFlow -> actualFlow.getLocations().iterator().next())
+      .forEach(l -> assertLocationMatches(l, toDbLocation.get(l.getFilePath())));
+  }
+
+  @Test
+  public void convertFlows_with2DbLocations_returns() {
+    DbIssues.Location location1 = createDbLocation("comp_id_1");
+    DbIssues.Location location2 = createDbLocation("comp_id_2");
+    DbIssues.Locations issueLocations = DbIssues.Locations.newBuilder()
+      .addFlow(createFlow(location1, location2))
+      .build();
+
+    List<Flow> flows = flowGenerator.convertFlows(COMPONENT_NAME, issueLocations);
+
+    assertThat(flows).hasSize(1);
+    Flow singleFlow = flows.iterator().next();
+
+    assertThat(singleFlow.getLocations()).hasSize(2);
+    Map<String, Location> pathToLocations = singleFlow.getLocations()
+      .stream()
+      .collect(toMap(Location::getFilePath, identity()));
+
+    assertLocationMatches(pathToLocations.get("file_path_comp_id_1"), location1);
+    assertLocationMatches(pathToLocations.get("file_path_comp_id_2"), location2);
+
+  }
+
+  private DbIssues.Location createDbLocation(String componentId) {
+    org.sonar.db.protobuf.DbCommons.TextRange textRange = org.sonar.db.protobuf.DbCommons.TextRange.newBuilder()
+      .setStartLine(RandomUtils.nextInt())
+      .setEndLine(RandomUtils.nextInt())
+      .setStartOffset(RandomUtils.nextInt())
+      .setEndOffset(RandomUtils.nextInt())
+      .build();
+
+    Component component = mock(Component.class);
+    when(component.getName()).thenReturn("file_path_" + componentId);
+    when(treeRootHolder.getComponentByUuid(componentId)).thenReturn(component);
+    return DbIssues.Location.newBuilder()
+      .setComponentId(componentId)
+      .setChecksum("hash" + randomAlphanumeric(10))
+      .setTextRange(textRange)
+      .setMsg("msg" + randomAlphanumeric(15))
+      .build();
+  }
+
+  private static DbIssues.Flow createFlow(DbIssues.Location ... locations) {
+    return DbIssues.Flow.newBuilder()
+      .addAllLocation(List.of(locations))
+      .build();
+  }
+
+  private static void assertLocationMatches(Location actualLocation, DbIssues.Location sourceLocation) {
+    assertThat(actualLocation.getMessage()).isEqualTo(sourceLocation.getMsg());
+    DbCommons.TextRange textRange = sourceLocation.getTextRange();
+    assertThat(actualLocation.getTextRange().getStartLine()).isEqualTo(textRange.getStartLine());
+    assertThat(actualLocation.getTextRange().getEndLine()).isEqualTo(textRange.getEndLine());
+    assertThat(actualLocation.getTextRange().getStartLineOffset()).isEqualTo(textRange.getStartOffset());
+    assertThat(actualLocation.getTextRange().getEndLineOffset()).isEqualTo(textRange.getEndOffset());
+    assertThat(actualLocation.getTextRange().getHash()).isEqualTo(sourceLocation.getChecksum());
+  }
+
+}
index 83aa9bf18366042664789b1bba06de6e285367a0..65548362402adba4d119ffbb31436a2c49a8c513 100644 (file)
@@ -33,6 +33,7 @@ import org.sonar.ce.task.projectanalysis.analysis.TestBranch;
 import org.sonar.ce.task.projectanalysis.component.Component.Type;
 import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolderRule;
 import org.sonar.ce.task.projectanalysis.component.ReportComponent;
+import org.sonar.ce.task.projectanalysis.locations.flow.FlowGenerator;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.FieldDiffs;
 import org.sonar.db.protobuf.DbCommons;
@@ -54,7 +55,8 @@ public class PushEventFactoryTest {
   public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule()
     .setBranch(new TestBranch("develop"));
 
-  private final PushEventFactory underTest = new PushEventFactory(treeRootHolder, analysisMetadataHolder, taintChecker);
+  private final FlowGenerator flowGenerator = new FlowGenerator(treeRootHolder);
+  private final PushEventFactory underTest = new PushEventFactory(treeRootHolder, analysisMetadataHolder, taintChecker, flowGenerator);
 
   @Before
   public void setUp() {