]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6834 filter components by name in api/ce/activity
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Fri, 2 Oct 2015 07:46:36 +0000 (09:46 +0200)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Fri, 2 Oct 2015 08:24:16 +0000 (10:24 +0200)
server/sonar-server/src/main/java/org/sonar/server/computation/ws/ActivityWsAction.java
server/sonar-server/src/main/java/org/sonar/server/ws/WsUtils.java
server/sonar-server/src/test/java/org/sonar/server/computation/ws/ActivityWsActionTest.java
server/sonar-server/src/test/java/org/sonar/server/ws/WsUtilsTest.java
sonar-db/src/main/java/org/sonar/db/DatabaseUtils.java
sonar-db/src/main/java/org/sonar/db/ce/CeActivityDao.java
sonar-db/src/main/java/org/sonar/db/ce/CeActivityQuery.java
sonar-db/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml
sonar-db/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java
sonar-db/src/test/java/org/sonar/db/ce/CeActivityQueryTest.java [new file with mode: 0644]

index 4d495f58acbb87f58fb621855cfbf33250cf5538..4d0dd94271e4ef99064a39b7e12e24dc8a0a6492 100644 (file)
  */
 package org.sonar.server.computation.ws;
 
+import com.google.common.collect.Lists;
 import java.util.Date;
 import java.util.List;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.apache.ibatis.session.RowBounds;
+import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
@@ -36,15 +38,23 @@ import org.sonar.db.DbSession;
 import org.sonar.db.ce.CeActivityDto;
 import org.sonar.db.ce.CeActivityQuery;
 import org.sonar.db.ce.CeTaskTypes;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentDtoFunctions;
+import org.sonar.db.component.ComponentQuery;
+import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.user.UserSession;
 import org.sonar.server.ws.WsUtils;
 import org.sonarqube.ws.Common;
 import org.sonarqube.ws.WsCe;
 
+import static java.lang.String.format;
+import static org.sonar.server.ws.WsUtils.checkRequest;
+
 public class ActivityWsAction implements CeWsAction {
 
   private static final String PARAM_COMPONENT_UUID = "componentId";
+  private static final String PARAM_COMPONENT_QUERY = "componentQuery";
   private static final String PARAM_TYPE = "type";
   private static final String PARAM_STATUS = "status";
   private static final String PARAM_ONLY_CURRENTS = "onlyCurrents";
@@ -70,6 +80,9 @@ public class ActivityWsAction implements CeWsAction {
     action.createParam(PARAM_COMPONENT_UUID)
       .setDescription("Optional id of the component (project) to filter on")
       .setExampleValue(Uuids.UUID_EXAMPLE_03);
+    action.createParam(PARAM_COMPONENT_QUERY)
+      .setDescription(format("Optional search by component name or key. Must not be set together with %s", PARAM_COMPONENT_UUID))
+      .setExampleValue("Apache");
     action.createParam(PARAM_STATUS)
       .setDescription("Optional filter on task status")
       .setPossibleValues(CeActivityDto.Status.values());
@@ -93,7 +106,9 @@ public class ActivityWsAction implements CeWsAction {
   public void handle(Request wsRequest, Response wsResponse) throws Exception {
     DbSession dbSession = dbClient.openSession(false);
     try {
-      CeActivityQuery query = readQuery(wsRequest);
+      CeActivityQuery query = buildQuery(dbSession, wsRequest);
+      checkPermissions(query);
+
       RowBounds rowBounds = readMyBatisRowBounds(wsRequest);
       List<CeActivityDto> dtos = dbClient.ceActivityDao().selectByQuery(dbSession, query, rowBounds);
       int total = dbClient.ceActivityDao().countByQuery(dbSession, query);
@@ -111,7 +126,24 @@ public class ActivityWsAction implements CeWsAction {
     }
   }
 
-  private CeActivityQuery readQuery(Request wsRequest) {
+  private void checkPermissions(CeActivityQuery query) {
+    List<String> componentUuids = query.getComponentUuids();
+    if (componentUuids != null && componentUuids.size() == 1) {
+      if (!userSession.hasGlobalPermission(GlobalPermissions.SYSTEM_ADMIN) &&
+        !userSession.hasComponentUuidPermission(UserRole.ADMIN, componentUuids.get(0))) {
+        throw new ForbiddenException("Requires administration permission");
+      }
+    } else {
+      userSession.checkGlobalPermission(UserRole.ADMIN);
+    }
+  }
+
+  private CeActivityQuery buildQuery(DbSession dbSession, Request wsRequest) {
+    String componentUuid = wsRequest.param(PARAM_COMPONENT_UUID);
+    String componentQuery = wsRequest.param(PARAM_COMPONENT_QUERY);
+    checkRequest(componentUuid == null || componentQuery == null,
+      format("Only one of following parameters is accepted: %s or %s", PARAM_COMPONENT_UUID, PARAM_COMPONENT_QUERY));
+
     CeActivityQuery query = new CeActivityQuery();
     query.setType(wsRequest.param(PARAM_TYPE));
     query.setOnlyCurrents(wsRequest.mandatoryParamAsBoolean(PARAM_ONLY_CURRENTS));
@@ -123,17 +155,25 @@ public class ActivityWsAction implements CeWsAction {
       query.setStatus(CeActivityDto.Status.valueOf(status));
     }
 
+    loadComponentUuids(dbSession, wsRequest, query);
+    return query;
+  }
+
+  private void loadComponentUuids(DbSession dbSession, Request wsRequest, CeActivityQuery query) {
     String componentUuid = wsRequest.param(PARAM_COMPONENT_UUID);
-    if (componentUuid == null) {
-      userSession.checkGlobalPermission(UserRole.ADMIN);
-    } else {
-      if (userSession.hasGlobalPermission(GlobalPermissions.SYSTEM_ADMIN) || userSession.hasComponentUuidPermission(UserRole.ADMIN, componentUuid)) {
-        query.setComponentUuid(componentUuid);
-      } else {
-        throw new ForbiddenException("Requires administration permission");
-      }
+    String componentQuery = wsRequest.param(PARAM_COMPONENT_QUERY);
+    if (componentUuid != null && componentQuery != null) {
+      throw new BadRequestException(format("Only one of parameters must be set: %s or %s", PARAM_COMPONENT_UUID, PARAM_COMPONENT_QUERY));
+    }
+
+    if (componentUuid != null) {
+      query.setComponentUuid(componentUuid);
+    }
+    if (componentQuery != null) {
+      ComponentQuery componentDtoQuery = new ComponentQuery(dbClient.getDatabase(), componentQuery, Qualifiers.PROJECT, Qualifiers.VIEW);
+      List<ComponentDto> componentDtos = dbClient.componentDao().selectByQuery(dbSession, componentDtoQuery, 0, CeActivityQuery.MAX_COMPONENT_UUIDS);
+      query.setComponentUuids(Lists.transform(componentDtos, ComponentDtoFunctions.toUuid()));
     }
-    return query;
   }
 
   private static RowBounds readMyBatisRowBounds(Request wsRequest) {
index 25a8400f61c131ebc48666dd4ed2026a8c1bfca4..2e010e6e85b5fc4e5999acd4a590db2a8e3cd3eb 100644 (file)
@@ -32,6 +32,8 @@ import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.plugins.MimeTypes;
 
+import static java.lang.String.format;
+
 public class WsUtils {
 
   private WsUtils() {
@@ -58,9 +60,9 @@ public class WsUtils {
   /**
    * @throws BadRequestException
    */
-  public static void checkRequest(boolean expression, String message) {
+  public static void checkRequest(boolean expression, String message, Object... messageArguments) {
     if (!expression) {
-      throw new BadRequestException(message);
+      throw new BadRequestException(format(message, messageArguments));
     }
   }
 
index ac3374ee6dbb0166ef022739af9b88ff2b8f8203..7e856fc5ed2de3d2670ed9515a76404ef7cacc62 100644 (file)
@@ -21,11 +21,13 @@ package org.sonar.server.computation.ws;
 
 import com.google.common.base.Optional;
 import java.io.File;
+import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.System2;
 import org.sonar.api.web.UserRole;
@@ -34,8 +36,10 @@ import org.sonar.db.DbTester;
 import org.sonar.db.ce.CeActivityDto;
 import org.sonar.db.ce.CeQueueDto;
 import org.sonar.db.ce.CeTaskTypes;
+import org.sonar.db.component.ComponentDbTester;
 import org.sonar.server.computation.log.CeLogging;
 import org.sonar.server.computation.log.LogFileRef;
+import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.plugins.MimeTypes;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.TestResponse;
@@ -47,15 +51,21 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import static org.sonar.db.component.ComponentTesting.newProjectDto;
 
 public class ActivityWsActionTest {
 
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
   @Rule
   public UserSessionRule userSession = UserSessionRule.standalone();
 
   @Rule
   public DbTester dbTester = DbTester.create(System2.INSTANCE);
 
+  ComponentDbTester componentDb = new ComponentDbTester(dbTester);
+
   CeLogging ceLogging = mock(CeLogging.class);
   TaskFormatter formatter = new TaskFormatter(dbTester.getDbClient(), ceLogging);
   ActivityWsAction underTest = new ActivityWsAction(userSession, dbTester.getDbClient(), formatter);
@@ -93,7 +103,7 @@ public class ActivityWsActionTest {
   }
 
   @Test
-   public void filter_by_status() {
+  public void filter_by_status() {
     userSession.setGlobalPermissions(UserRole.ADMIN);
     insert("T1", "PROJECT_1", CeActivityDto.Status.SUCCESS);
     insert("T2", "PROJECT_2", CeActivityDto.Status.FAILED);
@@ -157,7 +167,8 @@ public class ActivityWsActionTest {
   }
 
   @Test
-  public void get_project_activity() {
+  public void project_administrator_can_access_his_project_activity() {
+    // no need to be a system admin
     userSession.addComponentUuidPermission(UserRole.ADMIN, "PROJECT_1", "PROJECT_1");
     insert("T1", "PROJECT_1", CeActivityDto.Status.SUCCESS);
     insert("T2", "PROJECT_2", CeActivityDto.Status.FAILED);
@@ -167,7 +178,6 @@ public class ActivityWsActionTest {
       .setMediaType(MimeTypes.PROTOBUF)
       .execute();
 
-    // verify the protobuf response
     WsCe.ActivityResponse activityResponse = Protobuf.read(wsResponse.getInputStream(), WsCe.ActivityResponse.PARSER);
     assertThat(activityResponse.getTasksCount()).isEqualTo(1);
     assertThat(activityResponse.getTasks(0).getId()).isEqualTo("T1");
@@ -175,6 +185,39 @@ public class ActivityWsActionTest {
     assertThat(activityResponse.getTasks(0).getComponentId()).isEqualTo("PROJECT_1");
   }
 
+  @Test
+  public void search_activity_by_component_name() throws IOException {
+    componentDb.insertProjectAndSnapshot(dbTester.getSession(), newProjectDto().setName("apache struts").setUuid("P1"));
+    componentDb.insertProjectAndSnapshot(dbTester.getSession(), newProjectDto().setName("apache zookeeper").setUuid("P2"));
+    componentDb.insertProjectAndSnapshot(dbTester.getSession(), newProjectDto().setName("eclipse").setUuid("P3"));
+    dbTester.commit();
+    componentDb.indexProjects();
+    userSession.setGlobalPermissions(UserRole.ADMIN);
+    insert("T1", "P1", CeActivityDto.Status.SUCCESS);
+    insert("T2", "P2", CeActivityDto.Status.SUCCESS);
+    insert("T3", "P3", CeActivityDto.Status.SUCCESS);
+
+    TestResponse wsResponse = tester.newRequest()
+      .setParam("componentQuery", "apac")
+      .setMediaType(MimeTypes.PROTOBUF)
+      .execute();
+
+    WsCe.ActivityResponse activityResponse = WsCe.ActivityResponse.parseFrom(wsResponse.getInputStream());
+    assertThat(activityResponse.getTasksList()).extracting("id").containsOnly("T1", "T2");
+  }
+
+  @Test
+  public void fail_if_both_filters_on_component_id_and_name() {
+    expectedException.expect(BadRequestException.class);
+    expectedException.expectMessage("Only one of following parameters is accepted: componentId or componentQuery");
+
+    tester.newRequest()
+      .setParam("componentId", "ID1")
+      .setParam("componentQuery", "apache")
+      .setMediaType(MimeTypes.PROTOBUF)
+      .execute();
+  }
+
   private CeActivityDto insert(String taskUuid, String componentUuid, CeActivityDto.Status status) {
     CeQueueDto queueDto = new CeQueueDto();
     queueDto.setTaskType(CeTaskTypes.REPORT);
@@ -184,7 +227,7 @@ public class ActivityWsActionTest {
     activityDto.setStatus(status);
     activityDto.setExecutionTimeMs(500L);
     dbTester.getDbClient().ceActivityDao().insert(dbTester.getSession(), activityDto);
-    dbTester.getSession().commit();
+    dbTester.commit();
     return activityDto;
   }
 }
index a0227286f393567d042a0ea20cd5f9df2210eeb7..7693a82f8e00721c884ff48feb240290911dc7e6 100644 (file)
  */
 package org.sonar.server.ws;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.plugins.MimeTypes;
 import org.sonarqube.ws.Issues;
 
@@ -27,6 +30,9 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 public class WsUtilsTest {
 
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
   @Test
   public void write_json_by_default() throws Exception {
     TestRequest request = new TestRequest();
@@ -54,4 +60,19 @@ public class WsUtilsTest {
     assertThat(response.stream().mediaType()).isEqualTo(MimeTypes.PROTOBUF);
     assertThat(Issues.Issue.parseFrom(response.getFlushedOutput()).getKey()).isEqualTo("I1");
   }
+
+  @Test
+  public void checkRequest_ok() {
+    WsUtils.checkRequest(true, "Missing param: %s", "foo");
+    // do not fail
+  }
+
+  @Test
+  public void checkRequest_ko() {
+    expectedException.expect(BadRequestException.class);
+    expectedException.expectMessage("Missing param: foo");
+
+    WsUtils.checkRequest(false, "Missing param: %s", "foo");
+  }
+
 }
index df9fbc98739e2ff253f81d8bf7521eb8bfc03288..84360b3ff8d703dd182cac22d865d676b0ee4e23 100644 (file)
@@ -40,7 +40,7 @@ import static com.google.common.collect.Lists.newArrayList;
 
 public class DatabaseUtils {
 
-  private static final int PARTITION_SIZE_FOR_ORACLE = 1000;
+  public static final int PARTITION_SIZE_FOR_ORACLE = 1000;
 
   public static void closeQuietly(@Nullable Connection connection) {
     if (connection != null) {
index f54d80f566d8e65c7e75d94a5af46b620490eb84..62574c900e48f28a85a37942068315b99a6b569a 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.db.ce;
 
 import com.google.common.base.Optional;
+import java.util.Collections;
 import java.util.List;
 import org.apache.ibatis.session.RowBounds;
 import org.sonar.api.utils.System2;
@@ -64,10 +65,16 @@ public class CeActivityDao implements Dao {
    * Ordered by id desc -> newest to oldest
    */
   public List<CeActivityDto> selectByQuery(DbSession dbSession, CeActivityQuery query, RowBounds rowBounds) {
+    if (query.isShortCircuitedByComponentUuids()) {
+      return Collections.emptyList();
+    }
     return mapper(dbSession).selectByQuery(query, rowBounds);
   }
 
   public int countByQuery(DbSession dbSession, CeActivityQuery query) {
+    if (query.isShortCircuitedByComponentUuids()) {
+      return 0;
+    }
     return mapper(dbSession).countByQuery(query);
   }
 
index 355439d9ae237af6904e43a1caeb63af0c1845ec..942df9e40d38b053d8b5d42e0b763fe03d9e08eb 100644 (file)
  */
 package org.sonar.db.ce;
 
+import java.util.List;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
+import org.sonar.db.DatabaseUtils;
+
+import static java.util.Collections.singletonList;
 
 public class CeActivityQuery {
 
+  public static final int MAX_COMPONENT_UUIDS = DatabaseUtils.PARTITION_SIZE_FOR_ORACLE;
+
   private boolean onlyCurrents = false;
-  private String componentUuid;
+  private List<String> componentUuids;
   private CeActivityDto.Status status;
   private String type;
   private Long minSubmittedAt;
   private Long maxExecutedAt;
 
   @CheckForNull
-  public String getComponentUuid() {
-    return componentUuid;
+  public List<String> getComponentUuids() {
+    return componentUuids;
+  }
+
+  public CeActivityQuery setComponentUuids(@Nullable List<String> l) {
+    this.componentUuids = l;
+    return this;
+  }
+
+  public boolean isShortCircuitedByComponentUuids() {
+    return componentUuids != null && (componentUuids.isEmpty() || componentUuids.size() > MAX_COMPONENT_UUIDS);
   }
 
-  public CeActivityQuery setComponentUuid(@Nullable String componentUuid) {
-    this.componentUuid = componentUuid;
+  public CeActivityQuery setComponentUuid(@Nullable String s) {
+    if (s == null) {
+      this.componentUuids = null;
+    } else {
+      this.componentUuids = singletonList(s);
+    }
     return this;
   }
 
index 3c10c891e62a76b26a0513a6205702f028461865..25d79d14080e6e511e117c7653a8f0bcd8fe0948 100644 (file)
   <select id="selectByQuery" parameterType="map" resultType="org.sonar.db.ce.CeActivityDto">
     select
     <include refid="columns"/>
-    from ce_activity ca
-    <where>
-      <if test="query.onlyCurrents">
-        and ca.is_last=${_true}
-      </if>
-      <if test="query.componentUuid != null">
-        and ca.component_uuid=#{query.componentUuid}
-      </if>
-      <if test="query.status != null">
-        and ca.status=#{query.status}
-      </if>
-      <if test="query.type != null">
-        and ca.task_type=#{query.type}
-      </if>
-      <if test="query.minSubmittedAt != null">
-        and ca.submitted_at &gt;= #{query.minSubmittedAt}
-      </if>
-      <if test="query.maxExecutedAt != null">
-        and ca.executed_at &lt;= #{query.maxExecutedAt}
-      </if>
-    </where>
+    <include refid="sqlSelectByQuery" />
     order by ca.id desc
   </select>
 
   <select id="countByQuery" parameterType="map" resultType="int">
     select count(ca.id)
+    <include refid="sqlSelectByQuery" />
+  </select>
+
+  <sql id="sqlSelectByQuery">
     from ce_activity ca
     <where>
       <if test="query.onlyCurrents">
         and ca.is_last=${_true}
       </if>
-      <if test="query.componentUuid != null">
-        and ca.component_uuid=#{query.componentUuid}
+      <if test="query.componentUuids != null and query.componentUuids.size()>0">
+        and ca.component_uuid in
+        <foreach collection="query.componentUuids" open="(" close=")" item="cUuid" separator=",">
+          #{cUuid}
+        </foreach>
       </if>
       <if test="query.status != null">
         and ca.status=#{query.status}
@@ -91,7 +78,7 @@
         and ca.executed_at &lt;= #{query.maxExecutedAt}
       </if>
     </where>
-  </select>
+  </sql>
 
   <select id="selectOlderThan" parameterType="long" resultType="org.sonar.db.ce.CeActivityDto">
     select <include refid="columns"/>
index 6138e4de2eae3b9b81e6df22948b3e87bf9f40c9..564f413c29a9dfa5e24b1812096d3ab771b229d0 100644 (file)
@@ -21,6 +21,7 @@
 package org.sonar.db.ce;
 
 import com.google.common.base.Optional;
+import java.util.Collections;
 import java.util.List;
 import org.apache.ibatis.session.RowBounds;
 import org.junit.Rule;
@@ -145,6 +146,24 @@ public class CeActivityDaoTest {
     assertThat(underTest.countByQuery(db.getSession(), query)).isEqualTo(1);
   }
 
+  @Test
+  public void selectByQuery_no_results_if_shortcircuited_by_component_uuids() {
+    insert("TASK_1", REPORT, "PROJECT_1", CeActivityDto.Status.SUCCESS);
+
+    CeActivityQuery query = new CeActivityQuery();
+    query.setComponentUuids(Collections.<String>emptyList());
+    assertThat(underTest.selectByQuery(db.getSession(), query, new RowBounds(0, 10))).isEmpty();
+  }
+
+  @Test
+  public void countByQuery_no_results_if_shortcircuited_by_component_uuids() {
+    insert("TASK_1", REPORT, "PROJECT_1", CeActivityDto.Status.SUCCESS);
+
+    CeActivityQuery query = new CeActivityQuery();
+    query.setComponentUuids(Collections.<String>emptyList());
+    assertThat(underTest.countByQuery(db.getSession(), query)).isEqualTo(0);
+  }
+
   @Test
   public void select_and_count_by_date() {
     insertWithDates("UUID1", 1_450_000_000_000L, 1_470_000_000_000L);
diff --git a/sonar-db/src/test/java/org/sonar/db/ce/CeActivityQueryTest.java b/sonar-db/src/test/java/org/sonar/db/ce/CeActivityQueryTest.java
new file mode 100644 (file)
index 0000000..8acafd7
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.db.ce;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CeActivityQueryTest {
+
+  CeActivityQuery underTest = new CeActivityQuery();
+
+  @Test
+  public void no_filter_on_component_uuids_by_default() {
+    assertThat(underTest.getComponentUuids()).isNull();
+    assertThat(underTest.isShortCircuitedByComponentUuids()).isFalse();
+  }
+
+  @Test
+  public void filter_on_component_uuid() {
+    underTest.setComponentUuid("UUID1");
+    assertThat(underTest.getComponentUuids()).containsOnly("UUID1");
+    assertThat(underTest.isShortCircuitedByComponentUuids()).isFalse();
+  }
+
+  @Test
+  public void filter_on_multiple_component_uuids() {
+    underTest.setComponentUuids(asList("UUID1", "UUID2"));
+    assertThat(underTest.getComponentUuids()).containsOnly("UUID1", "UUID2");
+    assertThat(underTest.isShortCircuitedByComponentUuids()).isFalse();
+  }
+
+  /**
+   * componentUuid is not null but is set to empty
+   * --> no results
+   */
+  @Test
+  public void short_circuited_if_empty_component_uuid_filter() {
+    underTest.setComponentUuids(Collections.<String>emptyList());
+    assertThat(underTest.getComponentUuids()).isEmpty();
+    assertThat(underTest.isShortCircuitedByComponentUuids()).isTrue();
+  }
+
+  /**
+   * too many componentUuids for SQL request. Waiting for ES to improve this use-case
+   * --> no results
+   */
+  @Test
+  public void short_circuited_if_too_many_component_uuid_filters() {
+    List<String> uuids = new ArrayList<>();
+    for (int i = 0; i < CeActivityQuery.MAX_COMPONENT_UUIDS + 2; i++) {
+      uuids.add(String.valueOf(i));
+    }
+    underTest.setComponentUuids(uuids);
+    assertThat(underTest.getComponentUuids()).hasSize(CeActivityQuery.MAX_COMPONENT_UUIDS + 2);
+    assertThat(underTest.isShortCircuitedByComponentUuids()).isTrue();
+  }
+}