diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2016-10-12 17:30:41 +0200 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2016-10-12 18:11:08 +0200 |
commit | 2bf5b53cdb3c31bcac0b242393adeb72a01cdede (patch) | |
tree | 43902cca22c86be5cbd873bfc8815d77ff397dc6 | |
parent | 5ce0d722397b1811ed3e7a150d65a62cf33af43a (diff) | |
download | sonarqube-2bf5b53cdb3c31bcac0b242393adeb72a01cdede.tar.gz sonarqube-2bf5b53cdb3c31bcac0b242393adeb72a01cdede.zip |
SONAR-8222 Update project measures index at the end of each project analysis
10 files changed, 205 insertions, 17 deletions
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java index be426f6f7b7..fadc3875c60 100644 --- a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java +++ b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java @@ -112,6 +112,7 @@ import org.sonar.server.plugins.InstalledPluginReferentialFactory; import org.sonar.server.plugins.ServerExtensionInstaller; import org.sonar.server.plugins.privileged.PrivilegedPluginsBootstraper; import org.sonar.server.plugins.privileged.PrivilegedPluginsStopper; +import org.sonar.server.project.es.ProjectMeasuresIndexer; import org.sonar.server.property.InternalPropertiesImpl; import org.sonar.server.qualityprofile.QProfileLookup; import org.sonar.server.qualityprofile.QProfileProjectOperations; @@ -322,6 +323,7 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer { NewAlerts.class, NewAlerts.newMetadata(), ComponentCleanerService.class, + ProjectMeasuresIndexer.class, // views ViewIndexer.class, diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java index e1a8a4763d3..0f7d829a708 100644 --- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java +++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java @@ -88,7 +88,7 @@ public class ComputeEngineContainerImplTest { assertThat(picoContainer.getComponentAdapters()) .hasSize( CONTAINER_ITSELF - + 73 // level 4 + + 74 // level 4 + 4 // content of CeConfigurationModule + 3 // content of CeHttpModule + 5 // content of CeQueueModule diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/IndexProjectMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/IndexProjectMeasuresStep.java new file mode 100644 index 00000000000..3250a46d62d --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/IndexProjectMeasuresStep.java @@ -0,0 +1,45 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.computation.task.projectanalysis.step; + +import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder; +import org.sonar.server.computation.task.step.ComputationStep; +import org.sonar.server.project.es.ProjectMeasuresIndexer; + +public class IndexProjectMeasuresStep implements ComputationStep { + + private final ProjectMeasuresIndexer indexer; + private final TreeRootHolder treeRootHolder; + + public IndexProjectMeasuresStep(ProjectMeasuresIndexer indexer, TreeRootHolder treeRootHolder) { + this.indexer = indexer; + this.treeRootHolder = treeRootHolder; + } + + @Override + public void execute() { + indexer.index(treeRootHolder.getRoot().getUuid()); + } + + @Override + public String getDescription() { + return "Index project measures"; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java index 30e36ec7588..55c66df8e5c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java @@ -107,6 +107,7 @@ public class ReportComputationSteps extends AbstractComputationSteps { // ES indexing is done after all db changes IndexIssuesStep.class, IndexTestsStep.class, + IndexProjectMeasuresStep.class, // notifications are sent at the end, so that webapp displays up-to-date information SendIssueNotificationsStep.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/es/ProjectMeasuresIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/project/es/ProjectMeasuresIndexer.java index 2322ba94a11..0b933b0950e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/project/es/ProjectMeasuresIndexer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/project/es/ProjectMeasuresIndexer.java @@ -21,6 +21,7 @@ package org.sonar.server.project.es; import java.util.Iterator; +import javax.annotation.Nullable; import org.elasticsearch.action.index.IndexRequest; import org.sonar.db.DbClient; import org.sonar.db.DbSession; @@ -43,16 +44,20 @@ public class ProjectMeasuresIndexer extends BaseIndexer { @Override protected long doIndex(long lastUpdatedAt) { - doIndex(createBulkIndexer(false)); - return 0L; + return doIndex(createBulkIndexer(false), (String) null); } - private void doIndex(BulkIndexer bulk) { + public void index(String projectUuid) { + index(lastUpdatedAt -> doIndex(createBulkIndexer(false), projectUuid)); + } + + private long doIndex(BulkIndexer bulk, @Nullable String projectUuid) { DbSession dbSession = dbClient.openSession(false); try { - ProjectMeasuresResultSetIterator rowIt = ProjectMeasuresResultSetIterator.create(dbClient, dbSession); + ProjectMeasuresResultSetIterator rowIt = ProjectMeasuresResultSetIterator.create(dbClient, dbSession, projectUuid); doIndex(bulk, rowIt); rowIt.close(); + return 0L; } finally { dbClient.closeSession(dbSession); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/es/ProjectMeasuresResultSetIterator.java b/server/sonar-server/src/main/java/org/sonar/server/project/es/ProjectMeasuresResultSetIterator.java index a936c85789a..47ad6f59f75 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/project/es/ProjectMeasuresResultSetIterator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/project/es/ProjectMeasuresResultSetIterator.java @@ -24,6 +24,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; +import javax.annotation.Nullable; import org.apache.commons.lang.StringUtils; import org.sonar.api.resources.Qualifiers; import org.sonar.api.resources.Scopes; @@ -44,17 +45,25 @@ public class ProjectMeasuresResultSetIterator extends ResultSetIterator<ProjectM "LEFT OUTER JOIN snapshots s ON s.component_uuid=p.uuid AND s.islast=? " + "WHERE p.enabled=? AND p.scope=? AND p.qualifier=?"; + private static final String PROJECT_FILTER = " AND p.uuid=?"; + private ProjectMeasuresResultSetIterator(PreparedStatement stmt) throws SQLException { super(stmt); } - static ProjectMeasuresResultSetIterator create(DbClient dbClient, DbSession session) { + static ProjectMeasuresResultSetIterator create(DbClient dbClient, DbSession session, @Nullable String projectUuid) { try { - PreparedStatement stmt = dbClient.getMyBatis().newScrollingSelectStatement(session, SQL_ALL); + + String sql = SQL_ALL; + sql += projectUuid == null ? "" : PROJECT_FILTER; + PreparedStatement stmt = dbClient.getMyBatis().newScrollingSelectStatement(session, sql); stmt.setBoolean(1, true); stmt.setBoolean(2, true); stmt.setString(3, Scopes.PROJECT); stmt.setString(4, Qualifiers.PROJECT); + if (projectUuid != null) { + stmt.setString(5, projectUuid); + } return new ProjectMeasuresResultSetIterator(stmt); } catch (SQLException e) { throw new IllegalStateException("Fail to prepare SQL request to select all project measures", e); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/IndexIssuesStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/IndexIssuesStepTest.java index df1fc8c235f..663b7159a69 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/IndexIssuesStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/IndexIssuesStepTest.java @@ -26,8 +26,8 @@ import org.sonar.server.issue.index.IssueIndexer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.*; -import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.*; +import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.PROJECT; +import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder; public class IndexIssuesStepTest { @@ -38,7 +38,7 @@ public class IndexIssuesStepTest { .setRoot(builder(PROJECT, 1).setUuid(PROJECT_UUID).setKey("PROJECT_KEY").build()); @Test - public void call_indexers() { + public void call_indexer() { IssueIndexer issueIndexer = mock(IssueIndexer.class); IndexIssuesStep underTest = new IndexIssuesStep(issueIndexer, treeRootHolder); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/IndexProjectMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/IndexProjectMeasuresStepTest.java new file mode 100644 index 00000000000..bda9ddb020c --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/IndexProjectMeasuresStepTest.java @@ -0,0 +1,51 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.computation.task.projectanalysis.step; + +import org.junit.Rule; +import org.junit.Test; +import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule; +import org.sonar.server.project.es.ProjectMeasuresIndexer; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.PROJECT; +import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder; + +public class IndexProjectMeasuresStepTest { + + static String PROJECT_UUID = "PROJECT_UUID"; + + @Rule + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule() + .setRoot(builder(PROJECT, 1).setUuid(PROJECT_UUID).setKey("PROJECT_KEY").build()); + + @Test + public void call_indexer() { + ProjectMeasuresIndexer indexer = mock(ProjectMeasuresIndexer.class); + IndexProjectMeasuresStep underTest = new IndexProjectMeasuresStep(indexer, treeRootHolder); + + underTest.execute(); + + verify(indexer).index(PROJECT_UUID); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/es/ProjectMeasuresIndexerTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/es/ProjectMeasuresIndexerTest.java index 8867fc6d9a5..6b562b439bc 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/project/es/ProjectMeasuresIndexerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/project/es/ProjectMeasuresIndexerTest.java @@ -20,15 +20,22 @@ package org.sonar.server.project.es; +import java.util.Date; +import org.elasticsearch.action.search.SearchRequestBuilder; import org.junit.Rule; import org.junit.Test; import org.sonar.api.config.MapSettings; import org.sonar.api.utils.System2; import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDbTester; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.component.SnapshotDto; import org.sonar.server.es.EsTester; import static org.assertj.core.api.Assertions.assertThat; +import static org.elasticsearch.index.query.QueryBuilders.boolQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; +import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.sonar.db.component.ComponentTesting.newProjectDto; import static org.sonar.server.project.es.ProjectMeasuresIndexDefinition.INDEX_PROJECT_MEASURES; import static org.sonar.server.project.es.ProjectMeasuresIndexDefinition.TYPE_PROJECT_MEASURES; @@ -53,7 +60,7 @@ public class ProjectMeasuresIndexerTest { } @Test - public void index_one_project() { + public void index_all_project() { componentDbTester.insertProjectAndSnapshot(newProjectDto()); underTest.index(); @@ -61,4 +68,40 @@ public class ProjectMeasuresIndexerTest { assertThat(esTester.countDocuments(INDEX_PROJECT_MEASURES, TYPE_PROJECT_MEASURES)).isEqualTo(1); } + @Test + public void index_one_project() throws Exception { + ComponentDto project = newProjectDto(); + componentDbTester.insertProjectAndSnapshot(project); + componentDbTester.insertProjectAndSnapshot(newProjectDto()); + + underTest.index(project.uuid()); + + assertThat(esTester.getIds(INDEX_PROJECT_MEASURES, TYPE_PROJECT_MEASURES)).containsOnly(project.uuid()); + } + + @Test + public void update_existing_document_when_indexing_one_project() throws Exception { + String uuid = "PROJECT-UUID"; + esTester.putDocuments(INDEX_PROJECT_MEASURES, TYPE_PROJECT_MEASURES, new ProjectMeasuresDoc() + .setId(uuid) + .setKey("Old Key") + .setName("Old Name") + .setAnalysedAt(new Date(1_000_000L))); + ComponentDto project = newProjectDto(uuid).setKey("New key").setName("New name"); + SnapshotDto analysis = componentDbTester.insertProjectAndSnapshot(project); + + underTest.index(project.uuid()); + + assertThat(esTester.getIds(INDEX_PROJECT_MEASURES, TYPE_PROJECT_MEASURES)).containsOnly(uuid); + SearchRequestBuilder request = esTester.client() + .prepareSearch(INDEX_PROJECT_MEASURES) + .setTypes(TYPE_PROJECT_MEASURES) + .setQuery(boolQuery().must(matchAllQuery()).filter( + boolQuery() + .must(termQuery("_id", uuid)) + .must(termQuery(ProjectMeasuresIndexDefinition.FIELD_KEY, "New key")) + .must(termQuery(ProjectMeasuresIndexDefinition.FIELD_NAME, "New name")) + .must(termQuery(ProjectMeasuresIndexDefinition.FIELD_ANALYSED_AT, new Date(analysis.getCreatedAt()))))); + assertThat(request.get().getHits()).hasSize(1); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/es/ProjectMeasuresResultSetIteratorTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/es/ProjectMeasuresResultSetIteratorTest.java index 48c889f00af..b018d1b72f0 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/project/es/ProjectMeasuresResultSetIteratorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/project/es/ProjectMeasuresResultSetIteratorTest.java @@ -23,6 +23,7 @@ package org.sonar.server.project.es; import com.google.common.collect.Maps; import java.util.Date; import java.util.Map; +import javax.annotation.Nullable; import org.junit.Rule; import org.junit.Test; import org.sonar.api.utils.System2; @@ -54,7 +55,7 @@ public class ProjectMeasuresResultSetIteratorTest { ComponentDto project = newProjectDto().setKey("Project-Key").setName("Project Name"); SnapshotDto analysis = componentDbTester.insertProjectAndSnapshot(project); - Map<String, ProjectMeasuresDoc> docsById = docsById(); + Map<String, ProjectMeasuresDoc> docsById = createResultSetAndReturnDocsById(); assertThat(docsById).hasSize(1); ProjectMeasuresDoc doc = docsById.get(project.uuid()); @@ -71,7 +72,7 @@ public class ProjectMeasuresResultSetIteratorTest { componentDbTester.insertProjectAndSnapshot(newProjectDto()); componentDbTester.insertProjectAndSnapshot(newProjectDto()); - assertThat(docsById()).hasSize(3); + assertThat(createResultSetAndReturnDocsById()).hasSize(3); } @Test @@ -80,7 +81,7 @@ public class ProjectMeasuresResultSetIteratorTest { dbClient.snapshotDao().insert(dbSession, newAnalysis(project).setLast(false)); dbSession.commit(); - Map<String, ProjectMeasuresDoc> docsById = docsById(); + Map<String, ProjectMeasuresDoc> docsById = createResultSetAndReturnDocsById(); assertThat(docsById).hasSize(1); ProjectMeasuresDoc doc = docsById.get(project.uuid()); @@ -106,15 +107,46 @@ public class ProjectMeasuresResultSetIteratorTest { assertResultSetIsEmpty(); } - private Map<String, ProjectMeasuresDoc> docsById() { - ProjectMeasuresResultSetIterator it = ProjectMeasuresResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession()); + @Test + public void return_only_docs_from_given_project() throws Exception { + ComponentDto project = newProjectDto(); + SnapshotDto analysis = componentDbTester.insertProjectAndSnapshot(project); + componentDbTester.insertProjectAndSnapshot(newProjectDto()); + componentDbTester.insertProjectAndSnapshot(newProjectDto()); + + Map<String, ProjectMeasuresDoc> docsById = createResultSetAndReturnDocsById(project.uuid()); + + assertThat(docsById).hasSize(1); + ProjectMeasuresDoc doc = docsById.get(project.uuid()); + assertThat(doc).isNotNull(); + assertThat(doc.getId()).isEqualTo(project.uuid()); + assertThat(doc.getKey()).isNotNull().isEqualTo(project.getKey()); + assertThat(doc.getName()).isNotNull().isEqualTo(project.name()); + assertThat(doc.getAnalysedAt()).isNotNull().isEqualTo(new Date(analysis.getCreatedAt())); + } + + @Test + public void return_nothing_on_unknown_project() throws Exception { + componentDbTester.insertProjectAndSnapshot(newProjectDto()); + + Map<String, ProjectMeasuresDoc> docsById = createResultSetAndReturnDocsById("UNKNOWN"); + + assertThat(docsById).isEmpty(); + } + + private Map<String, ProjectMeasuresDoc> createResultSetAndReturnDocsById() { + return createResultSetAndReturnDocsById(null); + } + + private Map<String, ProjectMeasuresDoc> createResultSetAndReturnDocsById(@Nullable String projectUuid) { + ProjectMeasuresResultSetIterator it = ProjectMeasuresResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), projectUuid); Map<String, ProjectMeasuresDoc> docsById = Maps.uniqueIndex(it, ProjectMeasuresDoc::getId); it.close(); return docsById; } private void assertResultSetIsEmpty() { - assertThat(docsById()).isEmpty(); + assertThat(createResultSetAndReturnDocsById()).isEmpty(); } } |