]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6156 The search of projects on contextualized Issues page should support views 81/head
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 11 Feb 2015 14:03:51 +0000 (15:03 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 11 Feb 2015 16:27:14 +0000 (17:27 +0100)
33 files changed:
server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentDao.java
server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentIndexDao.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWs.java
server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java
server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java
server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java
server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentDaoTest.java
server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentIndexDaoTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java
server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java
server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchActionMediumTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/user/UserSessionTest.java
server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentIndexDaoTest/select_project_ids_from_query_and_view_or_sub_view_uuid.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_authorized_projects_from_view.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_authorized_projects_from_view.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_first_page.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_paged_result.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_projects_from_subview.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_projects_from_view.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/shared.xml [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java
sonar-core/src/main/java/org/sonar/core/component/db/ComponentIndexMapper.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/db/ComponentMapper.java
sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
sonar-core/src/main/java/org/sonar/core/user/AuthorizationDao.java
sonar-core/src/main/java/org/sonar/core/user/AuthorizationMapper.java [new file with mode: 0644]
sonar-core/src/main/resources/org/sonar/core/component/db/ComponentIndexMapper.xml [new file with mode: 0644]
sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml
sonar-core/src/main/resources/org/sonar/core/user/AuthorizationMapper.xml
sonar-core/src/test/java/org/sonar/core/user/AuthorizationDaoTest.java
sonar-core/src/test/resources/org/sonar/core/user/AuthorizationDaoTest/user_should_be_authorized.xml

index 85361463f9d541e36b35562498d713089198abf1..27cd270de816f51e356f25b4d16918bb5c97ad4d 100644 (file)
@@ -103,6 +103,15 @@ public class ComponentDao extends BaseDao<ComponentMapper, ComponentDto, String>
     return mapper(session).selectModuleFilesTree(rootComponentUuid, Scopes.FILE);
   }
 
+  public List<ComponentDto> getByIds(final DbSession session, Collection<Long> ids) {
+    return DaoUtils.executeLargeInputs(ids, new Function<List<Long>, List<ComponentDto>>() {
+      @Override
+      public List<ComponentDto> apply(List<Long> partition) {
+        return mapper(session).findByIds(partition);
+      }
+    });
+  }
+
   public List<ComponentDto> getByUuids(final DbSession session, Collection<String> uuids) {
     return DaoUtils.executeLargeInputs(uuids, new Function<List<String>, List<ComponentDto>>() {
       @Override
@@ -154,4 +163,5 @@ public class ComponentDao extends BaseDao<ComponentMapper, ComponentDto, String>
   public List<String> selectProjectsFromView(DbSession session, String viewUuid, String projectViewUuid) {
     return mapper(session).selectProjectsFromView("%." + viewUuid + ".%", projectViewUuid);
   }
+
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentIndexDao.java b/server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentIndexDao.java
new file mode 100644 (file)
index 0000000..7243b61
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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.server.component.db;
+
+import org.sonar.api.ServerComponent;
+import org.sonar.core.component.db.ComponentIndexMapper;
+import org.sonar.core.persistence.DaoComponent;
+import org.sonar.core.persistence.DbSession;
+
+import java.util.List;
+
+public class ComponentIndexDao implements ServerComponent, DaoComponent {
+
+  public List<Long> selectProjectIdsFromQueryAndViewOrSubViewUuid(DbSession session, String query, String viewOrSubViewUuid) {
+    return session.getMapper(ComponentIndexMapper.class).selectProjectIdsFromQueryAndViewOrSubViewUuid(query + "%", "%." + viewOrSubViewUuid + ".%");
+  }
+
+}
index edf407a500dc9fa6c30bd6c030d04b3de23d8096..94cadaa25f7e0cd68d49fc9cb6c23bcc74676397 100644 (file)
@@ -27,9 +27,11 @@ import org.sonar.api.server.ws.WebService;
 public class ComponentsWs implements WebService {
 
   private final ComponentAppAction appAction;
+  private final SearchAction searchAction;
 
-  public ComponentsWs(ComponentAppAction appAction) {
+  public ComponentsWs(ComponentAppAction appAction, SearchAction searchAction) {
     this.appAction = appAction;
+    this.searchAction = searchAction;
   }
 
   @Override
@@ -39,6 +41,7 @@ public class ComponentsWs implements WebService {
       .setDescription("Components management");
 
     appAction.define(controller);
+    searchAction.define(controller);
     defineSuggestionsAction(controller);
 
     controller.done();
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java
new file mode 100644 (file)
index 0000000..50599c6
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * 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.server.component.ws;
+
+import com.google.common.collect.Sets;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.MyBatis;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.es.SearchOptions;
+import org.sonar.server.user.UserSession;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.collect.Sets.newLinkedHashSet;
+import static org.sonar.api.server.ws.WebService.Param.PAGE;
+import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
+
+public class SearchAction implements RequestHandler {
+
+  private static final short MINIMUM_SEARCH_CHARACTERS = 2;
+
+  private static final String PARAM_COMPONENT_UUID = "componentUuid";
+  private static final String PARAM_QUERY = "q";
+
+  private final DbClient dbClient;
+
+  public SearchAction(DbClient dbClient) {
+    this.dbClient = dbClient;
+  }
+
+  void define(WebService.NewController controller) {
+    WebService.NewAction action = controller.createAction("search")
+      .setDescription("Search for components. Currently limited to projects in a view or a sub-view")
+      .setSince("5.1")
+      .setInternal(true)
+      .setHandler(this);
+
+    action
+      .createParam(PARAM_COMPONENT_UUID)
+      .setRequired(true)
+      .setDescription("View or sub view UUID")
+      .setExampleValue("d6d9e1e5-5e13-44fa-ab82-3ec29efa8935");
+
+    action
+      .createParam(PARAM_QUERY)
+      .setRequired(true)
+      .setDescription("UTF-8 search query")
+      .setExampleValue("sonar");
+
+    action.addPagingParams(10);
+  }
+
+  @Override
+  public void handle(Request request, Response response) {
+    String query = request.mandatoryParam(PARAM_QUERY);
+    if (query.length() < MINIMUM_SEARCH_CHARACTERS) {
+      throw new IllegalArgumentException(String.format("Minimum search is %s characters", MINIMUM_SEARCH_CHARACTERS));
+    }
+    String viewOrSubUuid = request.mandatoryParam(PARAM_COMPONENT_UUID);
+
+    JsonWriter json = response.newJsonWriter();
+    json.beginObject();
+
+    DbSession session = dbClient.openSession(false);
+    try {
+      ComponentDto componentDto = dbClient.componentDao().getByUuid(session, viewOrSubUuid);
+      UserSession.get().checkProjectUuidPermission(UserRole.USER, componentDto.projectUuid());
+
+      Set<Long> projectIds = newLinkedHashSet(dbClient.componentIndexDao().selectProjectIdsFromQueryAndViewOrSubViewUuid(session, query, componentDto.uuid()));
+      Collection<Long> authorizedProjectIds = dbClient.authorizationDao().keepAuthorizedProjectIds(session, projectIds, UserSession.get().userId(), UserRole.USER);
+
+      SearchOptions options = new SearchOptions();
+      options.setPage(request.mandatoryParamAsInt(PAGE), request.mandatoryParamAsInt(PAGE_SIZE));
+      Set<Long> pagedProjectIds = pagedProjectIds(authorizedProjectIds, options);
+
+      List<ComponentDto> projects = dbClient.componentDao().getByIds(session, pagedProjectIds);
+
+      options.writeJson(json, authorizedProjectIds.size());
+      json.name("components").beginArray();
+      for (ComponentDto project : projects) {
+        json.beginObject();
+        json.prop("uuid", project.uuid());
+        json.prop("name", project.name());
+        json.endObject();
+      }
+      json.endArray();
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
+
+    json.endObject();
+    json.close();
+  }
+
+  private Set<Long> pagedProjectIds(Collection<Long> projectIds, SearchOptions options) {
+    Set<Long> results = Sets.newLinkedHashSet();
+    int index = 0;
+    for (Long projectId : projectIds) {
+      if (index >= options.getOffset() && results.size() < options.getLimit()) {
+        results.add(projectId);
+      } else if (results.size() >= options.getLimit()) {
+        break;
+      }
+      index++;
+    }
+    return results;
+  }
+}
index ea6ac8f2980afffc2415e1436e6b3ee66a19806d..3c34603fe53a1af8472f8a105eb1c62af6882e86 100644 (file)
@@ -36,6 +36,7 @@ import org.sonar.core.user.AuthorDao;
 import org.sonar.core.user.AuthorizationDao;
 import org.sonar.server.activity.db.ActivityDao;
 import org.sonar.server.component.db.ComponentDao;
+import org.sonar.server.component.db.ComponentIndexDao;
 import org.sonar.server.component.db.SnapshotDao;
 import org.sonar.server.computation.db.AnalysisReportDao;
 import org.sonar.server.dashboard.db.DashboardDao;
@@ -87,6 +88,7 @@ public class DbClient implements ServerComponent {
   private final WidgetPropertyDao widgetPropertyDao;
   private final FileSourceDao fileSourceDao;
   private final AuthorDao authorDao;
+  private final ComponentIndexDao componentIndexDao;
 
   public DbClient(Database db, MyBatis myBatis, DaoComponent... daoComponents) {
     this.db = db;
@@ -120,6 +122,7 @@ public class DbClient implements ServerComponent {
     widgetPropertyDao = getDao(map, WidgetPropertyDao.class);
     fileSourceDao = getDao(map, FileSourceDao.class);
     authorDao = getDao(map, AuthorDao.class);
+    componentIndexDao = getDao(map, ComponentIndexDao.class);
   }
 
   public Database database() {
@@ -226,6 +229,10 @@ public class DbClient implements ServerComponent {
     return authorDao;
   }
 
+  public ComponentIndexDao componentIndexDao() {
+    return componentIndexDao;
+  }
+
   private <K> K getDao(Map<Class, DaoComponent> map, Class<K> clazz) {
     return (K) map.get(clazz);
   }
index 9de6fee0478d759ef990f4ee7358de2445c133e6..f6f1357628b1e0e87460440dedd986811af5f231 100644 (file)
@@ -85,6 +85,7 @@ import org.sonar.server.component.ComponentService;
 import org.sonar.server.component.DefaultComponentFinder;
 import org.sonar.server.component.DefaultRubyComponentService;
 import org.sonar.server.component.db.ComponentDao;
+import org.sonar.server.component.db.ComponentIndexDao;
 import org.sonar.server.component.db.SnapshotDao;
 import org.sonar.server.component.ws.*;
 import org.sonar.server.computation.AnalysisReportQueue;
@@ -243,9 +244,10 @@ class ServerComponents {
       MetricDao.class,
       ComponentDao.class,
       SnapshotDao.class,
-      DbClient.class,
       MeasureFilterDao.class,
       AnalysisReportDao.class,
+      ComponentIndexDao.class,
+      DbClient.class,
 
       // Elasticsearch
       SearchClient.class,
@@ -502,11 +504,11 @@ class ServerComponents {
     pico.addSingleton(DefaultComponentFinder.class);
     pico.addSingleton(DefaultRubyComponentService.class);
     pico.addSingleton(ComponentService.class);
-    pico.addSingleton(ComponentDao.class);
     pico.addSingleton(ResourcesWs.class);
     pico.addSingleton(ComponentsWs.class);
     pico.addSingleton(ProjectsWs.class);
     pico.addSingleton(ComponentAppAction.class);
+    pico.addSingleton(org.sonar.server.component.ws.SearchAction.class);
     pico.addSingleton(EventsWs.class);
     pico.addSingleton(ComponentCleanerService.class);
 
index ba6c33c9dc3b10d730a3322a16ef1a4ab6120b60..06d92231bd462f9b93e284fd036e559542a79311 100644 (file)
@@ -179,6 +179,16 @@ public class UserSession {
     return this;
   }
 
+  /**
+   * Ensures that user implies the specified project permission. If not a {@link org.sonar.server.exceptions.ForbiddenException} is thrown.
+   */
+  public UserSession checkProjectUuidPermission(String projectPermission, String projectUuid) {
+    if (!hasProjectPermissionByUuid(projectPermission, projectUuid)) {
+      throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
+    }
+    return this;
+  }
+
   /**
    * Does the user have the given project permission ?
    */
@@ -222,12 +232,11 @@ public class UserSession {
   }
 
   /**
-   * Does the user have the given project permission for a component ?
+   * Does the user have the given project permission for a component key ?
    */
   public boolean hasComponentPermission(String permission, String componentKey) {
     String projectKey = projectKeyByComponentKey.get(componentKey);
     if (projectKey == null) {
-      // TODO use method using UUID
       ResourceDto project = resourceDao().getRootProjectByComponentKey(componentKey);
       if (project == null) {
         return false;
index f7db3d0126b1570afdece6f490d19ae5d178ae45..e5dcb669c8af0f9fd39f489ba2b29b82fb21ee4b 100644 (file)
@@ -24,6 +24,7 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.experimental.categories.Category;
 import org.sonar.api.platform.Server;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.permission.GlobalPermissions;
@@ -36,9 +37,11 @@ import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.issue.db.IssueDao;
 import org.sonar.server.user.MockUserSession;
 import org.sonar.server.ws.WsTester;
+import org.sonar.test.DbTests;
 
 import static org.mockito.Mockito.mock;
 
+@Category(DbTests.class)
 public class IssuesActionTest {
 
   private final static String PROJECT_KEY = "struts";
index 53b7462d1dfe2f63551100b223a807e5d057f372..fde5e2162b94c2b6d28b84235829444bb1dbfcb2 100644 (file)
@@ -163,6 +163,27 @@ public class ComponentDaoTest extends AbstractDaoTestCase {
     assertThat(dao.getByKeys(session, "unknown")).isEmpty();
   }
 
+  @Test
+  public void get_by_ids() {
+    setupData("shared");
+
+    List<ComponentDto> results = dao.getByIds(session, newArrayList(4L));
+    assertThat(results).hasSize(1);
+
+    ComponentDto result = results.get(0);
+    assertThat(result).isNotNull();
+    assertThat(result.key()).isEqualTo("org.struts:struts-core:src/org/struts/RequestContext.java");
+    assertThat(result.path()).isEqualTo("src/org/struts/RequestContext.java");
+    assertThat(result.name()).isEqualTo("RequestContext.java");
+    assertThat(result.longName()).isEqualTo("org.struts.RequestContext");
+    assertThat(result.qualifier()).isEqualTo("FIL");
+    assertThat(result.scope()).isEqualTo("FIL");
+    assertThat(result.language()).isEqualTo("java");
+    assertThat(result.parentProjectId()).isEqualTo(2);
+
+    assertThat(dao.getByIds(session, newArrayList(555L))).isEmpty();
+  }
+
   @Test
   public void get_by_uuids() {
     setupData("shared");
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentIndexDaoTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentIndexDaoTest.java
new file mode 100644 (file)
index 0000000..76d9248
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.server.component.db;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.test.DbTests;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@Category(DbTests.class)
+public class ComponentIndexDaoTest {
+
+  @Rule
+  public DbTester dbTester = new DbTester();
+
+  DbSession session;
+
+  ComponentIndexDao dao;
+
+  @Before
+  public void createDao() throws Exception {
+    session = dbTester.myBatis().openSession(false);
+    dao = new ComponentIndexDao();
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    session.close();
+  }
+
+  @Test
+  public void select_project_ids_from_query_and_view_or_sub_view_uuid() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "select_project_ids_from_query_and_view_or_sub_view_uuid.xml");
+    String viewUuid = "EFGH";
+
+    assertThat(dao.selectProjectIdsFromQueryAndViewOrSubViewUuid(session, "project", viewUuid)).containsOnly(1L, 2L);
+    assertThat(dao.selectProjectIdsFromQueryAndViewOrSubViewUuid(session, "one", viewUuid)).containsOnly(1L);
+    assertThat(dao.selectProjectIdsFromQueryAndViewOrSubViewUuid(session, "two", viewUuid)).containsOnly(2L);
+    assertThat(dao.selectProjectIdsFromQueryAndViewOrSubViewUuid(session, "unknown", viewUuid)).isEmpty();
+  }
+}
index bf54f209aaaf235c91499034c4d2363419e1b7f2..19496fb97ada7e4b6ac0ea29db532202c5146661 100644 (file)
@@ -101,7 +101,7 @@ public class ComponentAppActionTest {
 
     when(measureDao.findByComponentKeyAndMetricKeys(anyString(), anyListOf(String.class), eq(session))).thenReturn(measures);
 
-    tester = new WsTester(new ComponentsWs(new ComponentAppAction(dbClient, durations, i18n)));
+    tester = new WsTester(new ComponentsWs(new ComponentAppAction(dbClient, durations, i18n), mock(SearchAction.class)));
   }
 
   @Test
index 0e88b962beca25c86fc92ca263a3e2ab9703e109..6d216685dc7a082b11ef34f8839be63853823471 100644 (file)
@@ -38,7 +38,7 @@ public class ComponentsWsTest {
 
   @Before
   public void setUp() throws Exception {
-    WsTester tester = new WsTester(new ComponentsWs(new ComponentAppAction(mock(DbClient.class), mock(Durations.class), mock(I18n.class))));
+    WsTester tester = new WsTester(new ComponentsWs(new ComponentAppAction(mock(DbClient.class), mock(Durations.class), mock(I18n.class)), new SearchAction(mock(DbClient.class))));
     controller = tester.controller("api/components");
   }
 
@@ -47,7 +47,7 @@ public class ComponentsWsTest {
     assertThat(controller).isNotNull();
     assertThat(controller.description()).isNotEmpty();
     assertThat(controller.since()).isEqualTo("4.2");
-    assertThat(controller.actions()).hasSize(2);
+    assertThat(controller.actions()).hasSize(3);
   }
 
   @Test
@@ -71,4 +71,14 @@ public class ComponentsWsTest {
     assertThat(action.params()).hasSize(2);
   }
 
+  @Test
+  public void define_search_action() throws Exception {
+    WebService.Action action = controller.action("search");
+    assertThat(action).isNotNull();
+    assertThat(action.isInternal()).isTrue();
+    assertThat(action.isPost()).isFalse();
+    assertThat(action.handler()).isNotNull();
+    assertThat(action.params()).hasSize(4);
+  }
+
 }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchActionMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchActionMediumTest.java
new file mode 100644 (file)
index 0000000..ab80ff1
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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.server.component.ws;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.core.user.AuthorizationDao;
+import org.sonar.server.component.db.ComponentDao;
+import org.sonar.server.component.db.ComponentIndexDao;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.ws.WsTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown;
+import static org.mockito.Mockito.mock;
+
+public class SearchActionMediumTest {
+
+  @Rule
+  public DbTester dbTester = new DbTester();
+
+  WsTester tester;
+
+  @Before
+  public void setUp() throws Exception {
+    DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(),
+      new ComponentDao(), new AuthorizationDao(dbTester.myBatis()), new ComponentIndexDao()
+      );
+    tester = new WsTester(new ComponentsWs(mock(ComponentAppAction.class), new SearchAction(dbClient)));
+  }
+
+  @Test
+  public void return_projects_from_view() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "shared.xml");
+    MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, "EFGH");
+
+    WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "EFGH").setParam("q", "st");
+    request.execute().assertJson(getClass(), "return_projects_from_view.json");
+  }
+
+  @Test
+  public void return_projects_from_subview() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "shared.xml");
+    MockUserSession.set().setLogin("john").addComponentUuidPermission(UserRole.USER, "EFGH", "FGHI");
+
+    WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "FGHI").setParam("q", "st");
+    request.execute().assertJson(getClass(), "return_projects_from_subview.json");
+  }
+
+  @Test
+  public void return_only_authorized_projects_from_view() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "return_only_authorized_projects_from_view.xml");
+    MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, "EFGH");
+
+    WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "EFGH").setParam("q", "st");
+    request.execute().assertJson(getClass(), "return_only_authorized_projects_from_view.json");
+  }
+
+  @Test
+  public void return_paged_result() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "shared.xml");
+    MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, "EFGH");
+
+    WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "EFGH").setParam("q", "st").setParam("p", "2").setParam("ps", "1");
+    request.execute().assertJson(getClass(), "return_paged_result.json", false);
+  }
+
+  @Test
+  public void return_only_first_page() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "shared.xml");
+    MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, "EFGH");
+
+    WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "EFGH").setParam("q", "st").setParam("p", "1").setParam("ps", "1");
+    request.execute().assertJson(getClass(), "return_only_first_page.json", false);
+  }
+
+  @Test
+  public void fail_when_search_param_is_too_short() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "shared.xml");
+    MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, "EFGH");
+
+    WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "EFGH").setParam("q", "s");
+
+    try {
+      request.execute();
+      failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessage("Minimum search is 2 characters");
+    }
+  }
+}
index e85708125faa392443b11d83b88a0e9b5eafa443..299b2631a7131425c09d2560d165a03071a28bda 100644 (file)
@@ -22,10 +22,12 @@ package org.sonar.server.user;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.api.web.UserRole;
+import org.sonar.core.component.ComponentDto;
 import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.core.resource.ResourceDao;
 import org.sonar.core.resource.ResourceDto;
 import org.sonar.core.user.AuthorizationDao;
+import org.sonar.server.component.ComponentTesting;
 import org.sonar.server.exceptions.ForbiddenException;
 
 import javax.annotation.Nullable;
@@ -148,6 +150,28 @@ public class UserSessionTest {
     session.checkProjectPermission(UserRole.USER, "com.foo:Bar");
   }
 
+  @Test
+  public void check_project_uuid_permission_ok() throws Exception {
+    AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
+    UserSession session = new SpyUserSession("marius", authorizationDao).setUserId(1);
+
+    ComponentDto project = ComponentTesting.newProjectDto();
+    when(authorizationDao.selectAuthorizedRootProjectsUuids(1, UserRole.USER)).thenReturn(newArrayList(project.uuid()));
+
+    session.checkProjectUuidPermission(UserRole.USER, project.uuid());
+  }
+
+  @Test(expected = ForbiddenException.class)
+  public void check_project_uuid_permission_ko() throws Exception {
+    AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
+    UserSession session = new SpyUserSession("marius", authorizationDao).setUserId(1);
+
+    ComponentDto project = ComponentTesting.newProjectDto();
+    when(authorizationDao.selectAuthorizedRootProjectsUuids(1, UserRole.USER)).thenReturn(newArrayList(project.uuid()));
+
+    session.checkProjectUuidPermission(UserRole.USER, "another project");
+  }
+
   @Test
   public void has_component_permission() throws Exception {
     AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
@@ -164,7 +188,7 @@ public class UserSessionTest {
   }
 
   @Test
-  public void check_component_permission_ok() throws Exception {
+  public void check_component_key_permission_ok() throws Exception {
     AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
     ResourceDao resourceDao = mock(ResourceDao.class);
     UserSession session = new SpyUserSession("marius", authorizationDao, resourceDao).setUserId(1);
@@ -176,7 +200,7 @@ public class UserSessionTest {
   }
 
   @Test(expected = ForbiddenException.class)
-  public void check_component_permission_ko() throws Exception {
+  public void check_component_key_permission_ko() throws Exception {
     AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
     ResourceDao resourceDao = mock(ResourceDao.class);
     UserSession session = new SpyUserSession("marius", authorizationDao, resourceDao).setUserId(1);
@@ -188,7 +212,7 @@ public class UserSessionTest {
   }
 
   @Test(expected = ForbiddenException.class)
-  public void check_component_permission_when_project_not_found() throws Exception {
+  public void check_component_key_permission_when_project_not_found() throws Exception {
     AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
     ResourceDao resourceDao = mock(ResourceDao.class);
     UserSession session = new SpyUserSession("marius", authorizationDao, resourceDao).setUserId(1);
@@ -198,6 +222,18 @@ public class UserSessionTest {
     session.checkComponentPermission(UserRole.USER, "com.foo:Bar:BarFile.xoo");
   }
 
+  @Test(expected = ForbiddenException.class)
+  public void check_component_dto_permission_ko() throws Exception {
+    AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
+    ResourceDao resourceDao = mock(ResourceDao.class);
+    UserSession session = new SpyUserSession("marius", authorizationDao, resourceDao).setUserId(1);
+
+    ComponentDto project = ComponentTesting.newProjectDto();
+    when(authorizationDao.selectAuthorizedRootProjectsKeys(1, UserRole.USER)).thenReturn(newArrayList(project.uuid()));
+
+    session.checkComponentPermission(UserRole.USER, "another");
+  }
+
   static class SpyUserSession extends UserSession {
     private AuthorizationDao authorizationDao;
     private ResourceDao resourceDao;
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentIndexDaoTest/select_project_ids_from_query_and_view_or_sub_view_uuid.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentIndexDaoTest/select_project_ids_from_query_and_view_or_sub_view_uuid.xml
new file mode 100644 (file)
index 0000000..aff6947
--- /dev/null
@@ -0,0 +1,32 @@
+<dataset>
+
+  <!-- Real projects -->
+  <projects id="1" uuid="ABCD" project_uuid="ABCD" module_uuid_path=".ABCD." copy_resource_id="[null]" name="Project One" qualifier="TRK" scope="PRJ"/>
+  <projects id="2" uuid="BCDE" project_uuid="BCDE" module_uuid_path=".BCDE." copy_resource_id="[null]" name="Project Two" qualifier="TRK" scope="PRJ"/>
+
+  <!-- Copy projects -->
+  <projects id="3" uuid="CDEF" project_uuid="EFGH" module_uuid_path=".EFGH." copy_resource_id="1" name="Copy Project One" qualifier="TRK" scope="FIL"/>
+  <projects id="4" uuid="DEFG" project_uuid="EFGH" module_uuid_path=".EFGH." copy_resource_id="2" name="Copy Project One" qualifier="TRK" scope="FIL"/>
+
+  <!-- View containing all projects -->
+  <projects id="5" uuid="EFGH" project_uuid="EFGH" module_uuid_path=".EFGH." copy_resource_id="[null]" name="All projects" qualifier="VW" scope="PRJ"/>
+
+  <resource_index id="1" kee="project one" resource_id="1" root_project_id="1" position="0" name_size="11" qualifier="TRK"/>
+  <resource_index id="2" kee="roject one" resource_id="1" root_project_id="1" position="1" name_size="11" qualifier="TRK"/>
+  <resource_index id="3" kee="oject one" resource_id="1" root_project_id="1" position="2" name_size="11" qualifier="TRK"/>
+  <resource_index id="4" kee="ject one" resource_id="1" root_project_id="1" position="3" name_size="11" qualifier="TRK"/>
+  <resource_index id="5" kee="ect one" resource_id="1" root_project_id="1" position="4" name_size="11" qualifier="TRK"/>
+  <resource_index id="6" kee="ct one" resource_id="1" root_project_id="1" position="5" name_size="11" qualifier="TRK"/>
+  <resource_index id="7" kee="t one" resource_id="1" root_project_id="1" position="6" name_size="11" qualifier="TRK"/>
+  <resource_index id="8" kee=" one" resource_id="1" root_project_id="1" position="7" name_size="11" qualifier="TRK"/>
+  <resource_index id="9" kee="one" resource_id="1" root_project_id="1" position="8" name_size="11" qualifier="TRK"/>
+  <resource_index id="10" kee="project two" resource_id="2" root_project_id="2" position="0" name_size="11" qualifier="TRK"/>
+  <resource_index id="11" kee="roject two" resource_id="2" root_project_id="2" position="1" name_size="11" qualifier="TRK"/>
+  <resource_index id="12" kee="oject two" resource_id="2" root_project_id="2" position="2" name_size="11" qualifier="TRK"/>
+  <resource_index id="13" kee="ject two" resource_id="2" root_project_id="2" position="3" name_size="11" qualifier="TRK"/>
+  <resource_index id="14" kee="ect two" resource_id="2" root_project_id="2" position="4" name_size="11" qualifier="TRK"/>
+  <resource_index id="15" kee="ct two" resource_id="2" root_project_id="2" position="5" name_size="11" qualifier="TRK"/>
+  <resource_index id="16" kee="t two" resource_id="2" root_project_id="2" position="6" name_size="11" qualifier="TRK"/>
+  <resource_index id="17" kee="two" resource_id="2" root_project_id="2" position="7" name_size="11" qualifier="TRK"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_authorized_projects_from_view.json b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_authorized_projects_from_view.json
new file mode 100644 (file)
index 0000000..9676629
--- /dev/null
@@ -0,0 +1,11 @@
+{
+  "total": 1,
+  "p": 1,
+  "ps": 10,
+  "components": [
+    {
+      "uuid": "JKLM",
+      "name": "Struts"
+    }
+  ]
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_authorized_projects_from_view.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_authorized_projects_from_view.xml
new file mode 100644 (file)
index 0000000..0b5c034
--- /dev/null
@@ -0,0 +1,30 @@
+<dataset>
+
+  <!-- Only struts is authorized for all user -->
+  <group_roles id="1" group_id="[null]" resource_id="100" role="user"/>
+
+  <!-- View -->
+  <projects id="11" uuid="EFGH" project_uuid="EFGH" module_uuid="[null]" module_uuid_path="." copy_resource_id="[null]" enabled="[true]"
+            kee="LANGUAGE_VIEW" scope="PRJ" qualifier="VW" name="By Language" path="[null]"/>
+
+  <projects id="112" uuid="GHIJ" project_uuid="EFGH" module_uuid="EFGH" module_uuid_path=".EFGH." copy_resource_id="101" enabled="[true]"
+            kee="VIEW2org.elasticsearch:elasticsearch" scope="FIL" qualifier="TRK" name="Elasticsearch" path="[null]"/>
+
+  <projects id="113" uuid="HIJK" project_uuid="EFGH" module_uuid="EFGH" module_uuid_path=".EFGH." copy_resource_id="100" enabled="[true]"
+            kee="VIEW2org.struts:struts" scope="FIL" qualifier="TRK" name="Struts" path="[null]"/>
+
+  <!-- Real projects -->
+
+  <projects id="100" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+            uuid="JKLM" project_uuid="JKLM" module_uuid="[null]" module_uuid_path="."
+            enabled="[true]" copy_resource_id="[null]" path="[null]"/>
+
+  <projects id="101" scope="PRJ" qualifier="TRK" kee="org.elasticsearch:elasticsearch" name="Elasticsearch"
+            uuid="KLMN" project_uuid="KLMN" module_uuid="[null]" module_uuid_path="."
+            enabled="[true]" copy_resource_id="[null]" path="[null]"/>
+
+  <resource_index id="1" kee="struts" resource_id="100" root_project_id="100" position="0" name_size="6" qualifier="TRK"/>
+  <resource_index id="2" kee="elasticsearch" resource_id="101" root_project_id="1" position="0" name_size="13" qualifier="TRK"/>
+  <resource_index id="3" kee="sticsearch" resource_id="101" root_project_id="1" position="1" name_size="13" qualifier="TRK"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_first_page.json b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_first_page.json
new file mode 100644 (file)
index 0000000..e913f5e
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "total": 2,
+  "p": 1,
+  "ps": 1
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_paged_result.json b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_paged_result.json
new file mode 100644 (file)
index 0000000..d871108
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "total": 2,
+  "p": 2,
+  "ps": 1
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_projects_from_subview.json b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_projects_from_subview.json
new file mode 100644 (file)
index 0000000..9676629
--- /dev/null
@@ -0,0 +1,11 @@
+{
+  "total": 1,
+  "p": 1,
+  "ps": 10,
+  "components": [
+    {
+      "uuid": "JKLM",
+      "name": "Struts"
+    }
+  ]
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_projects_from_view.json b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_projects_from_view.json
new file mode 100644 (file)
index 0000000..f7f99cc
--- /dev/null
@@ -0,0 +1,15 @@
+{
+  "total": 2,
+  "p": 1,
+  "ps": 10,
+  "components": [
+    {
+      "uuid": "JKLM",
+      "name": "Struts"
+    },
+    {
+      "uuid": "KLMN",
+      "name": "Elasticsearch"
+    }
+  ]
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/shared.xml
new file mode 100644 (file)
index 0000000..687ef3d
--- /dev/null
@@ -0,0 +1,35 @@
+<dataset>
+
+  <!-- Projects is authorized for all user -->
+  <group_roles id="1" group_id="[null]" resource_id="100" role="user"/>
+  <group_roles id="2" group_id="[null]" resource_id="101" role="user"/>
+
+  <!-- View with sub view -->
+  <projects id="11" uuid="EFGH" project_uuid="EFGH" module_uuid="[null]" module_uuid_path="." copy_resource_id="[null]" enabled="[true]"
+            kee="LANGUAGE_VIEW" scope="PRJ" qualifier="VW" name="By Language" path="[null]"/>
+
+  <projects id="112" uuid="GHIJ" project_uuid="EFGH" module_uuid="EFGH" module_uuid_path=".EFGH." copy_resource_id="101" enabled="[true]"
+            kee="VIEW2org.elasticsearch:elasticsearch" scope="FIL" qualifier="TRK" name="Elasticsearch" path="[null]"/>
+
+  <!-- Sub view -->
+  <projects id="13" uuid="FGHI" project_uuid="EFGH" module_uuid="EFGH" module_uuid_path=".EFGH." copy_resource_id="[null]" enabled="[true]"
+            kee="JAVA_PROJECTS" scope="PRJ" qualifier="SVW" name="Java projects" path="[null]"/>
+
+  <projects id="113" uuid="HIJK" project_uuid="EFGH" module_uuid="FGHI" module_uuid_path=".EFGH.FGHI." copy_resource_id="100" enabled="[true]"
+            kee="VIEW2org.struts:struts" scope="FIL" qualifier="TRK" name="Struts" path="[null]"/>
+
+  <!-- Real projects -->
+
+  <projects id="100" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+            uuid="JKLM" project_uuid="JKLM" module_uuid="[null]" module_uuid_path="."
+            enabled="[true]" copy_resource_id="[null]" path="[null]"/>
+
+  <projects id="101" scope="PRJ" qualifier="TRK" kee="org.elasticsearch:elasticsearch" name="Elasticsearch"
+            uuid="KLMN" project_uuid="KLMN" module_uuid="[null]" module_uuid_path="."
+            enabled="[true]" copy_resource_id="[null]" path="[null]"/>
+
+  <resource_index id="1" kee="struts" resource_id="100" root_project_id="100" position="0" name_size="6" qualifier="TRK"/>
+  <resource_index id="2" kee="elasticsearch" resource_id="101" root_project_id="1" position="0" name_size="13" qualifier="TRK"/>
+  <resource_index id="3" kee="sticsearch" resource_id="101" root_project_id="1" position="1" name_size="13" qualifier="TRK"/>
+
+</dataset>
index ca3a0b67a30f759b8a2467fbe029c0be1c6a0a4e..ecad07f8bbd0ada46efb2899a2dc4fb07b58513b 100644 (file)
@@ -105,7 +105,7 @@ public class ComponentDto extends Dto<String> implements Component {
   }
 
   /**
-   * Return the root project id. On a root project, return itself
+   * Return the root project uuid. On a root project, return itself
    */
   public String projectUuid() {
     return projectUuid;
diff --git a/sonar-core/src/main/java/org/sonar/core/component/db/ComponentIndexMapper.java b/sonar-core/src/main/java/org/sonar/core/component/db/ComponentIndexMapper.java
new file mode 100644 (file)
index 0000000..114b8da
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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.core.component.db;
+
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface ComponentIndexMapper {
+
+  List<Long> selectProjectIdsFromQueryAndViewOrSubViewUuid(@Param("query") String query, @Param("viewOrSubViewUuid") String viewOrSubViewUuid);
+}
index 269ede5a461bbb90fdec655d394333f1df21a6e5..1f057c62dc843c5ead3bf7a1e20b71f9b0b36ba2 100644 (file)
@@ -35,21 +35,12 @@ import java.util.List;
  */
 public interface ComponentMapper {
 
-  /**
-   * Warning, projectId is always null
-   */
   @CheckForNull
   ComponentDto selectByKey(String key);
 
-  /**
-   * Warning, projectId is always null
-   */
   @CheckForNull
   ComponentDto selectById(long id);
 
-  /**
-   * Warning, projectId is always null
-   */
   @CheckForNull
   ComponentDto selectByUuid(String uuid);
 
@@ -65,6 +56,8 @@ public interface ComponentMapper {
 
   List<ComponentDto> findByKeys(@Param("keys") Collection<String> keys);
 
+  List<ComponentDto> findByIds(@Param("ids") Collection<Long> ids);
+
   List<ComponentDto> findByUuids(@Param("uuids") Collection<String> uuids);
 
   List<String> selectExistingUuids(@Param("uuids") Collection<String> uuids);
index 0390d6fabdf52128f9c10c8f901cb553cdcddfe7..2b5fa87277f2f8463ec66114ff3138950307bdb8 100644 (file)
@@ -25,11 +25,7 @@ import com.google.common.io.Closeables;
 import org.apache.ibatis.builder.xml.XMLMapperBuilder;
 import org.apache.ibatis.logging.LogFactory;
 import org.apache.ibatis.mapping.Environment;
-import org.apache.ibatis.session.Configuration;
-import org.apache.ibatis.session.ExecutorType;
-import org.apache.ibatis.session.SqlSession;
-import org.apache.ibatis.session.SqlSessionFactory;
-import org.apache.ibatis.session.SqlSessionFactoryBuilder;
+import org.apache.ibatis.session.*;
 import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
 import org.apache.ibatis.type.JdbcType;
 import org.slf4j.LoggerFactory;
@@ -43,19 +39,13 @@ import org.sonar.core.component.ComponentDto;
 import org.sonar.core.component.FilePathWithHashDto;
 import org.sonar.core.component.SnapshotDto;
 import org.sonar.core.component.UuidWithProjectUuidDto;
+import org.sonar.core.component.db.ComponentIndexMapper;
 import org.sonar.core.component.db.ComponentMapper;
 import org.sonar.core.component.db.SnapshotMapper;
 import org.sonar.core.computation.db.AnalysisReportDto;
 import org.sonar.core.computation.db.AnalysisReportMapper;
 import org.sonar.core.config.Logback;
-import org.sonar.core.dashboard.ActiveDashboardDto;
-import org.sonar.core.dashboard.ActiveDashboardMapper;
-import org.sonar.core.dashboard.DashboardDto;
-import org.sonar.core.dashboard.DashboardMapper;
-import org.sonar.core.dashboard.WidgetDto;
-import org.sonar.core.dashboard.WidgetMapper;
-import org.sonar.core.dashboard.WidgetPropertyDto;
-import org.sonar.core.dashboard.WidgetPropertyMapper;
+import org.sonar.core.dashboard.*;
 import org.sonar.core.dependency.DependencyDto;
 import org.sonar.core.dependency.DependencyMapper;
 import org.sonar.core.dependency.ResourceSnapshotDto;
@@ -64,33 +54,11 @@ import org.sonar.core.duplication.DuplicationMapper;
 import org.sonar.core.duplication.DuplicationUnitDto;
 import org.sonar.core.graph.jdbc.GraphDto;
 import org.sonar.core.graph.jdbc.GraphDtoMapper;
-import org.sonar.core.issue.db.ActionPlanDto;
-import org.sonar.core.issue.db.ActionPlanMapper;
-import org.sonar.core.issue.db.ActionPlanStatsDto;
-import org.sonar.core.issue.db.ActionPlanStatsMapper;
-import org.sonar.core.issue.db.BatchIssueDto;
-import org.sonar.core.issue.db.IssueChangeDto;
-import org.sonar.core.issue.db.IssueChangeMapper;
-import org.sonar.core.issue.db.IssueDto;
-import org.sonar.core.issue.db.IssueFilterDto;
-import org.sonar.core.issue.db.IssueFilterFavouriteDto;
-import org.sonar.core.issue.db.IssueFilterFavouriteMapper;
-import org.sonar.core.issue.db.IssueFilterMapper;
-import org.sonar.core.issue.db.IssueMapper;
-import org.sonar.core.measure.db.MeasureDto;
-import org.sonar.core.measure.db.MeasureFilterDto;
-import org.sonar.core.measure.db.MeasureFilterMapper;
-import org.sonar.core.measure.db.MeasureMapper;
-import org.sonar.core.measure.db.MetricDto;
-import org.sonar.core.measure.db.MetricMapper;
+import org.sonar.core.issue.db.*;
+import org.sonar.core.measure.db.*;
 import org.sonar.core.notification.db.NotificationQueueDto;
 import org.sonar.core.notification.db.NotificationQueueMapper;
-import org.sonar.core.permission.GroupWithPermissionDto;
-import org.sonar.core.permission.PermissionTemplateDto;
-import org.sonar.core.permission.PermissionTemplateGroupDto;
-import org.sonar.core.permission.PermissionTemplateMapper;
-import org.sonar.core.permission.PermissionTemplateUserDto;
-import org.sonar.core.permission.UserWithPermissionDto;
+import org.sonar.core.permission.*;
 import org.sonar.core.persistence.dialect.Dialect;
 import org.sonar.core.persistence.migration.v44.Migration44Mapper;
 import org.sonar.core.persistence.migration.v45.Migration45Mapper;
@@ -100,22 +68,9 @@ import org.sonar.core.properties.PropertyDto;
 import org.sonar.core.purge.IdUuidPair;
 import org.sonar.core.purge.PurgeMapper;
 import org.sonar.core.purge.PurgeableSnapshotDto;
-import org.sonar.core.qualitygate.db.ProjectQgateAssociationDto;
-import org.sonar.core.qualitygate.db.ProjectQgateAssociationMapper;
-import org.sonar.core.qualitygate.db.QualityGateConditionDto;
-import org.sonar.core.qualitygate.db.QualityGateConditionMapper;
-import org.sonar.core.qualitygate.db.QualityGateDto;
-import org.sonar.core.qualitygate.db.QualityGateMapper;
-import org.sonar.core.qualityprofile.db.ActiveRuleDto;
-import org.sonar.core.qualityprofile.db.ActiveRuleMapper;
-import org.sonar.core.qualityprofile.db.ActiveRuleParamDto;
-import org.sonar.core.qualityprofile.db.QualityProfileDto;
-import org.sonar.core.qualityprofile.db.QualityProfileMapper;
-import org.sonar.core.resource.ResourceDto;
-import org.sonar.core.resource.ResourceIndexDto;
-import org.sonar.core.resource.ResourceIndexerMapper;
-import org.sonar.core.resource.ResourceKeyUpdaterMapper;
-import org.sonar.core.resource.ResourceMapper;
+import org.sonar.core.qualitygate.db.*;
+import org.sonar.core.qualityprofile.db.*;
+import org.sonar.core.resource.*;
 import org.sonar.core.rule.RuleDto;
 import org.sonar.core.rule.RuleMapper;
 import org.sonar.core.rule.RuleParamDto;
@@ -125,19 +80,7 @@ import org.sonar.core.technicaldebt.db.CharacteristicMapper;
 import org.sonar.core.technicaldebt.db.RequirementMigrationDto;
 import org.sonar.core.template.LoadedTemplateDto;
 import org.sonar.core.template.LoadedTemplateMapper;
-import org.sonar.core.user.AuthorDto;
-import org.sonar.core.user.AuthorMapper;
-import org.sonar.core.user.GroupDto;
-import org.sonar.core.user.GroupMapper;
-import org.sonar.core.user.GroupMembershipDto;
-import org.sonar.core.user.GroupMembershipMapper;
-import org.sonar.core.user.GroupRoleDto;
-import org.sonar.core.user.RoleMapper;
-import org.sonar.core.user.UserDto;
-import org.sonar.core.user.UserGroupDto;
-import org.sonar.core.user.UserGroupMapper;
-import org.sonar.core.user.UserMapper;
-import org.sonar.core.user.UserRoleDto;
+import org.sonar.core.user.*;
 
 import javax.annotation.Nullable;
 
@@ -262,7 +205,7 @@ public class MyBatis implements BatchComponent, ServerComponent {
       GroupMembershipMapper.class, QualityProfileMapper.class, ActiveRuleMapper.class,
       MeasureMapper.class, MetricMapper.class, QualityGateMapper.class, QualityGateConditionMapper.class, ComponentMapper.class, SnapshotMapper.class,
       ProjectQgateAssociationMapper.class,
-      AnalysisReportMapper.class,
+      AnalysisReportMapper.class, ComponentIndexMapper.class,
       Migration45Mapper.class, Migration50Mapper.class
     };
     loadMappers(conf, mappers);
index 530c9609f4de703f74664e357c72f394bef50e58..31b778a4a5cac0d43cb50ac23b1d06cf55057e9c 100644 (file)
  */
 package org.sonar.core.user;
 
-import com.google.common.collect.ImmutableMap;
+import com.google.common.base.Function;
 import com.google.common.collect.Sets;
 import org.apache.ibatis.session.SqlSession;
 import org.sonar.api.ServerComponent;
 import org.sonar.core.persistence.DaoComponent;
+import org.sonar.core.persistence.DaoUtils;
+import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.MyBatis;
 
 import javax.annotation.Nullable;
+
 import java.util.*;
 
 import static com.google.common.collect.Maps.newHashMap;
@@ -40,38 +43,48 @@ public class AuthorizationDao implements ServerComponent, DaoComponent {
     this.mybatis = mybatis;
   }
 
-  public Set<String> keepAuthorizedComponentKeys(Set<String> componentKeys, @Nullable Integer userId, String role) {
-    SqlSession session = mybatis.openSession(false);
-    try {
-      return keepAuthorizedComponentKeys(componentKeys, userId, role, session);
+  public Collection<Long> keepAuthorizedProjectIds(final DbSession session, final Collection<Long> componentIds, @Nullable final Integer userId, final String role) {
+    if (componentIds.isEmpty()) {
+      return Collections.emptySet();
+    }
+    return DaoUtils.executeLargeInputs(componentIds, new Function<List<Long>, List<Long>>() {
+      @Override
+      public List<Long> apply(List<Long> partition) {
+        if (userId == null) {
+          return session.getMapper(AuthorizationMapper.class).keepAuthorizedProjectIdsForAnonymous(role, componentIds);
+        } else {
+          return session.getMapper(AuthorizationMapper.class).keepAuthorizedProjectIdsForUser(userId, role, componentIds);
+        }
+      }
+    });
+  }
 
+  /**
+   * Used by the Views Plugin
+   */
+  public boolean isAuthorizedComponentKey(String componentKey, @Nullable Integer userId, String role) {
+    DbSession session = mybatis.openSession(false);
+    try {
+      return keepAuthorizedComponentKeys(session, Sets.newHashSet(componentKey), userId, role).size() == 1;
     } finally {
       MyBatis.closeQuietly(session);
     }
   }
 
-  public Set<String> keepAuthorizedComponentKeys(Set<String> componentKeys, @Nullable Integer userId, String role, SqlSession session) {
+  private Set<String> keepAuthorizedComponentKeys(final DbSession session, final Set<String> componentKeys, @Nullable final Integer userId, final String role) {
     if (componentKeys.isEmpty()) {
       return Collections.emptySet();
     }
-    String sql;
-    Map<String, Object> params;
-    if (userId == null) {
-      sql = "keepAuthorizedComponentKeysForAnonymous";
-      params = ImmutableMap.of("role", role, "componentKeys", componentKeys);
-    } else {
-      sql = "keepAuthorizedComponentKeysForUser";
-      params = ImmutableMap.of(USER_ID_PARAM, userId, "role", role, "componentKeys", componentKeys);
-    }
-
-    return Sets.newHashSet(session.<String>selectList(sql, params));
-  }
-
-  /**
-   * Used by the Views Plugin
-   */
-  public boolean isAuthorizedComponentKey(String componentKey, @Nullable Integer userId, String role) {
-    return keepAuthorizedComponentKeys(Sets.newHashSet(componentKey), userId, role).size() == 1;
+    return Sets.newHashSet(DaoUtils.executeLargeInputs(componentKeys, new Function<List<String>, List<String>>() {
+      @Override
+      public List<String> apply(List<String> partition) {
+        if (userId == null) {
+          return session.getMapper(AuthorizationMapper.class).keepAuthorizedComponentKeysForAnonymous(role, componentKeys);
+        } else {
+          return session.getMapper(AuthorizationMapper.class).keepAuthorizedComponentKeysForUser(userId, role, componentKeys);
+        }
+      }
+    }));
   }
 
   public Collection<String> selectAuthorizedRootProjectsKeys(@Nullable Integer userId, String role) {
diff --git a/sonar-core/src/main/java/org/sonar/core/user/AuthorizationMapper.java b/sonar-core/src/main/java/org/sonar/core/user/AuthorizationMapper.java
new file mode 100644 (file)
index 0000000..10d137d
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.core.user;
+
+import org.apache.ibatis.annotations.Param;
+
+import java.util.Collection;
+import java.util.List;
+
+public interface AuthorizationMapper {
+
+  List<Long> keepAuthorizedProjectIdsForAnonymous(@Param("role") String role, @Param("componentIds") Collection<Long> componentIds);
+
+  List<Long> keepAuthorizedProjectIdsForUser(@Param("userId") Integer userId, @Param("role") String role, @Param("componentIds") Collection<Long> componentIds);
+
+  List<String> keepAuthorizedComponentKeysForAnonymous(@Param("role") String role, @Param("componentKeys") Collection<String> componentKeys);
+
+  List<String> keepAuthorizedComponentKeysForUser(@Param("userId") Integer userId, @Param("role") String role, @Param("componentKeys") Collection<String> componentKeys);
+
+}
diff --git a/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentIndexMapper.xml b/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentIndexMapper.xml
new file mode 100644 (file)
index 0000000..6b80ef9
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.sonar.core.component.db.ComponentIndexMapper">
+
+  <select id="selectProjectIdsFromQueryAndViewOrSubViewUuid" parameterType="map" resultType="long">
+    SELECT r.resource_id FROM resource_index r
+    INNER JOIN projects copy ON copy.copy_resource_id = r.resource_id
+    <where>
+      AND copy.module_uuid_path LIKE #{viewOrSubViewUuid}
+      AND r.kee LIKE #{query}
+    </where>
+    ORDER BY r.name_size
+  </select>
+
+</mapper>
+
index 98426094cd2330f46220e4133017c97590b201fa..ee96cd81563a426ef6cebb7d016c701c4f528aff 100644 (file)
     </where>
   </select>
 
+  <select id="findByIds" parameterType="long" resultType="Component">
+    select
+    <include refid="componentColumns"/>
+    from projects p
+    <where>
+      p.enabled=${_true}
+      and p.id in
+      <foreach collection="ids" open="(" close=")" item="id" separator=",">
+        #{id}
+      </foreach>
+    </where>
+  </select>
+
   <select id="findByUuids" parameterType="String" resultType="Component">
     select <include refid="componentColumns"/>
     from projects p
index 12e1e32da23c830c8bfae32f91493dde2d49c8cc..1e757eaf58b7dc3de40928f5b5b2ed786b6716c0 100644 (file)
         <foreach collection="componentKeys" open="(" close=")" item="element" index="index" separator=" or " >p.kee=#{element}</foreach>
   </select>
 
+  <select id="keepAuthorizedProjectIdsForUser" parameterType="map" resultType="long">
+    SELECT gr.resource_id
+    FROM group_roles gr
+    WHERE
+    gr.role=#{role}
+    and (gr.group_id is null or gr.group_id in (select gu.group_id from groups_users gu where gu.user_id=#{userId}))
+    and
+    <foreach collection="componentIds" open="(" close=")" item="element" index="index" separator=" or ">gr.resource_id=#{element}</foreach>
+    UNION
+    SELECT p.id
+    FROM user_roles ur, projects p
+    WHERE
+    ur.role=#{role}
+    and ur.user_id=#{userId} and
+    <foreach collection="componentIds" open="(" close=")" item="element" index="index" separator=" or ">p.id=#{element}</foreach>
+  </select>
+
+  <select id="keepAuthorizedProjectIdsForAnonymous" parameterType="map" resultType="long">
+    SELECT gr.resource_id
+    FROM group_roles gr
+    WHERE
+    gr.role=#{role}
+    and gr.group_id is null
+    and
+    <foreach collection="componentIds" open="(" close=")" item="element" index="index" separator=" or ">gr.resource_id=#{element}</foreach>
+  </select>
+
   <select id="selectAuthorizedRootProjectsKeys" parameterType="map" resultType="string">
     <include refid="selectAuthorizedRootProjectsKeysQuery" />
   </select>
index 2e267d391addc9c781ef54037681b1b37b681f84..521d0af66a31cd301753549758e2361905432623 100644 (file)
 package org.sonar.core.user;
 
 import com.google.common.collect.Sets;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.sonar.core.persistence.AbstractDaoTestCase;
+import org.sonar.core.persistence.DbSession;
 
 import java.util.Collection;
-import java.util.Set;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class AuthorizationDaoTest extends AbstractDaoTestCase {
 
   private static final int USER = 100;
-  private static final String PROJECT = "pj-w-snapshot", PACKAGE = "pj-w-snapshot:package", FILE = "pj-w-snapshot:file", FILE_IN_OTHER_PROJECT = "another",
-    EMPTY_PROJECT = "pj-wo-snapshot";
+  private static final Long PROJECT_ID = 300L, EMPTY_PROJECT_ID = 400L;
+  private static final String PROJECT = "pj-w-snapshot";
+
+  DbSession session;
+
+  @Before
+  public void setUp() throws Exception {
+    session = getMyBatis().openSession(false);
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    session.close();
+  }
 
   @Test
   public void user_should_be_authorized() {
@@ -40,15 +54,15 @@ public class AuthorizationDaoTest extends AbstractDaoTestCase {
     setupData("user_should_be_authorized");
 
     AuthorizationDao authorization = new AuthorizationDao(getMyBatis());
-    Set<String> componentIds = authorization.keepAuthorizedComponentKeys(
-      Sets.<String>newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT),
+    Collection<Long> componentIds = authorization.keepAuthorizedProjectIds(session,
+      Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID),
       USER, "user");
 
-    assertThat(componentIds).containsOnly(PROJECT, PACKAGE, FILE, EMPTY_PROJECT);
+    assertThat(componentIds).containsOnly(PROJECT_ID, EMPTY_PROJECT_ID);
 
     // user does not have the role "admin"
-    componentIds = authorization.keepAuthorizedComponentKeys(
-      Sets.<String>newHashSet(PROJECT, PACKAGE, FILE),
+    componentIds = authorization.keepAuthorizedProjectIds(session,
+      Sets.newHashSet(PROJECT_ID),
       USER, "admin");
     assertThat(componentIds).isEmpty();
   }
@@ -72,15 +86,15 @@ public class AuthorizationDaoTest extends AbstractDaoTestCase {
     setupData("group_should_be_authorized");
 
     AuthorizationDao authorization = new AuthorizationDao(getMyBatis());
-    Set<String> componentIds = authorization.keepAuthorizedComponentKeys(
-      Sets.<String>newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT),
+    Collection<Long> componentIds = authorization.keepAuthorizedProjectIds(session,
+      Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID),
       USER, "user");
 
-    assertThat(componentIds).containsOnly(PROJECT, PACKAGE, FILE, EMPTY_PROJECT);
+    assertThat(componentIds).containsOnly(PROJECT_ID, EMPTY_PROJECT_ID);
 
     // group does not have the role "admin"
-    componentIds = authorization.keepAuthorizedComponentKeys(
-      Sets.<String>newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT),
+    componentIds = authorization.keepAuthorizedProjectIds(session,
+      Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID),
       USER, "admin");
     assertThat(componentIds).isEmpty();
   }
@@ -91,15 +105,15 @@ public class AuthorizationDaoTest extends AbstractDaoTestCase {
     setupData("group_should_have_global_authorization");
 
     AuthorizationDao authorization = new AuthorizationDao(getMyBatis());
-    Set<String> componentIds = authorization.keepAuthorizedComponentKeys(
-      Sets.<String>newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT),
+    Collection<Long> componentIds = authorization.keepAuthorizedProjectIds(session,
+      Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID),
       USER, "user");
 
-    assertThat(componentIds).containsOnly(PROJECT, PACKAGE, FILE, EMPTY_PROJECT);
+    assertThat(componentIds).containsOnly(PROJECT_ID, EMPTY_PROJECT_ID);
 
     // group does not have the role "admin"
-    componentIds = authorization.keepAuthorizedComponentKeys(
-      Sets.<String>newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT),
+    componentIds = authorization.keepAuthorizedProjectIds(session,
+      Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID),
       USER, "admin");
     assertThat(componentIds).isEmpty();
   }
@@ -109,15 +123,15 @@ public class AuthorizationDaoTest extends AbstractDaoTestCase {
     setupData("anonymous_should_be_authorized");
 
     AuthorizationDao authorization = new AuthorizationDao(getMyBatis());
-    Set<String> componentIds = authorization.keepAuthorizedComponentKeys(
-      Sets.<String>newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT),
+    Collection<Long> componentIds = authorization.keepAuthorizedProjectIds(session,
+      Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID),
       null, "user");
 
-    assertThat(componentIds).containsOnly(PROJECT, PACKAGE, FILE, EMPTY_PROJECT);
+    assertThat(componentIds).containsOnly(PROJECT_ID, EMPTY_PROJECT_ID);
 
     // group does not have the role "admin"
-    componentIds = authorization.keepAuthorizedComponentKeys(
-      Sets.<String>newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT),
+    componentIds = authorization.keepAuthorizedProjectIds(session,
+      Sets.newHashSet(PROJECT_ID),
       null, "admin");
     assertThat(componentIds).isEmpty();
   }
index b6371a7e0b1b8ae26f7d7edacb0d53b3f9c9d0c6..3771e09738d8473580f2a41f943a93bd12b9dde2 100644 (file)
@@ -6,9 +6,6 @@
   <groups_users user_id="100" group_id="200"/>
   <group_roles id="1" group_id="200" resource_id="999" role="user"/>
 
-  <projects id="301" kee="pj-w-snapshot:package" root_id="300" uuid="ABCD" module_uuid="DEFG"/>
-  <projects id="302" kee="pj-w-snapshot:file" root_id="300" uuid="BCDE" module_uuid="DEFG"/>
-  <projects id="303" kee="pj-w-snapshot:other" root_id="300" uuid="CDEF" module_uuid="DEFG"/>
   <projects id="300" kee="pj-w-snapshot" uuid="DEFG" module_uuid="[null]"/>
   <projects id="400" kee="pj-wo-snapshot" uuid="EFGH" module_uuid="[null]"/>
 </dataset>