Skip to content

Commit

Permalink
SONAR-8222 Update project measures index at the end of each project a…
Browse files Browse the repository at this point in the history
…nalysis
  • Loading branch information
julienlancelot committed Oct 12, 2016
1 parent 5ce0d72 commit 2bf5b53
Show file tree
Hide file tree
Showing 10 changed files with 205 additions and 17 deletions.
Expand Up @@ -112,6 +112,7 @@
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;
Expand Down Expand Up @@ -322,6 +323,7 @@ private static Object[] level4Components() {
NewAlerts.class,
NewAlerts.newMetadata(),
ComponentCleanerService.class,
ProjectMeasuresIndexer.class,

// views
ViewIndexer.class,
Expand Down
Expand Up @@ -88,7 +88,7 @@ public void real_start() throws IOException {
assertThat(picoContainer.getComponentAdapters())
.hasSize(
CONTAINER_ITSELF
+ 73 // level 4
+ 74 // level 4
+ 4 // content of CeConfigurationModule
+ 3 // content of CeHttpModule
+ 5 // content of CeQueueModule
Expand Down
@@ -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";
}
}
Expand Up @@ -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,
Expand Down
Expand Up @@ -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;
Expand All @@ -43,16 +44,20 @@ public ProjectMeasuresIndexer(DbClient dbClient, EsClient esClient) {

@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);
}
Expand Down
Expand Up @@ -24,6 +24,7 @@
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;
Expand All @@ -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);
Expand Down
Expand Up @@ -26,8 +26,8 @@

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 {

Expand All @@ -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);

Expand Down
@@ -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);
}

}
Expand Up @@ -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;
Expand All @@ -53,12 +60,48 @@ public void index_nothing() {
}

@Test
public void index_one_project() {
public void index_all_project() {
componentDbTester.insertProjectAndSnapshot(newProjectDto());

underTest.index();

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);
}
}
Expand Up @@ -23,6 +23,7 @@
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;
Expand Down Expand Up @@ -54,7 +55,7 @@ public void return_one_project_measure() {
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());
Expand All @@ -71,7 +72,7 @@ public void return_many_project_measures() {
componentDbTester.insertProjectAndSnapshot(newProjectDto());
componentDbTester.insertProjectAndSnapshot(newProjectDto());

assertThat(docsById()).hasSize(3);
assertThat(createResultSetAndReturnDocsById()).hasSize(3);
}

@Test
Expand All @@ -80,7 +81,7 @@ public void return_project_without_analysis() throws Exception {
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());
Expand All @@ -106,15 +107,46 @@ public void does_not_return_non_active_projects() throws Exception {
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();
}

}

0 comments on commit 2bf5b53

Please sign in to comment.