]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-12834 filter hotspots by current user
authorJacek <jacek.poreda@sonarsource.com>
Wed, 18 Dec 2019 07:48:30 +0000 (08:48 +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 3d3c6ec477b89ba4f307300dd64c26368dc3335b..8609c927d792499bbc5fdb7b3de05225247cb434 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.server.hotspot.ws;
 import com.google.common.collect.ImmutableSet;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -90,6 +91,7 @@ public class SearchAction implements HotspotsWsAction {
   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 static final String PARAM_ONLY_MINE = "onlyMine";
 
   private final DbClient dbClient;
   private final UserSession userSession;
@@ -146,6 +148,11 @@ public class SearchAction implements HotspotsWsAction {
       .setDescription("If '%s' is provided, only Security Hotspots created since the leak period are returned.")
       .setBooleanPossibleValues()
       .setDefaultValue("false");
+    action.createParam(PARAM_ONLY_MINE)
+      .setDescription("If 'projectKey' is provided, returns only Security Hotspots assigned to the current user")
+      .setBooleanPossibleValues()
+      .setRequired(false);
+
     // FIXME add response example and test it
     // action.setResponseExample()
   }
@@ -172,15 +179,15 @@ public class SearchAction implements HotspotsWsAction {
       request.param(PARAM_PROJECT_KEY), request.param(PARAM_BRANCH), request.param(PARAM_PULL_REQUEST),
       hotspotKeys,
       request.param(PARAM_STATUS), request.param(PARAM_RESOLUTION),
-      request.paramAsBoolean(PARAM_SINCE_LEAK_PERIOD));
+      request.paramAsBoolean(PARAM_SINCE_LEAK_PERIOD),
+      request.paramAsBoolean(PARAM_ONLY_MINE));
   }
 
-  private static void validateParameters(WsRequest wsRequest) {
+  private void validateParameters(WsRequest wsRequest) {
     Optional<String> projectKey = wsRequest.getProjectKey();
     Optional<String> branch = wsRequest.getBranch();
     Optional<String> pullRequest = wsRequest.getPullRequest();
     Set<String> hotspotKeys = wsRequest.getHotspotKeys();
-
     checkArgument(
       projectKey.isPresent() || !hotspotKeys.isEmpty(),
       "A value must be provided for either parameter '%s' or parameter '%s'", PARAM_PROJECT_KEY, PARAM_HOTSPOTS);
@@ -206,6 +213,13 @@ public class SearchAction implements HotspotsWsAction {
       r -> checkArgument(status.filter(STATUS_REVIEWED::equals).isPresent(),
         "Value '%s' of parameter '%s' can only be provided if value of parameter '%s' is '%s'",
         r, PARAM_RESOLUTION, PARAM_STATUS, STATUS_REVIEWED));
+
+    if (wsRequest.isOnlyMine()) {
+      checkArgument(userSession.isLoggedIn(),
+        "Parameter '%s' requires user to be logged in", PARAM_ONLY_MINE);
+      checkArgument(wsRequest.getProjectKey().isPresent(),
+        "Parameter '%s' can be used with parameter '%s' only", PARAM_ONLY_MINE, PARAM_PROJECT_KEY);
+    }
   }
 
   private Optional<ComponentDto> getAndValidateProject(DbSession dbSession, WsRequest wsRequest) {
@@ -276,6 +290,12 @@ public class SearchAction implements HotspotsWsAction {
     if (!hotspotKeys.isEmpty()) {
       builder.issueKeys(hotspotKeys);
     }
+
+    if (wsRequest.isOnlyMine()) {
+      userSession.checkLoggedIn();
+      builder.assigneeUuids(Collections.singletonList(userSession.getUuid()));
+    }
+
     wsRequest.getStatus().ifPresent(status -> builder.resolved(STATUS_REVIEWED.equals(status)));
     wsRequest.getResolution().ifPresent(resolution -> builder.resolutions(singleton(resolution)));
 
@@ -380,11 +400,13 @@ public class SearchAction implements HotspotsWsAction {
     private final String status;
     private final String resolution;
     private final boolean sinceLeakPeriod;
+    private final boolean onlyMine;
 
     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 Boolean sinceLeakPeriod) {
+      @Nullable String status, @Nullable String resolution, @Nullable Boolean sinceLeakPeriod,
+      @Nullable Boolean onlyMine) {
       this.page = page;
       this.index = index;
       this.projectKey = projectKey;
@@ -393,7 +415,8 @@ public class SearchAction implements HotspotsWsAction {
       this.hotspotKeys = hotspotKeys;
       this.status = status;
       this.resolution = resolution;
-      this.sinceLeakPeriod = sinceLeakPeriod == null ? false : sinceLeakPeriod;
+      this.sinceLeakPeriod = sinceLeakPeriod != null && sinceLeakPeriod;
+      this.onlyMine = onlyMine != null && onlyMine;
     }
 
     int getPage() {
@@ -431,6 +454,10 @@ public class SearchAction implements HotspotsWsAction {
     boolean isSinceLeakPeriod() {
       return sinceLeakPeriod;
     }
+
+    boolean isOnlyMine() {
+      return onlyMine;
+    }
   }
 
   private static final class SearchResponseData {
index b2d94ec5d124e86eb4b06b7cbda816208bfe3f71..2e9672211565803bb2c26bcc9ab2d4c523df9613 100644 (file)
@@ -71,6 +71,7 @@ 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;
@@ -111,8 +112,11 @@ public class SearchActionTest {
   private WsActionTester actionTester = new WsActionTester(underTest);
 
   @Test
-  public void ws_is_internal() {
+  public void verify_ws_def() {
     assertThat(actionTester.getDef().isInternal()).isTrue();
+    assertThat(actionTester.getDef().param("onlyMine").isRequired()).isFalse();
+    assertThat(actionTester.getDef().param("onlyMine").possibleValues())
+      .containsExactlyInAnyOrder("yes", "no", "true", "false");
   }
 
   @Test
@@ -506,6 +510,85 @@ public class SearchActionTest {
       .containsExactlyInAnyOrder(Arrays.stream(hotspotPR).map(IssueDto::getKey).toArray(String[]::new));
   }
 
+  @Test
+  @UseDataProvider("onlyMineParamValues")
+  public void returns_hotspots_of_specified_project_assigned_to_current_user_if_only_mine_is_set(String onlyMineParameter, boolean shouldFilter) {
+    ComponentDto project1 = dbTester.components().insertPublicProject();
+    String assigneeUuid = this.userSessionRule.logIn().registerComponents(project1).getUuid();
+
+    indexPermissions();
+    ComponentDto file1 = dbTester.components().insertComponent(newFileDto(project1));
+    IssueDto[] assigneeHotspots = IntStream.range(0, 1 + RANDOM.nextInt(10))
+      .mapToObj(i -> {
+        RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
+        insertHotspot(project1, file1, rule, randomAlphabetic(5));
+        return insertHotspot(project1, file1, rule, assigneeUuid);
+      })
+      .toArray(IssueDto[]::new);
+
+    indexIssues();
+
+    SearchWsResponse allHotspots = newRequest(project1)
+      .executeProtobuf(SearchWsResponse.class);
+
+    SearchWsResponse userHotspots = newRequest(project1, r -> r.setParam("onlyMine", onlyMineParameter))
+      .executeProtobuf(SearchWsResponse.class);
+
+    assertThat(allHotspots.getHotspotsList())
+      .extracting(SearchWsResponse.Hotspot::getKey)
+      .contains(Arrays.stream(assigneeHotspots).map(IssueDto::getKey).toArray(String[]::new))
+      .hasSizeGreaterThan(assigneeHotspots.length);
+
+    if (shouldFilter) {
+      assertThat(userHotspots.getHotspotsList())
+        .extracting(SearchWsResponse.Hotspot::getKey)
+        .containsOnly(Arrays.stream(assigneeHotspots).map(IssueDto::getKey).toArray(String[]::new));
+    } else {
+      assertThat(userHotspots.getHotspotsList())
+        .extracting(SearchWsResponse.Hotspot::getKey)
+        .containsOnly(allHotspots.getHotspotsList().stream().map(SearchWsResponse.Hotspot::getKey).toArray(String[]::new));
+    }
+  }
+
+  @DataProvider
+  public static Object[][] onlyMineParamValues() {
+    return new Object[][] {
+      {"yes", true},
+      {"true", true},
+      {"no", false},
+      {"false", false}
+    };
+  }
+
+  @Test
+  public void fail_if_hotspots_provided_with_onlyMine_param() {
+    ComponentDto project = dbTester.components().insertPrivateProject();
+
+    userSessionRule.registerComponents(project);
+    userSessionRule.logIn().addProjectPermission(UserRole.USER, project);
+
+    assertThatThrownBy(() -> actionTester.newRequest()
+      .setParam("hotspots", IntStream.range(2, 10).mapToObj(String::valueOf).collect(joining(",")))
+      .setParam("onlyMine", "true")
+      .execute())
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessage("Parameter 'onlyMine' can be used with parameter 'projectKey' only");
+  }
+
+  @Test
+  public void fail_if_user_not_authenticated_with_onlyMine_param() {
+    ComponentDto project = dbTester.components().insertPublicProject();
+
+    userSessionRule.anonymous();
+
+    assertThatThrownBy(() -> actionTester.newRequest()
+      .setParam("projectKey", project.getKey())
+      .setParam("onlyMine", "true")
+      .execute())
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessage("Parameter 'onlyMine' requires user to be logged in");
+  }
+
   @Test
   public void returns_hotpots_with_any_status_if_no_status_nor_resolution_parameter() {
     ComponentDto project = dbTester.components().insertPublicProject();
@@ -1127,6 +1210,10 @@ public class SearchActionTest {
     return dbTester.issues().insert(rule, project, file, t -> t.setType(SECURITY_HOTSPOT));
   }
 
+  private IssueDto insertHotspot(ComponentDto project, ComponentDto file, RuleDefinitionDto rule, String assigneeUuid) {
+    return dbTester.issues().insert(rule, project, file, t -> t.setType(SECURITY_HOTSPOT).setAssigneeUuid(assigneeUuid));
+  }
+
   private TestRequest newRequest(ComponentDto project) {
     return newRequest(project, null, null);
   }