*/
package org.sonar.server.component.es;
+import com.google.common.collect.ImmutableMap;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import org.sonar.core.util.stream.Collectors;
import org.sonar.server.es.BaseDoc;
import static org.sonar.server.component.es.ProjectMeasuresIndexDefinition.FIELD_MEASURES;
setField(FIELD_MEASURES, measures);
return this;
}
+
+ public ProjectMeasuresDoc setMeasuresFromMap(Map<String, Object> measures) {
+ setMeasures(
+ measures.entrySet().stream()
+ .map(entry -> ImmutableMap.of(
+ ProjectMeasuresIndexDefinition.FIELD_MEASURES_KEY, entry.getKey(),
+ ProjectMeasuresIndexDefinition.FIELD_MEASURES_VALUE, entry.getValue()))
+ .collect(Collectors.toList()));
+ return this;
+ }
+
}
package org.sonar.server.component.es;
+import com.google.common.base.Joiner;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.resources.Qualifiers;
import org.sonar.db.DbSession;
import org.sonar.db.ResultSetIterator;
+import static org.sonar.api.measures.Metric.ValueType.DATA;
+import static org.sonar.api.measures.Metric.ValueType.DISTRIB;
+import static org.sonar.db.DatabaseUtils.repeatCondition;
+
public class ProjectMeasuresResultSetIterator extends ResultSetIterator<ProjectMeasuresDoc> {
- private static final String[] FIELDS = {
- "p.uuid",
- "p.kee",
- "p.name",
- "s.created_at"
- };
+ private static final Joiner METRICS_JOINER = Joiner.on("','");
- private static final String SQL_ALL = "SELECT " + StringUtils.join(FIELDS, ",") + " FROM projects p " +
+ private static final String SQL_PROJECTS = "SELECT p.uuid, p.kee, p.name, s.uuid, s.created_at FROM projects p " +
"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 {
+ private static final String SQL_METRICS = "SELECT m.id, m.name FROM metrics m " +
+ "WHERE m.val_type NOT IN ('" + METRICS_JOINER.join(DATA.name(), DISTRIB.name()) + "') " +
+ "AND m.enabled=? AND m.hidden=?";
+
+ private static final String SQL_MEASURES = "SELECT pm.metric_id, pm.value, pm.variation_value_1 FROM project_measures pm " +
+ "WHERE pm.component_uuid = ? AND pm.analysis_uuid = ? " +
+ "AND pm.metric_id IN ({metricIds}) " +
+ "AND (pm.value IS NOT NULL OR pm.variation_value_1 IS NOT NULL) " +
+ "AND pm.person_id IS NULL ";
+
+ private final DbSession dbSession;
+ private final Map<Long, String> metrics;
+
+ private ProjectMeasuresResultSetIterator(PreparedStatement stmt, DbSession dbSession, Map<Long, String> metrics) throws SQLException {
super(stmt);
+ this.dbSession = dbSession;
+ this.metrics = metrics;
}
static ProjectMeasuresResultSetIterator create(DbClient dbClient, DbSession session, long afterDate, @Nullable String projectUuid) {
try {
- String sql = SQL_ALL;
+ PreparedStatement projectsStatement = createProjectsStatement(dbClient, session, afterDate, projectUuid);
+ Map<Long, String> metricIds = selectMetricIds(session);
+ return new ProjectMeasuresResultSetIterator(projectsStatement, session, metricIds);
+ } catch (SQLException e) {
+ throw new IllegalStateException("Fail to execute request to select all project measures", e);
+ }
+ }
+
+ private static PreparedStatement createProjectsStatement(DbClient dbClient, DbSession session, long afterDate, @Nullable String projectUuid) {
+ try {
+ String sql = SQL_PROJECTS;
sql += afterDate <= 0L ? "" : DATE_FILTER;
sql += projectUuid == null ? "" : PROJECT_FILTER;
PreparedStatement stmt = dbClient.getMyBatis().newScrollingSelectStatement(session, sql);
if (projectUuid != null) {
stmt.setString(index, projectUuid);
}
- return new ProjectMeasuresResultSetIterator(stmt);
+ return stmt;
} catch (SQLException e) {
throw new IllegalStateException("Fail to prepare SQL request to select all project measures", e);
}
}
+ private static Map<Long, String> selectMetricIds(DbSession session) {
+ Map<Long, String> metrics = new HashMap<>();
+ try (PreparedStatement stmt = createMetricsStatement(session);
+ ResultSet rs = stmt.executeQuery()) {
+ while (rs.next()) {
+ metrics.put(rs.getLong(1), rs.getString(2));
+ }
+ return metrics;
+ } catch (SQLException e) {
+ throw new IllegalStateException("Fail to execute request to select all metrics", e);
+ }
+ }
+
+ private static PreparedStatement createMetricsStatement(DbSession session) throws SQLException {
+ PreparedStatement stmt = session.getConnection().prepareStatement(SQL_METRICS);
+ stmt.setBoolean(1, true);
+ stmt.setBoolean(2, false);
+ return stmt;
+ }
+
@Override
protected ProjectMeasuresDoc read(ResultSet rs) throws SQLException {
+ String projectUuid = rs.getString(1);
ProjectMeasuresDoc doc = new ProjectMeasuresDoc()
- .setId(rs.getString(1))
+ .setId(projectUuid)
.setKey(rs.getString(2))
- .setName(rs.getString(3));
- long analysisDate = rs.getLong(4);
+ .setName(rs.getString(3))
+ .setMeasuresFromMap(selectMeasures(projectUuid, rs.getString(4)));
+ long analysisDate = rs.getLong(5);
doc.setAnalysedAt(rs.wasNull() ? null : new Date(analysisDate));
return doc;
}
+
+ private Map<String, Object> selectMeasures(String projectUuid, String analysisUuid) {
+ Map<String, Object> measures = new HashMap<>();
+ try (PreparedStatement stmt = createMeasuresStatement(projectUuid, analysisUuid);
+ ResultSet rs = stmt.executeQuery()) {
+ while (rs.next()) {
+ String metricKey = metrics.get(rs.getLong(1));
+ Double value = metricKey.startsWith("new_") ? getDouble(rs, 3) : getDouble(rs, 2);
+ measures.put(metricKey, value);
+ }
+ return measures;
+ } catch (SQLException e) {
+ throw new IllegalStateException("Fail to execute request to select measures", e);
+ }
+ }
+
+ private PreparedStatement createMeasuresStatement(String projectUuid, String analysisUuid) throws SQLException {
+ String sql = StringUtils.replace(SQL_MEASURES, "{metricIds}", repeatCondition("?", metrics.size(), ","));
+ PreparedStatement stmt = dbSession.getConnection().prepareStatement(sql);
+ stmt.setString(1, projectUuid);
+ stmt.setString(2, analysisUuid);
+ int index = 3;
+ for (Long metricId : metrics.keySet()) {
+ stmt.setLong(index, metricId);
+ index++;
+ }
+ return stmt;
+ }
+
+ private static Double getDouble(ResultSet rs, int index) {
+ try {
+ Double value = rs.getDouble(index);
+ if (!rs.wasNull()) {
+ return value;
+ }
+ throw new IllegalStateException("No value");
+ } catch (SQLException e) {
+ throw new IllegalStateException("Fail to get double value", e);
+ }
+ }
+
}
package org.sonar.server.component.es;
+import com.google.common.collect.ImmutableMap;
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.junit.rules.ExpectedException;
+import org.sonar.api.measures.Metric;
import org.sonar.api.utils.System2;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.measure.MeasureDto;
+import org.sonar.db.measure.MeasureTesting;
+import org.sonar.db.metric.MetricDto;
+import org.sonar.db.metric.MetricTesting;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.api.measures.Metric.ValueType.DATA;
+import static org.sonar.api.measures.Metric.ValueType.DISTRIB;
+import static org.sonar.api.measures.Metric.ValueType.INT;
import static org.sonar.db.component.ComponentTesting.newDeveloper;
import static org.sonar.db.component.ComponentTesting.newProjectDto;
import static org.sonar.db.component.ComponentTesting.newView;
public class ProjectMeasuresResultSetIteratorTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
@Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);
ComponentDbTester componentDbTester = new ComponentDbTester(dbTester);
@Test
- public void return_one_project_measure() {
+ public void return_project_measure() {
+ MetricDto metric1 = insertIntMetric("ncloc");
+ MetricDto metric2 = insertIntMetric("coverage");
ComponentDto project = newProjectDto().setKey("Project-Key").setName("Project Name");
SnapshotDto analysis = componentDbTester.insertProjectAndSnapshot(project);
+ insertMeasure(project, analysis, metric1, 10d);
+ insertMeasure(project, analysis, metric2, 20d);
Map<String, ProjectMeasuresDoc> docsById = createResultSetAndReturnDocsById();
assertThat(doc.getKey()).isEqualTo("Project-Key");
assertThat(doc.getName()).isEqualTo("Project Name");
assertThat(doc.getAnalysedAt()).isNotNull().isEqualTo(new Date(analysis.getCreatedAt()));
+ assertThat(doc.getMeasures()).containsOnly(
+ ImmutableMap.of("key", "ncloc", "value", 10d),
+ ImmutableMap.of("key", "coverage", "value", 20d));
+ }
+
+ @Test
+ public void return_project_measure_having_leak() throws Exception {
+ MetricDto metric = insertIntMetric("new_lines");
+ ComponentDto project = newProjectDto();
+ SnapshotDto analysis = componentDbTester.insertProjectAndSnapshot(project);
+ insertMeasureOnLeak(project, analysis, metric, 10d);
+
+ Map<String, ProjectMeasuresDoc> docsById = createResultSetAndReturnDocsById();
+
+ assertThat(docsById).hasSize(1);
+ ProjectMeasuresDoc doc = docsById.get(project.uuid());
+ assertThat(doc).isNotNull();
+ assertThat(doc.getMeasures()).containsOnly(ImmutableMap.of("key", "new_lines", "value", 10d));
+ }
+
+ @Test
+ public void does_not_return_none_numeric_metrics() throws Exception {
+ MetricDto dataMetric = insertMetric("data", DATA);
+ MetricDto distribMetric = insertMetric("distrib", DISTRIB);
+ ComponentDto project = newProjectDto();
+ SnapshotDto analysis = componentDbTester.insertProjectAndSnapshot(project);
+ insertMeasure(project, analysis, dataMetric, 10d);
+ insertMeasure(project, analysis, distribMetric, 10d);
+
+ Map<String, ProjectMeasuresDoc> docsById = createResultSetAndReturnDocsById();
+ assertThat(docsById).hasSize(1);
+ ProjectMeasuresDoc doc = docsById.get(project.uuid());
+ assertThat(doc.getMeasures()).isEmpty();
+ }
+
+ @Test
+ public void does_not_return_disabled_and_hidden_metrics() throws Exception {
+ MetricDto disabledMetric = insertMetric("disabled", false, false, INT);
+ MetricDto hiddenMetric = insertMetric("hidden", true, true, INT);
+ ComponentDto project = newProjectDto();
+ SnapshotDto analysis = componentDbTester.insertProjectAndSnapshot(project);
+ insertMeasure(project, analysis, disabledMetric, 10d);
+ insertMeasure(project, analysis, hiddenMetric, 10d);
+
+ Map<String, ProjectMeasuresDoc> docsById = createResultSetAndReturnDocsById();
+ assertThat(docsById).hasSize(1);
+ ProjectMeasuresDoc doc = docsById.get(project.uuid());
+ assertThat(doc.getMeasures()).isEmpty();
+ }
+
+ @Test
+ public void fail_when_measure_return_no_value() throws Exception {
+ MetricDto metric = insertIntMetric("new_lines");
+ ComponentDto project = newProjectDto();
+ SnapshotDto analysis = componentDbTester.insertProjectAndSnapshot(project);
+ insertMeasure(project, analysis, metric, 10d);
+
+ expectedException.expect(IllegalStateException.class);
+ createResultSetAndReturnDocsById();
}
@Test
assertThat(createResultSetAndReturnDocsById()).isEmpty();
}
+ private MetricDto insertIntMetric(String metricKey) {
+ return insertMetric(metricKey, true, false, INT);
+ }
+
+ private MetricDto insertMetric(String metricKey, Metric.ValueType type) {
+ return insertMetric(metricKey, true, false, type);
+ }
+
+ private MetricDto insertMetric(String metricKey, boolean enabled, boolean hidden, Metric.ValueType type) {
+ MetricDto metric = dbClient.metricDao().insert(dbSession,
+ MetricTesting.newMetricDto()
+ .setKey(metricKey)
+ .setEnabled(enabled)
+ .setHidden(hidden)
+ .setValueType(type.name()));
+ dbSession.commit();
+ return metric;
+ }
+
+ private MeasureDto insertMeasure(ComponentDto project, SnapshotDto analysis, MetricDto metric, double value) {
+ return insertMeasure(project, analysis, metric, value, null);
+ }
+
+ private MeasureDto insertMeasureOnLeak(ComponentDto project, SnapshotDto analysis, MetricDto metric, double value) {
+ return insertMeasure(project, analysis, metric, null, value);
+ }
+
+ private MeasureDto insertMeasure(ComponentDto project, SnapshotDto analysis, MetricDto metric, @Nullable Double value, @Nullable Double leakValue) {
+ MeasureDto measure = MeasureTesting.newMeasureDto(metric, project, analysis).setValue(value).setVariation(1, leakValue);
+ dbClient.measureDao().insert(dbSession, measure);
+ dbSession.commit();
+ return measure;
+ }
+
}