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;
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;
underTest.execute(testComputationStepContext);
- verify(projectIndexer).indexOnAnalysis(PROJECT_UUID, Set.of());
+ verify(analysisIndexer).indexOnAnalysis(PROJECT_UUID, Set.of());
}
@Test
underTest.execute(testComputationStepContext);
- verify(projectIndexer).indexOnAnalysis(PROJECT_UUID, Set.of());
+ verify(analysisIndexer).indexOnAnalysis(PROJECT_UUID, Set.of());
}
@Test
underTest.execute(testComputationStepContext);
- verify(projectIndexer).indexOnAnalysis(PROJECT_UUID, anyUuids);
+ verify(analysisIndexer).indexOnAnalysis(PROJECT_UUID, anyUuids);
}
@Test
underTest.execute(testComputationStepContext);
- verify(projectIndexer).indexOnAnalysis(PROJECT_UUID);
+ verify(analysisIndexer).indexOnAnalysis(PROJECT_UUID);
}
@Override
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
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 {
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;
@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))
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;
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() {
List<String> uuids = singletonList(uuid);
underTest.onComponentsDisabling(projectUuid, uuids);
- verify(componentIndexer).delete(projectUuid, uuids);
+ verify(entityDefinitionIndexer).delete(projectUuid, uuids);
}
@Test
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;
// components,
FavoriteUpdater.class,
- ProjectIndexersImpl.class,
+ IndexersImpl.class,
QGChangeNotificationHandler.class,
QGChangeNotificationHandler.newMetadata(),
ProjectMeasuresIndexer.class,
- ComponentIndexer.class,
+ EntityDefinitionIndexer.class,
// views
ViewIndexer.class,
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();
- }
}
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) {
List<EntityDto> selectByKeys(@Param("keys") Collection<String> keys);
- void scrollForIndexing(@Param("entityUuid") @Nullable String entityUuid, ResultHandler<EntityDto> handler);
+ void scrollForIndexing(ResultHandler<EntityDto> handler);
}
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());
}
</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>
+++ /dev/null
-/*
- * 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);
- }
-}
--- /dev/null
+/*
+ * 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);
+ }
+}
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;
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;
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();
}
@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();
}
@Test
- public void verify_indexed_fields() {
+ public void indexAllIssues_shouldIndexAllIssueFields() {
RuleDto rule = db.rules().insert();
ProjectData projectData = db.components().insertPrivateProject();
ComponentDto project = projectData.getMainBranchComponent();
}
@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"));
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);
@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);
}
@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)
}
@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();
@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);
}
@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));
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);
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");
}
@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");
}
@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");
}
@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);
}
@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);
}
@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);
}
@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) {
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;
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;
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);
}
@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());
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();
@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");
@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();
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();
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);
}
+++ /dev/null
-/*
- * 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());
- }
-}
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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));
+ }
+}
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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));
- }
-}
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
}
default void triggerAsyncIndexOnStartup(Set<IndexType> uninitializedIndexTypes) {
- throw new IllegalStateException("ASYNCHRONE StartupIndexer must implement initAsyncIndexOnStartup");
+ throw new IllegalStateException("ASYNCHRONOUS StartupIndexer must implement initAsyncIndexOnStartup");
}
/**
* 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();
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;
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;
}
@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());
}
/**
@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);
}
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;
}
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()));
}
}
}
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));
}
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;
}
@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) {
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) {
@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);
}
/**
}
/**
- * 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;
}
}
*/
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.
*/
--- /dev/null
+/*
+ * 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);
+ }
+}
+++ /dev/null
-/*
- * 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);
- }
- }
-}
+++ /dev/null
-/*
- * 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();
- }
- }
-}
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() {
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");
}
}
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
assertThatThrownBy(() -> underTest.getId())
.isInstanceOf(NullPointerException.class)
- .hasMessage("projectUuid can't be null");
+ .hasMessage("entityUuid can't be null");
}
@Test
--- /dev/null
+/*
+ * 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);
+ }
+}
+++ /dev/null
-/*
- * 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);
- }
-}
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);
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;
}
@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)))
bulkIndexer.start();
authorizations.stream()
- .filter(scope.getProjectPredicate())
+ .filter(scope.getEntityPredicate())
.map(dto -> AuthorizationDoc.fromDto(indexType, dto).toIndexRequest())
.forEach(bulkIndexer::add);
});
// 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);
@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);
@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);
*/
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);
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())
}
}
-
- @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();
- }
}
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;
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 {
// 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);
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);
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);
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);
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);
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);
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);
}
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;
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);
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;
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);
*/
package org.sonar.server.almintegration.ws.bitbucketserver;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
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;
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);
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;
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);
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;
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);
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)
.containsExactlyInAnyOrder(tuple(DEFAULT_MAIN_BRANCH_NAME, true));
}
-
@Test
public void import_project_without_NCD() {
UserDto user = db.users().insertUser();
assertThat(db.getDbClient().projectAlmSettingDao().selectByProject(db.getSession(), projectDto.get())).isPresent();
}
-
private Project getGitlabProject() {
return new Project(randomAlphanumeric(5), randomAlphanumeric(5));
}
@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();
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);
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
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;
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,
*/
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;
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 {
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
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
ProjectData projectData2 = db.components().insertPublicProject();
ComponentDto componentDto2 = projectData2.getMainBranchComponent();
- mockResourceTypeAsValidProject();
-
- underTest.delete(dbSession, projectData1.getProjectDto());
+ underTest.deleteEntity(dbSession, projectData1.getProjectDto());
dbSession.commit();
assertNotExists(componentDto1);
}
@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);
}
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);
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) {
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
assertExists(data1);
assertExists(data2);
assertExists(data3);
+ verifyNoMoreInteractions(indexers);
}
@Test
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();
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) {
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;
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);
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);
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
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;
@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);
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();
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()
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();
}
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);
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();
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;
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;
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);
authorizationIndexerTester.allowOnlyAnyone(r.getResultObject());
}
};
- db.getDbClient().entityDao().scrollForIndexing(dbSession, null, rh);
+ db.getDbClient().entityDao().scrollForIndexing(dbSession, rh);
}
private ComponentDto insertPortfolio() {
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;
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
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);
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()
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")
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")
ProjectDto project = db.components().insertPrivateProject().getProjectDto();
doReturn(singletonList(project)).when(favoriteFinder).list();
- componentIndexer.indexAll();
+ entityDefinitionIndexer.indexAll();
userSessionRule.addProjectPermission(USER, project);
SuggestionsWsResponse response = ws.newRequest()
ProjectDto project = db.components().insertPrivateProject().getProjectDto();
doReturn(singletonList(project)).when(favoriteFinder).list();
- componentIndexer.indexAll();
+ entityDefinitionIndexer.indexAll();
SuggestionsWsResponse response = ws.newRequest()
.setMethod("POST")
ProjectDto project = db.components().insertPrivateProject().getProjectDto();
doReturn(singletonList(project)).when(favoriteFinder).list();
- componentIndexer.indexAll();
+ entityDefinitionIndexer.indexAll();
userSessionRule.addProjectPermission(USER, project);
SuggestionsWsResponse response = ws.newRequest()
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()
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);
@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()
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()
public void exact_match_in_one_qualifier() {
ProjectDto project = db.components().insertPrivateProject().getProjectDto();
- componentIndexer.indexAll();
+ entityDefinitionIndexer.indexAll();
authorizationIndexerTester.allowOnlyAnyone(project);
SuggestionsWsResponse response = ws.newRequest()
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());
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()
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()
@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()
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()
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()
@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()
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());
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()
.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()
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;
@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;
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;
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
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;
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()));
}
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;
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() {
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;
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());
}
.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);
doNothing()
.doThrow(expectedException)
.when(componentCleanerService)
- .delete(any(), any(ProjectDto.class));
+ .deleteEntity(any(), any(ProjectDto.class));
try {
ws.newRequest()
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());
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;
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,
.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
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;
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;
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;
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,
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();
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)
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)
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();
}
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;
@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));
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;
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);
.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
.setParam(PARAM_VISIBILITY, initiallyPrivate ? PRIVATE : PUBLIC)
.execute();
- assertThat(projectIndexers.hasBeenCalled(project.projectUuid())).isFalse();
+ assertThat(projectIndexers.hasBeenCalledForEntity(project.projectUuid())).isFalse();
}
@Test
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;
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
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;
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(), "");
@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");
call(project.getKey(), "platform, lambda");
assertTags(project.getKey(), "platform", "lambda");
+ indexers.hasBeenCalledForEntity(project.getUuid(), PROJECT_TAGS_UPDATE);
}
@Test
newCodeDefinitionType, newCodeDefinitionValue);
}
- componentUpdater.commitAndIndex(dbSession, componentCreationData.mainBranchComponent());
+ componentUpdater.commitAndIndex(dbSession, componentCreationData);
return toCreateResponse(projectDto);
}
.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) {
newCodeDefinitionType, newCodeDefinitionValue);
}
- componentUpdater.commitAndIndex(dbSession, componentCreationData.mainBranchComponent());
+ componentUpdater.commitAndIndex(dbSession, componentCreationData);
return toCreateResponse(projectDto);
}
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) {
newCodeDefinitionType, newCodeDefinitionValue);
}
- componentUpdater.commitAndIndex(dbSession, componentCreationData.mainBranchComponent());
+ componentUpdater.commitAndIndex(dbSession, componentCreationData);
return toCreateResponse(projectDto);
}
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) {
newCodeDefinitionType, newCodeDefinitionValue);
}
- componentUpdater.commitAndIndex(dbSession, componentCreationData.mainBranchComponent());
+ componentUpdater.commitAndIndex(dbSession, componentCreationData);
return toCreateResponse(projectDto);
}
.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) {
newCodeDefinitionType, newCodeDefinitionValue);
}
- componentUpdater.commitAndIndex(dbSession, componentCreationData.mainBranchComponent());
+ componentUpdater.commitAndIndex(dbSession, componentCreationData);
return ImportHelper.toCreateResponse(projectDto);
}
.setPrivate(visibility)
.setQualifier(PROJECT)
.build(),
- userSession.getUuid(), userSession.getLogin(), mainBranchName, s -> {
- });
+ userSession.getUuid(), userSession.getLogin(), mainBranchName);
}
}
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;
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);
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())
.orElseGet(() -> branchSupport.createBranchComponent(dbSession, componentKey, mainBranchComponent, mainBranch));
}
- if (projectCreated) {
- componentUpdater.commitAndIndex(dbSession, mainBranchComponent);
+ if (componentCreationData != null) {
+ componentUpdater.commitAndIndex(dbSession, componentCreationData);
} else {
dbSession.commit();
}
}
}
- 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();
.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) {
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);
}
}
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);
}
}
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) {
}
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;
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;
}
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);
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;
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;
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;
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;
@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;
* - 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);
+ }
}
/**
* 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);
}
/**
* 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) {
}
}
- 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()
.setPrivate(newComponent.isPrivate())
.setCreatedAt(new Date(now));
- componentModifier.accept(component);
dbClient.componentDao().insert(session, component, true);
return component;
}
.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) {
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(
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;
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;
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)));
}
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
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;
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;
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) {
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;
}
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) {
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;
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();
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;
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);
.build(),
userSession.isLoggedIn() ? userSession.getUuid() : null,
userSession.isLoggedIn() ? userSession.getLogin() : null,
- request.getMainBranchKey(), s -> {
- }).mainBranchComponent();
+ request.getMainBranchKey());
}
.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();
}
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)));
}
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;
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;
}
} else {
updatePermissionsToPublic(dbSession, entityDto);
}
- projectIndexers.commitAndIndexEntities(dbSession, singletonList(entityDto), ProjectIndexer.Cause.PERMISSION_CHANGE);
+ indexers.commitAndIndexEntities(dbSession, singletonList(entityDto), Indexers.EntityEvent.PERMISSION_CHANGE);
}
response.noContent();
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 {
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;
}
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) {
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;
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;
ComponentCleanerService.class,
ComponentIndexDefinition.class,
ComponentIndex.class,
- ComponentIndexer.class,
+ EntityDefinitionIndexer.class,
new LiveMeasureModule(),
ComponentViewerJsonWriter.class,
new HttpRequestIdModule(),
RecoveryIndexer.class,
- ProjectIndexersImpl.class,
+ IndexersImpl.class,
// telemetry
TelemetryDataLoaderImpl.class,