From d04a9c61db7a523b1ac360b73e022fda431da78f Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Fri, 13 Dec 2019 16:54:32 +0100 Subject: [PATCH] SONAR-12721 add sinceLeakPeriod param to api/hotspots/search --- .../sonar/server/hotspot/ws/SearchAction.java | 26 +++++-- .../server/hotspot/ws/SearchActionTest.java | 69 ++++++++++++++++++- 2 files changed, 89 insertions(+), 6 deletions(-) diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java index 76a43a6231d..3d3c6ec477b 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java @@ -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 project, Set hotspotKeys) { - SearchResponse result = doIndexSearch(wsRequest, project, hotspotKeys); + SearchResponse result = doIndexSearch(wsRequest, dbSession, project, hotspotKeys); List 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 project, Set hotspotKeys) { + private SearchResponse doIndexSearch(WsRequest wsRequest, DbSession dbSession, Optional project, Set 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 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 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 getResolution() { return ofNullable(resolution); } + + boolean isSinceLeakPeriod() { + return sinceLeakPeriod; + } } private static final class SearchResponseData { diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/SearchActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/SearchActionTest.java index 30193c11263..b2d94ec5d12 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/SearchActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/SearchActionTest.java @@ -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 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 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 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 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 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 hotspotKeys) { return actionTester.newRequest() - .setParam("hotspots", hotspotKeys.stream().collect(joining(","))); + .setParam("hotspots", String.join(",", hotspotKeys)); } private void indexPermissions() { -- 2.39.5