]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19558 Refactor Project Indexers
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Wed, 28 Jun 2023 13:04:11 +0000 (08:04 -0500)
committersonartech <sonartech@sonarsource.com>
Wed, 5 Jul 2023 20:03:04 +0000 (20:03 +0000)
89 files changed:
server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/IndexAnalysisStepIT.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/purge/IndexPurgeListener.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/IndexAnalysisStep.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/purge/IndexPurgeListenerTest.java
server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java
server/sonar-db-dao/src/it/java/org/sonar/db/entity/EntityDaoIT.java
server/sonar-db-dao/src/main/java/org/sonar/db/entity/EntityDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/entity/EntityMapper.java
server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java
server/sonar-db-dao/src/main/resources/org/sonar/db/entity/EntityMapper.xml
server/sonar-server-common/src/it/java/org/sonar/server/component/index/ComponentIndexerIT.java [deleted file]
server/sonar-server-common/src/it/java/org/sonar/server/component/index/EntityDefinitionIndexerIT.java [new file with mode: 0644]
server/sonar-server-common/src/it/java/org/sonar/server/issue/index/IssueIndexerIT.java
server/sonar-server-common/src/it/java/org/sonar/server/measure/index/ProjectMeasuresIndexerIT.java
server/sonar-server-common/src/main/java/org/sonar/server/component/index/ComponentIndexer.java [deleted file]
server/sonar-server-common/src/main/java/org/sonar/server/component/index/EntityDefinitionIndexer.java [new file with mode: 0644]
server/sonar-server-common/src/main/java/org/sonar/server/es/AnalysisIndexer.java [new file with mode: 0644]
server/sonar-server-common/src/main/java/org/sonar/server/es/BranchIndexer.java [new file with mode: 0644]
server/sonar-server-common/src/main/java/org/sonar/server/es/EventIndexer.java [new file with mode: 0644]
server/sonar-server-common/src/main/java/org/sonar/server/es/Indexers.java [new file with mode: 0644]
server/sonar-server-common/src/main/java/org/sonar/server/es/IndexersImpl.java [new file with mode: 0644]
server/sonar-server-common/src/main/java/org/sonar/server/es/ProjectIndexer.java [deleted file]
server/sonar-server-common/src/main/java/org/sonar/server/es/ProjectIndexers.java [deleted file]
server/sonar-server-common/src/main/java/org/sonar/server/es/ProjectIndexersImpl.java [deleted file]
server/sonar-server-common/src/main/java/org/sonar/server/es/ResilientIndexer.java
server/sonar-server-common/src/main/java/org/sonar/server/es/StartupIndexer.java
server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIndexer.java
server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndexer.java
server/sonar-server-common/src/main/java/org/sonar/server/permission/index/AuthorizationDoc.java
server/sonar-server-common/src/main/java/org/sonar/server/permission/index/AuthorizationScope.java
server/sonar-server-common/src/main/java/org/sonar/server/permission/index/NeedAuthorizationIndexer.java
server/sonar-server-common/src/test/java/org/sonar/server/es/IndexersImplTest.java [new file with mode: 0644]
server/sonar-server-common/src/test/java/org/sonar/server/es/ProjectIndexersImplTest.java [deleted file]
server/sonar-server-common/src/test/java/org/sonar/server/es/ProjectIndexersTest.java [deleted file]
server/sonar-server-common/src/test/java/org/sonar/server/es/StartupIndexerTest.java
server/sonar-server-common/src/test/java/org/sonar/server/permission/index/AuthorizationDocTest.java
server/sonar-server-common/src/testFixtures/java/org/sonar/server/es/TestIndexers.java [new file with mode: 0644]
server/sonar-server-common/src/testFixtures/java/org/sonar/server/es/TestProjectIndexers.java [deleted file]
server/sonar-webserver-core/src/main/java/org/sonar/server/issue/index/AsyncIssueIndexingImpl.java
server/sonar-webserver-es/src/main/java/org/sonar/server/permission/index/PermissionIndexer.java
server/sonar-webserver-es/src/test/java/org/sonar/server/component/index/ComponentIndexSearchTest.java
server/sonar-webserver-es/src/test/java/org/sonar/server/component/index/ComponentIndexTest.java
server/sonar-webserver-es/src/test/java/org/sonar/server/permission/index/FooIndexer.java
server/sonar-webserver-es/src/test/java/org/sonar/server/permission/index/PermissionIndexerTest.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/azure/ImportAzureProjectActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/bitbucketcloud/ImportBitbucketCloudRepoActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/bitbucketserver/ImportBitbucketServerProjectActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/gitlab/ImportGitLabProjectActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/branch/ws/DeleteActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/ce/queue/BranchReportSubmitterIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/ce/queue/ReportSubmitterIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ComponentCleanerServiceIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ComponentServiceUpdateKeyIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ComponentUpdaterIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SearchActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SearchProjectsActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SuggestionsActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/live/LiveMeasureComputerImplIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/permission/PermissionTemplateServiceIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/permission/ws/BasePermissionWsIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/permission/ws/template/ApplyTemplateActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/permission/ws/template/BulkApplyTemplateActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/BulkDeleteActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/CreateActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/DeleteActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/UpdateKeyActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/UpdateVisibilityActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/projecttag/ws/SetActionIT.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/azure/ImportAzureProjectAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/bitbucketcloud/ImportBitbucketCloudRepoAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/bitbucketserver/ImportBitbucketServerProjectAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/gitlab/ImportGitLabProjectAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/queue/ReportSubmitter.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentCleanerService.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentCreationData.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentService.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentUpdater.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/AuthorsAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/LiveMeasureComputerImpl.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/permission/PermissionTemplateService.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/permission/PermissionUpdater.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/CreateAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/DeleteAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/projecttag/TagsWsSupport.java
server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java

index 342ffe9c14c822ed380a6f4bdb926c11c55ae938..dd7ba03a8738323949fccf060825b9f123b54026 100644 (file)
@@ -34,7 +34,7 @@ import org.sonar.ce.task.step.TestComputationStepContext;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.BranchDao;
-import org.sonar.server.es.ProjectIndexer;
+import org.sonar.server.es.AnalysisIndexer;
 
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -53,16 +53,11 @@ public class IndexAnalysisStepIT extends BaseStepTest {
   public BatchReportReaderRule reportReader = new BatchReportReaderRule();
 
   private final DbClient dbClient = mock(DbClient.class);
-
   private final FileStatuses fileStatuses = mock(FileStatuses.class);
-
-  private final ProjectIndexer projectIndexer = mock(ProjectIndexer.class);
-
+  private final AnalysisIndexer analysisIndexer = mock(AnalysisIndexer.class);
   private final DbSession dbSession = mock(DbSession.class);
-
   private final BranchDao branchDao = mock(BranchDao.class);
-
-  private final IndexAnalysisStep underTest = new IndexAnalysisStep(treeRootHolder, fileStatuses, dbClient, projectIndexer);
+  private final IndexAnalysisStep underTest = new IndexAnalysisStep(treeRootHolder, fileStatuses, dbClient, analysisIndexer);
 
   private TestComputationStepContext testComputationStepContext;
 
@@ -81,7 +76,7 @@ public class IndexAnalysisStepIT extends BaseStepTest {
 
     underTest.execute(testComputationStepContext);
 
-    verify(projectIndexer).indexOnAnalysis(PROJECT_UUID, Set.of());
+    verify(analysisIndexer).indexOnAnalysis(PROJECT_UUID, Set.of());
   }
 
   @Test
@@ -91,7 +86,7 @@ public class IndexAnalysisStepIT extends BaseStepTest {
 
     underTest.execute(testComputationStepContext);
 
-    verify(projectIndexer).indexOnAnalysis(PROJECT_UUID, Set.of());
+    verify(analysisIndexer).indexOnAnalysis(PROJECT_UUID, Set.of());
   }
 
   @Test
@@ -103,7 +98,7 @@ public class IndexAnalysisStepIT extends BaseStepTest {
 
     underTest.execute(testComputationStepContext);
 
-    verify(projectIndexer).indexOnAnalysis(PROJECT_UUID, anyUuids);
+    verify(analysisIndexer).indexOnAnalysis(PROJECT_UUID, anyUuids);
   }
 
   @Test
@@ -114,7 +109,7 @@ public class IndexAnalysisStepIT extends BaseStepTest {
 
     underTest.execute(testComputationStepContext);
 
-    verify(projectIndexer).indexOnAnalysis(PROJECT_UUID);
+    verify(analysisIndexer).indexOnAnalysis(PROJECT_UUID);
   }
 
   @Override
index d0dfa266685ba1a368e417f8ad4309f11e48b0bf..a77b11cf80fd70a1e562efc5969f558aa4138296 100644 (file)
@@ -23,22 +23,22 @@ import java.util.Collection;
 import java.util.List;
 import org.sonar.api.server.ServerSide;
 import org.sonar.db.purge.PurgeListener;
-import org.sonar.server.component.index.ComponentIndexer;
+import org.sonar.server.component.index.EntityDefinitionIndexer;
 import org.sonar.server.issue.index.IssueIndexer;
 
 @ServerSide
 public class IndexPurgeListener implements PurgeListener {
   private final IssueIndexer issueIndexer;
-  private final ComponentIndexer componentIndexer;
+  private final EntityDefinitionIndexer entityDefinitionIndexer;
 
-  public IndexPurgeListener(IssueIndexer issueIndexer, ComponentIndexer componentIndexer) {
+  public IndexPurgeListener(IssueIndexer issueIndexer, EntityDefinitionIndexer entityDefinitionIndexer) {
     this.issueIndexer = issueIndexer;
-    this.componentIndexer = componentIndexer;
+    this.entityDefinitionIndexer = entityDefinitionIndexer;
   }
 
   @Override
   public void onComponentsDisabling(String projectUuid, Collection<String> disabledComponentUuids) {
-    componentIndexer.delete(projectUuid, disabledComponentUuids);
+    entityDefinitionIndexer.delete(projectUuid, disabledComponentUuids);
   }
 
   @Override
index fc47c64a7391007e37156eff5f0b864bde1a97b4..7ede8d9dd0367ec1430636609737f34a841dfd14 100644 (file)
@@ -28,7 +28,7 @@ import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
 import org.sonar.ce.task.step.ComputationStep;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
-import org.sonar.server.es.ProjectIndexer;
+import org.sonar.server.es.AnalysisIndexer;
 
 public class IndexAnalysisStep implements ComputationStep {
 
@@ -36,10 +36,10 @@ public class IndexAnalysisStep implements ComputationStep {
 
   private final TreeRootHolder treeRootHolder;
   private final FileStatuses fileStatuses;
-  private final ProjectIndexer[] indexers;
+  private final AnalysisIndexer[] indexers;
   private final DbClient dbClient;
 
-  public IndexAnalysisStep(TreeRootHolder treeRootHolder, FileStatuses fileStatuses, DbClient dbClient, ProjectIndexer... indexers) {
+  public IndexAnalysisStep(TreeRootHolder treeRootHolder, FileStatuses fileStatuses, DbClient dbClient, AnalysisIndexer... indexers) {
     this.treeRootHolder = treeRootHolder;
     this.fileStatuses = fileStatuses;
     this.indexers = indexers;
@@ -49,14 +49,14 @@ public class IndexAnalysisStep implements ComputationStep {
   @Override
   public void execute(ComputationStep.Context context) {
     String branchUuid = treeRootHolder.getRoot().getUuid();
-    Consumer<ProjectIndexer> projectIndexerConsumer = getProjectIndexerConsumer(branchUuid);
-    for (ProjectIndexer indexer : indexers) {
+    Consumer<AnalysisIndexer> analysisIndexerConsumer = getAnalysisIndexerConsumer(branchUuid);
+    for (AnalysisIndexer indexer : indexers) {
       LOGGER.debug("Call {}", indexer);
-      projectIndexerConsumer.accept(indexer);
+      analysisIndexerConsumer.accept(indexer);
     }
   }
 
-  private Consumer<ProjectIndexer> getProjectIndexerConsumer(String branchUuid) {
+  private Consumer<AnalysisIndexer> getAnalysisIndexerConsumer(String branchUuid) {
     Set<String> fileUuidsMarkedAsUnchanged = fileStatuses.getFileUuidsMarkedAsUnchanged();
     return isBranchNeedIssueSync(branchUuid)
       ? (indexer -> indexer.indexOnAnalysis(branchUuid))
index 556387bf0f5dec5333f5b65a1d4355d9dca370f9..a5d9ad2e54e64ec785651547784846f87a31c433 100644 (file)
@@ -21,7 +21,7 @@ package org.sonar.ce.task.projectanalysis.purge;
 
 import java.util.List;
 import org.junit.Test;
-import org.sonar.server.component.index.ComponentIndexer;
+import org.sonar.server.component.index.EntityDefinitionIndexer;
 import org.sonar.server.issue.index.IssueIndexer;
 
 import static java.util.Arrays.asList;
@@ -32,9 +32,9 @@ import static org.mockito.Mockito.verify;
 public class IndexPurgeListenerTest {
 
   private IssueIndexer issueIndexer = mock(IssueIndexer.class);
-  private ComponentIndexer componentIndexer = mock(ComponentIndexer.class);
+  private EntityDefinitionIndexer entityDefinitionIndexer = mock(EntityDefinitionIndexer.class);
 
-  private IndexPurgeListener underTest = new IndexPurgeListener(issueIndexer, componentIndexer);
+  private IndexPurgeListener underTest = new IndexPurgeListener(issueIndexer, entityDefinitionIndexer);
 
   @Test
   public void test_onComponentDisabling() {
@@ -43,7 +43,7 @@ public class IndexPurgeListenerTest {
     List<String> uuids = singletonList(uuid);
     underTest.onComponentsDisabling(projectUuid, uuids);
 
-    verify(componentIndexer).delete(projectUuid, uuids);
+    verify(entityDefinitionIndexer).delete(projectUuid, uuids);
   }
 
   @Test
index 182942cd8360aadbfcd4b22d4a93c102606ba328..0716d8aa40ab8efd37b698c75eadf650684eaafd 100644 (file)
@@ -89,10 +89,10 @@ import org.sonar.db.purge.PurgeProfiler;
 import org.sonar.process.NetworkUtilsImpl;
 import org.sonar.process.Props;
 import org.sonar.process.logging.LogbackHelper;
-import org.sonar.server.component.index.ComponentIndexer;
+import org.sonar.server.component.index.EntityDefinitionIndexer;
 import org.sonar.server.config.ConfigurationProvider;
 import org.sonar.server.es.EsModule;
-import org.sonar.server.es.ProjectIndexersImpl;
+import org.sonar.server.es.IndexersImpl;
 import org.sonar.server.extension.CoreExtensionBootstraper;
 import org.sonar.server.extension.CoreExtensionStopper;
 import org.sonar.server.favorite.FavoriteUpdater;
@@ -381,11 +381,11 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer {
 
       // components,
       FavoriteUpdater.class,
-      ProjectIndexersImpl.class,
+      IndexersImpl.class,
       QGChangeNotificationHandler.class,
       QGChangeNotificationHandler.newMetadata(),
       ProjectMeasuresIndexer.class,
-      ComponentIndexer.class,
+      EntityDefinitionIndexer.class,
 
       // views
       ViewIndexer.class,
index 3f6276ac35ca7df8c73d8bbc44cac1d8a7348ffd..02f74e5c0c50ad33eb8bdd0b5e369afd8eb77b89 100644 (file)
@@ -162,36 +162,9 @@ public class EntityDaoIT {
 
     List<EntityDto> result = new LinkedList<>();
     ResultHandler<EntityDto> handler = resultContext -> result.add(resultContext.getResultObject());
-    entityDao.scrollForIndexing(db.getSession(), null, handler);
+    entityDao.scrollForIndexing(db.getSession(), handler);
 
     assertThat(result).extracting(EntityDto::getUuid)
       .containsOnly(project.projectUuid(), application.projectUuid(), portfolio.getUuid());
   }
-
-  @Test
-  public void scrollEntitiesForIndexing_whenEntityUuidSpecified_shouldReturnSpecificEntity() {
-    ProjectData application = db.components().insertPrivateApplication();
-    ProjectData project = db.components().insertPrivateProject();
-    PortfolioDto portfolio = db.components().insertPrivatePortfolioDto();
-
-    List<EntityDto> result = new LinkedList<>();
-    ResultHandler<EntityDto> handler = resultContext -> result.add(resultContext.getResultObject());
-    entityDao.scrollForIndexing(db.getSession(), project.projectUuid(), handler);
-
-    assertThat(result).extracting(EntityDto::getUuid)
-      .containsOnly(project.projectUuid());
-  }
-
-  @Test
-  public void scrollEntitiesForIndexing_whenNonExistingUuidSpecified_shouldReturnEmpty() {
-    ProjectData application = db.components().insertPrivateApplication();
-    ProjectData project = db.components().insertPrivateProject();
-    PortfolioDto portfolio = db.components().insertPrivatePortfolioDto();
-
-    List<EntityDto> result = new LinkedList<>();
-    ResultHandler<EntityDto> handler = resultContext -> result.add(resultContext.getResultObject());
-    entityDao.scrollForIndexing(db.getSession(), "unknown", handler);
-
-    assertThat(result).isEmpty();
-  }
 }
index d61cdcaf4fd65038f77d011f4349ff6b98859f9a..69a1830266fe1def2c2fd922745531d33a07c685 100644 (file)
@@ -58,8 +58,8 @@ public class EntityDao implements Dao {
     return Optional.ofNullable(mapper(dbSession).selectByComponentUuid(componentUuid));
   }
 
-  public void scrollForIndexing(DbSession session, @Nullable String entityUuid, ResultHandler<EntityDto> handler) {
-    mapper(session).scrollForIndexing(entityUuid, handler);
+  public void scrollForIndexing(DbSession session, ResultHandler<EntityDto> handler) {
+    mapper(session).scrollForIndexing(handler);
   }
 
   private static EntityMapper mapper(DbSession session) {
index 1131ef12ad48fcab9a1e0cd559e19be5ecf5999a..1b704d290aba2b89dc86bf8c7e83585a21c5dffb 100644 (file)
@@ -40,5 +40,5 @@ public interface EntityMapper {
 
   List<EntityDto> selectByKeys(@Param("keys") Collection<String> keys);
 
-  void scrollForIndexing(@Param("entityUuid") @Nullable String entityUuid, ResultHandler<EntityDto> handler);
+  void scrollForIndexing(ResultHandler<EntityDto> handler);
 }
index bdd569bb24b75d1ac4105e2db289b824caa6f0a9..adda1d6215c2bacd3660c715a10613a8678ad0d8 100644 (file)
@@ -171,10 +171,6 @@ public class UserDao implements Dao {
     mapper(dbSession).clearHomepages("PROJECT", entityDto.getUuid(), system2.now());
   }
 
-  public void cleanHomepage(DbSession dbSession, ComponentDto component) {
-    mapper(dbSession).clearHomepages("PROJECT", component.uuid(), system2.now());
-  }
-
   public void cleanHomepage(DbSession dbSession, UserDto user) {
     mapper(dbSession).clearHomepage(user.getLogin(), system2.now());
   }
index 5b04f607600c7b4d619d8916ea6161c2049d23cc..e501244617d59b474cd48809cd0c8348adfd9bf0 100644 (file)
       </foreach>)
   </select>
 
-  <select id="scrollForIndexing" parameterType="map" resultType="Entity" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
+  <select id="scrollForIndexing" resultType="Entity" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
     (select <include refid="entityProjectColumns"/>
-    from projects p
-    <if test="entityUuid != null">
-      where p.uuid = #{entityUuid,jdbcType=VARCHAR}
-    </if>)
+    from projects p)
     UNION
     (select <include refid="entityPortfolioColumns"/>
-    from portfolios p
-    <if test="entityUuid != null">
-      where p.uuid = #{entityUuid,jdbcType=VARCHAR}
-    </if>)
+    from portfolios p)
   </select>
 </mapper>
diff --git a/server/sonar-server-common/src/it/java/org/sonar/server/component/index/ComponentIndexerIT.java b/server/sonar-server-common/src/it/java/org/sonar/server/component/index/ComponentIndexerIT.java
deleted file mode 100644 (file)
index 0a00585..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.index;
-
-import java.util.Arrays;
-import java.util.Collection;
-import org.elasticsearch.search.SearchHit;
-import org.elasticsearch.search.builder.SearchSourceBuilder;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.BranchDto;
-import org.sonar.db.component.ProjectData;
-import org.sonar.db.entity.EntityDto;
-import org.sonar.db.es.EsQueueDto;
-import org.sonar.db.project.ProjectDto;
-import org.sonar.server.es.EsClient;
-import org.sonar.server.es.EsTester;
-import org.sonar.server.es.IndexingResult;
-import org.sonar.server.es.ProjectIndexer;
-
-import static java.util.Collections.emptySet;
-import static java.util.Collections.singletonList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
-import static org.sonar.api.resources.Qualifiers.PROJECT;
-import static org.sonar.server.component.index.ComponentIndexDefinition.FIELD_NAME;
-import static org.sonar.server.component.index.ComponentIndexDefinition.TYPE_COMPONENT;
-import static org.sonar.server.es.ProjectIndexer.Cause.PROJECT_CREATION;
-import static org.sonar.server.es.ProjectIndexer.Cause.PROJECT_DELETION;
-import static org.sonar.server.es.newindex.DefaultIndexSettingsElement.SORTABLE_ANALYZER;
-
-public class ComponentIndexerIT {
-
-  private System2 system2 = System2.INSTANCE;
-
-  @Rule
-  public EsTester es = EsTester.create();
-  @Rule
-  public DbTester db = DbTester.create(system2, true);
-
-  private DbClient dbClient = db.getDbClient();
-  private DbSession dbSession = db.getSession();
-  private ComponentIndexer underTest = new ComponentIndexer(db.getDbClient(), es.client());
-
-  @Test
-  public void test_getIndexTypes() {
-    assertThat(underTest.getIndexTypes()).containsExactly(TYPE_COMPONENT);
-  }
-
-  @Test
-  public void indexOnStartup_does_nothing_if_no_projects() {
-    underTest.indexOnStartup(emptySet());
-
-    assertThatIndexHasSize(0);
-  }
-
-  @Test
-  public void indexOnStartup_indexes_all_components() {
-    ProjectDto project1 = db.components().insertPrivateProject().getProjectDto();
-    ProjectDto project2 = db.components().insertPrivateProject().getProjectDto();
-
-    underTest.indexOnStartup(emptySet());
-
-    assertThatIndexContainsOnly(project1, project2);
-  }
-
-  @Test
-  public void indexOAll_indexes_all_components() {
-    ProjectDto project1 = db.components().insertPrivateProject().getProjectDto();
-    ProjectDto project2 = db.components().insertPrivateProject().getProjectDto();
-
-    underTest.indexAll();
-
-    assertThatIndexContainsOnly(project1, project2);
-  }
-
-  @Test
-  public void map_fields() {
-    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
-
-    underTest.indexOnStartup(emptySet());
-
-    assertThatIndexContainsOnly(project);
-    ComponentDoc doc = es.getDocuments(TYPE_COMPONENT, ComponentDoc.class).get(0);
-    assertThat(doc.getId()).isEqualTo(project.getUuid());
-    assertThat(doc.getKey()).isEqualTo(project.getKey());
-    assertThat(doc.getName()).isEqualTo(project.getName());
-  }
-
-  @Test
-  public void indexOnStartup_does_not_index_non_main_branches() {
-    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
-    BranchDto branch = db.components().insertProjectBranch(project, b -> b.setKey("feature/foo"));
-
-    underTest.indexOnStartup(emptySet());
-
-    assertThatIndexContainsOnly(project);
-  }
-
-  @Test
-  public void indexOnAnalysis_indexes_project() {
-    ProjectData project = db.components().insertPrivateProject();
-
-    underTest.indexOnAnalysis(project.getMainBranchComponent().uuid());
-
-    assertThatIndexContainsOnly(project.getProjectDto());
-  }
-
-  @Test
-  public void indexOnAnalysis_indexes_new_components() {
-    ProjectData projectData = db.components().insertPrivateProject();
-    ProjectDto project = projectData.getProjectDto();
-    underTest.indexOnAnalysis(projectData.getMainBranchComponent().uuid());
-    assertThatIndexContainsOnly(project);
-
-    underTest.indexOnAnalysis(projectData.getMainBranchComponent().uuid());
-    assertThatIndexContainsOnly(project);
-  }
-
-  @Test
-  public void indexOnAnalysis_does_not_index_non_main_branches() {
-    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
-    BranchDto branch = db.components().insertProjectBranch(project, b -> b.setKey("feature/foo"));
-
-    underTest.indexOnAnalysis(branch.getUuid());
-
-    assertThatIndexHasSize(0);
-  }
-
-  @Test
-  public void do_not_update_index_on_project_tag_update() {
-    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
-
-    indexEntity(project, ProjectIndexer.Cause.PROJECT_TAGS_UPDATE);
-
-    assertThatIndexHasSize(0);
-  }
-
-  @Test
-  public void do_not_update_index_on_permission_change() {
-    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
-
-    indexEntity(project, ProjectIndexer.Cause.PERMISSION_CHANGE);
-
-    assertThatIndexHasSize(0);
-  }
-
-  @Test
-  public void update_index_on_project_creation() {
-    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
-
-    IndexingResult result = indexEntity(project, PROJECT_CREATION);
-
-    assertThatIndexContainsOnly(project);
-    assertThat(result.getTotal()).isOne();
-    assertThat(result.getSuccess()).isOne();
-  }
-
-  @Test
-  public void delete_some_components() {
-    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
-    indexEntity(project, PROJECT_CREATION);
-
-    underTest.delete(project.getUuid(), emptySet());
-
-    assertThatIndexContainsOnly(project);
-  }
-
-  @Test
-  public void delete_project() {
-    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
-    indexEntity(project, PROJECT_CREATION);
-    assertThatIndexHasSize(1);
-
-    db.getDbClient().purgeDao().deleteProject(db.getSession(), project.getUuid(), PROJECT, project.getName(), project.getKey());
-    indexEntity(project, PROJECT_DELETION);
-
-    assertThatIndexHasSize(0);
-  }
-
-  @Test
-  public void indexOnAnalysis_updates_index_on_changes() {
-  ProjectData project = db.components().insertPrivateProject();
-  underTest.indexOnAnalysis(project.getMainBranchComponent().uuid());
-  assertThatEntityHasName(project.projectUuid(), project.getProjectDto().getName());
-
-  // modify
-  ProjectDto projectDto = project.getProjectDto();
-  projectDto.setName("NewName");
-
-  db.getDbClient().projectDao().update(dbSession, projectDto);
-  db.commit();
-
-  // verify that index is updated
-  underTest.indexOnAnalysis(project.getMainBranchComponent().uuid());
-
-  assertThatIndexContainsOnly(projectDto.getUuid());
-  assertThatEntityHasName(projectDto.getUuid(), "NewName");
-}
-
-
-
-  @Test
-  public void errors_during_indexing_are_recovered() {
-    ProjectDto project1 = db.components().insertPrivateProject().getProjectDto();
-    es.lockWrites(TYPE_COMPONENT);
-
-    IndexingResult result = indexEntity(project1, PROJECT_CREATION);
-    assertThat(result.getTotal()).isOne();
-    assertThat(result.getFailures()).isOne();
-
-    // index is still read-only, fail to recover
-    result = recover();
-    assertThat(result.getTotal()).isOne();
-    assertThat(result.getFailures()).isOne();
-    assertThat(es.countDocuments(TYPE_COMPONENT)).isZero();
-
-    es.unlockWrites(TYPE_COMPONENT);
-
-    result = recover();
-    assertThat(result.getTotal()).isOne();
-    assertThat(result.getFailures()).isZero();
-    assertThatIndexContainsOnly(project1);
-  }
-
-  private IndexingResult indexEntity(EntityDto entity, ProjectIndexer.Cause cause) {
-    DbSession dbSession = db.getSession();
-    Collection<EsQueueDto> items = underTest.prepareForRecovery(dbSession, singletonList(entity.getUuid()), cause);
-    dbSession.commit();
-    return underTest.index(dbSession, items);
-  }
-
-  private IndexingResult recover() {
-    Collection<EsQueueDto> items = db.getDbClient().esQueueDao().selectForRecovery(db.getSession(), System.currentTimeMillis() + 1_000L, 10);
-    return underTest.index(db.getSession(), items);
-  }
-
-  private void assertThatIndexHasSize(int expectedSize) {
-    assertThat(es.countDocuments(TYPE_COMPONENT)).isEqualTo(expectedSize);
-  }
-
-  private void assertThatIndexContainsOnly(EntityDto... expectedEntities) {
-    assertThat(es.getIds(TYPE_COMPONENT)).containsExactlyInAnyOrder(
-      Arrays.stream(expectedEntities).map(EntityDto::getUuid).toArray(String[]::new));
-  }
-
-  private void assertThatIndexContainsOnly(String uuid) {
-    assertThat(es.getIds(TYPE_COMPONENT)).containsExactlyInAnyOrder(uuid);
-  }
-
-  private void assertThatEntityHasName(String uuid, String expectedName) {
-    SearchHit[] hits = es.client()
-      .search(EsClient.prepareSearch(TYPE_COMPONENT.getMainType())
-        .source(new SearchSourceBuilder()
-          .query(matchQuery(SORTABLE_ANALYZER.subField(FIELD_NAME), expectedName))))
-      .getHits()
-      .getHits();
-    assertThat(hits)
-      .extracting(SearchHit::getId)
-      .contains(uuid);
-  }
-}
diff --git a/server/sonar-server-common/src/it/java/org/sonar/server/component/index/EntityDefinitionIndexerIT.java b/server/sonar-server-common/src/it/java/org/sonar/server/component/index/EntityDefinitionIndexerIT.java
new file mode 100644 (file)
index 0000000..a3bdaa8
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.index;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.elasticsearch.search.SearchHit;
+import org.elasticsearch.search.builder.SearchSourceBuilder;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ProjectData;
+import org.sonar.db.entity.EntityDto;
+import org.sonar.db.es.EsQueueDto;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.server.es.EsClient;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.es.Indexers;
+import org.sonar.server.es.IndexingResult;
+
+import static java.util.Collections.emptySet;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
+import static org.sonar.api.resources.Qualifiers.PROJECT;
+import static org.sonar.server.component.index.ComponentIndexDefinition.FIELD_NAME;
+import static org.sonar.server.component.index.ComponentIndexDefinition.TYPE_COMPONENT;
+import static org.sonar.server.es.Indexers.EntityEvent.PERMISSION_CHANGE;
+import static org.sonar.server.es.Indexers.EntityEvent.CREATION;
+import static org.sonar.server.es.Indexers.EntityEvent.DELETION;
+import static org.sonar.server.es.Indexers.EntityEvent.PROJECT_TAGS_UPDATE;
+import static org.sonar.server.es.newindex.DefaultIndexSettingsElement.SORTABLE_ANALYZER;
+
+public class EntityDefinitionIndexerIT {
+
+  private System2 system2 = System2.INSTANCE;
+
+  @Rule
+  public EsTester es = EsTester.create();
+  @Rule
+  public DbTester db = DbTester.create(system2, true);
+
+  private DbClient dbClient = db.getDbClient();
+  private DbSession dbSession = db.getSession();
+  private EntityDefinitionIndexer underTest = new EntityDefinitionIndexer(db.getDbClient(), es.client());
+
+  @Test
+  public void test_getIndexTypes() {
+    assertThat(underTest.getIndexTypes()).containsExactly(TYPE_COMPONENT);
+  }
+
+  @Test
+  public void indexOnStartup_does_nothing_if_no_projects() {
+    underTest.indexOnStartup(emptySet());
+
+    assertThatIndexHasSize(0);
+  }
+
+  @Test
+  public void indexOnStartup_indexes_all_components() {
+    ProjectDto project1 = db.components().insertPrivateProject().getProjectDto();
+    ProjectDto project2 = db.components().insertPrivateProject().getProjectDto();
+
+    underTest.indexOnStartup(emptySet());
+
+    assertThatIndexContainsOnly(project1, project2);
+  }
+
+  @Test
+  public void indexOAll_indexes_all_components() {
+    ProjectDto project1 = db.components().insertPrivateProject().getProjectDto();
+    ProjectDto project2 = db.components().insertPrivateProject().getProjectDto();
+
+    underTest.indexAll();
+
+    assertThatIndexContainsOnly(project1, project2);
+  }
+
+  @Test
+  public void map_fields() {
+    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
+
+    underTest.indexOnStartup(emptySet());
+
+    assertThatIndexContainsOnly(project);
+    ComponentDoc doc = es.getDocuments(TYPE_COMPONENT, ComponentDoc.class).get(0);
+    assertThat(doc.getId()).isEqualTo(project.getUuid());
+    assertThat(doc.getKey()).isEqualTo(project.getKey());
+    assertThat(doc.getName()).isEqualTo(project.getName());
+  }
+
+  @Test
+  public void indexOnStartup_does_not_index_non_main_branches() {
+    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
+    BranchDto branch = db.components().insertProjectBranch(project, b -> b.setKey("feature/foo"));
+
+    underTest.indexOnStartup(emptySet());
+
+    assertThatIndexContainsOnly(project);
+  }
+
+  @Test
+  public void indexOnAnalysis_indexes_project() {
+    ProjectData project = db.components().insertPrivateProject();
+
+    underTest.indexOnAnalysis(project.getMainBranchComponent().uuid());
+
+    assertThatIndexContainsOnly(project.getProjectDto());
+  }
+
+  @Test
+  public void indexOnAnalysis_indexes_new_components() {
+    ProjectData projectData = db.components().insertPrivateProject();
+    ProjectDto project = projectData.getProjectDto();
+    underTest.indexOnAnalysis(projectData.getMainBranchComponent().uuid());
+    assertThatIndexContainsOnly(project);
+
+    underTest.indexOnAnalysis(projectData.getMainBranchComponent().uuid());
+    assertThatIndexContainsOnly(project);
+  }
+
+  @Test
+  public void indexOnAnalysis_does_not_index_non_main_branches() {
+    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
+    BranchDto branch = db.components().insertProjectBranch(project, b -> b.setKey("feature/foo"));
+
+    underTest.indexOnAnalysis(branch.getUuid());
+
+    assertThatIndexHasSize(0);
+  }
+
+  @Test
+  public void do_not_update_index_on_project_tag_update() {
+    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
+
+    indexProject(project, PROJECT_TAGS_UPDATE);
+
+    assertThatIndexHasSize(0);
+  }
+
+  @Test
+  public void do_not_update_index_on_permission_change() {
+    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
+
+    indexProject(project, PERMISSION_CHANGE);
+
+    assertThatIndexHasSize(0);
+  }
+
+  @Test
+  public void update_index_on_project_creation() {
+    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
+
+    IndexingResult result = indexProject(project, CREATION);
+
+    assertThatIndexContainsOnly(project);
+    assertThat(result.getTotal()).isOne();
+    assertThat(result.getSuccess()).isOne();
+  }
+
+  @Test
+  public void delete_some_components() {
+    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
+    indexProject(project, CREATION);
+
+    underTest.delete(project.getUuid(), emptySet());
+
+    assertThatIndexContainsOnly(project);
+  }
+
+  @Test
+  public void delete_project() {
+    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
+    indexProject(project, CREATION);
+    assertThatIndexHasSize(1);
+
+    db.getDbClient().purgeDao().deleteProject(db.getSession(), project.getUuid(), PROJECT, project.getName(), project.getKey());
+    indexProject(project, DELETION);
+
+    assertThatIndexHasSize(0);
+  }
+
+  @Test
+  public void indexOnAnalysis_updates_index_on_changes() {
+    ProjectData project = db.components().insertPrivateProject();
+    ProjectDto projectDto = project.getProjectDto();
+
+    underTest.indexOnAnalysis(project.getMainBranchDto().getUuid());
+    assertThatEntityHasName(projectDto.getUuid(), projectDto.getName());
+
+    // modify
+    projectDto.setName("NewName");
+
+    db.getDbClient().projectDao().update(dbSession, projectDto);
+    db.commit();
+
+    // verify that index is updated
+    underTest.indexOnAnalysis(project.getMainBranchDto().getUuid());
+
+    assertThatIndexContainsOnly(projectDto.getUuid());
+    assertThatEntityHasName(projectDto.getUuid(), "NewName");
+  }
+
+  @Test
+  public void errors_during_indexing_are_recovered() {
+    ProjectDto project1 = db.components().insertPrivateProject().getProjectDto();
+    es.lockWrites(TYPE_COMPONENT);
+
+    IndexingResult result = indexProject(project1, CREATION);
+    assertThat(result.getTotal()).isOne();
+    assertThat(result.getFailures()).isOne();
+
+    // index is still read-only, fail to recover
+    result = recover();
+    assertThat(result.getTotal()).isOne();
+    assertThat(result.getFailures()).isOne();
+    assertThat(es.countDocuments(TYPE_COMPONENT)).isZero();
+
+    es.unlockWrites(TYPE_COMPONENT);
+
+    result = recover();
+    assertThat(result.getTotal()).isOne();
+    assertThat(result.getFailures()).isZero();
+    assertThatIndexContainsOnly(project1);
+  }
+
+  private IndexingResult indexProject(ProjectDto project, Indexers.EntityEvent cause) {
+    DbSession dbSession = db.getSession();
+    Collection<EsQueueDto> items = underTest.prepareForRecoveryOnEntityEvent(dbSession, singletonList(project.getUuid()), cause);
+    dbSession.commit();
+    return underTest.index(dbSession, items);
+  }
+
+  private IndexingResult recover() {
+    Collection<EsQueueDto> items = db.getDbClient().esQueueDao().selectForRecovery(db.getSession(), System.currentTimeMillis() + 1_000L, 10);
+    return underTest.index(db.getSession(), items);
+  }
+
+  private void assertThatIndexHasSize(int expectedSize) {
+    assertThat(es.countDocuments(TYPE_COMPONENT)).isEqualTo(expectedSize);
+  }
+
+  private void assertThatIndexContainsOnly(EntityDto... expectedEntities) {
+    assertThat(es.getIds(TYPE_COMPONENT)).containsExactlyInAnyOrder(
+      Arrays.stream(expectedEntities).map(EntityDto::getUuid).toArray(String[]::new));
+  }
+
+  private void assertThatIndexContainsOnly(String uuid) {
+    assertThat(es.getIds(TYPE_COMPONENT)).containsExactlyInAnyOrder(uuid);
+  }
+
+  private void assertThatEntityHasName(String uuid, String expectedName) {
+    SearchHit[] hits = es.client()
+      .search(EsClient.prepareSearch(TYPE_COMPONENT.getMainType())
+        .source(new SearchSourceBuilder()
+          .query(matchQuery(SORTABLE_ANALYZER.subField(FIELD_NAME), expectedName))))
+      .getHits()
+      .getHits();
+    assertThat(hits)
+      .extracting(SearchHit::getId)
+      .contains(uuid);
+  }
+}
index ef6166a12265e73331b3fcc0c59cdc1fc2e12202..6634ed703fae4c62d29f8e433f81b7382af87dae 100644 (file)
@@ -43,11 +43,13 @@ import org.sonar.db.component.ProjectData;
 import org.sonar.db.es.EsQueueDto;
 import org.sonar.db.issue.IssueDto;
 import org.sonar.db.issue.IssueTesting;
+import org.sonar.db.project.ProjectDto;
 import org.sonar.db.rule.RuleDto;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.es.IndexType;
+import org.sonar.server.es.Indexers;
+import org.sonar.server.es.Indexers.EntityEvent;
 import org.sonar.server.es.IndexingResult;
-import org.sonar.server.es.ProjectIndexer;
 import org.sonar.server.es.StartupIndexer;
 import org.sonar.server.permission.index.AuthorizationScope;
 import org.sonar.server.permission.index.IndexPermissions;
@@ -62,6 +64,9 @@ import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.server.es.Indexers.BranchEvent.DELETION;
+import static org.sonar.server.es.Indexers.EntityEvent.PROJECT_KEY_UPDATE;
+import static org.sonar.server.es.Indexers.EntityEvent.PROJECT_TAGS_UPDATE;
 import static org.sonar.server.issue.IssueDocTesting.newDoc;
 import static org.sonar.server.issue.index.IssueIndexDefinition.TYPE_ISSUE;
 import static org.sonar.server.permission.index.IndexAuthorizationConstants.TYPE_AUTHORIZATION;
@@ -79,17 +84,17 @@ public class IssueIndexerIT {
   private final IssueIndexer underTest = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()), null);
 
   @Test
-  public void test_getIndexTypes() {
+  public void getIndexTypes_shouldReturnTypeIssue() {
     assertThat(underTest.getIndexTypes()).containsExactly(TYPE_ISSUE);
   }
 
   @Test
-  public void test_getAuthorizationScope() {
+  public void getAuthorizationScope_shouldBeProject() {
     AuthorizationScope scope = underTest.getAuthorizationScope();
     assertThat(scope.getIndexType().getIndex()).isEqualTo(IssueIndexDefinition.DESCRIPTOR);
     assertThat(scope.getIndexType().getType()).isEqualTo(TYPE_AUTHORIZATION);
 
-    Predicate<IndexPermissions> projectPredicate = scope.getProjectPredicate();
+    Predicate<IndexPermissions> projectPredicate = scope.getEntityPredicate();
     IndexPermissions project = new IndexPermissions("P1", Qualifiers.PROJECT);
     IndexPermissions file = new IndexPermissions("F1", Qualifiers.FILE);
     assertThat(projectPredicate.test(project)).isTrue();
@@ -97,7 +102,7 @@ public class IssueIndexerIT {
   }
 
   @Test
-  public void indexOnStartup_scrolls_db_and_adds_all_issues_to_index() {
+  public void indexAllIssues_shouldIndexAllIssues() {
     IssueDto issue1 = db.issues().insert();
     IssueDto issue2 = db.issues().insert();
 
@@ -107,7 +112,7 @@ public class IssueIndexerIT {
   }
 
   @Test
-  public void verify_indexed_fields() {
+  public void indexAllIssues_shouldIndexAllIssueFields() {
     RuleDto rule = db.rules().insert();
     ProjectData projectData = db.components().insertPrivateProject();
     ComponentDto project = projectData.getMainBranchComponent();
@@ -142,7 +147,7 @@ public class IssueIndexerIT {
   }
 
   @Test
-  public void verify_security_standards_indexation() {
+  public void indexAllIssues_shouldIndexSecurityStandards() {
     RuleDto rule = db.rules().insert(r -> r.setSecurityStandards(new HashSet<>(Arrays.asList("cwe:123", "owaspTop10:a3", "cwe:863", "owaspAsvs-4.0:2.1.1"))));
     ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
     ComponentDto dir = db.components().insertComponent(ComponentTesting.newDirectory(project, "src/main/java/foo"));
@@ -166,7 +171,7 @@ public class IssueIndexerIT {
     Set<IndexType> uninitializedIndexTypes = emptySet();
     assertThatThrownBy(() -> underTest.indexOnStartup(uninitializedIndexTypes))
       .isInstanceOf(IllegalStateException.class)
-      .hasMessage("SYNCHRONE StartupIndexer must implement indexOnStartup");
+      .hasMessage("SYNCHRONOUS StartupIndexer must implement indexOnStartup");
     assertThatIndexHasSize(0);
     assertThatEsQueueTableHasSize(0);
     es.unlockWrites(TYPE_ISSUE);
@@ -175,13 +180,13 @@ public class IssueIndexerIT {
   @Test
   public void indexOnAnalysis_indexes_the_issues_of_project() {
     RuleDto rule = db.rules().insert();
-    ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
-    ComponentDto file = db.components().insertComponent(newFileDto(project));
-    IssueDto issue = db.issues().insert(rule, project, file);
+    ComponentDto branchComponent = db.components().insertPrivateProject().getMainBranchComponent();
+    ComponentDto file = db.components().insertComponent(newFileDto(branchComponent));
+    IssueDto issue = db.issues().insert(rule, branchComponent, file);
     ComponentDto otherProject = db.components().insertPrivateProject().getMainBranchComponent();
     db.components().insertComponent(newFileDto(otherProject));
 
-    underTest.indexOnAnalysis(project.uuid());
+    underTest.indexOnAnalysis(branchComponent.uuid());
 
     assertThatIndexHasOnly(issue);
   }
@@ -189,14 +194,14 @@ public class IssueIndexerIT {
   @Test
   public void indexOnAnalysis_does_not_delete_orphan_docs() {
     RuleDto rule = db.rules().insert();
-    ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
-    ComponentDto file = db.components().insertComponent(newFileDto(project));
-    IssueDto issue = db.issues().insert(rule, project, file);
+    ProjectData projectData = db.components().insertPrivateProject();
+    ComponentDto file = db.components().insertComponent(newFileDto(projectData.getMainBranchComponent()));
+    IssueDto issue = db.issues().insert(rule, projectData.getMainBranchComponent(), file);
 
     // orphan in the project
-    addIssueToIndex(project.uuid(), "orphan");
+    addIssueToIndex(projectData.projectUuid(), projectData.getMainBranchComponent().uuid(), "orphan");
 
-    underTest.indexOnAnalysis(project.uuid());
+    underTest.indexOnAnalysis(projectData.getMainBranchComponent().uuid());
 
     assertThat(es.getDocuments(TYPE_ISSUE))
       .extracting(SearchHit::getId)
@@ -222,43 +227,51 @@ public class IssueIndexerIT {
   }
 
   @Test
-  public void index_is_not_updated_when_creating_project() {
-    // it's impossible to already have an issue on a project
-    // that is being created, but it's just to verify that
-    // indexing is disabled
+  public void index_is_not_updated_when_updating_project_key() {
+    // issue is inserted to verify that indexing of project is not triggered
     IssueDto issue = db.issues().insert();
 
-    IndexingResult result = indexProject(issue.getProjectUuid(), ProjectIndexer.Cause.PROJECT_CREATION);
+    IndexingResult result = indexProject(issue.getProjectUuid(), PROJECT_KEY_UPDATE);
     assertThat(result.getTotal()).isZero();
     assertThatIndexHasSize(0);
   }
 
   @Test
-  public void index_is_not_updated_when_updating_project_key() {
+  public void index_is_not_updated_when_updating_tags() {
     // issue is inserted to verify that indexing of project is not triggered
     IssueDto issue = db.issues().insert();
 
-    IndexingResult result = indexProject(issue.getProjectUuid(), ProjectIndexer.Cause.PROJECT_KEY_UPDATE);
+    IndexingResult result = indexProject(issue.getProjectUuid(), PROJECT_TAGS_UPDATE);
     assertThat(result.getTotal()).isZero();
     assertThatIndexHasSize(0);
   }
 
   @Test
-  public void index_is_not_updated_when_updating_tags() {
-    // issue is inserted to verify that indexing of project is not triggered
-    IssueDto issue = db.issues().insert();
+  public void index_is_updated_when_deleting_branch() {
+    ProjectDto project = db.components().insertPublicProject().getProjectDto();
+    BranchDto branch1 = db.components().insertProjectBranch(project);
+    BranchDto branch2 = db.components().insertProjectBranch(project);
 
-    IndexingResult result = indexProject(issue.getProjectUuid(), ProjectIndexer.Cause.PROJECT_TAGS_UPDATE);
-    assertThat(result.getTotal()).isZero();
-    assertThatIndexHasSize(0);
+    addIssueToIndex(project.getUuid(), branch1.getUuid(), "I1");
+    addIssueToIndex(project.getUuid(), branch1.getUuid(), "I2");
+    addIssueToIndex(project.getUuid(), branch2.getUuid(), "I3");
+
+    assertThatIndexHasSize(3);
+
+    IndexingResult result = indexBranch(branch1.getUuid(), DELETION);
+
+    assertThat(result.getTotal()).isEqualTo(2);
+    assertThat(result.getSuccess()).isEqualTo(2);
+    assertThatIndexHasOnly("I3");
   }
 
   @Test
   public void index_is_updated_when_deleting_project() {
-    addIssueToIndex("P1", "I1");
+    BranchDto branch = db.components().insertPrivateProject().getMainBranchDto();
+    addIssueToIndex(branch.getProjectUuid(), branch.getUuid(), "I1");
     assertThatIndexHasSize(1);
 
-    IndexingResult result = indexProject("P1", ProjectIndexer.Cause.PROJECT_DELETION);
+    IndexingResult result = indexProject(branch.getProjectUuid(), EntityEvent.DELETION);
 
     assertThat(result.getTotal()).isOne();
     assertThat(result.getSuccess()).isOne();
@@ -267,24 +280,26 @@ public class IssueIndexerIT {
 
   @Test
   public void errors_during_project_deletion_are_recovered() {
-    addIssueToIndex("P1", "I1");
-    assertThatIndexHasSize(1);
+    addIssueToIndex("P1", "B1", "I1");
+    addIssueToIndex("P1", "B2", "I2");
+
+    assertThatIndexHasSize(2);
     es.lockWrites(TYPE_ISSUE);
 
-    IndexingResult result = indexProject("P1", ProjectIndexer.Cause.PROJECT_DELETION);
-    assertThat(result.getTotal()).isOne();
-    assertThat(result.getFailures()).isOne();
+    IndexingResult result = indexProject("P1", EntityEvent.DELETION);
+    assertThat(result.getTotal()).isEqualTo(2);
+    assertThat(result.getFailures()).isEqualTo(2);
 
     // index is still read-only, fail to recover
     result = recover();
-    assertThat(result.getTotal()).isOne();
-    assertThat(result.getFailures()).isOne();
-    assertThatIndexHasSize(1);
+    assertThat(result.getTotal()).isEqualTo(2);
+    assertThat(result.getFailures()).isEqualTo(2);
+    assertThatIndexHasSize(2);
 
     es.unlockWrites(TYPE_ISSUE);
 
     result = recover();
-    assertThat(result.getTotal()).isOne();
+    assertThat(result.getTotal()).isEqualTo(2);
     assertThat(result.getFailures()).isZero();
     assertThatIndexHasSize(0);
   }
@@ -310,8 +325,8 @@ public class IssueIndexerIT {
 
   @Test
   public void commitAndIndexIssues_removes_issue_from_index_if_it_does_not_exist_in_db() {
-    IssueDto issue1 = new IssueDto().setKee("I1").setProjectUuid("P1");
-    addIssueToIndex(issue1.getProjectUuid(), issue1.getKey());
+    IssueDto issue1 = new IssueDto().setKee("I1").setProjectUuid("B1");
+    addIssueToIndex("B1", issue1.getProjectUuid(), issue1.getKey());
     IssueDto issue2 = db.issues().insert();
 
     underTest.commitAndIndexIssues(db.getSession(), asList(issue1, issue2));
@@ -400,7 +415,7 @@ public class IssueIndexerIT {
 
     es.lockWrites(TYPE_ISSUE);
 
-    IndexingResult result = indexProject(project.uuid(), ProjectIndexer.Cause.PROJECT_DELETION);
+    IndexingResult result = indexBranch(project.uuid(), DELETION);
     assertThat(result.getTotal()).isEqualTo(2L);
     assertThat(result.getFailures()).isEqualTo(2L);
 
@@ -419,18 +434,24 @@ public class IssueIndexerIT {
     assertThatEsQueueTableHasSize(0);
   }
 
-  private IndexingResult indexProject(String projectUuid, ProjectIndexer.Cause cause) {
-    Collection<EsQueueDto> items = underTest.prepareForRecovery(db.getSession(), singletonList(projectUuid), cause);
+  private IndexingResult indexBranch(String branchUuid, Indexers.BranchEvent cause) {
+    Collection<EsQueueDto> items = underTest.prepareForRecoveryOnBranchEvent(db.getSession(), singletonList(branchUuid), cause);
+    db.commit();
+    return underTest.index(db.getSession(), items);
+  }
+
+  private IndexingResult indexProject(String projectUuid, EntityEvent cause) {
+    Collection<EsQueueDto> items = underTest.prepareForRecoveryOnEntityEvent(db.getSession(), singletonList(projectUuid), cause);
     db.commit();
     return underTest.index(db.getSession(), items);
   }
 
   @Test
-  public void deleteByKeys_deletes_docs_by_keys() {
-    addIssueToIndex("P1", "Issue1");
-    addIssueToIndex("P1", "Issue2");
-    addIssueToIndex("P1", "Issue3");
-    addIssueToIndex("P2", "Issue4");
+  public void deleteByKeys_shouldDeleteDocsByKeys() {
+    addIssueToIndex("P1", "B1", "Issue1");
+    addIssueToIndex("P1", "B1", "Issue2");
+    addIssueToIndex("P1", "B1", "Issue3");
+    addIssueToIndex("P2", "B2", "Issue4");
 
     assertThatIndexHasOnly("Issue1", "Issue2", "Issue3", "Issue4");
 
@@ -440,8 +461,8 @@ public class IssueIndexerIT {
   }
 
   @Test
-  public void deleteByKeys_does_not_recover_from_errors() {
-    addIssueToIndex("P1", "Issue1");
+  public void deleteByKeys_shouldNotRecoverFromErrors() {
+    addIssueToIndex("P1", "B1","Issue1");
     es.lockWrites(TYPE_ISSUE);
 
     List<String> issues = List.of("Issue1");
@@ -454,12 +475,12 @@ public class IssueIndexerIT {
   }
 
   @Test
-  public void nothing_to_do_when_delete_issues_on_empty_list() {
-    addIssueToIndex("P1", "Issue1");
-    addIssueToIndex("P1", "Issue2");
-    addIssueToIndex("P1", "Issue3");
+  public void deleteByKeys_whenEmptyList_shouldDoNothing() {
+    addIssueToIndex("P1", "B1", "Issue1");
+    addIssueToIndex("P1", "B1", "Issue2");
+    addIssueToIndex("P1", "B1", "Issue3");
 
-    underTest.deleteByKeys("P1", emptyList());
+    underTest.deleteByKeys("B1", emptyList());
 
     assertThatIndexHasOnly("Issue1", "Issue2", "Issue3");
   }
@@ -533,14 +554,14 @@ public class IssueIndexerIT {
   @Test
   public void issue_on_project_has_main_code_scope() {
     RuleDto rule = db.rules().insert();
-    ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
-    IssueDto issue = db.issues().insert(rule, project, project);
+    ComponentDto mainBranchComponent = db.components().insertPrivateProject().getMainBranchComponent();
+    IssueDto issue = db.issues().insert(rule, mainBranchComponent, mainBranchComponent);
 
     underTest.indexAllIssues();
 
     IssueDoc doc = es.getDocuments(TYPE_ISSUE, IssueDoc.class).get(0);
     assertThat(doc.getId()).isEqualTo(issue.getKey());
-    assertThat(doc.componentUuid()).isEqualTo(project.uuid());
+    assertThat(doc.componentUuid()).isEqualTo(mainBranchComponent.uuid());
     assertThat(doc.scope()).isEqualTo(IssueScope.MAIN);
   }
 
@@ -552,17 +573,17 @@ public class IssueIndexerIT {
   @Test
   public void indexOnAnalysis_whenChangedComponents_shouldReindexOnlyChangedComponents() {
     RuleDto rule = db.rules().insert();
-    ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
-    ComponentDto changedComponent1 = db.components().insertComponent(newFileDto(project));
-    ComponentDto unchangedComponent = db.components().insertComponent(newFileDto(project));
-    ComponentDto ChangedComponent2 = db.components().insertComponent(newFileDto(project));
-    IssueDto changedIssue1 = db.issues().insert(rule, project, changedComponent1);
-    IssueDto changedIssue2 = db.issues().insert(rule, project, changedComponent1);
-    IssueDto changedIssue3 = db.issues().insert(rule, project, ChangedComponent2);
-    db.issues().insert(rule, project, unchangedComponent);
-    db.issues().insert(rule, project, unchangedComponent);
-
-    underTest.indexOnAnalysis(project.uuid(), Set.of(unchangedComponent.uuid()));
+    ComponentDto mainBranchComponent = db.components().insertPrivateProject().getMainBranchComponent();
+    ComponentDto changedComponent1 = db.components().insertComponent(newFileDto(mainBranchComponent));
+    ComponentDto unchangedComponent = db.components().insertComponent(newFileDto(mainBranchComponent));
+    ComponentDto ChangedComponent2 = db.components().insertComponent(newFileDto(mainBranchComponent));
+    IssueDto changedIssue1 = db.issues().insert(rule, mainBranchComponent, changedComponent1);
+    IssueDto changedIssue2 = db.issues().insert(rule, mainBranchComponent, changedComponent1);
+    IssueDto changedIssue3 = db.issues().insert(rule, mainBranchComponent, ChangedComponent2);
+    db.issues().insert(rule, mainBranchComponent, unchangedComponent);
+    db.issues().insert(rule, mainBranchComponent, unchangedComponent);
+
+    underTest.indexOnAnalysis(mainBranchComponent.uuid(), Set.of(unchangedComponent.uuid()));
 
     assertThatIndexHasOnly(changedIssue1, changedIssue2, changedIssue3);
   }
@@ -570,12 +591,12 @@ public class IssueIndexerIT {
   @Test
   public void indexOnAnalysis_whenEmptyUnchangedComponents_shouldReindexEverything() {
     RuleDto rule = db.rules().insert();
-    ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
-    ComponentDto changedComponent = db.components().insertComponent(newFileDto(project));
-    IssueDto changedIssue1 = db.issues().insert(rule, project, changedComponent);
-    IssueDto changedIssue2 = db.issues().insert(rule, project, changedComponent);
+    ComponentDto mainBranchComponent = db.components().insertPrivateProject().getMainBranchComponent();
+    ComponentDto changedComponent = db.components().insertComponent(newFileDto(mainBranchComponent));
+    IssueDto changedIssue1 = db.issues().insert(rule, mainBranchComponent, changedComponent);
+    IssueDto changedIssue2 = db.issues().insert(rule, mainBranchComponent, changedComponent);
 
-    underTest.indexOnAnalysis(project.uuid(), Set.of());
+    underTest.indexOnAnalysis(mainBranchComponent.uuid(), Set.of());
 
     assertThatIndexHasOnly(changedIssue1, changedIssue2);
   }
@@ -583,17 +604,17 @@ public class IssueIndexerIT {
   @Test
   public void indexOnAnalysis_whenChangedComponentWithoutIssue_shouldReindexNothing() {
     db.rules().insert();
-    ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
-    db.components().insertComponent(newFileDto(project));
+    ComponentDto mainBranchComponent = db.components().insertPrivateProject().getMainBranchComponent();
+    db.components().insertComponent(newFileDto(mainBranchComponent));
 
-    underTest.indexOnAnalysis(project.uuid(), Set.of());
+    underTest.indexOnAnalysis(mainBranchComponent.uuid(), Set.of());
 
     assertThat(es.getDocuments(TYPE_ISSUE)).isEmpty();
   }
 
-  private void addIssueToIndex(String projectUuid, String issueKey) {
+  private void addIssueToIndex(String projectUuid, String branchUuid, String issueKey) {
     es.putDocuments(TYPE_ISSUE,
-      newDoc().setKey(issueKey).setProjectUuid(projectUuid));
+      newDoc().setKey(issueKey).setProjectUuid(projectUuid).setBranchUuid(branchUuid));
   }
 
   private void assertThatIndexHasSize(long expectedSize) {
index be0271154eeb3267d90aedbe76fb165ab4b7bd07..c186dc93fac2140bcf0500611d32c6da7fc379ac 100644 (file)
@@ -37,8 +37,8 @@ import org.sonar.db.component.SnapshotDto;
 import org.sonar.db.es.EsQueueDto;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.server.es.EsTester;
+import org.sonar.server.es.Indexers;
 import org.sonar.server.es.IndexingResult;
-import org.sonar.server.es.ProjectIndexer;
 import org.sonar.server.permission.index.AuthorizationScope;
 import org.sonar.server.permission.index.IndexPermissions;
 
@@ -52,10 +52,10 @@ import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
 import static org.sonar.server.es.EsClient.prepareSearch;
 import static org.sonar.server.es.IndexType.FIELD_INDEX_TYPE;
-import static org.sonar.server.es.ProjectIndexer.Cause.PROJECT_CREATION;
-import static org.sonar.server.es.ProjectIndexer.Cause.PROJECT_DELETION;
-import static org.sonar.server.es.ProjectIndexer.Cause.PROJECT_KEY_UPDATE;
-import static org.sonar.server.es.ProjectIndexer.Cause.PROJECT_TAGS_UPDATE;
+import static org.sonar.server.es.Indexers.EntityEvent.CREATION;
+import static org.sonar.server.es.Indexers.EntityEvent.DELETION;
+import static org.sonar.server.es.Indexers.EntityEvent.PROJECT_KEY_UPDATE;
+import static org.sonar.server.es.Indexers.EntityEvent.PROJECT_TAGS_UPDATE;
 import static org.sonar.server.measure.index.ProjectMeasuresIndexDefinition.FIELD_QUALIFIER;
 import static org.sonar.server.measure.index.ProjectMeasuresIndexDefinition.FIELD_TAGS;
 import static org.sonar.server.measure.index.ProjectMeasuresIndexDefinition.FIELD_UUID;
@@ -74,12 +74,12 @@ public class ProjectMeasuresIndexerIT {
   private final ProjectMeasuresIndexer underTest = new ProjectMeasuresIndexer(db.getDbClient(), es.client());
 
   @Test
-  public void test_getAuthorizationScope() {
+  public void getAuthorizationScope_shouldReturnTrueForProjectAndApp() {
     AuthorizationScope scope = underTest.getAuthorizationScope();
     assertThat(scope.getIndexType().getIndex()).isEqualTo(ProjectMeasuresIndexDefinition.DESCRIPTOR);
     assertThat(scope.getIndexType().getType()).isEqualTo(TYPE_AUTHORIZATION);
 
-    Predicate<IndexPermissions> projectPredicate = scope.getProjectPredicate();
+    Predicate<IndexPermissions> projectPredicate = scope.getEntityPredicate();
     IndexPermissions project = new IndexPermissions("P1", Qualifiers.PROJECT);
     IndexPermissions app = new IndexPermissions("P1", Qualifiers.APP);
     IndexPermissions file = new IndexPermissions("F1", Qualifiers.FILE);
@@ -89,14 +89,14 @@ public class ProjectMeasuresIndexerIT {
   }
 
   @Test
-  public void index_nothing() {
+  public void indexOnStartup_whenNoEntities_shouldNotIndexAnything() {
     underTest.indexOnStartup(emptySet());
 
     assertThat(es.countDocuments(TYPE_PROJECT_MEASURES)).isZero();
   }
 
   @Test
-  public void indexOnStartup_indexes_all_projects() {
+  public void indexOnStartup_shouldIndexAllProjects() {
     SnapshotDto project1 = db.components().insertProjectAndSnapshot(newPrivateProjectDto());
     SnapshotDto project2 = db.components().insertProjectAndSnapshot(newPrivateProjectDto());
     SnapshotDto project3 = db.components().insertProjectAndSnapshot(newPrivateProjectDto());
@@ -205,7 +205,7 @@ public class ProjectMeasuresIndexerIT {
   public void update_index_when_project_is_created() {
     ProjectDto project = db.components().insertPrivateProject().getProjectDto();
 
-    IndexingResult result = indexProject(project, PROJECT_CREATION);
+    IndexingResult result = indexProject(project, CREATION);
 
     assertThatIndexContainsOnly(project);
     assertThat(result.getTotal()).isOne();
@@ -215,12 +215,11 @@ public class ProjectMeasuresIndexerIT {
   @Test
   public void update_index_when_project_tags_are_updated() {
     ProjectDto project = db.components().insertPrivateProject(defaults(), p -> p.setTagsString("foo")).getProjectDto();
-    indexProject(project, PROJECT_CREATION);
+    indexProject(project, CREATION);
     assertThatProjectHasTag(project, "foo");
 
     project.setTagsString("bar");
     db.getDbClient().projectDao().updateTags(db.getSession(), project);
-    // TODO change indexing?
     IndexingResult result = indexProject(project, PROJECT_TAGS_UPDATE);
 
     assertThatProjectHasTag(project, "bar");
@@ -231,11 +230,11 @@ public class ProjectMeasuresIndexerIT {
   @Test
   public void delete_doc_from_index_when_project_is_deleted() {
     ProjectDto project = db.components().insertPrivateProject().getProjectDto();
-    indexProject(project, PROJECT_CREATION);
+    indexProject(project, CREATION);
     assertThatIndexContainsOnly(project);
 
     db.getDbClient().purgeDao().deleteProject(db.getSession(), project.getUuid(), Qualifiers.PROJECT, project.getName(), project.getKey());
-    IndexingResult result = indexProject(project, PROJECT_DELETION);
+    IndexingResult result = indexProject(project, DELETION);
 
     assertThat(es.countDocuments(TYPE_PROJECT_MEASURES)).isZero();
     assertThat(result.getTotal()).isOne();
@@ -258,7 +257,7 @@ public class ProjectMeasuresIndexerIT {
     ProjectDto project = db.components().insertPrivateProject().getProjectDto();
     es.lockWrites(TYPE_PROJECT_MEASURES);
 
-    IndexingResult result = indexProject(project, PROJECT_CREATION);
+    IndexingResult result = indexProject(project, CREATION);
     assertThat(result.getTotal()).isOne();
     assertThat(result.getFailures()).isOne();
 
@@ -288,9 +287,9 @@ public class ProjectMeasuresIndexerIT {
     assertThat(es.countDocuments(TYPE_PROJECT_MEASURES)).isZero();
   }
 
-  private IndexingResult indexProject(ProjectDto project, ProjectIndexer.Cause cause) {
+  private IndexingResult indexProject(ProjectDto project, Indexers.EntityEvent cause) {
     DbSession dbSession = db.getSession();
-    Collection<EsQueueDto> items = underTest.prepareForRecovery(dbSession, singletonList(project.getUuid()), cause);
+    Collection<EsQueueDto> items = underTest.prepareForRecoveryOnEntityEvent(dbSession, singletonList(project.getUuid()), cause);
     dbSession.commit();
     return underTest.index(dbSession, items);
   }
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/component/index/ComponentIndexer.java b/server/sonar-server-common/src/main/java/org/sonar/server/component/index/ComponentIndexer.java
deleted file mode 100644 (file)
index 1fbda41..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.index;
-
-import com.google.common.annotations.VisibleForTesting;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import org.elasticsearch.action.search.SearchRequest;
-import org.elasticsearch.index.query.QueryBuilders;
-import org.elasticsearch.search.builder.SearchSourceBuilder;
-import org.sonar.core.util.stream.MoreCollectors;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.component.BranchDto;
-import org.sonar.db.entity.EntityDto;
-import org.sonar.db.es.EsQueueDto;
-import org.sonar.server.es.BaseDoc;
-import org.sonar.server.es.BulkIndexer;
-import org.sonar.server.es.BulkIndexer.Size;
-import org.sonar.server.es.EsClient;
-import org.sonar.server.es.IndexType;
-import org.sonar.server.es.IndexingResult;
-import org.sonar.server.es.OneToManyResilientIndexingListener;
-import org.sonar.server.es.ProjectIndexer;
-import org.sonar.server.permission.index.AuthorizationDoc;
-import org.sonar.server.permission.index.AuthorizationScope;
-import org.sonar.server.permission.index.NeedAuthorizationIndexer;
-
-import static java.util.Collections.emptyList;
-import static org.sonar.server.component.index.ComponentIndexDefinition.TYPE_COMPONENT;
-
-public class ComponentIndexer implements ProjectIndexer, NeedAuthorizationIndexer {
-
-  private static final AuthorizationScope AUTHORIZATION_SCOPE = new AuthorizationScope(TYPE_COMPONENT, project -> true);
-  private static final Set<IndexType> INDEX_TYPES = Set.of(TYPE_COMPONENT);
-
-  private final DbClient dbClient;
-  private final EsClient esClient;
-
-  public ComponentIndexer(DbClient dbClient, EsClient esClient) {
-    this.dbClient = dbClient;
-    this.esClient = esClient;
-  }
-
-  @Override
-  public Set<IndexType> getIndexTypes() {
-    return INDEX_TYPES;
-  }
-
-  @Override
-  public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
-    doIndexByEntityUuid(Size.LARGE);
-  }
-
-  public void indexAll() {
-    doIndexByEntityUuid(Size.REGULAR);
-  }
-
-  @Override
-  public void indexOnAnalysis(String branchUuid) {
-    indexOnAnalysis(branchUuid, Set.of());
-  }
-
-  @Override
-  public void indexOnAnalysis(String branchUuid, Set<String> unchangedComponentUuids) {
-    try (DbSession dbSession = dbClient.openSession(false)) {
-      Optional<BranchDto> branchDto = dbClient.branchDao().selectByUuid(dbSession, branchUuid);
-
-      if (branchDto.isPresent() && !branchDto.get().isMain()) {
-        return;
-      }
-      EntityDto entity = dbClient.entityDao().selectByComponentUuid(dbSession, branchUuid)
-        .orElseThrow(() -> new IllegalStateException("Can't find entity " + branchUuid));
-      doIndexByEntityUuid(entity);
-    }
-  }
-
-  @Override
-  public AuthorizationScope getAuthorizationScope() {
-    return AUTHORIZATION_SCOPE;
-  }
-
-  @Override
-  public Collection<EsQueueDto> prepareForRecovery(DbSession dbSession, Collection<String> projectUuids, Cause cause) {
-    switch (cause) {
-      case MEASURE_CHANGE, PROJECT_TAGS_UPDATE, PERMISSION_CHANGE:
-        // measures, tags and permissions are not part of type components/component
-        return emptyList();
-      case PROJECT_CREATION, PROJECT_DELETION, PROJECT_KEY_UPDATE:
-        List<EsQueueDto> items = projectUuids.stream()
-          .map(projectUuid -> EsQueueDto.create(TYPE_COMPONENT.format(), projectUuid, null, projectUuid))
-          .collect(MoreCollectors.toArrayList(projectUuids.size()));
-        return dbClient.esQueueDao().insert(dbSession, items);
-      default:
-        // defensive case
-        throw new IllegalStateException("Unsupported cause: " + cause);
-    }
-  }
-
-  @Override
-  public IndexingResult index(DbSession dbSession, Collection<EsQueueDto> items) {
-    if (items.isEmpty()) {
-      return new IndexingResult();
-    }
-
-    OneToManyResilientIndexingListener listener = new OneToManyResilientIndexingListener(dbClient, dbSession, items);
-    BulkIndexer bulkIndexer = new BulkIndexer(esClient, TYPE_COMPONENT, Size.REGULAR, listener);
-    bulkIndexer.start();
-    Set<String> entityUuids = items.stream().map(EsQueueDto::getDocId).collect(MoreCollectors.toHashSet(items.size()));
-    Set<String> remaining = new HashSet<>(entityUuids);
-
-    for (String entityUuid : entityUuids) {
-      dbClient.entityDao().scrollForIndexing(dbSession, entityUuid, context -> {
-        EntityDto dto = context.getResultObject();
-        remaining.remove(dto.getUuid());
-        bulkIndexer.add(toDocument(dto).toIndexRequest());
-      });
-    }
-
-    // the remaining uuids reference projects that don't exist in db. They must
-    // be deleted from index.
-    remaining.forEach(projectUuid -> addProjectDeletionToBulkIndexer(bulkIndexer, projectUuid));
-
-    return bulkIndexer.stop();
-  }
-
-  /**
-   * @param entity the entity to analyze, or {@code null} if all content should be indexed.<br/>
-   *               <b>Warning:</b> only use {@code null} during startup.
-   */
-  private void doIndexByEntityUuid(EntityDto entity) {
-    BulkIndexer bulk = new BulkIndexer(esClient, TYPE_COMPONENT, Size.REGULAR);
-    bulk.start();
-
-    try (DbSession dbSession = dbClient.openSession(false)) {
-      bulk.add(toDocument(entity).toIndexRequest());
-
-      if (entity.getQualifier().equals("VW")) {
-        dbClient.portfolioDao().selectTree(dbSession, entity.getUuid()).forEach(sub ->
-          bulk.add(toDocument(sub).toIndexRequest()));
-      }
-    }
-
-    bulk.stop();
-  }
-
-  private void doIndexByEntityUuid(Size bulkSize) {
-    BulkIndexer bulk = new BulkIndexer(esClient, TYPE_COMPONENT, bulkSize);
-    bulk.start();
-    try (DbSession dbSession = dbClient.openSession(false)) {
-      dbClient.entityDao().scrollForIndexing(dbSession, null, context -> {
-        EntityDto dto = context.getResultObject();
-        bulk.add(toDocument(dto).toIndexRequest());
-      });
-    }
-
-    bulk.stop();
-  }
-
-  private static void addProjectDeletionToBulkIndexer(BulkIndexer bulkIndexer, String projectUuid) {
-    SearchRequest searchRequest = EsClient.prepareSearch(TYPE_COMPONENT.getMainType())
-      .source(new SearchSourceBuilder().query(QueryBuilders.termQuery(ComponentIndexDefinition.FIELD_UUID, projectUuid)))
-      .routing(AuthorizationDoc.idOf(projectUuid));
-    bulkIndexer.addDeletion(searchRequest);
-  }
-
-  public void delete(String projectUuid, Collection<String> disabledComponentUuids) {
-    BulkIndexer bulk = new BulkIndexer(esClient, TYPE_COMPONENT, Size.REGULAR);
-    bulk.start();
-    disabledComponentUuids.forEach(uuid -> bulk.addDeletion(TYPE_COMPONENT, uuid, AuthorizationDoc.idOf(projectUuid)));
-    bulk.stop();
-  }
-
-  @VisibleForTesting
-  void index(EntityDto... docs) {
-    BulkIndexer bulk = new BulkIndexer(esClient, TYPE_COMPONENT, Size.REGULAR);
-    bulk.start();
-    Arrays.stream(docs)
-      .map(ComponentIndexer::toDocument)
-      .map(BaseDoc::toIndexRequest)
-      .forEach(bulk::add);
-    bulk.stop();
-  }
-
-  public static ComponentDoc toDocument(EntityDto entity) {
-    return new ComponentDoc()
-      .setId(entity.getUuid())
-      .setAuthUuid(entity.getAuthUuid())
-      .setName(entity.getName())
-      .setKey(entity.getKey())
-      .setQualifier(entity.getQualifier());
-  }
-}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/component/index/EntityDefinitionIndexer.java b/server/sonar-server-common/src/main/java/org/sonar/server/component/index/EntityDefinitionIndexer.java
new file mode 100644 (file)
index 0000000..3f7a7f2
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.index;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.elasticsearch.action.search.SearchRequest;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.search.builder.SearchSourceBuilder;
+import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.entity.EntityDto;
+import org.sonar.db.es.EsQueueDto;
+import org.sonar.server.es.AnalysisIndexer;
+import org.sonar.server.es.BaseDoc;
+import org.sonar.server.es.BulkIndexer;
+import org.sonar.server.es.BulkIndexer.Size;
+import org.sonar.server.es.EsClient;
+import org.sonar.server.es.EventIndexer;
+import org.sonar.server.es.IndexType;
+import org.sonar.server.es.Indexers;
+import org.sonar.server.es.IndexingResult;
+import org.sonar.server.es.OneToManyResilientIndexingListener;
+import org.sonar.server.permission.index.AuthorizationDoc;
+import org.sonar.server.permission.index.AuthorizationScope;
+import org.sonar.server.permission.index.NeedAuthorizationIndexer;
+
+import static java.util.Collections.emptyList;
+import static org.sonar.server.component.index.ComponentIndexDefinition.TYPE_COMPONENT;
+
+/**
+ * Indexes the definition of all entities: projects, applications, portfolios and sub-portfolios.
+ */
+public class EntityDefinitionIndexer implements EventIndexer, AnalysisIndexer, NeedAuthorizationIndexer {
+
+  private static final AuthorizationScope AUTHORIZATION_SCOPE = new AuthorizationScope(TYPE_COMPONENT, entity -> true);
+  private static final Set<IndexType> INDEX_TYPES = Set.of(TYPE_COMPONENT);
+
+  private final DbClient dbClient;
+  private final EsClient esClient;
+
+  public EntityDefinitionIndexer(DbClient dbClient, EsClient esClient) {
+    this.dbClient = dbClient;
+    this.esClient = esClient;
+  }
+
+  @Override
+  public Set<IndexType> getIndexTypes() {
+    return INDEX_TYPES;
+  }
+
+  @Override
+  public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
+    doIndexByEntityUuid(Size.LARGE);
+  }
+
+  public void indexAll() {
+    doIndexByEntityUuid(Size.REGULAR);
+  }
+
+  @Override
+  public void indexOnAnalysis(String branchUuid) {
+    indexOnAnalysis(branchUuid, Set.of());
+  }
+
+  @Override
+  public void indexOnAnalysis(String branchUuid, Set<String> unchangedComponentUuids) {
+    try (DbSession dbSession = dbClient.openSession(false)) {
+      Optional<BranchDto> branchDto = dbClient.branchDao().selectByUuid(dbSession, branchUuid);
+
+      if (branchDto.isPresent() && !branchDto.get().isMain()) {
+        return;
+      }
+      EntityDto entity = dbClient.entityDao().selectByComponentUuid(dbSession, branchUuid)
+        .orElseThrow(() -> new IllegalStateException("Can't find entity for branch " + branchUuid));
+      doIndexByEntityUuid(entity);
+    }
+  }
+
+  @Override
+  public AuthorizationScope getAuthorizationScope() {
+    return AUTHORIZATION_SCOPE;
+  }
+
+  @Override
+  public Collection<EsQueueDto> prepareForRecoveryOnEntityEvent(DbSession dbSession, Collection<String> entityUuids, Indexers.EntityEvent cause) {
+    return switch (cause) {
+      case PROJECT_TAGS_UPDATE, PERMISSION_CHANGE ->
+        // measures, tags and permissions does not affect the definition of entities
+        emptyList();
+      case CREATION, DELETION, PROJECT_KEY_UPDATE -> {
+        List<EsQueueDto> items = entityUuids.stream()
+          .map(entityUuid -> EsQueueDto.create(TYPE_COMPONENT.format(), entityUuid, null, entityUuid))
+          .collect(MoreCollectors.toArrayList(entityUuids.size()));
+        yield dbClient.esQueueDao().insert(dbSession, items);
+      }
+    };
+  }
+
+  @Override
+  public Collection<EsQueueDto> prepareForRecoveryOnBranchEvent(DbSession dbSession, Collection<String> branchUuids, Indexers.BranchEvent cause) {
+    return emptyList();
+  }
+
+  @Override
+  public IndexingResult index(DbSession dbSession, Collection<EsQueueDto> items) {
+    if (items.isEmpty()) {
+      return new IndexingResult();
+    }
+
+    OneToManyResilientIndexingListener listener = new OneToManyResilientIndexingListener(dbClient, dbSession, items);
+    BulkIndexer bulkIndexer = new BulkIndexer(esClient, TYPE_COMPONENT, Size.REGULAR, listener);
+    bulkIndexer.start();
+    Set<String> entityUuids = items.stream().map(EsQueueDto::getDocId).collect(MoreCollectors.toHashSet(items.size()));
+    Set<String> remaining = new HashSet<>(entityUuids);
+
+    dbClient.entityDao().selectByUuids(dbSession, entityUuids).forEach(dto -> {
+      remaining.remove(dto.getUuid());
+      bulkIndexer.add(toDocument(dto).toIndexRequest());
+    });
+
+    // the remaining uuids reference projects that don't exist in db. They must
+    // be deleted from index.
+    remaining.forEach(projectUuid -> addProjectDeletionToBulkIndexer(bulkIndexer, projectUuid));
+
+    return bulkIndexer.stop();
+  }
+
+  /**
+   * @param entity the entity to analyze, or {@code null} if all content should be indexed.<br/>
+   *               <b>Warning:</b> only use {@code null} during startup.
+   */
+  private void doIndexByEntityUuid(EntityDto entity) {
+    BulkIndexer bulk = new BulkIndexer(esClient, TYPE_COMPONENT, Size.REGULAR);
+    bulk.start();
+
+    try (DbSession dbSession = dbClient.openSession(false)) {
+      bulk.add(toDocument(entity).toIndexRequest());
+
+      if (entity.getQualifier().equals("VW")) {
+        dbClient.portfolioDao().selectTree(dbSession, entity.getUuid()).forEach(sub ->
+          bulk.add(toDocument(sub).toIndexRequest()));
+      }
+    }
+
+    bulk.stop();
+  }
+
+  private void doIndexByEntityUuid(Size bulkSize) {
+    BulkIndexer bulk = new BulkIndexer(esClient, TYPE_COMPONENT, bulkSize);
+    bulk.start();
+    try (DbSession dbSession = dbClient.openSession(false)) {
+      dbClient.entityDao().scrollForIndexing(dbSession, context -> {
+        EntityDto dto = context.getResultObject();
+        bulk.add(toDocument(dto).toIndexRequest());
+      });
+    }
+
+    bulk.stop();
+  }
+
+  private static void addProjectDeletionToBulkIndexer(BulkIndexer bulkIndexer, String projectUuid) {
+    SearchRequest searchRequest = EsClient.prepareSearch(TYPE_COMPONENT.getMainType())
+      .source(new SearchSourceBuilder().query(QueryBuilders.termQuery(ComponentIndexDefinition.FIELD_UUID, projectUuid)))
+      .routing(AuthorizationDoc.idOf(projectUuid));
+    bulkIndexer.addDeletion(searchRequest);
+  }
+
+  public void delete(String projectUuid, Collection<String> disabledComponentUuids) {
+    BulkIndexer bulk = new BulkIndexer(esClient, TYPE_COMPONENT, Size.REGULAR);
+    bulk.start();
+    disabledComponentUuids.forEach(uuid -> bulk.addDeletion(TYPE_COMPONENT, uuid, AuthorizationDoc.idOf(projectUuid)));
+    bulk.stop();
+  }
+
+  @VisibleForTesting
+  void index(EntityDto... docs) {
+    BulkIndexer bulk = new BulkIndexer(esClient, TYPE_COMPONENT, Size.REGULAR);
+    bulk.start();
+    Arrays.stream(docs)
+      .map(EntityDefinitionIndexer::toDocument)
+      .map(BaseDoc::toIndexRequest)
+      .forEach(bulk::add);
+    bulk.stop();
+  }
+
+  public static ComponentDoc toDocument(EntityDto entity) {
+    return new ComponentDoc()
+      .setId(entity.getUuid())
+      .setAuthUuid(entity.getAuthUuid())
+      .setName(entity.getName())
+      .setKey(entity.getKey())
+      .setQualifier(entity.getQualifier());
+  }
+}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/es/AnalysisIndexer.java b/server/sonar-server-common/src/main/java/org/sonar/server/es/AnalysisIndexer.java
new file mode 100644 (file)
index 0000000..a37ce7e
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.es;
+
+import java.util.Set;
+
+/**
+ * Indexers that should be called when a project branch is analyzed
+ */
+public interface AnalysisIndexer {
+  /**
+   * This method is called when an analysis must be indexed.
+   *
+   * @param branchUuid UUID of a project or application branch
+   */
+  void indexOnAnalysis(String branchUuid);
+
+  /**
+   * This method is called when an analysis must be indexed.
+   *
+   * @param branchUuid UUID of a project or application branch
+   * @param unchangedComponentUuids UUIDs of components that didn't change in this analysis.
+   *                                Indexers can be optimized by not re-indexing data related to these components.
+   */
+  void indexOnAnalysis(String branchUuid, Set<String> unchangedComponentUuids);
+}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/es/BranchIndexer.java b/server/sonar-server-common/src/main/java/org/sonar/server/es/BranchIndexer.java
new file mode 100644 (file)
index 0000000..5aafc90
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.es;
+
+import java.util.Collection;
+import java.util.Set;
+import org.sonar.db.DbSession;
+import org.sonar.db.es.EsQueueDto;
+
+public interface BranchIndexer extends ResilientIndexer {
+
+  enum Cause {
+    CREATION,
+    DELETION,
+    MEASURE_CHANGE
+  }
+
+  /**
+   * This method is called when an analysis must be indexed.
+   *
+   * @param branchUuid UUID of a project or application branch, or the UUID of a portfolio.
+   */
+  void indexBranchOnAnalysis(String branchUuid);
+
+  void indexBranchOnAnalysis(String branchUuid, Set<String> unchangedComponentUuids);
+
+  Collection<EsQueueDto> prepareBranchIndexForRecovery(DbSession dbSession, Collection<String> branchUuids, BranchIndexer.Cause cause);
+}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/es/EventIndexer.java b/server/sonar-server-common/src/main/java/org/sonar/server/es/EventIndexer.java
new file mode 100644 (file)
index 0000000..3b1718c
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.es;
+
+import java.util.Collection;
+import org.sonar.db.DbSession;
+import org.sonar.db.es.EsQueueDto;
+
+/**
+ * A {@link EventIndexer} populates an Elasticsearch index
+ * based on events related to entities and branches. This interface allows to quickly integrate new
+ * indices in the lifecycle of entities and branches.
+ *
+ * If the related index handles verification of authorization,
+ * then the implementation of {@link EventIndexer} must
+ * also implement {@link org.sonar.server.permission.index.NeedAuthorizationIndexer}
+ */
+public interface EventIndexer extends ResilientIndexer {
+  Collection<EsQueueDto> prepareForRecoveryOnEntityEvent(DbSession dbSession, Collection<String> entityUuids, Indexers.EntityEvent cause);
+
+  Collection<EsQueueDto> prepareForRecoveryOnBranchEvent(DbSession dbSession, Collection<String> branchUuids, Indexers.BranchEvent cause);
+
+}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/es/Indexers.java b/server/sonar-server-common/src/main/java/org/sonar/server/es/Indexers.java
new file mode 100644 (file)
index 0000000..78bf19c
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.es;
+
+import java.util.Collection;
+import java.util.stream.Collectors;
+import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.entity.EntityDto;
+
+public interface Indexers {
+  enum EntityEvent {
+    CREATION,
+    DELETION,
+    PROJECT_KEY_UPDATE,
+    PROJECT_TAGS_UPDATE,
+    PERMISSION_CHANGE
+  }
+
+  enum BranchEvent {
+    // Note that when a project/app is deleted, no events are sent for each branch removed as part of that process
+    DELETION,
+    MEASURE_CHANGE
+  }
+
+  /**
+   * Re-index data based on the event. It commits the DB session once any indexation request was written in the same session,
+   * ensuring consistency between the DB and the indexes. Therefore, DB data changes that cause the indexation event should
+   * be done using the same DB session and the session should be uncommitted.
+   */
+  void commitAndIndexOnEntityEvent(DbSession dbSession, Collection<String> entityUuids, EntityEvent cause);
+
+  /**
+   * Re-index data based on the event. It commits the DB session once any indexation request was written in the same session,
+   * ensuring consistency between the DB and the indexes. Therefore, DB data changes that cause the indexation event should
+   * be done using the same DB session and the session should be uncommitted.
+   */
+  void commitAndIndexOnBranchEvent(DbSession dbSession, Collection<String> branchUuids, BranchEvent cause);
+
+  /**
+   * Re-index data based on the event. It commits the DB session once any indexation request was written in the same session,
+   * ensuring consistency between the DB and the indexes. Therefore, DB data changes that cause the indexation event should
+   * be done using the same DB session and the session should be uncommitted.
+   */
+  default void commitAndIndexEntities(DbSession dbSession, Collection<? extends EntityDto> entities, EntityEvent cause) {
+    Collection<String> entityUuids = entities.stream()
+      .map(EntityDto::getUuid)
+      .collect(MoreCollectors.toSet(entities.size()));
+    commitAndIndexOnEntityEvent(dbSession, entityUuids, cause);
+  }
+
+  /**
+   * Re-index data based on the event. It commits the DB session once any indexation request was written in the same session,
+   * ensuring consistency between the DB and the indexes. Therefore, DB data changes that cause the indexation event should
+   * be done using the same DB session and the session should be uncommitted.
+   */
+  default void commitAndIndexBranches(DbSession dbSession, Collection<BranchDto> branches, BranchEvent cause) {
+    Collection<String> branchUuids = branches.stream()
+      .map(BranchDto::getUuid)
+      .collect(Collectors.toSet());
+    commitAndIndexOnBranchEvent(dbSession, branchUuids, cause);
+  }
+}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/es/IndexersImpl.java b/server/sonar-server-common/src/main/java/org/sonar/server/es/IndexersImpl.java
new file mode 100644 (file)
index 0000000..4df20b7
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.es;
+
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import org.sonar.db.DbSession;
+import org.sonar.db.es.EsQueueDto;
+
+import static java.util.Arrays.asList;
+
+/**
+ * Delegates events to indexers, that may want to reindex something based on the event.
+ */
+public class IndexersImpl implements Indexers {
+
+  private final List<EventIndexer> indexers;
+
+  public IndexersImpl(EventIndexer... indexers) {
+    this.indexers = asList(indexers);
+  }
+
+  /**
+   * Asks all indexers to queue an indexation request in the DB to index the specified entities, if needed (according to
+   * "cause" parameter), then call all indexers to index the requests.
+   * The idea is that the indexation requests are committed into the DB at the same time as the data that caused those requests
+   * to be created, for consistency.
+   * If the indexation fails, the indexation requests will still be in the DB and can be processed again later.
+   */
+  @Override
+  public void commitAndIndexOnEntityEvent(DbSession dbSession, Collection<String> entityUuids, EntityEvent cause) {
+    indexOnEvent(dbSession, indexer -> indexer.prepareForRecoveryOnEntityEvent(dbSession, entityUuids, cause));
+  }
+
+  /**
+   * Asks all indexers to queue an indexation request in the DB to index the specified branches, if needed (according to
+   * "cause" parameter), then call all indexers to index the requests.
+   * The idea is that the indexation requests are committed into the DB at the same time as the data that caused those requests
+   * to be created, for consistency.
+   * If the indexation fails, the indexation requests will still be in the DB and can be processed again later.
+   */
+  @Override
+  public void commitAndIndexOnBranchEvent(DbSession dbSession, Collection<String> branchUuids, BranchEvent cause) {
+    indexOnEvent(dbSession, indexer -> indexer.prepareForRecoveryOnBranchEvent(dbSession, branchUuids, cause));
+  }
+
+
+  private void indexOnEvent(DbSession dbSession, Function<EventIndexer, Collection<EsQueueDto>> esQueueSupplier) {
+    Map<EventIndexer, Collection<EsQueueDto>> itemsByIndexer = new IdentityHashMap<>();
+    indexers.forEach(i -> itemsByIndexer.put(i, esQueueSupplier.apply(i)));
+    dbSession.commit();
+
+    // ensure that indexer#index() is called only with the item type that it supports
+    itemsByIndexer.forEach((indexer, items) -> indexer.index(dbSession, items));
+  }
+}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/es/ProjectIndexer.java b/server/sonar-server-common/src/main/java/org/sonar/server/es/ProjectIndexer.java
deleted file mode 100644 (file)
index 1db14f0..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.es;
-
-import java.util.Collection;
-import java.util.Set;
-import org.sonar.db.DbSession;
-import org.sonar.db.es.EsQueueDto;
-
-/**
- * A {@link ProjectIndexer} populates an Elasticsearch index
- * containing project-related documents, for instance issues
- * or tests. This interface allows to quickly integrate new
- * indices in the lifecycle of projects.
- *
- * If the related index handles verification of authorization,
- * then the implementation of {@link ProjectIndexer} must
- * also implement {@link org.sonar.server.permission.index.NeedAuthorizationIndexer}
- */
-public interface ProjectIndexer extends ResilientIndexer {
-
-  enum Cause {
-    PROJECT_CREATION,
-    PROJECT_DELETION,
-    PROJECT_KEY_UPDATE,
-    PROJECT_TAGS_UPDATE,
-    PERMISSION_CHANGE,
-    MEASURE_CHANGE
-  }
-
-  /**
-   * This method is called when an analysis must be indexed.
-   *
-   * @param branchUuid UUID of a project or application branch, or the UUID of a portfolio.
-   */
-  void indexOnAnalysis(String branchUuid);
-
-  void indexOnAnalysis(String branchUuid, Set<String> unchangedComponentUuids);
-
-  Collection<EsQueueDto> prepareForRecovery(DbSession dbSession, Collection<String> projectUuids, ProjectIndexer.Cause cause);
-}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/es/ProjectIndexers.java b/server/sonar-server-common/src/main/java/org/sonar/server/es/ProjectIndexers.java
deleted file mode 100644 (file)
index abcbebb..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.es;
-
-import java.util.Collection;
-import org.sonar.core.util.stream.MoreCollectors;
-import org.sonar.db.DbSession;
-import org.sonar.db.component.BranchDto;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.entity.EntityDto;
-import org.sonar.db.project.ProjectDto;
-
-public interface ProjectIndexers {
-
-  /**
-   * Commits the DB transaction and indexes the specified projects, if needed (according to
-   * "cause" parameter).
-   * IMPORTANT - UUIDs must relate to applications and projects only. Modules, directories and files are forbidden
-   * and will lead to lack of indexing.
-   */
-  void commitAndIndexByProjectUuids(DbSession dbSession, Collection<String> projectUuids, ProjectIndexer.Cause cause);
-
-  default void commitAndIndexEntities(DbSession dbSession, Collection<? extends EntityDto> entities, ProjectIndexer.Cause cause) {
-    Collection<String> entityUuids = entities.stream()
-      .map(EntityDto::getUuid)
-      .collect(MoreCollectors.toSet(entities.size()));
-    commitAndIndexByProjectUuids(dbSession, entityUuids, cause);
-  }
-
-  default void commitAndIndexProjects(DbSession dbSession, Collection<ProjectDto> projects, ProjectIndexer.Cause cause) {
-    commitAndIndexEntities(dbSession, projects, cause);
-  }
-
-  default void commitAndIndexComponents(DbSession dbSession, Collection<ComponentDto> projects, ProjectIndexer.Cause cause) {
-    Collection<String> projectUuids = projects.stream()
-      .map(ComponentDto::branchUuid)
-      .collect(MoreCollectors.toSet(projects.size()));
-    commitAndIndexByProjectUuids(dbSession, projectUuids, cause);
-  }
-
-  default void commitAndIndexBranches(DbSession dbSession, Collection<BranchDto> branches, ProjectIndexer.Cause cause) {
-    Collection<String> branchUuids = branches.stream()
-      .map(BranchDto::getUuid)
-      .toList();
-    commitAndIndexByProjectUuids(dbSession, branchUuids, cause);
-  }
-}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/es/ProjectIndexersImpl.java b/server/sonar-server-common/src/main/java/org/sonar/server/es/ProjectIndexersImpl.java
deleted file mode 100644 (file)
index 5b35fdd..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.es;
-
-import java.util.Collection;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
-import org.sonar.db.DbSession;
-import org.sonar.db.es.EsQueueDto;
-
-import static java.util.Arrays.asList;
-
-public class ProjectIndexersImpl implements ProjectIndexers {
-
-  private final List<ProjectIndexer> indexers;
-
-  public ProjectIndexersImpl(ProjectIndexer... indexers) {
-    this.indexers = asList(indexers);
-  }
-
-  @Override
-  public void commitAndIndexByProjectUuids(DbSession dbSession, Collection<String> projectUuids, ProjectIndexer.Cause cause) {
-    Map<ProjectIndexer, Collection<EsQueueDto>> itemsByIndexer = new IdentityHashMap<>();
-    indexers.forEach(i -> itemsByIndexer.put(i, i.prepareForRecovery(dbSession, projectUuids, cause)));
-    dbSession.commit();
-
-    // ensure that indexer#index() is called only with the item type that it supports
-    itemsByIndexer.forEach((indexer, items) -> indexer.index(dbSession, items));
-  }
-}
index 6727f89825c59dfb86f1f1debaf907bff6e2fd35..3ec60e95236a5a2646b39acc671a2a331af11250 100644 (file)
@@ -24,13 +24,13 @@ import org.sonar.db.DbSession;
 import org.sonar.db.es.EsQueueDto;
 
 /**
- * Indexers that are resilient
+ * Indexers that are resilient. These indexers handle indexation items that are queued in the DB.
  */
 public interface ResilientIndexer extends StartupIndexer {
 
   /**
    * Index the items and delete them from es_queue DB table when the indexation
-   * is done, keeping them if there is a failure on the item of the collection
+   * is done. If there is a failure, the items are kept in DB to be re-processed later.
    *
    * @param dbSession the db session
    * @param items     the items to be indexed
index 9dc5e06a2ffa7d36402f9cbea31c5a9076a59547..497adf2b28ecb5088fb3882ae5556b999f165e89 100644 (file)
@@ -34,7 +34,7 @@ public interface StartupIndexer {
   }
 
   default void triggerAsyncIndexOnStartup(Set<IndexType> uninitializedIndexTypes) {
-    throw new IllegalStateException("ASYNCHRONE StartupIndexer must implement initAsyncIndexOnStartup");
+    throw new IllegalStateException("ASYNCHRONOUS StartupIndexer must implement initAsyncIndexOnStartup");
   }
 
   /**
@@ -42,7 +42,7 @@ public interface StartupIndexer {
    * if there is at least one uninitialized type.
    */
   default void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
-    throw new IllegalStateException("SYNCHRONE StartupIndexer must implement indexOnStartup");
+    throw new IllegalStateException("SYNCHRONOUS StartupIndexer must implement indexOnStartup");
   }
 
   Set<IndexType> getIndexTypes();
index d446c4e491d5d539718381758e3011681f429235..a058dfdb287f79d7346a08455ce52bce425b6f33 100644 (file)
@@ -25,27 +25,31 @@ import com.google.common.collect.ListMultimap;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 import org.elasticsearch.action.index.IndexRequest;
 import org.elasticsearch.action.search.SearchRequest;
 import org.elasticsearch.search.builder.SearchSourceBuilder;
-import org.sonar.api.resources.Qualifiers;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.api.resources.Qualifiers;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchDto;
 import org.sonar.db.es.EsQueueDto;
 import org.sonar.db.issue.IssueDto;
+import org.sonar.server.es.AnalysisIndexer;
 import org.sonar.server.es.BulkIndexer;
 import org.sonar.server.es.BulkIndexer.Size;
 import org.sonar.server.es.EsClient;
+import org.sonar.server.es.EventIndexer;
 import org.sonar.server.es.IndexType;
+import org.sonar.server.es.Indexers;
 import org.sonar.server.es.IndexingListener;
 import org.sonar.server.es.IndexingResult;
 import org.sonar.server.es.OneToManyResilientIndexingListener;
 import org.sonar.server.es.OneToOneResilientIndexingListener;
-import org.sonar.server.es.ProjectIndexer;
 import org.sonar.server.permission.index.AuthorizationDoc;
 import org.sonar.server.permission.index.AuthorizationScope;
 import org.sonar.server.permission.index.NeedAuthorizationIndexer;
@@ -53,21 +57,31 @@ import org.sonar.server.permission.index.NeedAuthorizationIndexer;
 import static java.util.Collections.emptyList;
 import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
 import static org.elasticsearch.index.query.QueryBuilders.termQuery;
+import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID;
 import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID;
 import static org.sonar.server.issue.index.IssueIndexDefinition.TYPE_ISSUE;
 
-public class IssueIndexer implements ProjectIndexer, NeedAuthorizationIndexer {
+/**
+ * Indexes issues. All issues belong directly to a project branch, so they only change when a project branch changes.
+ */
+public class IssueIndexer implements EventIndexer, AnalysisIndexer, NeedAuthorizationIndexer {
 
   /**
    * Indicates that es_queue.doc_id references an issue. Only this issue must be indexed.
    */
   private static final String ID_TYPE_ISSUE_KEY = "issueKey";
   /**
-   * Indicates that es_queue.doc_id references a project. All the issues of the project must be indexed.
+   * Indicates that es_queue.doc_id references a branch. All the issues of the branch must be indexed.
+   * Note that the constant is misleading, but we can't update it since there might some items in the DB during the upgrade.
+   */
+  private static final String ID_TYPE_BRANCH_UUID = "projectUuid";
+  /**
+   * Indicates that es_queue.doc_id references a project and that all issues in it should be delete.
    */
-  private static final String ID_TYPE_PROJECT_UUID = "projectUuid";
+  private static final String ID_TYPE_DELETE_PROJECT_UUID = "deleteProjectUuid";
+
   private static final Logger LOGGER = LoggerFactory.getLogger(IssueIndexer.class);
-  private static final AuthorizationScope AUTHORIZATION_SCOPE = new AuthorizationScope(TYPE_ISSUE, project -> Qualifiers.PROJECT.equals(project.getQualifier()));
+  private static final AuthorizationScope AUTHORIZATION_SCOPE = new AuthorizationScope(TYPE_ISSUE, entity -> Qualifiers.PROJECT.equals(entity.getQualifier()));
   private static final Set<IndexType> INDEX_TYPES = Set.of(TYPE_ISSUE);
 
   private final EsClient esClient;
@@ -127,23 +141,44 @@ public class IssueIndexer implements ProjectIndexer, NeedAuthorizationIndexer {
   }
 
   @Override
-  public Collection<EsQueueDto> prepareForRecovery(DbSession dbSession, Collection<String> projectUuids, ProjectIndexer.Cause cause) {
-    switch (cause) {
-      case PROJECT_CREATION, MEASURE_CHANGE, PROJECT_KEY_UPDATE, PROJECT_TAGS_UPDATE, PERMISSION_CHANGE:
+  public Collection<EsQueueDto> prepareForRecoveryOnEntityEvent(DbSession dbSession, Collection<String> entityUuids, Indexers.EntityEvent cause) {
+    return switch (cause) {
+      case CREATION, PROJECT_KEY_UPDATE, PROJECT_TAGS_UPDATE, PERMISSION_CHANGE ->
         // Nothing to do, issues do not exist at project creation
         // Measures, permissions, project key and tags are not used in type issues/issue
-        return emptyList();
+        emptyList();
 
-      case PROJECT_DELETION:
-        List<EsQueueDto> items = projectUuids.stream()
-          .map(projectUuid -> createQueueDto(projectUuid, ID_TYPE_PROJECT_UUID, projectUuid))
-          .collect(MoreCollectors.toArrayList(projectUuids.size()));
-        return dbClient.esQueueDao().insert(dbSession, items);
+      case DELETION -> {
+        List<EsQueueDto> items = createProjectDeleteRecoveryItems(entityUuids);
+        yield dbClient.esQueueDao().insert(dbSession, items);
+      }
+    };
+  }
 
-      default:
-        // defensive case
-        throw new IllegalStateException("Unsupported cause: " + cause);
-    }
+  @Override
+  public Collection<EsQueueDto> prepareForRecoveryOnBranchEvent(DbSession dbSession, Collection<String> branchUuids, Indexers.BranchEvent cause) {
+    return switch (cause) {
+      case MEASURE_CHANGE ->
+        // Measures, permissions, project key and tags are not used in type issues/issue
+        emptyList();
+
+      case DELETION -> {
+        List<EsQueueDto> items = createBranchRecoveryItems(branchUuids);
+        yield dbClient.esQueueDao().insert(dbSession, items);
+      }
+    };
+  }
+
+  private List<EsQueueDto> createProjectDeleteRecoveryItems(Collection<String> entityUuids) {
+    return entityUuids.stream()
+      .map(entityUuid -> createQueueDto(entityUuid, ID_TYPE_DELETE_PROJECT_UUID, entityUuid))
+      .collect(Collectors.toList());
+  }
+
+  private List<EsQueueDto> createBranchRecoveryItems(Collection<String> branchUuids) {
+    return branchUuids.stream()
+      .map(branchUuid -> createQueueDto(branchUuid, ID_TYPE_BRANCH_UUID, branchUuid))
+      .collect(Collectors.toList());
   }
 
   /**
@@ -170,12 +205,16 @@ public class IssueIndexer implements ProjectIndexer, NeedAuthorizationIndexer {
   @Override
   public IndexingResult index(DbSession dbSession, Collection<EsQueueDto> items) {
     ListMultimap<String, EsQueueDto> itemsByIssueKey = ArrayListMultimap.create();
-    ListMultimap<String, EsQueueDto> itemsByProjectUuid = ArrayListMultimap.create();
+    ListMultimap<String, EsQueueDto> itemsByBranchUuid = ArrayListMultimap.create();
+    ListMultimap<String, EsQueueDto> itemsByDeleteProjectUuid = ArrayListMultimap.create();
+
     items.forEach(i -> {
       if (ID_TYPE_ISSUE_KEY.equals(i.getDocIdType())) {
         itemsByIssueKey.put(i.getDocId(), i);
-      } else if (ID_TYPE_PROJECT_UUID.equals(i.getDocIdType())) {
-        itemsByProjectUuid.put(i.getDocId(), i);
+      } else if (ID_TYPE_BRANCH_UUID.equals(i.getDocIdType())) {
+        itemsByBranchUuid.put(i.getDocId(), i);
+      } else if (ID_TYPE_DELETE_PROJECT_UUID.equals(i.getDocIdType())) {
+        itemsByDeleteProjectUuid.put(i.getDocId(), i);
       } else {
         LOGGER.error("Unsupported es_queue.doc_id_type for issues. Manual fix is required: " + i);
       }
@@ -183,7 +222,8 @@ public class IssueIndexer implements ProjectIndexer, NeedAuthorizationIndexer {
 
     IndexingResult result = new IndexingResult();
     result.add(doIndexIssueItems(dbSession, itemsByIssueKey));
-    result.add(doIndexProjectItems(dbSession, itemsByProjectUuid));
+    result.add(doIndexBranchItems(dbSession, itemsByBranchUuid));
+    result.add(doDeleteProjectIndexItems(dbSession, itemsByDeleteProjectUuid));
     return result;
   }
 
@@ -211,28 +251,38 @@ public class IssueIndexer implements ProjectIndexer, NeedAuthorizationIndexer {
     return bulkIndexer.stop();
   }
 
-  private IndexingResult doIndexProjectItems(DbSession dbSession, ListMultimap<String, EsQueueDto> itemsByProjectUuid) {
-    if (itemsByProjectUuid.isEmpty()) {
+  private IndexingResult doDeleteProjectIndexItems(DbSession dbSession, ListMultimap<String, EsQueueDto> itemsByDeleteProjectUuid) {
+    IndexingListener listener = new OneToManyResilientIndexingListener(dbClient, dbSession, itemsByDeleteProjectUuid.values());
+    BulkIndexer bulkIndexer = createBulkIndexer(listener);
+    bulkIndexer.start();
+    for (String projectUuid : itemsByDeleteProjectUuid.keySet()) {
+      addProjectDeletionToBulkIndexer(bulkIndexer, projectUuid);
+    }
+    return bulkIndexer.stop();
+  }
+
+  private IndexingResult doIndexBranchItems(DbSession dbSession, ListMultimap<String, EsQueueDto> itemsByBranchUuid) {
+    if (itemsByBranchUuid.isEmpty()) {
       return new IndexingResult();
     }
 
-    // one project, referenced by es_queue.doc_id = many issues
-    IndexingListener listener = new OneToManyResilientIndexingListener(dbClient, dbSession, itemsByProjectUuid.values());
+    // one branch, referenced by es_queue.doc_id = many issues
+    IndexingListener listener = new OneToManyResilientIndexingListener(dbClient, dbSession, itemsByBranchUuid.values());
     BulkIndexer bulkIndexer = createBulkIndexer(listener);
     bulkIndexer.start();
 
-    for (String projectUuid : itemsByProjectUuid.keySet()) {
-      // TODO support loading of multiple projects in a single SQL request
-      try (IssueIterator issues = issueIteratorFactory.createForBranch(projectUuid)) {
+    for (String branchUuid : itemsByBranchUuid.keySet()) {
+      try (IssueIterator issues = issueIteratorFactory.createForBranch(branchUuid)) {
         if (issues.hasNext()) {
           do {
             IssueDoc doc = issues.next();
             bulkIndexer.add(newIndexRequest(doc));
           } while (issues.hasNext());
         } else {
-          // project does not exist or has no issues. In both case
-          // all the documents related to this project are deleted.
-          addProjectDeletionToBulkIndexer(bulkIndexer, projectUuid);
+          // branch does not exist or has no issues. In both cases,
+          // all the documents related to this branch are deleted.
+          Optional<BranchDto> branch = dbClient.branchDao().selectByUuid(dbSession, branchUuid);
+          branch.ifPresent(b -> addBranchDeletionToBulkIndexer(bulkIndexer, b.getProjectUuid(), b.getUuid()));
         }
       }
     }
@@ -288,6 +338,17 @@ public class IssueIndexer implements ProjectIndexer, NeedAuthorizationIndexer {
     bulkIndexer.addDeletion(search);
   }
 
+  private static void addBranchDeletionToBulkIndexer(BulkIndexer bulkIndexer, String projectUUid, String branchUuid) {
+    SearchRequest search = EsClient.prepareSearch(TYPE_ISSUE.getMainType())
+      // routing is based on the parent (See BaseDoc#getRouting).
+      // The parent is set to the projectUUid when an issue is indexed (See IssueDoc#setProjectUuid). We need to set it here
+      // so that the search finds the indexed docs to be deleted.
+      .routing(AuthorizationDoc.idOf(projectUUid))
+      .source(new SearchSourceBuilder().query(boolQuery().must(termQuery(FIELD_ISSUE_BRANCH_UUID, branchUuid))));
+
+    bulkIndexer.addDeletion(search);
+  }
+
   private static EsQueueDto createQueueDto(String docId, String docIdType, String projectUuid) {
     return EsQueueDto.create(TYPE_ISSUE.format(), docId, docIdType, AuthorizationDoc.idOf(projectUuid));
   }
index 4ffeaca7bbc1296e8110ae4f39bcbd462b3588e8..7bedc634001b53054e394f859a346936f1be23a6 100644 (file)
@@ -26,32 +26,41 @@ import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 import javax.annotation.Nullable;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchDto;
 import org.sonar.db.es.EsQueueDto;
 import org.sonar.db.measure.ProjectMeasuresIndexerIterator;
 import org.sonar.db.measure.ProjectMeasuresIndexerIterator.ProjectMeasures;
+import org.sonar.server.es.AnalysisIndexer;
 import org.sonar.server.es.BulkIndexer;
 import org.sonar.server.es.BulkIndexer.Size;
 import org.sonar.server.es.EsClient;
+import org.sonar.server.es.EventIndexer;
 import org.sonar.server.es.IndexType;
+import org.sonar.server.es.Indexers;
 import org.sonar.server.es.IndexingListener;
 import org.sonar.server.es.IndexingResult;
 import org.sonar.server.es.OneToOneResilientIndexingListener;
-import org.sonar.server.es.ProjectIndexer;
 import org.sonar.server.permission.index.AuthorizationDoc;
 import org.sonar.server.permission.index.AuthorizationScope;
 import org.sonar.server.permission.index.NeedAuthorizationIndexer;
 
 import static org.sonar.server.measure.index.ProjectMeasuresIndexDefinition.TYPE_PROJECT_MEASURES;
 
-public class ProjectMeasuresIndexer implements ProjectIndexer, NeedAuthorizationIndexer {
+/**
+ * Indexes data related to projects and applications.
+ * The name is a bit misleading - it indexes a lot more data than just measures.
+ * We index by project/app UUID, but we get a lot of the data from their main branches.
+ */
+public class ProjectMeasuresIndexer implements EventIndexer, AnalysisIndexer, NeedAuthorizationIndexer {
 
   private static final AuthorizationScope AUTHORIZATION_SCOPE = new AuthorizationScope(TYPE_PROJECT_MEASURES,
-    project -> Qualifiers.PROJECT.equals(project.getQualifier()) || Qualifiers.APP.equals(project.getQualifier()));
+    entity -> Qualifiers.PROJECT.equals(entity.getQualifier()) || Qualifiers.APP.equals(entity.getQualifier()));
   private static final Set<IndexType> INDEX_TYPES = Set.of(TYPE_PROJECT_MEASURES);
 
   private final DbClient dbClient;
@@ -82,33 +91,46 @@ public class ProjectMeasuresIndexer implements ProjectIndexer, NeedAuthorization
   }
 
   @Override
-  public void indexOnAnalysis(String projectUuid) {
-    indexOnAnalysis(projectUuid, Set.of());
+  public void indexOnAnalysis(String branchUuid) {
+    indexOnAnalysis(branchUuid, Set.of());
   }
 
   @Override
-  public void indexOnAnalysis(String projectUuid, Set<String> unchangedComponentUuids) {
-    doIndex(Size.REGULAR, projectUuid);
+  public void indexOnAnalysis(String branchUuid, Set<String> unchangedComponentUuids) {
+    doIndex(Size.REGULAR, branchUuid);
   }
 
   @Override
-  public Collection<EsQueueDto> prepareForRecovery(DbSession dbSession, Collection<String> projectUuids, ProjectIndexer.Cause cause) {
-    switch (cause) {
-      case PERMISSION_CHANGE:
-        // nothing to do, permissions are not used in type projectmeasures/projectmeasure
-        return Collections.emptyList();
-      case MEASURE_CHANGE, PROJECT_KEY_UPDATE, PROJECT_CREATION, PROJECT_TAGS_UPDATE, PROJECT_DELETION:
+  public Collection<EsQueueDto> prepareForRecoveryOnEntityEvent(DbSession dbSession, Collection<String> entityUuids, Indexers.EntityEvent cause) {
+    return switch (cause) {
+      case PERMISSION_CHANGE ->
+        // nothing to do, permissions are not used in index type projectmeasures/projectmeasure
+        Collections.emptyList();
+      case PROJECT_KEY_UPDATE, CREATION, PROJECT_TAGS_UPDATE, DELETION ->
         // when MEASURE_CHANGE or PROJECT_KEY_UPDATE project must be re-indexed because key is used in this index
         // when PROJECT_CREATION provisioned projects are supported by WS api/components/search_projects
-        List<EsQueueDto> items = projectUuids.stream()
-          .map(projectUuid -> EsQueueDto.create(TYPE_PROJECT_MEASURES.format(), projectUuid, null, projectUuid))
-          .collect(MoreCollectors.toArrayList(projectUuids.size()));
-        return dbClient.esQueueDao().insert(dbSession, items);
-
-      default:
-        // defensive case
-        throw new IllegalStateException("Unsupported cause: " + cause);
-    }
+        prepareForRecovery(dbSession, entityUuids);
+    };
+  }
+
+  @Override
+  public Collection<EsQueueDto> prepareForRecoveryOnBranchEvent(DbSession dbSession, Collection<String> branchUuids, Indexers.BranchEvent cause) {
+    return switch (cause) {
+      case DELETION -> Collections.emptyList();
+      case MEASURE_CHANGE -> {
+        Set<String> entityUuids = dbClient.branchDao().selectByUuids(dbSession, branchUuids)
+          .stream().map(BranchDto::getProjectUuid)
+          .collect(Collectors.toSet());
+        yield prepareForRecovery(dbSession, entityUuids);
+      }
+    };
+  }
+
+  private Collection<EsQueueDto> prepareForRecovery(DbSession dbSession, Collection<String> entityUuids) {
+    List<EsQueueDto> items = entityUuids.stream()
+      .map(entityUuid -> EsQueueDto.create(TYPE_PROJECT_MEASURES.format(), entityUuid, null, entityUuid))
+      .collect(MoreCollectors.toArrayList(entityUuids.size()));
+    return dbClient.esQueueDao().insert(dbSession, items);
   }
 
   public IndexingResult commitAndIndex(DbSession dbSession, Collection<String> projectUuids) {
index 45769e0fd597e3564faf2f2c6d95a02a333c297a..8a6bc2217ba249af524e82a6510ef53932d7c6e7 100644 (file)
@@ -51,9 +51,9 @@ public class AuthorizationDoc extends BaseDoc {
     return idOf(entityUuid);
   }
 
-  public static String idOf(String projectUuid) {
-    requireNonNull(projectUuid, "projectUuid can't be null");
-    return ID_PREFIX + projectUuid;
+  public static String idOf(String entityUuid) {
+    requireNonNull(entityUuid, "entityUuid can't be null");
+    return ID_PREFIX + entityUuid;
   }
 
   public static String entityUuidOf(String id) {
index 46f94bc1fa01aaf7c2ba379fa2a2d9210a48922a..2a32cd0fa3323355a64a2dcc29a90b1ffa1f2dc4 100644 (file)
@@ -31,11 +31,11 @@ import static org.sonar.server.permission.index.IndexAuthorizationConstants.TYPE
 @Immutable
 public final class AuthorizationScope {
   private final IndexMainType indexType;
-  private final Predicate<IndexPermissions> projectPredicate;
+  private final Predicate<IndexPermissions> entityPredicate;
 
-  public AuthorizationScope(IndexRelationType functionalType, Predicate<IndexPermissions> projectPredicate) {
+  public AuthorizationScope(IndexRelationType functionalType, Predicate<IndexPermissions> entityPredicate) {
     this.indexType = getAuthorizationIndexType(functionalType);
-    this.projectPredicate = requireNonNull(projectPredicate);
+    this.entityPredicate = requireNonNull(entityPredicate);
   }
 
   /**
@@ -59,9 +59,9 @@ public final class AuthorizationScope {
   }
 
   /**
-   * Predicates that filters the projects to be involved in authorization.
+   * Predicates that filters the entities to be involved in authorization.
    */
-  public Predicate<IndexPermissions> getProjectPredicate() {
-    return projectPredicate;
+  public Predicate<IndexPermissions> getEntityPredicate() {
+    return entityPredicate;
   }
 }
index a3dcc0ecdcb026a0a0583a47cd628e772394b60b..94e65d410f116c0d9c6e6af7161595cbb1631170 100644 (file)
  */
 package org.sonar.server.permission.index;
 
+import org.sonar.server.es.EventIndexer;
+
 /**
  * An {@link NeedAuthorizationIndexer} defines how
- * a {@link org.sonar.server.es.ProjectIndexer} populates
+ * a {@link EventIndexer} populates
  * the type named {@link WebAuthorizationTypeSupport#TYPE_AUTHORIZATION}, which
  * is used to verify that a user can access to projects.
  */
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/es/IndexersImplTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/es/IndexersImplTest.java
new file mode 100644 (file)
index 0000000..7c4b3dd
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.es;
+
+import java.util.List;
+import java.util.Set;
+import org.junit.Test;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.es.EsQueueDto;
+import org.sonar.db.project.ProjectDto;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.sonar.server.es.Indexers.BranchEvent.DELETION;
+import static org.sonar.server.es.Indexers.EntityEvent.CREATION;
+
+public class IndexersImplTest {
+
+  @Test
+  public void commitAndIndexOnEntityEvent_shouldCallIndexerWithSupportedItems() {
+    List<EsQueueDto> items1 = List.of(EsQueueDto.create("fake/fake1", "P1"), EsQueueDto.create("fake/fake1", "P1"));
+    List<EsQueueDto> items2 = List.of(EsQueueDto.create("fake/fake2", "P1"));
+
+    EventIndexer indexer1 = mock(EventIndexer.class);
+    EventIndexer indexer2 = mock(EventIndexer.class);
+
+    DbSession dbSession = mock(DbSession.class);
+
+    IndexersImpl underTest = new IndexersImpl(indexer1, indexer2);
+    when(indexer1.prepareForRecoveryOnEntityEvent(dbSession, Set.of("P1"), CREATION)).thenReturn(items1);
+    when(indexer2.prepareForRecoveryOnEntityEvent(dbSession, Set.of("P1"), CREATION)).thenReturn(items2);
+
+    underTest.commitAndIndexOnEntityEvent(dbSession, Set.of("P1"), CREATION);
+
+    verify(indexer1).index(dbSession, items1);
+    verify(indexer2).index(dbSession, items2);
+  }
+
+  @Test
+  public void commitAndIndexOnBranchEvent_shouldCallIndexerWithSupportedItems() {
+    List<EsQueueDto> items1 = List.of(EsQueueDto.create("fake/fake1", "P1"), EsQueueDto.create("fake/fake1", "P1"));
+    List<EsQueueDto> items2 = List.of(EsQueueDto.create("fake/fake2", "P1"));
+
+    EventIndexer indexer1 = mock(EventIndexer.class);
+    EventIndexer indexer2 = mock(EventIndexer.class);
+
+    DbSession dbSession = mock(DbSession.class);
+
+    IndexersImpl underTest = new IndexersImpl(indexer1, indexer2);
+    when(indexer1.prepareForRecoveryOnBranchEvent(dbSession, Set.of("P1"), DELETION)).thenReturn(items1);
+    when(indexer2.prepareForRecoveryOnBranchEvent(dbSession, Set.of("P1"), DELETION)).thenReturn(items2);
+
+    underTest.commitAndIndexOnBranchEvent(dbSession, Set.of("P1"), DELETION);
+
+    verify(indexer1).index(dbSession, items1);
+    verify(indexer2).index(dbSession, items2);
+  }
+
+  @Test
+  public void commitAndIndexEntities_shouldIndexAllUuids() {
+    EventIndexer indexer1 = mock(EventIndexer.class);
+    DbSession dbSession = mock(DbSession.class);
+
+    IndexersImpl underTest = new IndexersImpl(indexer1);
+
+    ProjectDto p1 = ComponentTesting.newProjectDto();
+    underTest.commitAndIndexEntities(dbSession, Set.of(p1), CREATION);
+
+    verify(indexer1).prepareForRecoveryOnEntityEvent(dbSession, Set.of(p1.getUuid()), CREATION);
+  }
+
+  @Test
+  public void commitAndIndexBranches_shouldIndexAllBranchUuids() {
+    EventIndexer indexer1 = mock(EventIndexer.class);
+    DbSession dbSession = mock(DbSession.class);
+
+    IndexersImpl underTest = new IndexersImpl(indexer1);
+
+    BranchDto b1 = new BranchDto().setUuid("b1");
+    underTest.commitAndIndexBranches(dbSession, Set.of(b1), DELETION);
+    verify(indexer1).prepareForRecoveryOnBranchEvent(dbSession, Set.of(b1.getUuid()), DELETION);
+  }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/es/ProjectIndexersImplTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/es/ProjectIndexersImplTest.java
deleted file mode 100644 (file)
index 8ed1a66..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.es;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import org.junit.Test;
-import org.sonar.db.DbSession;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentTesting;
-import org.sonar.server.es.ProjectIndexer.Cause;
-
-import static java.util.Collections.singletonList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-
-public class ProjectIndexersImplTest {
-
-  @Test
-  public void commitAndIndex_indexes_project() {
-    ComponentDto project = ComponentTesting.newPublicProjectDto();
-
-    FakeIndexers underTest = new FakeIndexers();
-    underTest.commitAndIndexComponents(mock(DbSession.class), singletonList(project), Cause.PROJECT_CREATION);
-
-    assertThat(underTest.calls).containsExactly(project.uuid());
-  }
-
-  private static class FakeIndexers implements ProjectIndexers {
-    private final List<String> calls = new ArrayList<>();
-
-    @Override
-    public void commitAndIndexByProjectUuids(DbSession dbSession, Collection<String> projectUuids, Cause cause) {
-      calls.addAll(projectUuids);
-    }
-  }
-}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/es/ProjectIndexersTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/es/ProjectIndexersTest.java
deleted file mode 100644 (file)
index ce69963..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.es;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-import org.junit.Test;
-import org.sonar.db.DbSession;
-import org.sonar.db.component.ComponentTesting;
-import org.sonar.db.es.EsQueueDto;
-import org.sonar.db.project.ProjectDto;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.singletonList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-
-public class ProjectIndexersTest {
-
-  @Test
-  public void commitAndIndexByProjectUuids_calls_indexer_with_only_its_supported_items() {
-    EsQueueDto item1a = EsQueueDto.create("fake/fake1", "P1");
-    EsQueueDto item1b = EsQueueDto.create("fake/fake1", "P1");
-    EsQueueDto item2 = EsQueueDto.create("fake/fake2", "P1");
-    FakeIndexer indexer1 = new FakeIndexer(asList(item1a, item1b));
-    FakeIndexer indexer2 = new FakeIndexer(singletonList(item2));
-    DbSession dbSession = mock(DbSession.class);
-
-    ProjectIndexersImpl underTest = new ProjectIndexersImpl(indexer1, indexer2);
-    underTest.commitAndIndexByProjectUuids(dbSession, singletonList("P1"), ProjectIndexer.Cause.PROJECT_CREATION);
-
-    assertThat(indexer1.calledItems).containsExactlyInAnyOrder(item1a, item1b);
-    assertThat(indexer2.calledItems).containsExactlyInAnyOrder(item2);
-  }
-
-  @Test
-  public void commitAndIndexByEntityUuids_calls_indexer_with_only_its_supported_items() {
-    ProjectIndexersTestImpl projectIndexers = new ProjectIndexersTestImpl();
-    ProjectDto p1 = ComponentTesting.newProjectDto();
-    ProjectDto p2 = ComponentTesting.newProjectDto();
-    p1.setUuid("p1");
-    p2.setUuid("p2");
-
-    projectIndexers.commitAndIndexEntities(null, List.of(p1, p2), ProjectIndexer.Cause.PROJECT_CREATION);
-
-    assertThat(projectIndexers.cause).isEqualTo(ProjectIndexer.Cause.PROJECT_CREATION);
-    assertThat(projectIndexers.calledProjectUuids).containsOnly("p1", "p2");
-  }
-
-  private static class ProjectIndexersTestImpl implements ProjectIndexers {
-
-    Collection<String> calledProjectUuids;
-    ProjectIndexer.Cause cause;
-
-    @Override
-    public void commitAndIndexByProjectUuids(DbSession dbSession, Collection<String> projectUuids, ProjectIndexer.Cause cause) {
-      this.calledProjectUuids = projectUuids;
-      this.cause = cause;
-    }
-  }
-
-  private static class FakeIndexer implements ProjectIndexer {
-
-    private final List<EsQueueDto> items;
-    private Collection<EsQueueDto> calledItems;
-
-    private FakeIndexer(List<EsQueueDto> items) {
-      this.items = items;
-    }
-
-    @Override
-    public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Set<IndexType> getIndexTypes() {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Collection<EsQueueDto> prepareForRecovery(DbSession dbSession, Collection<String> projectUuids, Cause cause) {
-      return items;
-    }
-
-    @Override
-    public IndexingResult index(DbSession dbSession, Collection<EsQueueDto> items) {
-      this.calledItems = items;
-      return new IndexingResult();
-    }
-
-    @Override
-    public void indexOnAnalysis(String branchUuid) {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void indexOnAnalysis(String branchUuid, Set<String> unchangedComponentUuids) {
-      throw new UnsupportedOperationException();
-    }
-  }
-}
index bc93ebf253eecbbbef451deec8292bf45e4543be..4fcfb76add51830b28ab47062e265e4d83b527a9 100644 (file)
@@ -27,9 +27,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.sonar.server.es.StartupIndexer.Type.SYNCHRONOUS;
 
 public class StartupIndexerTest {
-
-
-  private StartupIndexer underTest = () -> null;
+  private final StartupIndexer underTest = () -> null;
 
   @Test
   public void getType() {
@@ -40,14 +38,14 @@ public class StartupIndexerTest {
   public void triggerAsyncIndexOnStartup() {
     assertThatThrownBy(() -> underTest.triggerAsyncIndexOnStartup(Collections.emptySet()))
       .isInstanceOf(IllegalStateException.class)
-      .hasMessage("ASYNCHRONE StartupIndexer must implement initAsyncIndexOnStartup");
+      .hasMessage("ASYNCHRONOUS StartupIndexer must implement initAsyncIndexOnStartup");
   }
 
   @Test
   public void indexOnStartup() {
     assertThatThrownBy(() -> underTest.indexOnStartup(Collections.emptySet()))
       .isInstanceOf(IllegalStateException.class)
-      .hasMessage("SYNCHRONE StartupIndexer must implement indexOnStartup");
+      .hasMessage("SYNCHRONOUS StartupIndexer must implement indexOnStartup");
   }
 
 }
index 9eedb55c79b1e92b9890a7355515af8da440d7af..8de943432a510418d68c057ad190f63f6e7a1fba 100644 (file)
@@ -49,7 +49,7 @@ public class AuthorizationDocTest {
   public void idOf_fails_with_NPE_if_argument_is_null() {
     assertThatThrownBy(() -> AuthorizationDoc.idOf(null))
       .isInstanceOf(NullPointerException.class)
-      .hasMessage("projectUuid can't be null");
+      .hasMessage("entityUuid can't be null");
   }
 
   @Test
@@ -81,7 +81,7 @@ public class AuthorizationDocTest {
 
     assertThatThrownBy(() -> underTest.getId())
       .isInstanceOf(NullPointerException.class)
-      .hasMessage("projectUuid can't be null");
+      .hasMessage("entityUuid can't be null");
   }
 
   @Test
diff --git a/server/sonar-server-common/src/testFixtures/java/org/sonar/server/es/TestIndexers.java b/server/sonar-server-common/src/testFixtures/java/org/sonar/server/es/TestIndexers.java
new file mode 100644 (file)
index 0000000..7b0eb79
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.es;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+import java.util.Collection;
+import org.sonar.db.DbSession;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class TestIndexers implements Indexers {
+
+  private final ListMultimap<String, EntityEvent> entityCalls = ArrayListMultimap.create();
+  private final ListMultimap<String, BranchEvent> branchCalls = ArrayListMultimap.create();
+
+  @Override
+  public void commitAndIndexOnEntityEvent(DbSession dbSession, Collection<String> entityUuids, EntityEvent cause) {
+    dbSession.commit();
+    entityUuids.forEach(entityUuid -> entityCalls.put(entityUuid, cause));
+  }
+
+  @Override
+  public void commitAndIndexOnBranchEvent(DbSession dbSession, Collection<String> branchUuids, BranchEvent cause) {
+    dbSession.commit();
+    branchUuids.forEach(branchUuid -> branchCalls.put(branchUuid, cause));
+  }
+
+  public boolean hasBeenCalledForEntity(String entityUuid, EntityEvent expectedCause) {
+    assertThat(branchCalls.keySet()).isEmpty();
+    return entityCalls.get(entityUuid).contains(expectedCause);
+  }
+
+  public boolean hasBeenCalledForEntity(String projectUuid) {
+    assertThat(branchCalls.keySet()).isEmpty();
+    return entityCalls.containsKey(projectUuid);
+  }
+
+  public boolean hasBeenCalledForBranch(String branchUuid, BranchEvent expectedCause) {
+    assertThat(entityCalls.keySet()).isEmpty();
+    return branchCalls.get(branchUuid).contains(expectedCause);
+  }
+
+  public boolean hasBeenCalledForBranch(String branchUuid) {
+    assertThat(entityCalls.keySet()).isEmpty();
+    return branchCalls.containsKey(branchUuid);
+  }
+}
diff --git a/server/sonar-server-common/src/testFixtures/java/org/sonar/server/es/TestProjectIndexers.java b/server/sonar-server-common/src/testFixtures/java/org/sonar/server/es/TestProjectIndexers.java
deleted file mode 100644 (file)
index 6b6f617..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.es;
-
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ListMultimap;
-import java.util.Collection;
-import org.sonar.db.DbSession;
-
-public class TestProjectIndexers implements ProjectIndexers {
-
-  private final ListMultimap<String, ProjectIndexer.Cause> calls = ArrayListMultimap.create();
-
-  @Override
-  public void commitAndIndexByProjectUuids(DbSession dbSession, Collection<String> projectUuids, ProjectIndexer.Cause cause) {
-    dbSession.commit();
-    projectUuids.forEach(projectUuid -> calls.put(projectUuid, cause));
-
-  }
-
-  public boolean hasBeenCalled(String projectUuid, ProjectIndexer.Cause expectedCause) {
-    return calls.get(projectUuid).contains(expectedCause);
-  }
-
-  public boolean hasBeenCalled(String projectUuid) {
-    return calls.containsKey(projectUuid);
-  }
-}
index 4338423164aaf9666267d7a8fd2911f7dda0b037..f3d025177c6e95939b8d1b2ded803b136097782b 100644 (file)
@@ -47,7 +47,6 @@ import static org.sonar.db.ce.CeTaskCharacteristicDto.BRANCH_TYPE_KEY;
 import static org.sonar.db.ce.CeTaskCharacteristicDto.PULL_REQUEST;
 import static org.sonar.db.ce.CeTaskTypes.BRANCH_ISSUE_SYNC;
 
-@ServerSide
 public class AsyncIssueIndexingImpl implements AsyncIssueIndexing {
 
   private static final Logger LOG = LoggerFactory.getLogger(AsyncIssueIndexingImpl.class);
index 3fc534a420a00c8464313eadd99f68a2adb9e196..4ea1228322ce89aa8dcc517ae85ce9fcb063fd12 100644 (file)
@@ -35,21 +35,21 @@ import org.sonar.db.es.EsQueueDto;
 import org.sonar.server.es.BulkIndexer;
 import org.sonar.server.es.BulkIndexer.Size;
 import org.sonar.server.es.EsClient;
+import org.sonar.server.es.EventIndexer;
 import org.sonar.server.es.IndexType;
+import org.sonar.server.es.Indexers;
 import org.sonar.server.es.IndexingResult;
 import org.sonar.server.es.OneToOneResilientIndexingListener;
-import org.sonar.server.es.ProjectIndexer;
 import org.springframework.beans.factory.annotation.Autowired;
 
 import static java.util.Collections.emptyList;
 import static org.sonar.core.util.stream.MoreCollectors.toArrayList;
 
 /**
- * Populates the types "authorization" of each index requiring project
+ * Populates the types "authorization" of each index requiring entity
  * authorization.
  */
-public class PermissionIndexer implements ProjectIndexer {
-
+public class PermissionIndexer implements EventIndexer {
   private final DbClient dbClient;
   private final EsClient esClient;
   private final Collection<AuthorizationScope> authorizationScopes;
@@ -99,28 +99,20 @@ public class PermissionIndexer implements ProjectIndexer {
   }
 
   @Override
-  public void indexOnAnalysis(String branchUuid) {
-    // nothing to do, permissions don't change during an analysis
-  }
-
-  @Override
-  public void indexOnAnalysis(String branchUuid, Set<String> unchangedComponentUuids) {
-    // nothing to do, permissions don't change during an analysis
-  }
-
-  @Override
-  public Collection<EsQueueDto> prepareForRecovery(DbSession dbSession, Collection<String> projectUuids, ProjectIndexer.Cause cause) {
+  public Collection<EsQueueDto> prepareForRecoveryOnEntityEvent(DbSession dbSession, Collection<String> entityUuids, Indexers.EntityEvent cause) {
     return switch (cause) {
-      case MEASURE_CHANGE, PROJECT_KEY_UPDATE, PROJECT_TAGS_UPDATE ->
+      case PROJECT_KEY_UPDATE, PROJECT_TAGS_UPDATE ->
         // nothing to change. Measures, project key and tags are not part of this index
         emptyList();
-      case PROJECT_CREATION, PROJECT_DELETION, PERMISSION_CHANGE -> insertIntoEsQueue(dbSession, projectUuids);
-      default ->
-        // defensive case
-        throw new IllegalStateException("Unsupported cause: " + cause);
+      case CREATION, DELETION, PERMISSION_CHANGE -> insertIntoEsQueue(dbSession, entityUuids);
     };
   }
 
+  @Override
+  public Collection<EsQueueDto> prepareForRecoveryOnBranchEvent(DbSession dbSession, Collection<String> branchUuids, Indexers.BranchEvent cause) {
+    return emptyList();
+  }
+
   private Collection<EsQueueDto> insertIntoEsQueue(DbSession dbSession, Collection<String> projectUuids) {
     List<EsQueueDto> items = indexTypeByFormat.values().stream()
       .flatMap(indexType -> projectUuids.stream().map(projectUuid -> EsQueueDto.create(indexType.format(), AuthorizationDoc.idOf(projectUuid), null, projectUuid)))
@@ -143,7 +135,7 @@ public class PermissionIndexer implements ProjectIndexer {
       bulkIndexer.start();
 
       authorizations.stream()
-        .filter(scope.getProjectPredicate())
+        .filter(scope.getEntityPredicate())
         .map(dto -> AuthorizationDoc.fromDto(indexType, dto).toIndexRequest())
         .forEach(bulkIndexer::add);
 
@@ -179,7 +171,7 @@ public class PermissionIndexer implements ProjectIndexer {
     });
 
     // the remaining references on entities that don't exist in db. They must
-    // be deleted from index.
+    // be deleted from the index.
     remainingEntityUuids.forEach(entityUuid -> bulkIndexers.forEach(bi -> {
       String authorizationDocId = AuthorizationDoc.idOf(entityUuid);
       bi.addDeletion(bi.getIndexType(), authorizationDocId, authorizationDocId);
index 9f0414d5e4b4077e91daf128d5fdc9d57f498595..bc98d029d74aa471d9a03516ff9603507cc8fd74 100644 (file)
@@ -52,7 +52,7 @@ public class ComponentIndexSearchTest {
   @Rule
   public ComponentTextSearchFeatureRule features = new ComponentTextSearchFeatureRule();
 
-  private final ComponentIndexer indexer = new ComponentIndexer(db.getDbClient(), es.client());
+  private final EntityDefinitionIndexer indexer = new EntityDefinitionIndexer(db.getDbClient(), es.client());
   private final PermissionIndexerTester authorizationIndexerTester = new PermissionIndexerTester(es, indexer);
   private final ComponentIndex underTest = new ComponentIndex(es.client(), new WebAuthorizationTypeSupport(userSession), System2.INSTANCE);
 
index 6755e90309f07038d20c4e44c9d95fc2d0104790..7ccbf3d3f411ed3314c8e595ea8fc5b0c72132c3 100644 (file)
@@ -52,7 +52,7 @@ public abstract class ComponentIndexTest {
   @Rule
   public ComponentTextSearchFeatureRule features = new ComponentTextSearchFeatureRule();
 
-  protected ComponentIndexer indexer = new ComponentIndexer(db.getDbClient(), es.client());
+  protected EntityDefinitionIndexer indexer = new EntityDefinitionIndexer(db.getDbClient(), es.client());
   protected ComponentIndex index = new ComponentIndex(es.client(), new WebAuthorizationTypeSupport(userSession), System2.INSTANCE);
   protected PermissionIndexerTester authorizationIndexerTester = new PermissionIndexerTester(es, indexer);
 
index b25b742318b7f006efcc8e59db655846ceaea806..4e20afb0870f512e79d46d7600766de15c9d7985 100644 (file)
  */
 package org.sonar.server.permission.index;
 
-import com.google.common.collect.ImmutableSet;
-import java.util.Collection;
 import java.util.Set;
 import org.elasticsearch.action.index.IndexRequest;
-import org.sonar.db.DbSession;
-import org.sonar.db.es.EsQueueDto;
+import org.sonar.server.es.AnalysisIndexer;
 import org.sonar.server.es.BaseDoc;
 import org.sonar.server.es.EsClient;
-import org.sonar.server.es.IndexType;
-import org.sonar.server.es.IndexingResult;
-import org.sonar.server.es.ProjectIndexer;
 
 import static org.sonar.server.permission.index.FooIndexDefinition.TYPE_FOO;
 
-public class FooIndexer implements ProjectIndexer, NeedAuthorizationIndexer {
+public class FooIndexer implements AnalysisIndexer, NeedAuthorizationIndexer {
 
   private static final AuthorizationScope AUTHORIZATION_SCOPE = new AuthorizationScope(TYPE_FOO, p -> true);
 
@@ -59,11 +53,6 @@ public class FooIndexer implements ProjectIndexer, NeedAuthorizationIndexer {
     addToIndex(branchUuid, "baz");
   }
 
-  @Override
-  public Collection<EsQueueDto> prepareForRecovery(DbSession dbSession, Collection<String> projectUuids, Cause cause) {
-    throw new UnsupportedOperationException();
-  }
-
   private void addToIndex(String projectUuid, String name) {
     FooDoc fooDoc = new FooDoc(projectUuid, name);
     esClient.index(new IndexRequest(TYPE_FOO.getMainType().getIndex().getName())
@@ -92,19 +81,4 @@ public class FooIndexer implements ProjectIndexer, NeedAuthorizationIndexer {
     }
 
   }
-
-  @Override
-  public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
-    throw new UnsupportedOperationException();
-  }
-
-  @Override
-  public Set<IndexType> getIndexTypes() {
-    return ImmutableSet.of(TYPE_FOO);
-  }
-
-  @Override
-  public IndexingResult index(DbSession dbSession, Collection<EsQueueDto> items) {
-    throw new UnsupportedOperationException();
-  }
 }
index be278b496c0f6a99b20585ddd2aba778d379dedd..afbcfbc64b008e646c38c9094e009e8c01eb0afd 100644 (file)
@@ -34,8 +34,8 @@ import org.sonar.db.user.UserDto;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.es.IndexType;
 import org.sonar.server.es.IndexType.IndexMainType;
+import org.sonar.server.es.Indexers.EntityEvent;
 import org.sonar.server.es.IndexingResult;
-import org.sonar.server.es.ProjectIndexer;
 import org.sonar.server.tester.UserSessionRule;
 
 import static java.util.Arrays.asList;
@@ -44,7 +44,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.sonar.api.resources.Qualifiers.PROJECT;
 import static org.sonar.api.web.UserRole.ADMIN;
 import static org.sonar.api.web.UserRole.USER;
-import static org.sonar.server.es.ProjectIndexer.Cause.PERMISSION_CHANGE;
+import static org.sonar.server.es.Indexers.EntityEvent.PERMISSION_CHANGE;
 import static org.sonar.server.permission.index.IndexAuthorizationConstants.TYPE_AUTHORIZATION;
 
 public class PermissionIndexerTest {
@@ -98,7 +98,7 @@ public class PermissionIndexerTest {
 
     // Simulate a indexation issue
     db.getDbClient().purgeDao().deleteProject(db.getSession(), project1.getUuid(), PROJECT, project1.getName(), project1.getKey());
-    underTest.prepareForRecovery(db.getSession(), asList(project1.getUuid()), ProjectIndexer.Cause.PROJECT_DELETION);
+    underTest.prepareForRecoveryOnEntityEvent(db.getSession(), asList(project1.getUuid()), EntityEvent.DELETION);
     assertThat(db.countRowsOfTable(db.getSession(), "es_queue")).isOne();
     Collection<EsQueueDto> esQueueDtos = db.getDbClient().esQueueDao().selectForRecovery(db.getSession(), Long.MAX_VALUE, 2);
 
@@ -246,21 +246,11 @@ public class PermissionIndexerTest {
     verifyAuthorized(projectOnOrg1, user);
   }
 
-  @Test
-  public void indexOnAnalysis_does_nothing_because_CE_does_not_touch_permissions() {
-    ProjectDto project = createAndIndexPublicProject();
-
-    underTest.indexOnAnalysis(project.getUuid());
-
-    assertThatAuthIndexHasSize(0);
-    verifyAnyoneNotAuthorized(project);
-  }
-
   @Test
   public void permissions_are_not_updated_on_project_tags_update() {
     ProjectDto project = createAndIndexPublicProject();
 
-    indexPermissions(project, ProjectIndexer.Cause.PROJECT_TAGS_UPDATE);
+    indexPermissions(project, EntityEvent.PROJECT_TAGS_UPDATE);
 
     assertThatAuthIndexHasSize(0);
     verifyAnyoneNotAuthorized(project);
@@ -270,7 +260,7 @@ public class PermissionIndexerTest {
   public void permissions_are_not_updated_on_project_key_update() {
     ProjectDto project = createAndIndexPublicProject();
 
-    indexPermissions(project, ProjectIndexer.Cause.PROJECT_TAGS_UPDATE);
+    indexPermissions(project, EntityEvent.PROJECT_TAGS_UPDATE);
 
     assertThatAuthIndexHasSize(0);
     verifyAnyoneNotAuthorized(project);
@@ -282,7 +272,7 @@ public class PermissionIndexerTest {
     UserDto user = db.users().insertUser();
     db.users().insertProjectPermissionOnUser(user, USER, project);
 
-    indexPermissions(project, ProjectIndexer.Cause.PROJECT_CREATION);
+    indexPermissions(project, EntityEvent.CREATION);
 
     assertThatAuthIndexHasSize(1);
     verifyAuthorized(project, user);
@@ -294,7 +284,7 @@ public class PermissionIndexerTest {
     UserDto user1 = db.users().insertUser();
     UserDto user2 = db.users().insertUser();
     db.users().insertProjectPermissionOnUser(user1, USER, project);
-    indexPermissions(project, ProjectIndexer.Cause.PROJECT_CREATION);
+    indexPermissions(project, EntityEvent.CREATION);
     verifyAuthorized(project, user1);
     verifyNotAuthorized(project, user2);
 
@@ -310,11 +300,11 @@ public class PermissionIndexerTest {
     ProjectDto project = createAndIndexPrivateProject();
     UserDto user = db.users().insertUser();
     db.users().insertProjectPermissionOnUser(user, USER, project);
-    indexPermissions(project, ProjectIndexer.Cause.PROJECT_CREATION);
+    indexPermissions(project, EntityEvent.CREATION);
     verifyAuthorized(project, user);
 
     db.getDbClient().purgeDao().deleteProject(db.getSession(), project.getUuid(), PROJECT, project.getUuid(), project.getKey());
-    indexPermissions(project, ProjectIndexer.Cause.PROJECT_DELETION);
+    indexPermissions(project, EntityEvent.DELETION);
 
     verifyNotAuthorized(project, user);
     assertThatAuthIndexHasSize(0);
@@ -392,9 +382,9 @@ public class PermissionIndexerTest {
     return userSession;
   }
 
-  private IndexingResult indexPermissions(EntityDto entity, ProjectIndexer.Cause cause) {
+  private IndexingResult indexPermissions(EntityDto entity, EntityEvent cause) {
     DbSession dbSession = db.getSession();
-    Collection<EsQueueDto> items = underTest.prepareForRecovery(dbSession, singletonList(entity.getUuid()), cause);
+    Collection<EsQueueDto> items = underTest.prepareForRecoveryOnEntityEvent(dbSession, singletonList(entity.getUuid()), cause);
     dbSession.commit();
     return underTest.index(dbSession, items);
   }
index 3d655419222552b394c5393ba0ec8495e3b4f3da..b532fdf0a52af3006a3910467b5120a92509cd17 100644 (file)
@@ -43,7 +43,7 @@ import org.sonar.db.user.UserDto;
 import org.sonar.server.almintegration.ws.ImportHelper;
 import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
 import org.sonar.server.component.ComponentUpdater;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.TestIndexers;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
@@ -92,7 +92,7 @@ public class ImportAzureProjectActionIT {
   private final DefaultBranchNameResolver defaultBranchNameResolver = mock(DefaultBranchNameResolver.class);
 
   private final ComponentUpdater componentUpdater = new ComponentUpdater(db.getDbClient(), i18n, System2.INSTANCE,
-    mock(PermissionTemplateService.class), new FavoriteUpdater(db.getDbClient()), new TestProjectIndexers(), new SequenceUuidFactory(),
+    mock(PermissionTemplateService.class), new FavoriteUpdater(db.getDbClient()), new TestIndexers(), new SequenceUuidFactory(),
     defaultBranchNameResolver, true);
 
   private final Encryption encryption = mock(Encryption.class);
index a25e53acd6d258e9c2a2354e2f1b530bd812187b..ba9dc340a719c749cdb8bbfa5a5bad20ff7e118a 100644 (file)
@@ -43,7 +43,7 @@ import org.sonar.db.user.UserDto;
 import org.sonar.server.almintegration.ws.ImportHelper;
 import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
 import org.sonar.server.component.ComponentUpdater;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.TestIndexers;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
@@ -92,7 +92,7 @@ public class ImportBitbucketCloudRepoActionIT {
 
   DefaultBranchNameResolver defaultBranchNameResolver = mock(DefaultBranchNameResolver.class);
   private final ComponentUpdater componentUpdater = new ComponentUpdater(db.getDbClient(), i18n, System2.INSTANCE,
-    mock(PermissionTemplateService.class), new FavoriteUpdater(db.getDbClient()), new TestProjectIndexers(), new SequenceUuidFactory(),
+    mock(PermissionTemplateService.class), new FavoriteUpdater(db.getDbClient()), new TestIndexers(), new SequenceUuidFactory(),
     defaultBranchNameResolver, true);
 
   private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession);
index 3ac432bb989fce7ee9ca21a1e8760a393ffbb6f4..61e41602355e738a0540c2a3bf0638baffff9f58 100644 (file)
@@ -19,7 +19,6 @@
  */
 package org.sonar.server.almintegration.ws.bitbucketserver;
 
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -48,7 +47,7 @@ import org.sonar.db.user.UserDto;
 import org.sonar.server.almintegration.ws.ImportHelper;
 import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
 import org.sonar.server.component.ComponentUpdater;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.TestIndexers;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
@@ -100,7 +99,7 @@ public class ImportBitbucketServerProjectActionIT {
   private NewCodeDefinitionResolver newCodeDefinitionResolver = new NewCodeDefinitionResolver(db.getDbClient(), editionProvider);
 
   private final ComponentUpdater componentUpdater = new ComponentUpdater(db.getDbClient(), i18n, System2.INSTANCE,
-    mock(PermissionTemplateService.class), new FavoriteUpdater(db.getDbClient()), new TestProjectIndexers(), new SequenceUuidFactory(),
+    mock(PermissionTemplateService.class), new FavoriteUpdater(db.getDbClient()), new TestIndexers(), new SequenceUuidFactory(),
     defaultBranchNameResolver, true);
 
   private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession);
index 52ac7f05a487e1fbc6fc10870b38ca83c3fc0995..ec75c90e2a5a96e8d51763942b372980a9835213 100644 (file)
@@ -41,7 +41,7 @@ import org.sonar.db.user.UserDto;
 import org.sonar.server.almintegration.ws.ImportHelper;
 import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
 import org.sonar.server.component.ComponentUpdater;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.TestIndexers;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.exceptions.UnauthorizedException;
 import org.sonar.server.favorite.FavoriteUpdater;
@@ -87,7 +87,7 @@ public class ImportGithubProjectActionIT {
 
 
   private final ComponentUpdater componentUpdater = new ComponentUpdater(db.getDbClient(), mock(I18n.class), System2.INSTANCE,
-    mock(PermissionTemplateService.class), new FavoriteUpdater(db.getDbClient()), new TestProjectIndexers(), new SequenceUuidFactory(),
+    mock(PermissionTemplateService.class), new FavoriteUpdater(db.getDbClient()), new TestIndexers(), new SequenceUuidFactory(),
     defaultBranchNameResolver, true);
 
   private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession);
index a2748b8af4254d020a43d6505ab40ffa2c3583ab..7c81d1c26f2b954091f6e7e2cad26cbba8ae460f 100644 (file)
@@ -41,7 +41,7 @@ import org.sonar.db.user.UserDto;
 import org.sonar.server.almintegration.ws.ImportHelper;
 import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
 import org.sonar.server.component.ComponentUpdater;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.TestIndexers;
 import org.sonar.server.favorite.FavoriteUpdater;
 import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver;
 import org.sonar.server.permission.PermissionTemplateService;
@@ -83,7 +83,7 @@ public class ImportGitLabProjectActionIT {
   DefaultBranchNameResolver defaultBranchNameResolver = mock(DefaultBranchNameResolver.class);
 
   private final ComponentUpdater componentUpdater = new ComponentUpdater(db.getDbClient(), mock(I18n.class), System2.INSTANCE,
-    mock(PermissionTemplateService.class), new FavoriteUpdater(db.getDbClient()), new TestProjectIndexers(), new SequenceUuidFactory(),
+    mock(PermissionTemplateService.class), new FavoriteUpdater(db.getDbClient()), new TestIndexers(), new SequenceUuidFactory(),
     defaultBranchNameResolver, true);
 
   private final GitlabHttpClient gitlabHttpClient = mock(GitlabHttpClient.class);
@@ -137,7 +137,7 @@ public class ImportGitLabProjectActionIT {
     assertThat(projectDto).isPresent();
     assertThat(db.getDbClient().projectAlmSettingDao().selectByProject(db.getSession(), projectDto.get())).isPresent();
 
-    assertThat(db.getDbClient().newCodePeriodDao().selectByProject(db.getSession(),  projectDto.get().getUuid()))
+    assertThat(db.getDbClient().newCodePeriodDao().selectByProject(db.getSession(), projectDto.get().getUuid()))
       .isPresent()
       .get()
       .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue, NewCodePeriodDto::getBranchUuid)
@@ -252,7 +252,6 @@ public class ImportGitLabProjectActionIT {
       .containsExactlyInAnyOrder(tuple(DEFAULT_MAIN_BRANCH_NAME, true));
   }
 
-
   @Test
   public void import_project_without_NCD() {
     UserDto user = db.users().insertUser();
@@ -284,7 +283,6 @@ public class ImportGitLabProjectActionIT {
     assertThat(db.getDbClient().projectAlmSettingDao().selectByProject(db.getSession(), projectDto.get())).isPresent();
   }
 
-
   private Project getGitlabProject() {
     return new Project(randomAlphanumeric(5), randomAlphanumeric(5));
   }
index 24a66e066107e05b118d0cf014a1bb32830787ba..040aa8cfef4e26a56ea1e230554f81f02174cca4 100644 (file)
@@ -51,9 +51,9 @@ public class DeleteActionIT {
   @Rule
   public DbTester db = DbTester.create(System2.INSTANCE, true);
 
-  private ComponentCleanerService componentCleanerService = mock(ComponentCleanerService.class);
-  private ComponentFinder componentFinder = TestComponentFinder.from(db);
-  private ProjectLifeCycleListeners projectLifeCycleListeners = mock(ProjectLifeCycleListeners.class);
+  private final ComponentCleanerService componentCleanerService = mock(ComponentCleanerService.class);
+  private final ComponentFinder componentFinder = TestComponentFinder.from(db);
+  private final ProjectLifeCycleListeners projectLifeCycleListeners = mock(ProjectLifeCycleListeners.class);
 
   @Rule
   public UserSessionRule userSession = UserSessionRule.standalone();
index 24358548f74bc94b92f1135939104613be838a48..f2a7f498dd78d7bd69be019b84ca6a641ff2765c 100644 (file)
@@ -182,7 +182,7 @@ public class BranchReportSubmitterIT {
     ComponentCreationData componentCreationData = mock(ComponentCreationData.class);
     when(componentCreationData.mainBranchComponent())
       .thenAnswer((Answer<ComponentDto>) invocation -> db.components().insertPrivateProject(nonExistingProject).getMainBranchComponent());
-    when(componentUpdater.createWithoutCommit(any(), any(), eq(user.getUuid()), eq(user.getLogin()), any()))
+    when(componentUpdater.createWithoutCommit(any(), any(), eq(user.getUuid()), eq(user.getLogin())))
       .thenReturn(componentCreationData);
     when(branchSupportDelegate.createBranchComponent(any(DbSession.class), same(componentKey), eq(nonExistingProject), any())).thenReturn(createdBranch);
     when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), any(), eq(nonExistingProject.getKey()))).thenReturn(true);
@@ -197,7 +197,7 @@ public class BranchReportSubmitterIT {
     verify(branchSupportDelegate).createBranchComponent(any(DbSession.class), same(componentKey), eq(nonExistingProject), eq(exitingProjectMainBranch));
     verifyNoMoreInteractions(branchSupportDelegate);
     verifyQueueSubmit(nonExistingProject, createdBranch, user, randomCharacteristics, taskUuid);
-    verify(componentUpdater).commitAndIndex(any(DbSession.class), eq(nonExistingProject));
+    verify(componentUpdater).commitAndIndex(any(DbSession.class), eq(componentCreationData));
   }
 
   @Test
index 4cccc2498ebd0b432a34f31451ea5b13fe51a69f..16b61c3698f82377ede35e24b43169ede838af55 100644 (file)
@@ -41,7 +41,7 @@ import org.sonar.db.permission.GlobalPermission;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.component.ComponentUpdater;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.TestIndexers;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.favorite.FavoriteUpdater;
@@ -87,7 +87,7 @@ public class ReportSubmitterIT {
   private final DefaultBranchNameResolver defaultBranchNameResolver = mock(DefaultBranchNameResolver.class);
 
   private final CeQueue queue = mock(CeQueueImpl.class);
-  private final TestProjectIndexers projectIndexers = new TestProjectIndexers();
+  private final TestIndexers projectIndexers = new TestIndexers();
   private final PermissionTemplateService permissionTemplateService = mock(PermissionTemplateService.class);
 
   private final ComponentUpdater componentUpdater = new ComponentUpdater(db.getDbClient(), mock(I18n.class), mock(System2.class), permissionTemplateService,
index 4590889a6788bf6bc4b367c7a2b781e603dbc5e6..97e2095c1f344c01ee3c5a03d32a5ce76f6371da 100644 (file)
  */
 package org.sonar.server.component;
 
+import java.util.List;
 import org.junit.Rule;
 import org.junit.Test;
-import org.sonar.api.resources.ResourceType;
-import org.sonar.api.resources.ResourceTypes;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
@@ -36,15 +35,18 @@ import org.sonar.db.portfolio.PortfolioDto;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.db.rule.RuleDto;
 import org.sonar.db.webhook.WebhookDto;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.Indexers;
+import org.sonar.server.es.Indexers.BranchEvent;
 
 import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.server.es.ProjectIndexer.Cause.PROJECT_DELETION;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.sonar.server.es.Indexers.EntityEvent.DELETION;
 
 public class ComponentCleanerServiceIT {
 
@@ -55,19 +57,20 @@ public class ComponentCleanerServiceIT {
 
   private final DbClient dbClient = db.getDbClient();
   private final DbSession dbSession = db.getSession();
-  private final TestProjectIndexers projectIndexers = new TestProjectIndexers();
-  private final ResourceTypes mockResourceTypes = mock(ResourceTypes.class);
-  private final ComponentCleanerService underTest = new ComponentCleanerService(dbClient, mockResourceTypes, projectIndexers);
+  private final Indexers indexers = mock(Indexers.class);
+  private final ComponentCleanerService underTest = new ComponentCleanerService(dbClient, indexers);
 
   @Test
   public void delete_project_from_db_and_index() {
     DbData data1 = insertProjectData();
     DbData data2 = insertProjectData();
 
-    underTest.delete(dbSession, data1.project);
+    underTest.deleteEntity(dbSession, data1.project);
 
     assertNotExists(data1);
     assertExists(data2);
+    verify(indexers).commitAndIndexEntities(any(), eq(List.of(data1.project)), eq(DELETION));
+    verifyNoMoreInteractions(indexers);
   }
 
   @Test
@@ -82,6 +85,10 @@ public class ComponentCleanerServiceIT {
     assertNotExists(data1);
     assertNotExists(data2);
     assertExists(data3);
+    verify(indexers).commitAndIndexEntities(any(), eq(List.of(data1.project)), eq(DELETION));
+    verify(indexers).commitAndIndexEntities(any(), eq(List.of(data2.project)), eq(DELETION));
+
+    verifyNoMoreInteractions(indexers);
   }
 
   @Test
@@ -91,9 +98,7 @@ public class ComponentCleanerServiceIT {
     ProjectData projectData2 = db.components().insertPublicProject();
     ComponentDto componentDto2 = projectData2.getMainBranchComponent();
 
-    mockResourceTypeAsValidProject();
-
-    underTest.delete(dbSession, projectData1.getProjectDto());
+    underTest.deleteEntity(dbSession, projectData1.getProjectDto());
     dbSession.commit();
 
     assertNotExists(componentDto1);
@@ -101,12 +106,9 @@ public class ComponentCleanerServiceIT {
   }
 
   @Test
-  public void fail_with_IAE_if_project_non_deletable() {
-    ProjectData projectData = db.components().insertPublicProject();
-    mockResourceTypeAsNonDeletable();
-    dbSession.commit();
-    ProjectDto project = projectData.getProjectDto();
-    assertThatThrownBy(() -> underTest.delete(dbSession, project))
+  public void fail_with_IAE_if_deleting_subview() {
+    PortfolioDto portfolio = new PortfolioDto().setParentUuid("parent").setRootUuid("root").setUuid("uuid");
+    assertThatThrownBy(() -> underTest.deleteEntity(dbSession, portfolio))
       .withFailMessage("Only projects can be deleted")
       .isInstanceOf(IllegalArgumentException.class);
   }
@@ -119,7 +121,7 @@ public class ComponentCleanerServiceIT {
     ProjectData app1 = insertApplication(data2.project);
     ProjectData app2 = insertApplication(data3.project);
 
-    underTest.delete(dbSession, app1.getProjectDto());
+    underTest.deleteEntity(dbSession, app1.getProjectDto());
     dbSession.commit();
 
     assertProjectOrAppExists(app1.getProjectDto(), app1.getMainBranchDto(), false);
@@ -127,20 +129,21 @@ public class ComponentCleanerServiceIT {
     assertExists(data1);
     assertExists(data2);
     assertExists(data3);
+    verify(indexers).commitAndIndexEntities(any(), eq(List.of(app1.getProjectDto())), eq(DELETION));
+    verifyNoMoreInteractions(indexers);
   }
 
   @Test
   public void delete_WhenDeletingPortfolio_ShouldDeleteComponents() {
     PortfolioDto portfolioDto1 = db.components().insertPrivatePortfolioDto();
     PortfolioDto portfolioDto2 = db.components().insertPrivatePortfolioDto();
-    mockResourceTypeAsValidProject();
-    underTest.delete(dbSession, portfolioDto1);
+    underTest.deleteEntity(dbSession, portfolioDto1);
 
     assertThat(dbClient.componentDao().selectByUuid(dbSession, portfolioDto1.getUuid())).isEmpty();
     assertThat(dbClient.componentDao().selectByUuid(dbSession, portfolioDto2.getUuid())).isPresent();
 
-    assertThat(projectIndexers.hasBeenCalled(portfolioDto1.getUuid(), PROJECT_DELETION)).isTrue();
-
+    verify(indexers).commitAndIndexEntities(any(), eq(List.of(portfolioDto1)), eq(DELETION));
+    verifyNoMoreInteractions(indexers);
   }
 
   private ProjectData insertApplication(ProjectDto project) {
@@ -165,6 +168,8 @@ public class ComponentCleanerServiceIT {
 
     assertThat(dbClient.componentDao().selectByUuid(dbSession, otherBranch.getUuid())).isEmpty();
     assertThat(dbClient.branchDao().selectByUuid(dbSession, otherBranch.getUuid())).isEmpty();
+    verify(indexers).commitAndIndexBranches(any(), eq(List.of(otherBranch)), eq(BranchEvent.DELETION));
+    verifyNoMoreInteractions(indexers);
   }
 
   @Test
@@ -179,6 +184,7 @@ public class ComponentCleanerServiceIT {
     assertExists(data1);
     assertExists(data2);
     assertExists(data3);
+    verifyNoMoreInteractions(indexers);
   }
 
   @Test
@@ -196,8 +202,6 @@ public class ComponentCleanerServiceIT {
     ProjectDto projectDto1 = dbClient.projectDao().selectByUuid(dbSession, project1.getUuid()).get();
     ProjectDto projectDto2 = dbClient.projectDao().selectByUuid(dbSession, project2.getUuid()).get();
 
-    mockResourceTypeAsValidProject();
-
     underTest.delete(dbSession, asList(projectDto1, projectDto2));
 
     assertThat(db.countRowsOfTable(db.getSession(), "webhooks")).isOne();
@@ -211,30 +215,15 @@ public class ComponentCleanerServiceIT {
     RuleDto rule = db.rules().insert();
     IssueDto issue = db.issues().insert(rule, mainBranch, mainBranch);
     SnapshotDto analysis = db.components().insertSnapshot(mainBranch);
-    mockResourceTypeAsValidProject();
     return new DbData(projectData.getProjectDto(), projectData.getMainBranchDto(), analysis, issue);
   }
 
-  private void mockResourceTypeAsValidProject() {
-    ResourceType resourceType = mock(ResourceType.class);
-    when(resourceType.getBooleanProperty(anyString())).thenReturn(true);
-    when(mockResourceTypes.get(anyString())).thenReturn(resourceType);
-  }
-
-  private void mockResourceTypeAsNonDeletable() {
-    ResourceType resourceType = mock(ResourceType.class);
-    when(resourceType.getBooleanProperty("deletable")).thenReturn(false);
-    when(mockResourceTypes.get(anyString())).thenReturn(resourceType);
-  }
-
   private void assertNotExists(DbData data) {
     assertDataInDb(data, false);
-    assertThat(projectIndexers.hasBeenCalled(data.project.getUuid(), PROJECT_DELETION)).isTrue();
   }
 
   private void assertExists(DbData data) {
     assertDataInDb(data, true);
-    assertThat(projectIndexers.hasBeenCalled(data.project.getUuid(), PROJECT_DELETION)).isFalse();
   }
 
   private void assertDataInDb(DbData data, boolean exists) {
index e9d88885b27dcf471b9e84e6212b966275593ca9..cebfeaff1f73f448bc64d54cc84a1fb943f68f2d 100644 (file)
@@ -30,13 +30,12 @@ import org.sonar.api.web.UserRole;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
-import org.sonar.db.component.ComponentDbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTesting;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.db.pushevent.PushEventDto;
-import org.sonar.server.es.ProjectIndexer;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.Indexers;
+import org.sonar.server.es.TestIndexers;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.project.ProjectLifeCycleListeners;
 import org.sonar.server.tester.UserSessionRule;
@@ -56,7 +55,7 @@ public class ComponentServiceUpdateKeyIT {
 
   private DbClient dbClient = db.getDbClient();
   private DbSession dbSession = db.getSession();
-  private TestProjectIndexers projectIndexers = new TestProjectIndexers();
+  private TestIndexers projectIndexers = new TestIndexers();
   private ProjectLifeCycleListeners projectLifeCycleListeners = mock(ProjectLifeCycleListeners.class);
   private ComponentService underTest = new ComponentService(dbClient, userSession, projectIndexers, projectLifeCycleListeners);
 
@@ -84,7 +83,7 @@ public class ComponentServiceUpdateKeyIT {
 
     assertThat(dbClient.componentDao().selectByKey(dbSession, inactiveFile.getKey())).isEmpty();
 
-    assertThat(projectIndexers.hasBeenCalled(project.getUuid(), ProjectIndexer.Cause.PROJECT_KEY_UPDATE)).isTrue();
+    assertThat(projectIndexers.hasBeenCalledForEntity(project.getUuid(), Indexers.EntityEvent.PROJECT_KEY_UPDATE)).isTrue();
 
     Deque<PushEventDto> pushEvents = db.getDbClient().pushEventDao().selectChunkByProjectUuids(dbSession, Set.of(project.getUuid()), 0L, "id", 20);
 
@@ -109,7 +108,7 @@ public class ComponentServiceUpdateKeyIT {
     dbSession.commit();
 
     assertComponentKeyHasBeenUpdated(provisionedProject.getKey(), "provisionedProject2");
-    assertThat(projectIndexers.hasBeenCalled(provisionedProject.getUuid(), ProjectIndexer.Cause.PROJECT_KEY_UPDATE)).isTrue();
+    assertThat(projectIndexers.hasBeenCalledForEntity(provisionedProject.getUuid(), Indexers.EntityEvent.PROJECT_KEY_UPDATE)).isTrue();
   }
 
   @Test
index 854e097b1141dac382b68d2dc0b1c700d8165469..807b8b01a6efa799d92987e56dd734a882dc4b9c 100644 (file)
@@ -36,14 +36,13 @@ import org.sonar.db.component.BranchType;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.db.user.UserDto;
-import org.sonar.server.es.ProjectIndexer;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.Indexers;
+import org.sonar.server.es.TestIndexers;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.favorite.FavoriteUpdater;
 import org.sonar.server.l18n.I18nRule;
 import org.sonar.server.permission.PermissionTemplateService;
 import org.sonar.server.project.DefaultBranchNameResolver;
-import org.sonar.server.project.Project;
 
 import static java.util.stream.IntStream.rangeClosed;
 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
@@ -71,7 +70,7 @@ public class ComponentUpdaterIT {
   @Rule
   public final I18nRule i18n = new I18nRule().put("qualifier.TRK", "Project");
 
-  private final TestProjectIndexers projectIndexers = new TestProjectIndexers();
+  private final TestIndexers projectIndexers = new TestIndexers();
   private final PermissionTemplateService permissionTemplateService = mock(PermissionTemplateService.class);
   private final DefaultBranchNameResolver defaultBranchNameResolver = mock(DefaultBranchNameResolver.class);
 
@@ -108,7 +107,7 @@ public class ComponentUpdaterIT {
     assertThat(loaded.getCreatedAt()).isNotNull();
     assertThat(db.getDbClient().componentDao().selectByKey(db.getSession(), DEFAULT_PROJECT_KEY)).isPresent();
 
-    assertThat(projectIndexers.hasBeenCalled(loaded.uuid(), ProjectIndexer.Cause.PROJECT_CREATION)).isTrue();
+    assertThat(projectIndexers.hasBeenCalledForEntity(returned.projectDto().getUuid(), Indexers.EntityEvent.CREATION)).isTrue();
 
     Optional<BranchDto> branch = db.getDbClient().branchDao().selectByUuid(db.getSession(), returned.mainBranchComponent().uuid());
     assertThat(branch).isPresent();
@@ -134,21 +133,6 @@ public class ComponentUpdaterIT {
     assertThat(branch).get().extracting(BranchDto::getBranchKey).isEqualTo("main-branch-global");
   }
 
-  @Test
-  public void create_project_with_main_branch_param() {
-    String customBranchName = "main-branch-custom";
-    NewComponent project = NewComponent.newComponentBuilder()
-      .setKey(DEFAULT_PROJECT_KEY)
-      .setName(DEFAULT_PROJECT_NAME)
-      .setPrivate(true)
-      .build();
-
-    ComponentDto returned = underTest.create(db.getSession(), project, null, null, customBranchName).mainBranchComponent();
-
-    Optional<BranchDto> branch = db.getDbClient().branchDao().selectByUuid(db.getSession(), returned.branchUuid());
-    assertThat(branch).get().extracting(BranchDto::getBranchKey).isEqualTo(customBranchName);
-  }
-
   @Test
   public void persist_private_flag_true_when_creating_project() {
     NewComponent project = NewComponent.newComponentBuilder()
@@ -187,7 +171,7 @@ public class ComponentUpdaterIT {
     assertThat(loaded.getKey()).isEqualTo("view-key");
     assertThat(loaded.name()).isEqualTo("view-name");
     assertThat(loaded.qualifier()).isEqualTo("VW");
-    assertThat(projectIndexers.hasBeenCalled(loaded.uuid(), ProjectIndexer.Cause.PROJECT_CREATION)).isTrue();
+    assertThat(projectIndexers.hasBeenCalledForEntity(loaded.uuid(), Indexers.EntityEvent.CREATION)).isTrue();
     Optional<BranchDto> branch = db.getDbClient().branchDao().selectByUuid(db.getSession(), returned.uuid());
     assertThat(branch).isNotPresent();
   }
@@ -202,11 +186,11 @@ public class ComponentUpdaterIT {
 
     ComponentCreationData returned = underTest.create(db.getSession(), application, null, null);
 
-    ComponentDto loaded = db.getDbClient().componentDao().selectOrFailByUuid(db.getSession(), returned.mainBranchComponent().uuid());
+    ProjectDto loaded = db.getDbClient().projectDao().selectByUuid(db.getSession(), returned.projectDto().getUuid()).get();
     assertThat(loaded.getKey()).isEqualTo("app-key");
-    assertThat(loaded.name()).isEqualTo("app-name");
-    assertThat(loaded.qualifier()).isEqualTo("APP");
-    assertThat(projectIndexers.hasBeenCalled(loaded.uuid(), ProjectIndexer.Cause.PROJECT_CREATION)).isTrue();
+    assertThat(loaded.getName()).isEqualTo("app-name");
+    assertThat(loaded.getQualifier()).isEqualTo("APP");
+    assertThat(projectIndexers.hasBeenCalledForEntity(loaded.getUuid(), Indexers.EntityEvent.CREATION)).isTrue();
     Optional<BranchDto> branch = db.getDbClient().branchDao().selectByUuid(db.getSession(), returned.mainBranchComponent().uuid());
     assertThat(branch).isPresent();
     assertThat(branch.get().getKey()).isEqualTo(DEFAULT_MAIN_BRANCH_NAME);
@@ -398,7 +382,7 @@ public class ComponentUpdaterIT {
   public void create_createsComponentWithMasterBranchName() {
     String componentNameAndKey = "createApplicationOrPortfolio";
     ComponentDto app = underTest.create(db.getSession(), NewComponent.newComponentBuilder().setName(componentNameAndKey)
-      .setKey(componentNameAndKey).setQualifier("APP").build(), null, null, null).mainBranchComponent();
+      .setKey(componentNameAndKey).setQualifier("APP").build(), null, null).mainBranchComponent();
 
     Optional<BranchDto> branch = db.getDbClient().branchDao().selectByUuid(db.getSession(), app.branchUuid());
     assertThat(branch).isPresent();
index de0af82b8de78d0e674fc961ce9bce38a5e510b4..c89ff22122ef36f29af4745d2bf427abab149fbe 100644 (file)
@@ -22,7 +22,6 @@ package org.sonar.server.component.ws;
 import com.google.common.base.Joiner;
 import java.util.ArrayList;
 import java.util.List;
-import org.eclipse.jface.text.projection.ProjectionDocument;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -36,7 +35,7 @@ import org.sonar.db.component.ResourceTypesRule;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.component.index.ComponentIndex;
-import org.sonar.server.component.index.ComponentIndexer;
+import org.sonar.server.component.index.EntityDefinitionIndexer;
 import org.sonar.server.component.ws.SearchAction.SearchRequest;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.l18n.I18nRule;
@@ -77,7 +76,7 @@ public class SearchActionIT {
 
   private final I18nRule i18n = new I18nRule();
   private final ResourceTypesRule resourceTypes = new ResourceTypesRule();
-  private final ComponentIndexer indexer = new ComponentIndexer(db.getDbClient(), es.client());
+  private final EntityDefinitionIndexer indexer = new EntityDefinitionIndexer(db.getDbClient(), es.client());
   private final PermissionIndexerTester authorizationIndexerTester = new PermissionIndexerTester(es, indexer);
   private final ComponentIndex index = new ComponentIndex(es.client(), new WebAuthorizationTypeSupport(userSession), System2.INSTANCE);
 
index 75db28e67ba2bfb0ba521acab800c3cc5f9785a7..c50e4ed2ed68d24f02e7b37c2acfd13a24febc97 100644 (file)
@@ -1397,7 +1397,7 @@ public class SearchProjectsActionIT {
         authorizationIndexerTester.allowOnlyAnyone(r.getResultObject());
       }
     };
-    db.getDbClient().entityDao().scrollForIndexing(dbSession, null, rh);
+    db.getDbClient().entityDao().scrollForIndexing(dbSession, rh);
   }
 
   private ComponentDto insertPortfolio() {
index 2408c613180a171f81bef6f19fe346b95b6790d8..a88b8c3316c438a1b7971b61f2aac202e2b8fdcc 100644 (file)
@@ -38,7 +38,7 @@ import org.sonar.db.component.ProjectData;
 import org.sonar.db.component.ResourceTypesRule;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.server.component.index.ComponentIndex;
-import org.sonar.server.component.index.ComponentIndexer;
+import org.sonar.server.component.index.EntityDefinitionIndexer;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.favorite.FavoriteFinder;
 import org.sonar.server.permission.index.PermissionIndexerTester;
@@ -89,11 +89,11 @@ public class SuggestionsActionIT {
   public final UserSessionRule userSessionRule = UserSessionRule.standalone();
   public final ResourceTypesRule resourceTypes = new ResourceTypesRule();
 
-  private final ComponentIndexer componentIndexer = new ComponentIndexer(db.getDbClient(), es.client());
+  private final EntityDefinitionIndexer entityDefinitionIndexer = new EntityDefinitionIndexer(db.getDbClient(), es.client());
   private final FavoriteFinder favoriteFinder = mock(FavoriteFinder.class);
   private final ComponentIndex index = new ComponentIndex(es.client(), new WebAuthorizationTypeSupport(userSessionRule), System2.INSTANCE);
   private final SuggestionsAction underTest = new SuggestionsAction(db.getDbClient(), index, favoriteFinder, userSessionRule, resourceTypes);
-  private final PermissionIndexerTester authorizationIndexerTester = new PermissionIndexerTester(es, componentIndexer);
+  private final PermissionIndexerTester authorizationIndexerTester = new PermissionIndexerTester(es, entityDefinitionIndexer);
   private final WsActionTester ws = new WsActionTester(underTest);
 
   @Before
@@ -134,7 +134,7 @@ public class SuggestionsActionIT {
   public void test_example_json_response() {
     ProjectDto project1 = db.components().insertPublicProject(p -> p.setKey("org.sonarsource:sonarqube").setName("SonarSource :: SonarQube")).getProjectDto();
     ProjectDto project2 = db.components().insertPublicProject(p -> p.setKey("org.sonarsource:sonarlint").setName("SonarSource :: SonarLint")).getProjectDto();
-    componentIndexer.indexAll();
+    entityDefinitionIndexer.indexAll();
     authorizationIndexerTester.allowOnlyAnyone(project1);
     authorizationIndexerTester.allowOnlyAnyone(project2);
 
@@ -152,7 +152,7 @@ public class SuggestionsActionIT {
   public void suggestions_without_query_should_contain_recently_browsed() {
     ProjectDto project = db.components().insertPrivateProject().getProjectDto();
 
-    componentIndexer.indexAll();
+    entityDefinitionIndexer.indexAll();
     userSessionRule.addProjectPermission(USER, project);
 
     SuggestionsWsResponse response = ws.newRequest()
@@ -177,7 +177,7 @@ public class SuggestionsActionIT {
   public void suggestions_without_query_should_contain_recently_browsed_public_project() {
     ProjectDto project = db.components().insertPublicProject().getProjectDto();
 
-    componentIndexer.indexAll();
+    entityDefinitionIndexer.indexAll();
 
     SuggestionsWsResponse response = ws.newRequest()
       .setMethod("POST")
@@ -201,7 +201,7 @@ public class SuggestionsActionIT {
   public void suggestions_without_query_should_not_contain_recently_browsed_without_permission() {
     ProjectDto project = db.components().insertPrivateProject().getProjectDto();
 
-    componentIndexer.indexAll();
+    entityDefinitionIndexer.indexAll();
 
     SuggestionsWsResponse response = ws.newRequest()
       .setMethod("POST")
@@ -218,7 +218,7 @@ public class SuggestionsActionIT {
     ProjectDto project = db.components().insertPrivateProject().getProjectDto();
     doReturn(singletonList(project)).when(favoriteFinder).list();
 
-    componentIndexer.indexAll();
+    entityDefinitionIndexer.indexAll();
     userSessionRule.addProjectPermission(USER, project);
 
     SuggestionsWsResponse response = ws.newRequest()
@@ -243,7 +243,7 @@ public class SuggestionsActionIT {
     ProjectDto project = db.components().insertPrivateProject().getProjectDto();
     doReturn(singletonList(project)).when(favoriteFinder).list();
 
-    componentIndexer.indexAll();
+    entityDefinitionIndexer.indexAll();
 
     SuggestionsWsResponse response = ws.newRequest()
       .setMethod("POST")
@@ -259,7 +259,7 @@ public class SuggestionsActionIT {
     ProjectDto project = db.components().insertPrivateProject().getProjectDto();
     doReturn(singletonList(project)).when(favoriteFinder).list();
 
-    componentIndexer.indexAll();
+    entityDefinitionIndexer.indexAll();
     userSessionRule.addProjectPermission(USER, project);
 
     SuggestionsWsResponse response = ws.newRequest()
@@ -284,7 +284,7 @@ public class SuggestionsActionIT {
   public void suggestions_without_query_should_not_contain_matches_that_are_neither_favorites_nor_recently_browsed() {
     ProjectDto project = db.components().insertPrivateProject().getProjectDto();
 
-    componentIndexer.indexAll();
+    entityDefinitionIndexer.indexAll();
     userSessionRule.addProjectPermission(USER, project);
 
     SuggestionsWsResponse response = ws.newRequest()
@@ -306,7 +306,7 @@ public class SuggestionsActionIT {
     ProjectDto project4 = db.components().insertPrivateProject(p -> p.setName("Delta")).getProjectDto();
     doReturn(asList(project4, project2)).when(favoriteFinder).list();
 
-    componentIndexer.indexAll();
+    entityDefinitionIndexer.indexAll();
     userSessionRule.addProjectPermission(USER, project1);
     userSessionRule.addProjectPermission(USER, project2);
     userSessionRule.addProjectPermission(USER, project3);
@@ -331,7 +331,7 @@ public class SuggestionsActionIT {
   @Test
   public void suggestions_without_query_should_return_empty_qualifiers() {
     ProjectData project = db.components().insertPrivateProject();
-    componentIndexer.indexOnAnalysis(project.getMainBranchDto().getUuid());
+    entityDefinitionIndexer.indexOnAnalysis(project.getMainBranchDto().getUuid());
     userSessionRule.addProjectPermission(USER, project.getProjectDto());
 
     SuggestionsWsResponse response = ws.newRequest()
@@ -349,7 +349,7 @@ public class SuggestionsActionIT {
   public void suggestions_should_filter_allowed_qualifiers() {
     resourceTypes.setAllQualifiers(PROJECT, FILE, UNIT_TEST_FILE);
     ProjectData project = db.components().insertPrivateProject();
-    componentIndexer.indexOnAnalysis(project.getMainBranchDto().getUuid());
+    entityDefinitionIndexer.indexOnAnalysis(project.getMainBranchDto().getUuid());
     userSessionRule.addProjectPermission(USER, project.getProjectDto());
 
     SuggestionsWsResponse response = ws.newRequest()
@@ -366,7 +366,7 @@ public class SuggestionsActionIT {
   public void exact_match_in_one_qualifier() {
     ProjectDto project = db.components().insertPrivateProject().getProjectDto();
 
-    componentIndexer.indexAll();
+    entityDefinitionIndexer.indexAll();
     authorizationIndexerTester.allowOnlyAnyone(project);
 
     SuggestionsWsResponse response = ws.newRequest()
@@ -391,7 +391,7 @@ public class SuggestionsActionIT {
   public void should_not_return_suggestion_on_non_existing_project() {
     ProjectDto project = db.components().insertPrivateProject().getProjectDto();
 
-    componentIndexer.indexAll();
+    entityDefinitionIndexer.indexAll();
     authorizationIndexerTester.allowOnlyAnyone(project);
 
     db.getDbClient().purgeDao().deleteProject(db.getSession(), project.getUuid(), PROJECT, project.getName(), project.getKey());
@@ -412,7 +412,7 @@ public class SuggestionsActionIT {
   public void must_not_search_if_no_valid_tokens_are_provided() {
     ProjectDto project = db.components().insertPrivateProject(p -> p.setName("SonarQube")).getProjectDto();
 
-    componentIndexer.indexAll();
+    entityDefinitionIndexer.indexAll();
     authorizationIndexerTester.allowOnlyAnyone(project);
 
     SuggestionsWsResponse response = ws.newRequest()
@@ -438,7 +438,7 @@ public class SuggestionsActionIT {
   public void should_warn_about_short_inputs_but_return_results_based_on_other_terms() {
     ProjectDto project = db.components().insertPrivateProject(p -> p.setName("SonarQube")).getProjectDto();
 
-    componentIndexer.indexAll();
+    entityDefinitionIndexer.indexAll();
     authorizationIndexerTester.allowOnlyAnyone(project);
 
     SuggestionsWsResponse response = ws.newRequest()
@@ -456,7 +456,7 @@ public class SuggestionsActionIT {
   @Test
   public void should_contain_component_names() {
     ProjectData project1 = db.components().insertPrivateProject(p -> p.setName("Project1"));
-    componentIndexer.indexOnAnalysis(project1.getMainBranchDto().getUuid());
+    entityDefinitionIndexer.indexOnAnalysis(project1.getMainBranchDto().getUuid());
     authorizationIndexerTester.allowOnlyAnyone(project1.getProjectDto());
 
     SuggestionsWsResponse response = ws.newRequest()
@@ -476,7 +476,7 @@ public class SuggestionsActionIT {
     ComponentDto project = projectData.getMainBranchComponent();
     ComponentDto file1 = newFileDto(project).setName("File1");
     ComponentDto file2 = newFileDto(project).setName("File2");
-    componentIndexer.indexOnAnalysis(project.branchUuid());
+    entityDefinitionIndexer.indexOnAnalysis(project.branchUuid());
     authorizationIndexerTester.allowOnlyAnyone(projectData.getProjectDto());
 
     SuggestionsWsResponse response = ws.newRequest()
@@ -497,8 +497,8 @@ public class SuggestionsActionIT {
     ProjectData nonFavouriteProject = db.components().insertPublicProject(p -> p.setName("Project2"));
 
     doReturn(singletonList(favouriteProject.getProjectDto())).when(favoriteFinder).list();
-    componentIndexer.indexOnAnalysis(favouriteProject.getMainBranchDto().getUuid());
-    componentIndexer.indexOnAnalysis(nonFavouriteProject.getMainBranchDto().getUuid());
+    entityDefinitionIndexer.indexOnAnalysis(favouriteProject.getMainBranchDto().getUuid());
+    entityDefinitionIndexer.indexOnAnalysis(nonFavouriteProject.getMainBranchDto().getUuid());
     authorizationIndexerTester.allowOnlyAnyone(favouriteProject.getProjectDto(), nonFavouriteProject.getProjectDto());
 
     SuggestionsWsResponse response = ws.newRequest()
@@ -515,7 +515,7 @@ public class SuggestionsActionIT {
   @Test
   public void should_return_empty_qualifiers() {
     ProjectData project = db.components().insertPrivateProject();
-    componentIndexer.indexOnAnalysis(project.getMainBranchComponent().uuid());
+    entityDefinitionIndexer.indexOnAnalysis(project.getMainBranchComponent().uuid());
     authorizationIndexerTester.allowOnlyAnyone(project.getProjectDto());
 
     SuggestionsWsResponse response = ws.newRequest()
@@ -541,7 +541,7 @@ public class SuggestionsActionIT {
     ComponentDto dir = db.components().insertComponent(ComponentTesting.newDirectory(project, "path").setName(query));
     ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project, dir).setName(query));
     ComponentDto test = db.components().insertComponent(ComponentTesting.newFileDto(project, dir).setName(query).setQualifier(UNIT_TEST_FILE));
-    componentIndexer.indexAll();
+    entityDefinitionIndexer.indexAll();
     authorizationIndexerTester.allowOnlyAnyone(projectData.getProjectDto());
     authorizationIndexerTester.allowOnlyAnyone(view);
     authorizationIndexerTester.allowOnlyAnyone(appData.getProjectDto());
@@ -565,7 +565,7 @@ public class SuggestionsActionIT {
     ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
     authorizationIndexerTester.allowOnlyAnyone(project);
     ComponentDto branch = db.components().insertProjectBranch(project);
-    componentIndexer.indexAll();
+    entityDefinitionIndexer.indexAll();
     authorizationIndexerTester.allowOnlyAnyone(project);
 
     SuggestionsWsResponse response = ws.newRequest()
@@ -684,7 +684,7 @@ public class SuggestionsActionIT {
       .mapToObj(i -> db.components().insertPublicProject(p -> p.setName(namePrefix + i)).getProjectDto())
       .collect(Collectors.toList());
 
-    componentIndexer.indexAll();
+    entityDefinitionIndexer.indexAll();
     projects.forEach(authorizationIndexerTester::allowOnlyAnyone);
 
     TestRequest request = ws.newRequest()
index ca207f02f38f7f13e19a3adab1a84e0e94f88b16..7573ec464298dcfa37c096b5d3dd8d8ce4541c12 100644 (file)
@@ -40,7 +40,7 @@ import org.sonar.db.component.BranchDto;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.SnapshotDto;
 import org.sonar.db.metric.MetricDto;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.TestIndexers;
 import org.sonar.server.qualitygate.EvaluatedQualityGate;
 import org.sonar.server.qualitygate.QualityGate;
 import org.sonar.server.qualitygate.changeevent.QGChangeEvent;
@@ -61,7 +61,7 @@ public class LiveMeasureComputerImplIT {
   @Rule
   public DbTester db = DbTester.create(true);
 
-  private final TestProjectIndexers projectIndexer = new TestProjectIndexers();
+  private final TestIndexers projectIndexer = new TestIndexers();
   private MetricDto metric1;
   private MetricDto metric2;
   private ComponentDto project;
index 10b339c08f35337d86247b5a05a94de1df2d352d..c8bd27bb5a92d6088329283ee640db3b92a1c1ab 100644 (file)
@@ -37,8 +37,8 @@ import org.sonar.db.portfolio.PortfolioDto;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.db.user.GroupDto;
 import org.sonar.db.user.UserDto;
-import org.sonar.server.es.ProjectIndexers;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.Indexers;
+import org.sonar.server.es.TestIndexers;
 import org.sonar.server.exceptions.TemplateMatchingKeyException;
 import org.sonar.server.tester.UserSessionRule;
 
@@ -60,8 +60,8 @@ public class PermissionTemplateServiceIT {
   private final UserSessionRule userSession = UserSessionRule.standalone();
   private final PermissionTemplateDbTester templateDb = dbTester.permissionTemplates();
   private final DbSession session = dbTester.getSession();
-  private final ProjectIndexers projectIndexers = new TestProjectIndexers();
-  private final PermissionTemplateService underTest = new PermissionTemplateService(dbTester.getDbClient(), projectIndexers, userSession, defaultTemplatesResolver,
+  private final Indexers indexers = new TestIndexers();
+  private final PermissionTemplateService underTest = new PermissionTemplateService(dbTester.getDbClient(), indexers, userSession, defaultTemplatesResolver,
     new SequenceUuidFactory());
 
   @Test
index 7e2e5c54315246d8aa94cf7cf99cb5e398dbfd92..a2e049c1dc761ed6ca3efc9083adc64ca6c91aa1 100644 (file)
@@ -31,7 +31,7 @@ import org.sonar.db.component.ResourceTypesRule;
 import org.sonar.db.permission.template.PermissionTemplateDto;
 import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.es.EsTester;
-import org.sonar.server.es.ProjectIndexersImpl;
+import org.sonar.server.es.IndexersImpl;
 import org.sonar.server.permission.GroupPermissionChanger;
 import org.sonar.server.permission.PermissionUpdater;
 import org.sonar.server.permission.UserPermissionChanger;
@@ -82,7 +82,7 @@ public abstract class BasePermissionWsIT<A extends PermissionsWsAction> {
 
   protected PermissionUpdater newPermissionUpdater() {
     return new PermissionUpdater(
-      new ProjectIndexersImpl(new PermissionIndexer(db.getDbClient(), es.client())),
+      new IndexersImpl(new PermissionIndexer(db.getDbClient(), es.client())),
       new UserPermissionChanger(db.getDbClient(), new SequenceUuidFactory()),
       new GroupPermissionChanger(db.getDbClient(), new SequenceUuidFactory()));
   }
index 28015aec9c7b98767fdec1198bdc0cd5e0dc7f88..6a065c29e14a1445b3896880476eaf0e20f3e6bd 100644 (file)
@@ -34,7 +34,7 @@ import org.sonar.db.permission.template.PermissionTemplateDto;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.db.user.GroupDto;
 import org.sonar.db.user.UserDto;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.TestIndexers;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
@@ -70,7 +70,7 @@ public class ApplyTemplateActionIT extends BasePermissionWsIT<ApplyTemplateActio
   private final ResourceTypesRule resourceTypesRule = new ResourceTypesRule().setRootQualifiers(PROJECT, VIEW, APP);
   private final DefaultTemplatesResolver defaultTemplatesResolver = new DefaultTemplatesResolverImpl(dbTester.getDbClient(), resourceTypesRule);
   private final PermissionTemplateService permissionTemplateService = new PermissionTemplateService(db.getDbClient(),
-    new TestProjectIndexers(), userSession, defaultTemplatesResolver, new SequenceUuidFactory());
+    new TestIndexers(), userSession, defaultTemplatesResolver, new SequenceUuidFactory());
 
   @Override
   protected ApplyTemplateAction buildWsAction() {
index d0a21f067ed8e8b9e90e2505c7287305a07268c8..1fa591ec7e34dacbda8ae59ad25aaf25664e6a40 100644 (file)
@@ -39,8 +39,8 @@ import org.sonar.db.portfolio.PortfolioDto;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.db.user.GroupDto;
 import org.sonar.db.user.UserDto;
-import org.sonar.server.es.ProjectIndexers;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.Indexers;
+import org.sonar.server.es.TestIndexers;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.l18n.I18nRule;
@@ -71,14 +71,14 @@ public class BulkApplyTemplateActionIT extends BasePermissionWsIT<BulkApplyTempl
   private GroupDto group1;
   private GroupDto group2;
   private PermissionTemplateDto template1;
-  private final ProjectIndexers projectIndexers = new TestProjectIndexers();
+  private final Indexers indexers = new TestIndexers();
   private final ResourceTypesRule resourceTypesRule = new ResourceTypesRule().setRootQualifiers(PROJECT, VIEW, APP);
   private final DefaultTemplatesResolver defaultTemplatesResolver = new DefaultTemplatesResolverImpl(db.getDbClient(), resourceTypesRule);
 
   @Override
   protected BulkApplyTemplateAction buildWsAction() {
     PermissionTemplateService permissionTemplateService = new PermissionTemplateService(db.getDbClient(),
-      projectIndexers, userSession, defaultTemplatesResolver, new SequenceUuidFactory());
+      indexers, userSession, defaultTemplatesResolver, new SequenceUuidFactory());
     return new BulkApplyTemplateAction(db.getDbClient(), userSession, permissionTemplateService, newPermissionWsSupport(), new I18nRule(), newRootResourceTypes());
   }
 
index 39b93707262a1b2d1c445b06ea0c7acf41299007..4d096c1af82e99d01bdd8f15d9ff76faa9c8cc4f 100644 (file)
@@ -238,7 +238,7 @@ public class BulkDeleteActionIT {
       .setParam("projects", StringUtils.join(keys, ","))
       .execute();
 
-    verify(componentCleanerService, times(1_000)).delete(any(DbSession.class), any(EntityDto.class));
+    verify(componentCleanerService, times(1_000)).deleteEntity(any(DbSession.class), any(EntityDto.class));
     ArgumentCaptor<Set<Project>> projectsCaptor = ArgumentCaptor.forClass(Set.class);
     verify(projectLifeCycleListeners).onProjectsDeleted(projectsCaptor.capture());
     assertThat(projectsCaptor.getValue()).hasSize(1_000);
@@ -255,7 +255,7 @@ public class BulkDeleteActionIT {
     doNothing()
       .doThrow(expectedException)
       .when(componentCleanerService)
-      .delete(any(), any(ProjectDto.class));
+      .deleteEntity(any(), any(ProjectDto.class));
 
     try {
       ws.newRequest()
@@ -314,7 +314,7 @@ public class BulkDeleteActionIT {
 
   private void verifyEntityDeleted(EntityDto... entities) {
     ArgumentCaptor<EntityDto> argument = ArgumentCaptor.forClass(EntityDto.class);
-    verify(componentCleanerService, times(entities.length)).delete(any(DbSession.class), argument.capture());
+    verify(componentCleanerService, times(entities.length)).deleteEntity(any(DbSession.class), argument.capture());
 
     for (EntityDto entity : entities) {
       assertThat(argument.getAllValues()).extracting(EntityDto::getUuid).contains(entity.getUuid());
index 01562458c77538c2c17e9beaaa7769c47b1f163c..e0a4536f168bf60112fcf727adbe9ce3773c1354 100644 (file)
@@ -38,7 +38,8 @@ import org.sonar.db.newcodeperiod.NewCodePeriodDto;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.component.ComponentUpdater;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.Indexers;
+import org.sonar.server.es.TestIndexers;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.favorite.FavoriteUpdater;
@@ -96,12 +97,12 @@ public class CreateActionIT {
 
   private final DefaultBranchNameResolver defaultBranchNameResolver = mock(DefaultBranchNameResolver.class);
   private final ProjectDefaultVisibility projectDefaultVisibility = mock(ProjectDefaultVisibility.class);
-  private final TestProjectIndexers projectIndexers = new TestProjectIndexers();
+  private final TestIndexers projectIndexers = new TestIndexers();
   private final PermissionTemplateService permissionTemplateService = mock(PermissionTemplateService.class);
 
-  private PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class);
+  private final PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class);
 
-  private NewCodeDefinitionResolver newCodeDefinitionResolver = new NewCodeDefinitionResolver(db.getDbClient(), editionProvider);
+  private final NewCodeDefinitionResolver newCodeDefinitionResolver = new NewCodeDefinitionResolver(db.getDbClient(), editionProvider);
   private final WsActionTester ws = new WsActionTester(
     new CreateAction(
       db.getDbClient(), userSession,
@@ -133,9 +134,11 @@ public class CreateActionIT {
       .extracting(ComponentDto::getKey, ComponentDto::name, ComponentDto::qualifier, ComponentDto::scope, ComponentDto::isPrivate)
       .containsOnly(DEFAULT_PROJECT_KEY, DEFAULT_PROJECT_NAME, "TRK", "PRJ", false);
 
-    assertThat(db.getDbClient().branchDao().selectByUuid(db.getSession(), component.branchUuid()).get())
+    BranchDto branch = db.getDbClient().branchDao().selectByUuid(db.getSession(), component.branchUuid()).get();
+    assertThat(branch)
       .extracting(BranchDto::getKey)
       .isEqualTo(MAIN_BRANCH);
+    projectIndexers.hasBeenCalledForEntity(branch.getProjectUuid(), Indexers.EntityEvent.CREATION);
   }
 
   @Test
index ee78e7c66c7f6c0ab29154965761a5bc5d2d846c..035b3ef9ffc504d1a6006fe01a9ad5e46f84689a 100644 (file)
 package org.sonar.server.project.ws;
 
 import java.util.List;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
-import org.sonar.api.resources.ResourceType;
-import org.sonar.api.resources.ResourceTypes;
 import org.sonar.api.utils.System2;
 import org.sonar.api.web.UserRole;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
-import org.sonar.db.component.ComponentDbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ProjectData;
 import org.sonar.db.permission.GlobalPermission;
@@ -40,7 +36,7 @@ import org.sonar.db.user.UserDto;
 import org.sonar.db.webhook.WebhookDbTester;
 import org.sonar.db.webhook.WebhookDto;
 import org.sonar.server.component.ComponentCleanerService;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.TestIndexers;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.UnauthorizedException;
 import org.sonar.server.project.Project;
@@ -54,10 +50,8 @@ import static java.util.Collections.singleton;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 import static org.sonar.db.user.UserTesting.newUserDto;
 import static org.sonar.server.component.TestComponentFinder.from;
 import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT;
@@ -75,7 +69,6 @@ public class DeleteActionIT {
   private final WebhookDbTester webhookDbTester = db.webhooks();
   private final ComponentCleanerService componentCleanerService = mock(ComponentCleanerService.class);
   private final ProjectLifeCycleListeners projectLifeCycleListeners = mock(ProjectLifeCycleListeners.class);
-  private final ResourceTypes mockResourceTypes = mock(ResourceTypes.class);
 
   private final DeleteAction underTest = new DeleteAction(
     componentCleanerService,
@@ -84,17 +77,6 @@ public class DeleteActionIT {
     userSessionRule, projectLifeCycleListeners);
   private final WsActionTester tester = new WsActionTester(underTest);
 
-  @Before
-  public void before() {
-    mockResourceTypeAsValidProject();
-  }
-
-  private void mockResourceTypeAsValidProject() {
-    ResourceType resourceType = mock(ResourceType.class);
-    when(resourceType.getBooleanProperty(anyString())).thenReturn(true);
-    when(mockResourceTypes.get(anyString())).thenReturn(resourceType);
-  }
-
   @Test
   public void global_administrator_deletes_project_by_key() {
     ProjectData projectData = db.components().insertPrivateProject();
@@ -128,7 +110,7 @@ public class DeleteActionIT {
     dbSession.commit();
     userSessionRule.logIn().addProjectPermission(UserRole.ADMIN, projectData.getProjectDto());
     DeleteAction underTest = new DeleteAction(
-      new ComponentCleanerService(dbClient, mockResourceTypes, new TestProjectIndexers()),
+      new ComponentCleanerService(dbClient, new TestIndexers()),
       from(db), dbClient, userSessionRule, projectLifeCycleListeners);
 
     new WsActionTester(underTest)
@@ -151,7 +133,7 @@ public class DeleteActionIT {
 
     userSessionRule.logIn().addProjectPermission(UserRole.ADMIN, project);
     DeleteAction underTest = new DeleteAction(
-      new ComponentCleanerService(dbClient, mockResourceTypes, new TestProjectIndexers()),
+      new ComponentCleanerService(dbClient, new TestIndexers()),
       from(db), dbClient, userSessionRule, projectLifeCycleListeners);
 
     new WsActionTester(underTest)
@@ -190,7 +172,7 @@ public class DeleteActionIT {
 
   private String verifyDeletedKey() {
     ArgumentCaptor<ProjectDto> argument = ArgumentCaptor.forClass(ProjectDto.class);
-    verify(componentCleanerService).delete(any(DbSession.class), argument.capture());
+    verify(componentCleanerService).deleteEntity(any(DbSession.class), argument.capture());
     return argument.getValue().getKey();
   }
 
index fd9624f43d7ef2d1a4c1daa5fa42e361212be25b..1c14be1725ddff20dd2c9bd9912eda27df221c0b 100644 (file)
@@ -34,8 +34,8 @@ import org.sonar.db.component.ProjectData;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.component.ComponentService;
-import org.sonar.server.es.ProjectIndexers;
-import org.sonar.server.es.ProjectIndexersImpl;
+import org.sonar.server.es.Indexers;
+import org.sonar.server.es.IndexersImpl;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.project.ProjectLifeCycleListenersImpl;
@@ -56,8 +56,8 @@ public class UpdateKeyActionIT {
   @Rule
   public final UserSessionRule userSessionRule = UserSessionRule.standalone();
   private final DbClient dbClient = db.getDbClient();
-  private final ProjectIndexers projectIndexers = new ProjectIndexersImpl();
-  private final ComponentService componentService = new ComponentService(dbClient, userSessionRule, projectIndexers, new ProjectLifeCycleListenersImpl());
+  private final Indexers indexers = new IndexersImpl();
+  private final ComponentService componentService = new ComponentService(dbClient, userSessionRule, indexers, new ProjectLifeCycleListenersImpl());
   private final ComponentFinder componentFinder = new ComponentFinder(dbClient, null);
   private final WsActionTester ws = new WsActionTester(new UpdateKeyAction(dbClient, componentService, componentFinder));
 
index 7d04fff851b41544fc83a1388362b17bee701efe..333163ea383324dbc789138a1da9f933e2b7e1ba 100644 (file)
@@ -54,8 +54,8 @@ import org.sonar.db.project.ProjectDto;
 import org.sonar.db.user.GroupDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.es.EsTester;
-import org.sonar.server.es.ProjectIndexer;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.Indexers;
+import org.sonar.server.es.TestIndexers;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.UnauthorizedException;
@@ -101,7 +101,7 @@ public class UpdateVisibilityActionIT {
 
   private final DbClient dbClient = dbTester.getDbClient();
   private final DbSession dbSession = dbTester.getSession();
-  private final TestProjectIndexers projectIndexers = new TestProjectIndexers();
+  private final TestIndexers projectIndexers = new TestIndexers();
   private final Configuration configuration = mock(Configuration.class);
 
   private final UpdateVisibilityAction underTest = new UpdateVisibilityAction(dbClient, userSessionRule, projectIndexers, new SequenceUuidFactory(), configuration);
@@ -423,7 +423,7 @@ public class UpdateVisibilityActionIT {
       .setParam(PARAM_VISIBILITY, initiallyPrivate ? PUBLIC : PRIVATE)
       .execute();
 
-    assertThat(projectIndexers.hasBeenCalled(project.projectUuid(), ProjectIndexer.Cause.PERMISSION_CHANGE)).isTrue();
+    assertThat(projectIndexers.hasBeenCalledForEntity(project.projectUuid(), Indexers.EntityEvent.PERMISSION_CHANGE)).isTrue();
   }
 
   @Test
@@ -436,7 +436,7 @@ public class UpdateVisibilityActionIT {
       .setParam(PARAM_VISIBILITY, initiallyPrivate ? PRIVATE : PUBLIC)
       .execute();
 
-    assertThat(projectIndexers.hasBeenCalled(project.projectUuid())).isFalse();
+    assertThat(projectIndexers.hasBeenCalledForEntity(project.projectUuid())).isFalse();
   }
 
   @Test
index 2903770460b016fcdb7c5e7569b1dfd6ebfc8390..dab7a1005689fb15921af4cd7fffdc0533dbd4ab 100644 (file)
@@ -32,7 +32,7 @@ import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ProjectData;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.server.component.TestComponentFinder;
-import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.es.TestIndexers;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
@@ -48,7 +48,9 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.sonar.api.web.UserRole.ADMIN;
 import static org.sonar.api.web.UserRole.USER;
+import static org.sonar.db.component.ComponentDbTester.defaults;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.server.es.Indexers.EntityEvent.PROJECT_TAGS_UPDATE;
 
 public class SetActionIT {
   @Rule
@@ -58,8 +60,8 @@ public class SetActionIT {
 
   private final DbClient dbClient = db.getDbClient();
   private final DbSession dbSession = db.getSession();
-  private final TestProjectIndexers projectIndexers = new TestProjectIndexers();
-  private final TagsWsSupport tagsWsSupport = new TagsWsSupport(dbClient, TestComponentFinder.from(db), userSession, projectIndexers, System2.INSTANCE);
+  private final TestIndexers indexers = new TestIndexers();
+  private final TagsWsSupport tagsWsSupport = new TagsWsSupport(dbClient, TestComponentFinder.from(db), userSession, indexers, System2.INSTANCE);
   private final WsActionTester ws = new WsActionTester(new SetAction(dbClient, tagsWsSupport));
   private ProjectDto project;
   private ComponentDto projectComponent;
@@ -77,15 +79,13 @@ public class SetActionIT {
     TestResponse response = call(project.getKey(), "finance , offshore, platform,   ,");
 
     assertTags(project.getKey(), "finance", "offshore", "platform");
-    // FIXME verify(indexer).indexProject(project.uuid(), PROJECT_TAGS_UPDATE);
-
+    indexers.hasBeenCalledForEntity(project.getUuid(), PROJECT_TAGS_UPDATE);
     assertThat(response.getStatus()).isEqualTo(HTTP_NO_CONTENT);
   }
 
   @Test
   public void reset_tags() {
-    project = db.components().insertPrivateProject(c -> {
-    }, p -> p.setTagsString("platform,scanner")).getProjectDto();
+    project = db.components().insertPrivateProject(defaults(), p -> p.setTagsString("platform,scanner")).getProjectDto();
     userSession.addProjectPermission(ADMIN, project);
 
     call(project.getKey(), "");
@@ -95,8 +95,7 @@ public class SetActionIT {
 
   @Test
   public void override_existing_tags() {
-    project = db.components().insertPrivateProject(c -> {
-    }, p -> p.setTagsString("marketing,languages")).getProjectDto();
+    project = db.components().insertPrivateProject(defaults(), p -> p.setTagsString("marketing,languages")).getProjectDto();
     userSession.addProjectPermission(ADMIN, project);
 
     call(project.getKey(), "finance,offshore,platform");
@@ -111,6 +110,7 @@ public class SetActionIT {
     call(project.getKey(), "platform, lambda");
 
     assertTags(project.getKey(), "platform", "lambda");
+    indexers.hasBeenCalledForEntity(project.getUuid(), PROJECT_TAGS_UPDATE);
   }
 
   @Test
index b61c5584cd632fa5999e34a90bfb6eb85d4c816d..de64a6458c6a85a2d606d033485a7215bc5ff989 100644 (file)
@@ -156,7 +156,7 @@ public class ImportAzureProjectAction implements AlmIntegrationsWsAction {
           newCodeDefinitionType, newCodeDefinitionValue);
       }
 
-      componentUpdater.commitAndIndex(dbSession, componentCreationData.mainBranchComponent());
+      componentUpdater.commitAndIndex(dbSession, componentCreationData);
 
       return toCreateResponse(projectDto);
     }
@@ -180,9 +180,7 @@ public class ImportAzureProjectAction implements AlmIntegrationsWsAction {
         .build(),
       userSession.isLoggedIn() ? userSession.getUuid() : null,
       userSession.isLoggedIn() ? userSession.getLogin() : null,
-      repo.getDefaultBranchName(),
-      s -> {
-      });
+      repo.getDefaultBranchName());
   }
 
   private void populatePRSetting(DbSession dbSession, GsonAzureRepo repo, ProjectDto projectDto, AlmSettingDto almSettingDto) {
index 69afb6408549bf47b9d228a27a5d06960fa09b6c..793fc5b4299d5c2e9577f10217208ac07715674b 100644 (file)
@@ -153,7 +153,7 @@ public class ImportBitbucketCloudRepoAction implements AlmIntegrationsWsAction {
           newCodeDefinitionType, newCodeDefinitionValue);
       }
 
-      componentUpdater.commitAndIndex(dbSession, componentCreationData.mainBranchComponent());
+      componentUpdater.commitAndIndex(dbSession, componentCreationData);
 
       return toCreateResponse(projectDto);
     }
@@ -180,8 +180,7 @@ public class ImportBitbucketCloudRepoAction implements AlmIntegrationsWsAction {
     String userUuid = userSession.isLoggedIn() ? userSession.getUuid() : null;
     String userLogin = userSession.isLoggedIn() ? userSession.getLogin() : null;
 
-    return componentUpdater.createWithoutCommit(dbSession, mainBranch, userUuid, userLogin, defaultBranchName, p -> {
-    });
+    return componentUpdater.createWithoutCommit(dbSession, mainBranch, userUuid, userLogin, defaultBranchName);
   }
 
   private void populatePRSetting(DbSession dbSession, Repository repo, ProjectDto projectDto, AlmSettingDto almSettingDto) {
index 85bc552f2d460100b4d37554235d6f0a30b1b3b2..cf6b63507108090c4b5bf6ab4292375ded618cda 100644 (file)
@@ -165,7 +165,7 @@ public class ImportBitbucketServerProjectAction implements AlmIntegrationsWsActi
           newCodeDefinitionType, newCodeDefinitionValue);
       }
 
-      componentUpdater.commitAndIndex(dbSession, componentCreationData.mainBranchComponent());
+      componentUpdater.commitAndIndex(dbSession, componentCreationData);
 
       return toCreateResponse(projectDto);
     }
@@ -198,8 +198,7 @@ public class ImportBitbucketServerProjectAction implements AlmIntegrationsWsActi
     String userUuid = userSession.isLoggedIn() ? userSession.getUuid() : null;
     String userLogin = userSession.isLoggedIn() ? userSession.getLogin() : null;
 
-    return componentUpdater.createWithoutCommit(dbSession, newProject, userUuid, userLogin, defaultBranchName, p -> {
-    });
+    return componentUpdater.createWithoutCommit(dbSession, newProject, userUuid, userLogin, defaultBranchName);
   }
 
   private void populatePRSetting(DbSession dbSession, Repository repo, ProjectDto componentDto, AlmSettingDto almSettingDto) {
index 56fbda880083bbecf8201be3b7ac4eccbc5a1e9e..6a57fe33088153068ec350e44d461179e82f1a34 100644 (file)
@@ -164,7 +164,7 @@ public class ImportGithubProjectAction implements AlmIntegrationsWsAction {
           newCodeDefinitionType, newCodeDefinitionValue);
       }
 
-      componentUpdater.commitAndIndex(dbSession, componentCreationData.mainBranchComponent());
+      componentUpdater.commitAndIndex(dbSession, componentCreationData);
 
       return toCreateResponse(projectDto);
     }
@@ -187,8 +187,7 @@ public class ImportGithubProjectAction implements AlmIntegrationsWsAction {
         .setPrivate(visibility)
         .setQualifier(PROJECT)
         .build(),
-      userSession.getUuid(), userSession.getLogin(), mainBranchName, s -> {
-      });
+      userSession.getUuid(), userSession.getLogin(), mainBranchName);
   }
 
   private void populatePRSetting(DbSession dbSession, Repository repo, ProjectDto projectDto, AlmSettingDto almSettingDto) {
index e895173cf7dd9b3d48819c246d2bfd64f67508fe..3d9ab26f8dfa6b82b08415f867fbd49aec25f79f 100644 (file)
@@ -144,7 +144,7 @@ public class ImportGitLabProjectAction implements AlmIntegrationsWsAction {
           newCodeDefinitionType, newCodeDefinitionValue);
       }
 
-      componentUpdater.commitAndIndex(dbSession, componentCreationData.mainBranchComponent());
+      componentUpdater.commitAndIndex(dbSession, componentCreationData);
 
       return ImportHelper.toCreateResponse(projectDto);
     }
@@ -183,8 +183,7 @@ public class ImportGitLabProjectAction implements AlmIntegrationsWsAction {
         .setPrivate(visibility)
         .setQualifier(PROJECT)
         .build(),
-      userSession.getUuid(), userSession.getLogin(), mainBranchName, s -> {
-      });
+      userSession.getUuid(), userSession.getLogin(), mainBranchName);
   }
 
 }
index 9aa203138f4143d06f37c50aeb7412f7fb3227c5..81427f7d77daaef119f66c49dbcd3c0b6349865b 100644 (file)
@@ -38,6 +38,7 @@ import org.sonar.db.ce.CeTaskTypes;
 import org.sonar.db.component.BranchDto;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.permission.GlobalPermission;
+import org.sonar.server.component.ComponentCreationData;
 import org.sonar.server.component.ComponentUpdater;
 import org.sonar.server.component.NewComponent;
 import org.sonar.server.exceptions.BadRequestException;
@@ -75,7 +76,7 @@ public class ReportSubmitter {
 
   public CeTask submit(String projectKey, @Nullable String projectName, Map<String, String> characteristics, InputStream reportInput) {
     try (DbSession dbSession = dbClient.openSession(false)) {
-      boolean projectCreated = false;
+      ComponentCreationData componentCreationData = null;
       // Note: when the main branch is analyzed, the characteristics may or may not have the branch name, so componentKey#isMainBranch is not
       // reliable!
       BranchSupport.ComponentKey componentKey = branchSupport.createComponentKey(projectKey, characteristics);
@@ -86,8 +87,8 @@ public class ReportSubmitter {
         mainBranchComponent = mainBranchComponentOpt.get();
         validateProject(dbSession, mainBranchComponent, projectKey);
       } else {
-        mainBranchComponent = createProject(dbSession, componentKey.getKey(), projectName);
-        projectCreated = true;
+        componentCreationData = createProject(dbSession, componentKey.getKey(), projectName);
+        mainBranchComponent = componentCreationData.mainBranchComponent();
       }
 
       BranchDto mainBranch = dbClient.branchDao().selectByUuid(dbSession, mainBranchComponent.branchUuid())
@@ -103,8 +104,8 @@ public class ReportSubmitter {
           .orElseGet(() -> branchSupport.createBranchComponent(dbSession, componentKey, mainBranchComponent, mainBranch));
       }
 
-      if (projectCreated) {
-        componentUpdater.commitAndIndex(dbSession, mainBranchComponent);
+      if (componentCreationData != null) {
+        componentUpdater.commitAndIndex(dbSession, componentCreationData);
       } else {
         dbSession.commit();
       }
@@ -151,7 +152,7 @@ public class ReportSubmitter {
     }
   }
 
-  private ComponentDto createProject(DbSession dbSession, String projectKey, @Nullable String projectName) {
+  private ComponentCreationData createProject(DbSession dbSession, String projectKey, @Nullable String projectName) {
     userSession.checkPermission(GlobalPermission.PROVISION_PROJECTS);
     String userUuid = userSession.getUuid();
     String userName = userSession.getLogin();
@@ -167,8 +168,7 @@ public class ReportSubmitter {
       .setQualifier(Qualifiers.PROJECT)
       .setPrivate(getDefaultVisibility(dbSession).isPrivate())
       .build();
-    return componentUpdater.createWithoutCommit(dbSession, newProject, userUuid, userName, c -> {
-    }).mainBranchComponent();
+    return componentUpdater.createWithoutCommit(dbSession, newProject, userUuid, userName);
   }
 
   private Visibility getDefaultVisibility(DbSession dbSession) {
index 9325afef07101e59e22f72a703c0480c7294b2fe..413fe60ca3b7de9452268514e265dd39d62800d8 100644 (file)
@@ -21,36 +21,33 @@ package org.sonar.server.component;
 
 import java.util.List;
 import org.sonar.api.resources.Qualifiers;
-import org.sonar.api.resources.ResourceType;
-import org.sonar.api.resources.ResourceTypes;
 import org.sonar.api.server.ServerSide;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.BranchDto;
 import org.sonar.db.entity.EntityDto;
 import org.sonar.db.project.ProjectDto;
-import org.sonar.server.es.ProjectIndexers;
+import org.sonar.server.es.Indexers;
+import org.sonar.server.es.Indexers.BranchEvent;
+import org.sonar.server.es.Indexers.EntityEvent;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static java.util.Collections.singletonList;
-import static org.sonar.server.es.ProjectIndexer.Cause.PROJECT_DELETION;
 
 @ServerSide
 public class ComponentCleanerService {
 
   private final DbClient dbClient;
-  private final ResourceTypes resourceTypes;
-  private final ProjectIndexers projectIndexers;
+  private final Indexers indexers;
 
-  public ComponentCleanerService(DbClient dbClient, ResourceTypes resourceTypes, ProjectIndexers projectIndexers) {
+  public ComponentCleanerService(DbClient dbClient, Indexers indexers) {
     this.dbClient = dbClient;
-    this.resourceTypes = resourceTypes;
-    this.projectIndexers = projectIndexers;
+    this.indexers = indexers;
   }
 
   public void delete(DbSession dbSession, List<ProjectDto> projects) {
     for (ProjectDto project : projects) {
-      delete(dbSession, project);
+      deleteEntity(dbSession, project);
     }
   }
 
@@ -59,23 +56,17 @@ public class ComponentCleanerService {
       throw new IllegalArgumentException("Only non-main branches can be deleted");
     }
     dbClient.purgeDao().deleteBranch(dbSession, branch.getUuid());
-    projectIndexers.commitAndIndexBranches(dbSession, singletonList(branch), PROJECT_DELETION);
+    indexers.commitAndIndexBranches(dbSession, singletonList(branch), BranchEvent.DELETION);
   }
 
-  public void delete(DbSession dbSession, EntityDto entity) {
-    checkArgument(isDeletable(entity), "Only projects can be deleted");
-
+  public void deleteEntity(DbSession dbSession, EntityDto entity) {
+    checkArgument(!entity.getQualifier().equals(Qualifiers.SUBVIEW), "Qualifier can't be subview");
     dbClient.purgeDao().deleteProject(dbSession, entity.getUuid(), entity.getQualifier(), entity.getName(), entity.getKey());
     dbClient.userDao().cleanHomepage(dbSession, entity);
     if (Qualifiers.PROJECT.equals(entity.getQualifier())) {
       dbClient.userTokenDao().deleteByProjectUuid(dbSession, entity.getKey(), entity.getUuid());
     }
-    projectIndexers.commitAndIndexEntities(dbSession, singletonList(entity), PROJECT_DELETION);
-  }
-
-  private boolean isDeletable(EntityDto entityDto) {
-    ResourceType resourceType = resourceTypes.get(entityDto.getQualifier());
-    // this essentially means PROJECTS, VIEWS and APPS (not SUBVIEWS)
-    return resourceType != null && resourceType.getBooleanProperty("deletable");
+    // Note that we do not send an event for each individual branch being deleted with the project
+    indexers.commitAndIndexEntities(dbSession, singletonList(entity), EntityEvent.DELETION);
   }
 }
index f3b2aabf5827767dedd85e107071884234b56eeb..9751629c1146c980801cb4a2945b9399d99466c0 100644 (file)
@@ -22,8 +22,9 @@ package org.sonar.server.component;
 import javax.annotation.Nullable;
 import org.sonar.db.component.BranchDto;
 import org.sonar.db.component.ComponentDto;
+import org.sonar.db.portfolio.PortfolioDto;
 import org.sonar.db.project.ProjectDto;
 
-public record ComponentCreationData(ComponentDto mainBranchComponent, @Nullable BranchDto mainBranchDto,
+public record ComponentCreationData(ComponentDto mainBranchComponent, @Nullable PortfolioDto portfolioDto, @Nullable BranchDto mainBranchDto,
                                     @Nullable ProjectDto projectDto) {
 }
index fdd54e406e8b139f785071a24a4497ac8b6fb047..d8e6792b1e9962ac8efa99924bcb10cb13bd687a 100644 (file)
@@ -27,8 +27,8 @@ import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.db.pushevent.PushEventDto;
-import org.sonar.server.es.ProjectIndexer;
-import org.sonar.server.es.ProjectIndexers;
+import org.sonar.server.es.EventIndexer;
+import org.sonar.server.es.Indexers;
 import org.sonar.server.project.Project;
 import org.sonar.server.project.ProjectLifeCycleListeners;
 import org.sonar.server.project.RekeyedProject;
@@ -45,13 +45,13 @@ public class ComponentService {
 
   private final DbClient dbClient;
   private final UserSession userSession;
-  private final ProjectIndexers projectIndexers;
+  private final Indexers indexers;
   private final ProjectLifeCycleListeners projectLifeCycleListeners;
 
-  public ComponentService(DbClient dbClient, UserSession userSession, ProjectIndexers projectIndexers, ProjectLifeCycleListeners projectLifeCycleListeners) {
+  public ComponentService(DbClient dbClient, UserSession userSession, Indexers indexers, ProjectLifeCycleListeners projectLifeCycleListeners) {
     this.dbClient = dbClient;
     this.userSession = userSession;
-    this.projectIndexers = projectIndexers;
+    this.indexers = indexers;
     this.projectLifeCycleListeners = projectLifeCycleListeners;
   }
 
@@ -59,7 +59,7 @@ public class ComponentService {
     userSession.checkEntityPermission(UserRole.ADMIN, project);
     checkProjectKey(newKey);
     dbClient.componentKeyUpdaterDao().updateKey(dbSession, project.getUuid(), project.getKey(), newKey);
-    projectIndexers.commitAndIndexProjects(dbSession, singletonList(project), ProjectIndexer.Cause.PROJECT_KEY_UPDATE);
+    indexers.commitAndIndexEntities(dbSession, singletonList(project), Indexers.EntityEvent.PROJECT_KEY_UPDATE);
     Project newProject = new Project(project.getUuid(), newKey, project.getName(), project.getDescription(), project.getTags());
     projectLifeCycleListeners.onProjectsRekeyed(singleton(new RekeyedProject(newProject, project.getKey())));
     persistEvent(project, newKey);
index 4a0f002cd6b7aea47b615fd7feaf9b778a6e961a..95aece5b40ec8577a8fd3a07838f898873e1404c 100644 (file)
@@ -25,7 +25,6 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Optional;
 import java.util.Set;
-import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import javax.annotation.Nullable;
 import org.sonar.api.resources.Qualifiers;
@@ -41,8 +40,7 @@ import org.sonar.db.component.ComponentDto;
 import org.sonar.db.portfolio.PortfolioDto;
 import org.sonar.db.portfolio.PortfolioDto.SelectionMode;
 import org.sonar.db.project.ProjectDto;
-import org.sonar.server.es.ProjectIndexer.Cause;
-import org.sonar.server.es.ProjectIndexers;
+import org.sonar.server.es.Indexers;
 import org.sonar.server.favorite.FavoriteUpdater;
 import org.sonar.server.permission.PermissionTemplateService;
 import org.sonar.server.project.DefaultBranchNameResolver;
@@ -56,7 +54,7 @@ import static org.sonar.server.exceptions.BadRequestException.throwBadRequestExc
 
 public class ComponentUpdater {
 
-  private static final Set<String> MAIN_BRANCH_QUALIFIERS = Set.of(Qualifiers.PROJECT, Qualifiers.APP);
+  private static final Set<String> PROJ_APP_QUALIFIERS = Set.of(Qualifiers.PROJECT, Qualifiers.APP);
   private static final String KEY_ALREADY_EXISTS_ERROR = "Could not create %s with key: \"%s\". A similar key already exists: \"%s\"";
   private static final String MALFORMED_KEY_ERROR = "Malformed key for %s: '%s'. %s.";
   private final DbClient dbClient;
@@ -64,7 +62,7 @@ public class ComponentUpdater {
   private final System2 system2;
   private final PermissionTemplateService permissionTemplateService;
   private final FavoriteUpdater favoriteUpdater;
-  private final ProjectIndexers projectIndexers;
+  private final Indexers indexers;
   private final UuidFactory uuidFactory;
   private final DefaultBranchNameResolver defaultBranchNameResolver;
   private final boolean useDifferentUuids;
@@ -72,21 +70,21 @@ public class ComponentUpdater {
   @Autowired
   public ComponentUpdater(DbClient dbClient, I18n i18n, System2 system2,
     PermissionTemplateService permissionTemplateService, FavoriteUpdater favoriteUpdater,
-    ProjectIndexers projectIndexers, UuidFactory uuidFactory, DefaultBranchNameResolver defaultBranchNameResolver) {
-    this(dbClient, i18n, system2, permissionTemplateService, favoriteUpdater, projectIndexers, uuidFactory, defaultBranchNameResolver, false);
+    Indexers indexers, UuidFactory uuidFactory, DefaultBranchNameResolver defaultBranchNameResolver) {
+    this(dbClient, i18n, system2, permissionTemplateService, favoriteUpdater, indexers, uuidFactory, defaultBranchNameResolver, false);
   }
 
   @VisibleForTesting
   public ComponentUpdater(DbClient dbClient, I18n i18n, System2 system2,
     PermissionTemplateService permissionTemplateService, FavoriteUpdater favoriteUpdater,
-    ProjectIndexers projectIndexers, UuidFactory uuidFactory, DefaultBranchNameResolver defaultBranchNameResolver,
+    Indexers indexers, UuidFactory uuidFactory, DefaultBranchNameResolver defaultBranchNameResolver,
     boolean useDifferentUuids) {
     this.dbClient = dbClient;
     this.i18n = i18n;
     this.system2 = system2;
     this.permissionTemplateService = permissionTemplateService;
     this.favoriteUpdater = favoriteUpdater;
-    this.projectIndexers = projectIndexers;
+    this.indexers = indexers;
     this.uuidFactory = uuidFactory;
     this.defaultBranchNameResolver = defaultBranchNameResolver;
     this.useDifferentUuids = useDifferentUuids;
@@ -99,19 +97,17 @@ public class ComponentUpdater {
    * - Index component in es indexes
    */
   public ComponentCreationData create(DbSession dbSession, NewComponent newComponent, @Nullable String userUuid, @Nullable String userLogin) {
-    return create(dbSession, newComponent, userUuid, userLogin, null);
-  }
-
-  public ComponentCreationData create(DbSession dbSession, NewComponent newComponent, @Nullable String userUuid, @Nullable String userLogin,
-    @Nullable String mainBranchName) {
-    ComponentCreationData componentCreationData = createWithoutCommit(dbSession, newComponent, userUuid, userLogin, mainBranchName, c -> {
-    });
-    commitAndIndex(dbSession, componentCreationData.mainBranchComponent());
+    ComponentCreationData componentCreationData = createWithoutCommit(dbSession, newComponent, userUuid, userLogin, null);
+    commitAndIndex(dbSession, componentCreationData);
     return componentCreationData;
   }
 
-  public void commitAndIndex(DbSession dbSession, ComponentDto componentDto) {
-    projectIndexers.commitAndIndexComponents(dbSession, singletonList(componentDto), Cause.PROJECT_CREATION);
+  public void commitAndIndex(DbSession dbSession, ComponentCreationData componentCreationData) {
+    if (componentCreationData.portfolioDto() != null) {
+      indexers.commitAndIndexEntities(dbSession, singletonList(componentCreationData.portfolioDto()), Indexers.EntityEvent.CREATION);
+    } else if (componentCreationData.projectDto() != null) {
+      indexers.commitAndIndexEntities(dbSession, singletonList(componentCreationData.projectDto()), Indexers.EntityEvent.CREATION);
+    }
   }
 
   /**
@@ -119,8 +115,8 @@ public class ComponentUpdater {
    * Don't forget to call commitAndIndex(...) when ready to commit.
    */
   public ComponentCreationData createWithoutCommit(DbSession dbSession, NewComponent newComponent,
-    @Nullable String userUuid, @Nullable String userLogin, Consumer<ComponentDto> componentModifier) {
-    return createWithoutCommit(dbSession, newComponent, userUuid, userLogin, null, componentModifier);
+    @Nullable String userUuid, @Nullable String userLogin) {
+    return createWithoutCommit(dbSession, newComponent, userUuid, userLogin, null);
   }
 
   /**
@@ -128,33 +124,33 @@ public class ComponentUpdater {
    * Don't forget to call commitAndIndex(...) when ready to commit.
    */
   public ComponentCreationData createWithoutCommit(DbSession dbSession, NewComponent newComponent,
-    @Nullable String userUuid, @Nullable String userLogin, @Nullable String mainBranchName,
-    Consumer<ComponentDto> componentModifier) {
+    @Nullable String userUuid, @Nullable String userLogin, @Nullable String mainBranchName) {
     checkKeyFormat(newComponent.qualifier(), newComponent.key());
     checkKeyAlreadyExists(dbSession, newComponent);
 
     long now = system2.now();
 
-    ComponentDto componentDto = createRootComponent(dbSession, newComponent, componentModifier, now);
+    ComponentDto componentDto = createRootComponent(dbSession, newComponent, now);
 
     BranchDto mainBranch = null;
     ProjectDto projectDto = null;
+    PortfolioDto portfolioDto = null;
 
-    if (isRootProject(componentDto)) {
+    if (isProjectOrApp(componentDto)) {
       projectDto = toProjectDto(componentDto, now);
       dbClient.projectDao().insert(dbSession, projectDto);
       addToFavourites(dbSession, projectDto, userUuid, userLogin);
       mainBranch = createMainBranch(dbSession, componentDto.uuid(), projectDto.getUuid(), mainBranchName);
       permissionTemplateService.applyDefaultToNewComponent(dbSession, projectDto, userUuid);
-    } else if (isRootView(componentDto)) {
-      PortfolioDto portfolioDto = toPortfolioDto(componentDto, now);
+    } else if (isPortfolio(componentDto)) {
+      portfolioDto = toPortfolioDto(componentDto, now);
       dbClient.portfolioDao().insert(dbSession, portfolioDto);
       permissionTemplateService.applyDefaultToNewComponent(dbSession, portfolioDto, userUuid);
     } else {
       throw new IllegalArgumentException("Component " + componentDto + " is not a top level entity");
     }
 
-    return new ComponentCreationData(componentDto, mainBranch, projectDto);
+    return new ComponentCreationData(componentDto, portfolioDto, mainBranch, projectDto);
   }
 
   private void addToFavourites(DbSession dbSession, ProjectDto projectDto, @Nullable String userUuid, @Nullable String userLogin) {
@@ -179,7 +175,7 @@ public class ComponentUpdater {
     }
   }
 
-  private ComponentDto createRootComponent(DbSession session, NewComponent newComponent, Consumer<ComponentDto> componentModifier, long now) {
+  private ComponentDto createRootComponent(DbSession session, NewComponent newComponent, long now) {
     String uuid = uuidFactory.create();
 
     ComponentDto component = new ComponentDto()
@@ -195,7 +191,6 @@ public class ComponentUpdater {
       .setPrivate(newComponent.isPrivate())
       .setCreatedAt(new Date(now));
 
-    componentModifier.accept(component);
     dbClient.componentDao().insert(session, component, true);
     return component;
   }
@@ -225,14 +220,12 @@ public class ComponentUpdater {
       .setCreatedAt(now);
   }
 
-  private static boolean isRootProject(ComponentDto componentDto) {
-    return Scopes.PROJECT.equals(componentDto.scope())
-      && MAIN_BRANCH_QUALIFIERS.contains(componentDto.qualifier());
+  private static boolean isProjectOrApp(ComponentDto componentDto) {
+    return PROJ_APP_QUALIFIERS.contains(componentDto.qualifier());
   }
 
-  private static boolean isRootView(ComponentDto componentDto) {
-    return Scopes.PROJECT.equals(componentDto.scope())
-      && Qualifiers.VIEW.contains(componentDto.qualifier());
+  private static boolean isPortfolio(ComponentDto componentDto) {
+    return Qualifiers.VIEW.contains(componentDto.qualifier());
   }
 
   private BranchDto createMainBranch(DbSession session, String componentUuid, String projectUuid, @Nullable String mainBranch) {
index f3e12e9e0b12000dc7d553246f9ed81805258896..c75a637fd32510063ab1cfbe0cdab08ccafa46cb 100644 (file)
@@ -119,16 +119,17 @@ public class AuthorsAction implements IssuesWsAction {
 
   private List<String> getAuthors(DbSession session, @Nullable EntityDto entity, Request request) {
     IssueQuery.Builder issueQueryBuilder = IssueQuery.builder();
-    ofNullable(entity).ifPresent(p -> {
-      switch (p.getQualifier()) {
-        case Qualifiers.PROJECT -> issueQueryBuilder.projectUuids(Set.of(p.getUuid()));
-        case Qualifiers.VIEW -> issueQueryBuilder.viewUuids(Set.of(p.getUuid()));
+    ofNullable(entity).ifPresent(e -> {
+      switch (e.getQualifier()) {
+        case Qualifiers.PROJECT -> issueQueryBuilder.projectUuids(Set.of(e.getUuid()));
+        case Qualifiers.VIEW -> issueQueryBuilder.viewUuids(Set.of(e.getUuid()));
         case Qualifiers.APP -> {
-          BranchDto appMainBranch = dbClient.branchDao().selectMainBranchByProjectUuid(session, entity.getUuid())
-            .orElseThrow(() -> new IllegalStateException("Couldn't find main branch for APP " + entity.getUuid()));
-          issueQueryBuilder.viewUuids(Set.of(appMainBranch.getUuid()));
+          BranchDto appMainBranch = dbClient.branchDao().selectMainBranchByProjectUuid(session, e.getUuid())
+            .orElseThrow(() -> new IllegalStateException("Couldn't find main branch for APP " + e.getUuid()));
+          issueQueryBuilder.viewUuids(Set.of(entity.getUuid()));
+          issueQueryBuilder.branchUuid(appMainBranch.getUuid());
         }
-        default -> throw new IllegalArgumentException(String.format("Component of type '%s' is not supported", p.getQualifier()));
+        default -> throw new IllegalArgumentException(String.format("Component of type '%s' is not supported", e.getQualifier()));
       }
     });
     return issueIndex.searchAuthors(
index 643f5335f54185fec944c570c847892cbc9d9c47..9c346e2cfecd31c9d145b9df1f2452086d42f5ee 100644 (file)
@@ -39,8 +39,8 @@ import org.sonar.db.measure.LiveMeasureComparator;
 import org.sonar.db.measure.LiveMeasureDto;
 import org.sonar.db.metric.MetricDto;
 import org.sonar.db.project.ProjectDto;
-import org.sonar.server.es.ProjectIndexer;
-import org.sonar.server.es.ProjectIndexers;
+import org.sonar.server.es.EventIndexer;
+import org.sonar.server.es.Indexers;
 import org.sonar.server.qualitygate.EvaluatedQualityGate;
 import org.sonar.server.qualitygate.QualityGate;
 import org.sonar.server.qualitygate.changeevent.QGChangeEvent;
@@ -59,11 +59,11 @@ public class LiveMeasureComputerImpl implements LiveMeasureComputer {
   private final ComponentIndexFactory componentIndexFactory;
   private final LiveQualityGateComputer qGateComputer;
   private final ProjectConfigurationLoader projectConfigurationLoader;
-  private final ProjectIndexers projectIndexer;
+  private final Indexers projectIndexer;
   private final LiveMeasureTreeUpdater treeUpdater;
 
   public LiveMeasureComputerImpl(DbClient dbClient, MeasureUpdateFormulaFactory formulaFactory, ComponentIndexFactory componentIndexFactory,
-    LiveQualityGateComputer qGateComputer, ProjectConfigurationLoader projectConfigurationLoader, ProjectIndexers projectIndexer, LiveMeasureTreeUpdater treeUpdater) {
+    LiveQualityGateComputer qGateComputer, ProjectConfigurationLoader projectConfigurationLoader, Indexers projectIndexer, LiveMeasureTreeUpdater treeUpdater) {
     this.dbClient = dbClient;
     this.formulaFactory = formulaFactory;
     this.componentIndexFactory = componentIndexFactory;
@@ -106,7 +106,7 @@ public class LiveMeasureComputerImpl implements LiveMeasureComputer {
 
     Metric.Level previousStatus = loadPreviousStatus(dbSession, branchComponent);
     EvaluatedQualityGate evaluatedQualityGate = qGateComputer.refreshGateStatus(branchComponent, qualityGate, matrix, config);
-    persistAndIndex(dbSession, matrix, branchComponent);
+    persistAndIndex(dbSession, matrix, branch);
 
     return Optional.of(new QGChangeEvent(project, branch, lastAnalysis.get(), config, previousStatus, () -> Optional.of(evaluatedQualityGate)));
   }
@@ -118,10 +118,10 @@ public class LiveMeasureComputerImpl implements LiveMeasureComputer {
     return new MeasureMatrix(componentUuids, metricsPerUuid.values(), measures);
   }
 
-  private void persistAndIndex(DbSession dbSession, MeasureMatrix matrix, ComponentDto branchComponent) {
+  private void persistAndIndex(DbSession dbSession, MeasureMatrix matrix, BranchDto branch) {
     // persist the measures that have been created or updated
     matrix.getChanged().sorted(LiveMeasureComparator.INSTANCE).forEach(m -> dbClient.liveMeasureDao().insertOrUpdate(dbSession, m));
-    projectIndexer.commitAndIndexComponents(dbSession, singleton(branchComponent), ProjectIndexer.Cause.MEASURE_CHANGE);
+    projectIndexer.commitAndIndexBranches(dbSession, singleton(branch), Indexers.BranchEvent.MEASURE_CHANGE);
   }
 
   @CheckForNull
index 7390880e6cd8e6e9866e44c470e588751dbf513a..43072bf34f917d2f04d3fcbf6a3ceff06a35a1e4 100644 (file)
@@ -45,8 +45,7 @@ import org.sonar.db.permission.template.PermissionTemplateUserDto;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.db.user.UserId;
-import org.sonar.server.es.ProjectIndexer;
-import org.sonar.server.es.ProjectIndexers;
+import org.sonar.server.es.Indexers;
 import org.sonar.server.exceptions.TemplateMatchingKeyException;
 import org.sonar.server.permission.DefaultTemplatesResolver.ResolvedDefaultTemplates;
 import org.sonar.server.user.UserSession;
@@ -62,15 +61,15 @@ import static org.sonar.db.permission.GlobalPermission.SCAN;
 public class PermissionTemplateService {
 
   private final DbClient dbClient;
-  private final ProjectIndexers projectIndexers;
+  private final Indexers indexers;
   private final UserSession userSession;
   private final DefaultTemplatesResolver defaultTemplatesResolver;
   private final UuidFactory uuidFactory;
 
-  public PermissionTemplateService(DbClient dbClient, ProjectIndexers projectIndexers, UserSession userSession,
+  public PermissionTemplateService(DbClient dbClient, Indexers indexers, UserSession userSession,
     DefaultTemplatesResolver defaultTemplatesResolver, UuidFactory uuidFactory) {
     this.dbClient = dbClient;
-    this.projectIndexers = projectIndexers;
+    this.indexers = indexers;
     this.userSession = userSession;
     this.defaultTemplatesResolver = defaultTemplatesResolver;
     this.uuidFactory = uuidFactory;
@@ -106,11 +105,12 @@ public class PermissionTemplateService {
       dbClient.userPermissionDao().deleteEntityPermissions(dbSession, entity);
       copyPermissions(dbSession, template, entity, null);
     }
-    projectIndexers.commitAndIndexEntities(dbSession, entities, ProjectIndexer.Cause.PERMISSION_CHANGE);
+    indexers.commitAndIndexEntities(dbSession, entities, Indexers.EntityEvent.PERMISSION_CHANGE);
   }
 
   /**
    * Apply the default permission template to a new project (has no permissions yet).
+   *
    * @param projectCreatorUserId id of the user creating the project.
    */
   public void applyDefaultToNewComponent(DbSession dbSession, EntityDto entityDto, @Nullable String projectCreatorUserId) {
index f3bdf455d6bbce5e8897a09ad23bcda52ee1fa44..3564da995162b9cc587da72a160fe926dac2ffbe 100644 (file)
@@ -23,21 +23,20 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import org.sonar.db.DbSession;
-import org.sonar.server.es.ProjectIndexer;
-import org.sonar.server.es.ProjectIndexers;
+import org.sonar.server.es.Indexers;
 
 /**
  * Add or remove global/project permissions to a group. This class does not verify that caller has administration right on the related project.
  */
 public class PermissionUpdater {
 
-  private final ProjectIndexers projectIndexers;
+  private final Indexers indexers;
   private final UserPermissionChanger userPermissionChanger;
   private final GroupPermissionChanger groupPermissionChanger;
 
-  public PermissionUpdater(ProjectIndexers projectIndexers,
+  public PermissionUpdater(Indexers indexers,
     UserPermissionChanger userPermissionChanger, GroupPermissionChanger groupPermissionChanger) {
-    this.projectIndexers = projectIndexers;
+    this.indexers = indexers;
     this.userPermissionChanger = userPermissionChanger;
     this.groupPermissionChanger = groupPermissionChanger;
   }
@@ -51,7 +50,7 @@ public class PermissionUpdater {
         projectOrViewUuids.add(projectUuid);
       }
     }
-    projectIndexers.commitAndIndexByProjectUuids(dbSession, projectOrViewUuids, ProjectIndexer.Cause.PERMISSION_CHANGE);
+    indexers.commitAndIndexOnEntityEvent(dbSession, projectOrViewUuids, Indexers.EntityEvent.PERMISSION_CHANGE);
   }
 
   private boolean doApply(DbSession dbSession, PermissionChange change) {
index 3f8ed5b98fa7746741b229dfa1017454b61b126a..57d33bc5ff8c7b67e410e162f1705d013f93ca84 100644 (file)
@@ -25,6 +25,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.apache.commons.lang.StringUtils;
@@ -151,9 +152,9 @@ public class BulkDeleteAction implements ProjectsWsAction {
       List<EntityDto> entities = dbClient.entityDao().selectByKeys(dbSession, componentDtos.stream().map(ComponentDto::getKey).collect(toSet()));
 
       try {
-        entities.forEach(e -> componentCleanerService.delete(dbSession, e));
+        entities.forEach(p -> componentCleanerService.deleteEntity(dbSession, p));
       } finally {
-        projectLifeCycleListeners.onProjectsDeleted(entities.stream().map(Project::from).collect(MoreCollectors.toSet(componentDtos.size())));
+        projectLifeCycleListeners.onProjectsDeleted(entities.stream().map(Project::from).collect(Collectors.toSet()));
       }
     }
     response.noContent();
index 49a10dc1336616729a04c09c9f009e19daf7e3a7..9c67047cf25286cd0b66e677006bfb9f88b80158 100644 (file)
@@ -28,7 +28,9 @@ import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
-import org.sonar.db.component.ComponentDto;
+import org.sonar.db.entity.EntityDto;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.server.component.ComponentCreationData;
 import org.sonar.server.component.ComponentUpdater;
 import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver;
 import org.sonar.server.project.DefaultBranchNameResolver;
@@ -132,18 +134,18 @@ public class CreateAction implements ProjectsWsAction {
     try (DbSession dbSession = dbClient.openSession(false)) {
       userSession.checkPermission(PROVISION_PROJECTS);
       checkNewCodeDefinitionParam(request.getNewCodeDefinitionType(), request.getNewCodeDefinitionValue());
-      ComponentDto componentDto = createProject(request, dbSession);
+      ComponentCreationData componentData = createProject(request, dbSession);
       if (request.getNewCodeDefinitionType() != null) {
         String defaultBranchName = Optional.ofNullable(request.getMainBranchKey()).orElse(defaultBranchNameResolver.getEffectiveMainBranchName());
-        newCodeDefinitionResolver.createNewCodeDefinition(dbSession, componentDto.uuid(), defaultBranchName, request.getNewCodeDefinitionType(),
+        newCodeDefinitionResolver.createNewCodeDefinition(dbSession, componentData.mainBranchDto().getUuid(), defaultBranchName, request.getNewCodeDefinitionType(),
           request.getNewCodeDefinitionValue());
       }
-      componentUpdater.commitAndIndex(dbSession, componentDto);
-      return toCreateResponse(componentDto);
+      componentUpdater.commitAndIndex(dbSession, componentData);
+      return toCreateResponse(componentData.projectDto());
     }
   }
 
-  private ComponentDto createProject(CreateRequest request, DbSession dbSession) {
+  private ComponentCreationData createProject(CreateRequest request, DbSession dbSession) {
     String visibility = request.getVisibility();
     boolean changeToPrivate = visibility == null ? projectDefaultVisibility.get(dbSession).isPrivate() : "private".equals(visibility);
 
@@ -155,8 +157,7 @@ public class CreateAction implements ProjectsWsAction {
         .build(),
       userSession.isLoggedIn() ? userSession.getUuid() : null,
       userSession.isLoggedIn() ? userSession.getLogin() : null,
-      request.getMainBranchKey(), s -> {
-      }).mainBranchComponent();
+      request.getMainBranchKey());
 
   }
 
@@ -171,13 +172,13 @@ public class CreateAction implements ProjectsWsAction {
       .build();
   }
 
-  private static CreateWsResponse toCreateResponse(ComponentDto componentDto) {
+  private static CreateWsResponse toCreateResponse(EntityDto project) {
     return CreateWsResponse.newBuilder()
       .setProject(CreateWsResponse.Project.newBuilder()
-        .setKey(componentDto.getKey())
-        .setName(componentDto.name())
-        .setQualifier(componentDto.qualifier())
-        .setVisibility(Visibility.getLabel(componentDto.isPrivate())))
+        .setKey(project.getKey())
+        .setName(project.getName())
+        .setQualifier(project.getQualifier())
+        .setVisibility(Visibility.getLabel(project.isPrivate())))
       .build();
   }
 
index f6ff00362e50fb05a01b585f5d4c1f54e4eb9b2d..3fc9097d39895858d64dd912ac83791f8d219f73 100644 (file)
@@ -81,7 +81,7 @@ public class DeleteAction implements ProjectsWsAction {
     try (DbSession dbSession = dbClient.openSession(false)) {
       ProjectDto project = componentFinder.getProjectByKey(dbSession, key);
       checkPermission(project);
-      componentCleanerService.delete(dbSession, project);
+      componentCleanerService.deleteEntity(dbSession, project);
       projectLifeCycleListeners.onProjectsDeleted(singleton(Project.fromProjectDtoWithTags(project)));
     }
 
index 6d0ca25d61fd58649240d46f8e76962fd767bf15..0510d1fd95653a25c1794919a6d12b529609ff73 100644 (file)
@@ -34,8 +34,8 @@ import org.sonar.db.permission.GroupPermissionDto;
 import org.sonar.db.permission.UserPermissionDto;
 import org.sonar.db.user.GroupDto;
 import org.sonar.db.user.UserId;
-import org.sonar.server.es.ProjectIndexer;
-import org.sonar.server.es.ProjectIndexers;
+import org.sonar.server.es.EventIndexer;
+import org.sonar.server.es.Indexers;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.project.Visibility;
 import org.sonar.server.user.UserSession;
@@ -57,14 +57,14 @@ public class UpdateVisibilityAction implements ProjectsWsAction {
 
   private final DbClient dbClient;
   private final UserSession userSession;
-  private final ProjectIndexers projectIndexers;
+  private final Indexers indexers;
   private final UuidFactory uuidFactory;
   private final Configuration configuration;
 
-  public UpdateVisibilityAction(DbClient dbClient, UserSession userSession, ProjectIndexers projectIndexers, UuidFactory uuidFactory, Configuration configuration) {
+  public UpdateVisibilityAction(DbClient dbClient, UserSession userSession, Indexers indexers, UuidFactory uuidFactory, Configuration configuration) {
     this.dbClient = dbClient;
     this.userSession = userSession;
-    this.projectIndexers = projectIndexers;
+    this.indexers = indexers;
     this.uuidFactory = uuidFactory;
     this.configuration = configuration;
   }
@@ -119,7 +119,7 @@ public class UpdateVisibilityAction implements ProjectsWsAction {
         } else {
           updatePermissionsToPublic(dbSession, entityDto);
         }
-        projectIndexers.commitAndIndexEntities(dbSession, singletonList(entityDto), ProjectIndexer.Cause.PERMISSION_CHANGE);
+        indexers.commitAndIndexEntities(dbSession, singletonList(entityDto), Indexers.EntityEvent.PERMISSION_CHANGE);
       }
 
       response.noContent();
index 5f5f88c3a3df9b999c229661b4adee47a5d15afd..da16ab046d35823d3fcaf7a00a5d967618be4ebd 100644 (file)
@@ -30,11 +30,11 @@ import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.es.ProjectIndexers;
+import org.sonar.server.es.Indexers;
 import org.sonar.server.user.UserSession;
 
 import static java.util.Collections.singletonList;
-import static org.sonar.server.es.ProjectIndexer.Cause.PROJECT_TAGS_UPDATE;
+import static org.sonar.server.es.Indexers.EntityEvent.PROJECT_TAGS_UPDATE;
 import static org.sonar.server.exceptions.BadRequestException.checkRequest;
 
 public class TagsWsSupport {
@@ -47,14 +47,14 @@ public class TagsWsSupport {
   private final DbClient dbClient;
   private final ComponentFinder componentFinder;
   private final UserSession userSession;
-  private final ProjectIndexers projectIndexers;
+  private final Indexers indexers;
   private final System2 system2;
 
-  public TagsWsSupport(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, ProjectIndexers projectIndexers, System2 system2) {
+  public TagsWsSupport(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, Indexers indexers, System2 system2) {
     this.dbClient = dbClient;
     this.componentFinder = componentFinder;
     this.userSession = userSession;
-    this.projectIndexers = projectIndexers;
+    this.indexers = indexers;
     this.system2 = system2;
   }
 
@@ -76,7 +76,7 @@ public class TagsWsSupport {
     projectOrApplication.setUpdatedAt(system2.now());
     dbClient.projectDao().updateTags(dbSession, projectOrApplication);
 
-    projectIndexers.commitAndIndexProjects(dbSession, singletonList(projectOrApplication), PROJECT_TAGS_UPDATE);
+    indexers.commitAndIndexEntities(dbSession, singletonList(projectOrApplication), PROJECT_TAGS_UPDATE);
   }
 
   public static List<String> checkAndUnifyTags(List<String> tags) {
index e46e8aa59095e4995f7573627d5aad995679be9d..dfc05cadc6a29363d1689dce25b254c4e3f2971b 100644 (file)
@@ -82,7 +82,7 @@ import org.sonar.server.component.ComponentService;
 import org.sonar.server.component.ComponentUpdater;
 import org.sonar.server.component.index.ComponentIndex;
 import org.sonar.server.component.index.ComponentIndexDefinition;
-import org.sonar.server.component.index.ComponentIndexer;
+import org.sonar.server.component.index.EntityDefinitionIndexer;
 import org.sonar.server.component.ws.ComponentViewerJsonWriter;
 import org.sonar.server.component.ws.ComponentsWsModule;
 import org.sonar.server.developers.ws.DevelopersWsModule;
@@ -92,7 +92,7 @@ import org.sonar.server.duplication.ws.ShowResponseBuilder;
 import org.sonar.server.email.ws.EmailsWsModule;
 import org.sonar.server.es.IndexCreator;
 import org.sonar.server.es.IndexDefinitions;
-import org.sonar.server.es.ProjectIndexersImpl;
+import org.sonar.server.es.IndexersImpl;
 import org.sonar.server.es.RecoveryIndexer;
 import org.sonar.server.es.metadata.EsDbCompatibilityImpl;
 import org.sonar.server.es.metadata.MetadataIndexDefinition;
@@ -434,7 +434,7 @@ public class PlatformLevel4 extends PlatformLevel {
       ComponentCleanerService.class,
       ComponentIndexDefinition.class,
       ComponentIndex.class,
-      ComponentIndexer.class,
+      EntityDefinitionIndexer.class,
       new LiveMeasureModule(),
       ComponentViewerJsonWriter.class,
 
@@ -615,7 +615,7 @@ public class PlatformLevel4 extends PlatformLevel {
       new HttpRequestIdModule(),
 
       RecoveryIndexer.class,
-      ProjectIndexersImpl.class,
+      IndexersImpl.class,
 
       // telemetry
       TelemetryDataLoaderImpl.class,