package org.sonar.server.measure.ws;
import com.google.common.collect.Sets;
+import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import org.sonar.db.component.SnapshotQuery.SORT_FIELD;
import org.sonar.db.component.SnapshotQuery.SORT_ORDER;
import org.sonar.db.measure.MeasureDto;
+import org.sonar.db.measure.PastMeasureQuery;
import org.sonar.db.metric.MetricDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.user.UserSession;
.setComponent(component)
.setAnalyses(searchAnalyses(dbSession, request, component))
.setMetrics(searchMetrics(dbSession, request));
- return result.setMeasures(searchMeasures(dbSession, component, result.getAnalyses(), result.getMetrics()));
+ return result.setMeasures(searchMeasures(dbSession, request, result));
}
};
}
return component;
}
- private List<MeasureDto> searchMeasures(DbSession dbSession, ComponentDto component, List<SnapshotDto> analyses, List<MetricDto> metrics) {
- return dbClient.measureDao().selectPastMeasures(
- dbSession,
- component.uuid(),
- analyses.stream().map(SnapshotDto::getUuid).collect(Collectors.toList()),
- metrics.stream().map(MetricDto::getId).collect(Collectors.toList()));
+ private List<MeasureDto> searchMeasures(DbSession dbSession, SearchHistoryRequest request, SearchHistoryResult result) {
+ Date from = parseStartingDateOrDateTime(request.getFrom());
+ Date to = parseEndingDateOrDateTime(request.getTo());
+ PastMeasureQuery dbQuery = new PastMeasureQuery(
+ result.getComponent().uuid(),
+ result.getMetrics().stream().map(MetricDto::getId).collect(Collectors.toList()),
+ from == null ? null : from.getTime(),
+ to == null ? null : (to.getTime() + 1_000L));
+ return dbClient.measureDao().selectPastMeasures(dbSession, dbQuery);
}
private List<SnapshotDto> searchAnalyses(DbSession dbSession, SearchHistoryRequest request, ComponentDto component) {
import com.google.common.collect.Table;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
+import org.sonar.core.util.stream.Collectors;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.MeasureDto;
import static java.util.Collections.emptyList;
import static java.util.Objects.requireNonNull;
import static org.sonar.api.utils.Paging.offset;
-import static org.sonar.core.util.stream.Collectors.toList;
import static org.sonar.db.metric.MetricDtoFunctions.isOptimizedForBestValue;
import static org.sonar.server.measure.ws.MetricDtoWithBestValue.isEligibleForBestValue;
this.request = request;
}
- boolean hasResults() {
- return !analyses.isEmpty();
+ public ComponentDto getComponent() {
+ return requireNonNull(component);
}
SearchHistoryResult setComponent(ComponentDto component) {
SearchHistoryResult setAnalyses(List<SnapshotDto> analyses) {
this.paging = Common.Paging.newBuilder().setPageIndex(request.getPage()).setPageSize(request.getPageSize()).setTotal(analyses.size()).build();
- this.analyses = analyses.stream().skip(offset(request.getPage(), request.getPageSize())).limit(request.getPageSize()).collect(toList());
+ this.analyses = analyses.stream().skip(offset(request.getPage(), request.getPageSize())).limit(request.getPageSize()).collect(Collectors.toList());
+
return this;
}
}
SearchHistoryResult setMeasures(List<MeasureDto> measures) {
- this.measures = ImmutableList.<MeasureDto>builder()
- .addAll(measures)
- .addAll(addBestValuesToMeasures(component, measures)).build();
+ Set<String> analysisUuids = analyses.stream().map(SnapshotDto::getUuid).collect(Collectors.toHashSet());
+ ImmutableList.Builder<MeasureDto> measuresBuilder = ImmutableList.builder();
+ List<MeasureDto> filteredMeasures = measures.stream()
+ .filter(measure -> analysisUuids.contains(measure.getAnalysisUuid()))
+ .collect(Collectors.toArrayList());
+ measuresBuilder.addAll(filteredMeasures);
+ measuresBuilder.addAll(computeBestValues(filteredMeasures));
+
+ this.measures = measuresBuilder.build();
+
return this;
}
* <li>metric is optimized for best value</li>
* </ul>
*/
- private List<MeasureDto> addBestValuesToMeasures(ComponentDto component, List<MeasureDto> measures) {
+ private List<MeasureDto> computeBestValues(List<MeasureDto> measures) {
if (!isEligibleForBestValue().test(component)) {
return emptyList();
}
ids -> mapper(dbSession).selectPastMeasuresOnSingleAnalysis(componentUuid, analysisUuid, ids));
}
- public List<MeasureDto> selectPastMeasures(DbSession dbSession, String componentUuid, List<String> analysisUuids, List<Integer> metricIds) {
- if (analysisUuids.isEmpty() || metricIds.isEmpty()) {
- return emptyList();
- }
-
- return executeLargeInputs(
- analysisUuids,
- analyses -> mapper(dbSession).selectPastMeasuresOnSeveralAnalyses(componentUuid, analyses, metricIds));
+ /**
+ * Select measures of:
+ * - one component
+ * - for a list of metrics
+ * - with analysis from a date (inclusive) - optional
+ * - with analysis to a date (exclusive) - optional
+ *
+ * If no constraints on dates, all the history is returned
+ */
+ public List<MeasureDto> selectPastMeasures(DbSession dbSession, PastMeasureQuery query) {
+ return mapper(dbSession).selectPastMeasuresOnSeveralAnalyses(query);
}
/**
List<PastMeasureDto> selectPastMeasuresOnSingleAnalysis(@Param("componentUuid") String componentUuid, @Param("analysisUuid") String analysisUuid,
@Param("metricIds") List<Integer> metricIds);
- List<MeasureDto> selectPastMeasuresOnSeveralAnalyses(@Param("componentUuid") String componentUuid, @Param("analysisUuids") Collection<String> analysisUuid,
- @Param("metricIds") Collection<Integer> metricIds);
+ List<MeasureDto> selectPastMeasuresOnSeveralAnalyses(@Param("query") PastMeasureQuery query);
List<MeasureDto> selectProjectMeasuresOfDeveloper(@Param("developerId") long developerId, @Param("metricIds") Collection<Integer> metricIds);
--- /dev/null
+/*
+ * 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.db.measure;
+
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.db.component.SnapshotDto;
+
+import static java.util.Objects.requireNonNull;
+
+public class PastMeasureQuery {
+ private final String componentUuid;
+ private final List<Integer> metricIds;
+ private final Long from;
+ private final Long to;
+ private final String status;
+
+ public PastMeasureQuery(String componentUuid, List<Integer> metricIds, @Nullable Long from, @Nullable Long to) {
+ this.componentUuid = requireNonNull(componentUuid);
+ this.metricIds = requireNonNull(metricIds);
+ this.from = from;
+ this.to = to;
+ this.status = SnapshotDto.STATUS_PROCESSED;
+ }
+
+ public String getComponentUuid() {
+ return componentUuid;
+ }
+
+ public List<Integer> getMetricIds() {
+ return metricIds;
+ }
+
+ @CheckForNull
+ public Long getFrom() {
+ return from;
+ }
+
+ @CheckForNull
+ public Long getTo() {
+ return to;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+}
<select id="selectPastMeasuresOnSeveralAnalyses" parameterType="map" resultType="Measure">
select <include refid="measureColumns"/>
from project_measures pm
- inner join snapshots analysis on analysis.uuid = pm.analysis_uuid
+ inner join snapshots analysis on analysis.uuid = pm.analysis_uuid
where
- pm.component_uuid = #{componentUuid}
- and analysis.uuid in <foreach item="analysisUuid" collection="analysisUuids" open="(" separator="," close=")">#{analysisUuid}</foreach>
- and pm.metric_id in <foreach item="metricId" collection="metricIds" open="(" separator="," close=")">#{metricId}</foreach>
+ pm.component_uuid = #{query.componentUuid, jdbcType=VARCHAR}
+ <if test="query.from!= null">
+ and analysis.created_at>=#{query.from, jdbcType=BIGINT}
+ </if>
+ <if test="query.to!=null">
+ and analysis.created_at<#{query.to, jdbcType=BIGINT}
+ </if>
+ and pm.metric_id in <foreach item="metricId" collection="query.metricIds" open="(" separator="," close=")">#{metricId, jdbcType=VARCHAR}</foreach>
and pm.person_id is null
+ and analysis.status=#{query.status, jdbcType=VARCHAR}
</select>
<select id="selectProjectMeasuresOfDeveloper" parameterType="map" resultType="Measure">
import org.sonar.api.resources.Scopes;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactoryImpl;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.SnapshotDto;
import org.sonar.db.component.SnapshotTesting;
import org.sonar.db.organization.OrganizationDto;
-import static com.google.common.collect.Lists.newArrayList;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.sonar.api.resources.Qualifiers.PROJECT;
import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE;
import static org.sonar.api.resources.Qualifiers.VIEW;
+import static org.sonar.api.utils.DateUtils.parseDate;
import static org.sonar.db.component.ComponentTesting.newDeveloper;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newModuleDto;
+import static org.sonar.db.component.SnapshotTesting.newAnalysis;
import static org.sonar.db.measure.MeasureTreeQuery.Strategy.CHILDREN;
import static org.sonar.db.measure.MeasureTreeQuery.Strategy.LEAVES;
@Rule
public DbTester db = DbTester.create(System2.INSTANCE);
+ private DbClient dbClient = db.getDbClient();
+ private DbSession dbSession = db.getSession();
private MeasureDao underTest = db.getDbClient().measureDao();
@Test
public void select_past_measures_with_several_analyses() {
ComponentDto project = db.components().insertProject();
- insertAnalysis(LAST_ANALYSIS_UUID, project.uuid(), true);
- insertAnalysis(OTHER_ANALYSIS_UUID, project.uuid(), false);
+ long lastAnalysisDate = parseDate("2017-01-25").getTime();
+ long previousAnalysisDate = lastAnalysisDate - 10_000_000_000L;
+ long oldAnalysisDate = lastAnalysisDate - 100_000_000_000L;
+ dbClient.snapshotDao().insert(dbSession, newAnalysis(project).setUuid(LAST_ANALYSIS_UUID).setCreatedAt(lastAnalysisDate));
+ dbClient.snapshotDao().insert(dbSession, newAnalysis(project).setUuid(OTHER_ANALYSIS_UUID).setCreatedAt(previousAnalysisDate).setLast(false));
+ dbClient.snapshotDao().insert(dbSession, newAnalysis(project).setUuid("OLD_ANALYSIS_UUID").setCreatedAt(oldAnalysisDate).setLast(false));
+ db.commit();
// project
insertMeasure("PROJECT_M1", LAST_ANALYSIS_UUID, project.uuid(), NCLOC_METRIC_ID);
insertMeasure("PROJECT_M2", OTHER_ANALYSIS_UUID, project.uuid(), NCLOC_METRIC_ID);
+ insertMeasure("PROJECT_M3", "OLD_ANALYSIS_UUID", project.uuid(), NCLOC_METRIC_ID);
db.commit();
- // Children measures of project
- List<MeasureDto> result = underTest.selectPastMeasures(db.getSession(), project.uuid(), newArrayList(LAST_ANALYSIS_UUID, OTHER_ANALYSIS_UUID), singletonList(NCLOC_METRIC_ID));
+ // Measures of project for last and previous analyses
+ List<MeasureDto> result = underTest.selectPastMeasures(db.getSession(),
+ new PastMeasureQuery(project.uuid(), singletonList(NCLOC_METRIC_ID), previousAnalysisDate, lastAnalysisDate + 1_000L));
assertThat(result).hasSize(2).extracting(MeasureDto::getData).containsOnly("PROJECT_M1", "PROJECT_M2");
}
db.getDbClient().measureDao().insert(db.getSession(), measure);
}
- private void insertAnalysis(String uuid, String projectUuid, boolean isLast) {
- db.getDbClient().snapshotDao().insert(db.getSession(), SnapshotTesting.newSnapshot()
+ private SnapshotDto insertAnalysis(String uuid, String projectUuid, boolean isLast) {
+ return db.getDbClient().snapshotDao().insert(db.getSession(), SnapshotTesting.newSnapshot()
.setUuid(uuid)
.setComponentUuid(projectUuid)
.setLast(isLast));