]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-12721 add sinceLeakPeriod param to api/hotspots/search
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Fri, 13 Dec 2019 15:54:32 +0000 (16:54 +0100)
committerSonarTech <sonartech@sonarsource.com>
Mon, 13 Jan 2020 19:46:29 +0000 (20:46 +0100)
server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/SearchActionTest.java

index 76a43a6231dd4dabfbd3a5799912641a83507e4c..3d3c6ec477b89ba4f307300dd64c26368dc3335b 100644 (file)
@@ -71,6 +71,7 @@ import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
 import static org.sonar.api.server.ws.WebService.Param.PAGE;
 import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
 import static org.sonar.api.utils.DateUtils.formatDateTime;
+import static org.sonar.api.utils.DateUtils.longToDate;
 import static org.sonar.api.utils.Paging.forPageIndex;
 import static org.sonar.core.util.stream.MoreCollectors.toList;
 import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
@@ -88,6 +89,7 @@ public class SearchAction implements HotspotsWsAction {
   private static final String PARAM_HOTSPOTS = "hotspots";
   private static final String PARAM_BRANCH = "branch";
   private static final String PARAM_PULL_REQUEST = "pullRequest";
+  private static final String PARAM_SINCE_LEAK_PERIOD = "sinceLeakPeriod";
 
   private final DbClient dbClient;
   private final UserSession userSession;
@@ -140,6 +142,10 @@ public class SearchAction implements HotspotsWsAction {
         PARAM_PROJECT_KEY, STATUS_REVIEWED))
       .setPossibleValues(RESOLUTION_FIXED, RESOLUTION_SAFE)
       .setRequired(false);
+    action.createParam(PARAM_SINCE_LEAK_PERIOD)
+      .setDescription("If '%s' is provided, only Security Hotspots created since the leak period are returned.")
+      .setBooleanPossibleValues()
+      .setDefaultValue("false");
     // FIXME add response example and test it
     // action.setResponseExample()
   }
@@ -165,7 +171,8 @@ public class SearchAction implements HotspotsWsAction {
       request.mandatoryParamAsInt(PAGE), request.mandatoryParamAsInt(PAGE_SIZE),
       request.param(PARAM_PROJECT_KEY), request.param(PARAM_BRANCH), request.param(PARAM_PULL_REQUEST),
       hotspotKeys,
-      request.param(PARAM_STATUS), request.param(PARAM_RESOLUTION));
+      request.param(PARAM_STATUS), request.param(PARAM_RESOLUTION),
+      request.paramAsBoolean(PARAM_SINCE_LEAK_PERIOD));
   }
 
   private static void validateParameters(WsRequest wsRequest) {
@@ -222,7 +229,7 @@ public class SearchAction implements HotspotsWsAction {
   }
 
   private SearchResponseData searchHotspots(WsRequest wsRequest, DbSession dbSession, Optional<ComponentDto> project, Set<String> hotspotKeys) {
-    SearchResponse result = doIndexSearch(wsRequest, project, hotspotKeys);
+    SearchResponse result = doIndexSearch(wsRequest, dbSession, project, hotspotKeys);
     List<String> issueKeys = Arrays.stream(result.getHits().getHits())
       .map(SearchHit::getId)
       .collect(toList(result.getHits().getHits().length));
@@ -245,7 +252,7 @@ public class SearchAction implements HotspotsWsAction {
       .collect(Collectors.toList());
   }
 
-  private SearchResponse doIndexSearch(WsRequest wsRequest, Optional<ComponentDto> project, Set<String> hotspotKeys) {
+  private SearchResponse doIndexSearch(WsRequest wsRequest, DbSession dbSession, Optional<ComponentDto> project, Set<String> hotspotKeys) {
     IssueQuery.Builder builder = IssueQuery.builder()
       .types(singleton(RuleType.SECURITY_HOTSPOT.name()))
       .sort(IssueQuery.SORT_HOTSPOTS)
@@ -260,6 +267,11 @@ public class SearchAction implements HotspotsWsAction {
         builder.branchUuid(p.projectUuid());
         builder.mainBranch(false);
       }
+      if (wsRequest.isSinceLeakPeriod()) {
+        dbClient.snapshotDao().selectLastAnalysisByComponentUuid(dbSession, p.uuid())
+          .map(s -> longToDate(s.getPeriodDate()))
+          .ifPresent(d -> builder.createdAfter(d, false));
+      }
     });
     if (!hotspotKeys.isEmpty()) {
       builder.issueKeys(hotspotKeys);
@@ -367,11 +379,12 @@ public class SearchAction implements HotspotsWsAction {
     private final Set<String> hotspotKeys;
     private final String status;
     private final String resolution;
+    private final boolean sinceLeakPeriod;
 
     private WsRequest(int page, int index,
       @Nullable String projectKey, @Nullable String branch, @Nullable String pullRequest,
       Set<String> hotspotKeys,
-      @Nullable String status, @Nullable String resolution) {
+      @Nullable String status, @Nullable String resolution, @Nullable Boolean sinceLeakPeriod) {
       this.page = page;
       this.index = index;
       this.projectKey = projectKey;
@@ -380,6 +393,7 @@ public class SearchAction implements HotspotsWsAction {
       this.hotspotKeys = hotspotKeys;
       this.status = status;
       this.resolution = resolution;
+      this.sinceLeakPeriod = sinceLeakPeriod == null ? false : sinceLeakPeriod;
     }
 
     int getPage() {
@@ -413,6 +427,10 @@ public class SearchAction implements HotspotsWsAction {
     Optional<String> getResolution() {
       return ofNullable(resolution);
     }
+
+    boolean isSinceLeakPeriod() {
+      return sinceLeakPeriod;
+    }
   }
 
   private static final class SearchResponseData {
index 30193c112633c31cf9e28f78d8641d8e10165cfd..b2d94ec5d124e86eb4b06b7cbda816208bfe3f71 100644 (file)
@@ -71,7 +71,6 @@ import org.sonarqube.ws.Hotspots.Component;
 import org.sonarqube.ws.Hotspots.SearchWsResponse;
 
 import static java.util.Collections.singleton;
-import static java.util.stream.Collectors.joining;
 import static java.util.stream.Collectors.toList;
 import static java.util.stream.Collectors.toSet;
 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
@@ -91,6 +90,7 @@ import static org.sonar.db.issue.IssueTesting.newIssue;
 @RunWith(DataProviderRunner.class)
 public class SearchActionTest {
   private static final Random RANDOM = new Random();
+  private static final int ONE_MINUTE = 60_000;
 
   @Rule
   public DbTester dbTester = DbTester.create(System2.INSTANCE);
@@ -1068,6 +1068,61 @@ public class SearchActionTest {
       .containsExactlyInAnyOrder(selectedHotspots.stream().map(IssueDto::getKey).toArray(String[]::new));
   }
 
+  @Test
+  public void returns_hotspots_on_the_leak_period_when_sinceLeakPeriod_is_true() {
+    ComponentDto project = dbTester.components().insertPublicProject();
+    userSessionRule.registerComponents(project);
+    indexPermissions();
+    ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
+    long periodDate = 800_996_999_332L;
+    dbTester.components().insertSnapshot(project, t -> t.setPeriodDate(periodDate).setLast(false));
+    dbTester.components().insertSnapshot(project, t -> t.setPeriodDate(periodDate - 1_500).setLast(true));
+    RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
+    List<IssueDto> hotspotsInLeakPeriod = IntStream.range(0, 1 + RANDOM.nextInt(20))
+      .mapToObj(i -> {
+        long issueCreationDate = periodDate + ONE_MINUTE + (RANDOM.nextInt(300) * ONE_MINUTE);
+        return newIssue(rule, project, file).setType(SECURITY_HOTSPOT).setLine(i).setIssueCreationTime(issueCreationDate);
+      })
+      .map(i -> dbTester.issues().insertIssue(i))
+      .collect(toList());
+    // included because
+    List<IssueDto> atLeakPeriod = IntStream.range(0, 1 + RANDOM.nextInt(20))
+      .mapToObj(i -> newIssue(rule, project, file).setType(SECURITY_HOTSPOT).setLine(i).setIssueCreationTime(periodDate))
+      .map(i -> dbTester.issues().insertIssue(i))
+      .collect(toList());
+    List<IssueDto> hotspotsBefore = IntStream.range(0, 1 + RANDOM.nextInt(20))
+      .mapToObj(i -> {
+        long issueCreationDate = periodDate - ONE_MINUTE - (RANDOM.nextInt(300) * ONE_MINUTE);
+        return newIssue(rule, project, file).setType(SECURITY_HOTSPOT).setLine(i).setIssueCreationTime(issueCreationDate);
+      })
+      .map(i -> dbTester.issues().insertIssue(i))
+      .collect(toList());
+    indexIssues();
+
+    SearchWsResponse responseAll = newRequest(project)
+      .executeProtobuf(SearchWsResponse.class);
+    assertThat(responseAll.getHotspotsList())
+      .extracting(SearchWsResponse.Hotspot::getKey)
+      .containsExactlyInAnyOrder(Stream.of(
+        hotspotsInLeakPeriod.stream(),
+        atLeakPeriod.stream(),
+        hotspotsBefore.stream())
+        .flatMap(t -> t)
+        .map(IssueDto::getKey)
+        .toArray(String[]::new));
+
+    SearchWsResponse responseOnLeak = newRequest(project,
+      t -> t.setParam("sinceLeakPeriod", "true"))
+        .executeProtobuf(SearchWsResponse.class);
+    assertThat(responseOnLeak.getHotspotsList())
+      .extracting(SearchWsResponse.Hotspot::getKey)
+      .containsExactlyInAnyOrder(Stream.concat(
+        hotspotsInLeakPeriod.stream(),
+        atLeakPeriod.stream())
+        .map(IssueDto::getKey)
+        .toArray(String[]::new));
+  }
+
   private IssueDto insertHotspot(ComponentDto project, ComponentDto file, RuleDefinitionDto rule) {
     return dbTester.issues().insert(rule, project, file, t -> t.setType(SECURITY_HOTSPOT));
   }
@@ -1076,7 +1131,16 @@ public class SearchActionTest {
     return newRequest(project, null, null);
   }
 
+  private TestRequest newRequest(ComponentDto project, Consumer<TestRequest> consumer) {
+    return newRequest(project, null, null, consumer);
+  }
+
   private TestRequest newRequest(ComponentDto project, @Nullable String status, @Nullable String resolution) {
+    return newRequest(project, status, resolution, t -> {
+    });
+  }
+
+  private TestRequest newRequest(ComponentDto project, @Nullable String status, @Nullable String resolution, Consumer<TestRequest> consumer) {
     TestRequest res = actionTester.newRequest()
       .setParam("projectKey", project.getKey());
     String branch = project.getBranch();
@@ -1093,12 +1157,13 @@ public class SearchActionTest {
     if (resolution != null) {
       res.setParam("resolution", resolution);
     }
+    consumer.accept(res);
     return res;
   }
 
   private TestRequest newRequest(Collection<String> hotspotKeys) {
     return actionTester.newRequest()
-      .setParam("hotspots", hotspotKeys.stream().collect(joining(",")));
+      .setParam("hotspots", String.join(",", hotspotKeys));
   }
 
   private void indexPermissions() {