From d987a6a527bc10734fd9402ce728d27359ccdc41 Mon Sep 17 00:00:00 2001 From: Daniel Schwarz Date: Thu, 23 Nov 2017 11:13:26 +0100 Subject: [PATCH] SONAR-10116 Better scalability of loading of project measures --- .../ComputeEngineContainerImplTest.java | 2 +- .../java/org/sonar/db/version/SqTables.java | 1 + .../org/sonar/db/version/schema-h2.ddl | 18 +- .../src/main/java/org/sonar/db/DaoModule.java | 2 + .../src/main/java/org/sonar/db/DbClient.java | 7 + .../src/main/java/org/sonar/db/MyBatis.java | 2 + .../org/sonar/db/component/ComponentDto.java | 13 - .../org/sonar/db/component/ResourceDto.java | 11 - .../org/sonar/db/measure/LiveMeasureDao.java | 100 +++++ .../org/sonar/db/measure/LiveMeasureDto.java | 141 ++++++ .../sonar/db/measure/LiveMeasureMapper.java | 58 +++ .../java/org/sonar/db/measure/MeasureDao.java | 36 +- .../java/org/sonar/db/measure/MeasureDto.java | 12 - .../org/sonar/db/measure/MeasureMapper.java | 24 +- .../org/sonar/db/measure/MeasureQuery.java | 29 +- .../sonar/db/measure/MeasureTreeQuery.java | 17 - .../org/sonar/db/measure/PastMeasureDto.java | 14 - .../ProjectMeasuresIndexerIterator.java | 39 +- .../org/sonar/db/purge/PurgeCommands.java | 34 +- .../sonar/db/purge/PurgeConfiguration.java | 4 +- .../java/org/sonar/db/purge/PurgeDao.java | 1 + .../java/org/sonar/db/purge/PurgeMapper.java | 2 + .../sonar/db/component/ComponentMapper.xml | 3 - .../sonar/db/measure/LiveMeasureMapper.xml | 110 +++++ .../org/sonar/db/measure/MeasureMapper.xml | 105 +---- .../org/sonar/db/purge/PurgeMapper.xml | 15 +- .../test/java/org/sonar/db/DaoModuleTest.java | 2 +- .../sonar/db/component/ComponentDaoTest.java | 1 - .../sonar/db/component/ComponentDtoTest.java | 4 +- .../sonar/db/measure/LiveMeasureDaoTest.java | 146 +++++++ .../org/sonar/db/measure/MeasureDaoTest.java | 361 ++++------------ .../org/sonar/db/measure/MeasureDbTester.java | 12 +- .../sonar/db/measure/MeasureQueryTest.java | 7 - .../org/sonar/db/measure/MeasureTesting.java | 23 +- .../db/measure/MeasureTreeQueryTest.java | 3 - .../sonar/db/measure/PastMeasureDtoTest.java | 10 +- .../ProjectMeasuresIndexerIteratorTest.java | 225 ++++------ .../org/sonar/db/purge/PurgeCommandsTest.java | 2 +- .../java/org/sonar/db/purge/PurgeDaoTest.java | 27 +- .../measure/MeasureDaoTest/insert-result.xml | 2 +- .../past_measures_with_person_id.xml | 59 --- .../with_some_measures_for_developer.xml | 123 ------ ...eleteWastedMeasuresWhenPurgingAnalysis.xml | 19 - .../PurgeCommandsTest/shouldPurgeAnalysis.xml | 1 - .../db/migration/step/BaseSqlStatement.java | 11 + .../db/migration/step/DataChange.java | 5 + .../db/migration/step/SqlStatement.java | 2 + .../version/v70/CreateTableLiveMeasures.java | 125 ++++++ .../db/migration/version/v70/DbVersion70.java | 8 +- .../v70/DeletePersonAndFileMeasures.java | 84 ++++ .../v70/DropIndexOnPersonMeasures.java | 40 ++ .../version/v70/PopulateLiveMeasures.java | 72 +++ .../v70/CreateTableLiveMeasuresTest.java | 59 +++ .../version/v70/DbVersion70Test.java | 2 +- .../v70/DeletePersonAndFileMeasuresTest.java | 125 ++++++ .../v70/DropIndexOnPersonMeasuresTest.java | 48 ++ .../version/v70/PopulateLiveMeasuresTest.java | 140 ++++++ .../v70/CreateTableLiveMeasuresTest/empty.sql | 0 .../initial.sql | 98 +++++ .../DropIndexOnPersonMeasuresTest/initial.sql | 21 + .../v70/PopulateLiveMeasuresTest/initial.sql | 116 +++++ .../sonar/server/branch/ws/ListAction.java | 20 +- .../sonar/server/component/ws/AppAction.java | 34 +- .../computation/dbcleaner/ProjectCleaner.java | 4 +- .../measure/BestValueOptimization.java | 7 +- .../measure/MapBasedRawMeasureRepository.java | 6 - .../measure/MeasureRepository.java | 9 +- .../measure/MeasureRepositoryImpl.java | 44 +- .../measure/MeasureToMeasureDto.java | 27 +- .../step/PersistLiveMeasuresStep.java | 133 ++++++ .../step/PersistMeasuresStep.java | 80 ++-- .../step/ReportComputationSteps.java | 1 + .../server/duplication/ws/ShowAction.java | 11 +- .../server/measure/ws/ComponentAction.java | 53 +-- .../measure/ws/ComponentDtoToWsComponent.java | 6 +- .../measure/ws/ComponentTreeAction.java | 33 +- .../server/measure/ws/ComponentTreeData.java | 8 +- .../measure/ws/ComponentTreeRequest.java | 1 - .../measure/ws/MeasureDtoToWsMeasure.java | 7 + .../measure/ws/MetricDtoWithBestValue.java | 18 +- .../sonar/server/measure/ws/SearchAction.java | 14 +- .../project/ws/SearchMyProjectsAction.java | 23 +- .../project/ws/SearchMyProjectsData.java | 10 +- .../qualitygate/ws/ProjectStatusAction.java | 48 +- .../ws/QualityGateDetailsFormatter.java | 2 +- .../sonar/server/ui/ws/ComponentAction.java | 18 +- .../server/branch/ws/ListActionTest.java | 4 +- .../server/component/ws/AppActionTest.java | 20 +- .../ws/SearchProjectsActionTest.java | 44 +- .../measure/BestValueOptimizationTest.java | 70 +-- .../measure/MeasureRepositoryRule.java | 32 +- .../measure/MeasureToMeasureDtoTest.java | 41 +- .../step/PersistLiveMeasuresStepTest.java | 290 +++++++++++++ .../step/PersistMeasuresStepTest.java | 409 ++++++------------ .../server/duplication/ws/ShowActionTest.java | 7 +- .../measure/ws/ComponentActionTest.java | 199 +++++---- .../measure/ws/ComponentTreeActionTest.java | 141 +++--- .../measure/ws/ComponentTreeSortTest.java | 8 +- .../server/measure/ws/SearchActionTest.java | 385 ++++++----------- .../measure/ws/SearchHistoryActionTest.java | 14 - .../ws/SearchMyProjectsActionTest.java | 6 +- .../ws/ProjectStatusActionTest.java | 86 ++-- .../ws/QualityGateDetailsFormatterTest.java | 4 +- .../server/telemetry/TelemetryDaemonTest.java | 28 +- .../server/ui/ws/ComponentActionTest.java | 19 +- .../java/org/sonar/api/measures/Measure.java | 17 +- .../sonar/api/measures/MeasuresFilters.java | 6 +- .../org/sonar/api/measures/RuleMeasure.java | 3 - .../org/sonar/api/measures/MeasureTest.java | 17 - tests/src/test/java/util/ItUtils.java | 2 +- 110 files changed, 3087 insertions(+), 2165 deletions(-) create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDao.java create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDto.java create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureMapper.java create mode 100644 server/sonar-db-dao/src/main/resources/org/sonar/db/measure/LiveMeasureMapper.xml create mode 100644 server/sonar-db-dao/src/test/java/org/sonar/db/measure/LiveMeasureDaoTest.java delete mode 100644 server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/past_measures_with_person_id.xml delete mode 100644 server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/with_some_measures_for_developer.xml create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasures.java create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasures.java create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasures.java create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasures.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasuresTest.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasuresTest.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasuresTest.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest.java create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasuresTest/empty.sql create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasuresTest/initial.sql create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasuresTest/initial.sql create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest/initial.sql create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistLiveMeasuresStep.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistLiveMeasuresStepTest.java 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 5a9d59c19ed..950cbde71b7 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 @@ -115,7 +115,7 @@ public class ComputeEngineContainerImplTest { assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize( COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION + 26 // level 1 - + 51 // content of DaoModule + + 52 // content of DaoModule + 3 // content of EsSearchModule + 67 // content of CorePropertyDefinitions + 1 // StopFlagContainer diff --git a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java index c01be6b844a..421e67fa5e8 100644 --- a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java +++ b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java @@ -69,6 +69,7 @@ public final class SqTables { "internal_properties", "issues", "issue_changes", + "live_measures", "manual_measures", "metrics", "notifications", diff --git a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl index a4b54efd55e..1e43bd3cfbb 100644 --- a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl +++ b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl @@ -284,6 +284,23 @@ CREATE INDEX "DUPLICATIONS_INDEX_HASH" ON "DUPLICATIONS_INDEX" ("HASH"); CREATE INDEX "DUPLICATION_ANALYSIS_COMPONENT" ON "DUPLICATIONS_INDEX" ("ANALYSIS_UUID", "COMPONENT_UUID"); +CREATE TABLE "LIVE_MEASURES" ( + "UUID" VARCHAR(40) NOT NULL PRIMARY KEY, + "PROJECT_UUID" VARCHAR(50) NOT NULL, + "COMPONENT_UUID" VARCHAR(50) NOT NULL, + "METRIC_ID" INTEGER NOT NULL, + "VALUE" DOUBLE, + "TEXT_VALUE" VARCHAR(4000), + "VARIATION" DOUBLE, + "MEASURE_DATA" BINARY, + "UPDATE_MARKER" VARCHAR(40), + "CREATED_AT" BIGINT NOT NULL, + "UPDATED_AT" BIGINT NOT NULL +); +CREATE INDEX "LIVE_MEASURES_PROJECT" ON "LIVE_MEASURES" ("PROJECT_UUID"); +CREATE UNIQUE INDEX "LIVE_MEASURES_COMPONENT" ON "LIVE_MEASURES" ("COMPONENT_UUID", "METRIC_ID"); + + CREATE TABLE "PROJECT_MEASURES" ( "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), "VALUE" DOUBLE, @@ -304,7 +321,6 @@ CREATE TABLE "PROJECT_MEASURES" ( ); CREATE INDEX "MEASURES_COMPONENT_UUID" ON "PROJECT_MEASURES" ("COMPONENT_UUID"); CREATE INDEX "MEASURES_ANALYSIS_METRIC" ON "PROJECT_MEASURES" ("ANALYSIS_UUID", "METRIC_ID"); -CREATE INDEX "MEASURES_PERSON" ON "PROJECT_MEASURES" ("PERSON_ID"); CREATE TABLE "INTERNAL_PROPERTIES" ( diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java index 06a3216c6c7..e9419b755fb 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java @@ -39,6 +39,7 @@ import org.sonar.db.es.EsQueueDao; import org.sonar.db.event.EventDao; import org.sonar.db.issue.IssueChangeDao; import org.sonar.db.issue.IssueDao; +import org.sonar.db.measure.LiveMeasureDao; import org.sonar.db.measure.MeasureDao; import org.sonar.db.measure.custom.CustomMeasureDao; import org.sonar.db.metric.MetricDao; @@ -92,6 +93,7 @@ public class DaoModule extends Module { ComponentDao.class, ComponentKeyUpdaterDao.class, ComponentLinkDao.class, + LiveMeasureDao.class, CustomMeasureDao.class, DefaultQProfileDao.class, DuplicationDao.class, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java index 1cc3778af49..ce3e71e8087 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java @@ -37,6 +37,7 @@ import org.sonar.db.es.EsQueueDao; import org.sonar.db.event.EventDao; import org.sonar.db.issue.IssueChangeDao; import org.sonar.db.issue.IssueDao; +import org.sonar.db.measure.LiveMeasureDao; import org.sonar.db.measure.MeasureDao; import org.sonar.db.measure.custom.CustomMeasureDao; import org.sonar.db.metric.MetricDao; @@ -130,6 +131,7 @@ public class DbClient { private final AnalysisPropertiesDao analysisPropertiesDao; private final QProfileEditUsersDao qProfileEditUsersDao; private final QProfileEditGroupsDao qProfileEditGroupsDao; + private final LiveMeasureDao liveMeasureDao; public DbClient(Database database, MyBatis myBatis, DBSessions dbSessions, Dao... daos) { this.database = database; @@ -191,6 +193,7 @@ public class DbClient { analysisPropertiesDao = getDao(map, AnalysisPropertiesDao.class); qProfileEditUsersDao = getDao(map, QProfileEditUsersDao.class); qProfileEditGroupsDao = getDao(map, QProfileEditGroupsDao.class); + liveMeasureDao = getDao(map, LiveMeasureDao.class); } public DbSession openSession(boolean batch) { @@ -405,6 +408,10 @@ public class DbClient { return qProfileEditGroupsDao; } + public LiveMeasureDao liveMeasureDao() { + return liveMeasureDao; + } + protected K getDao(Map map, Class clazz) { return (K) map.get(clazz); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java index 03f2eb1a556..0310390b272 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java @@ -62,6 +62,7 @@ import org.sonar.db.issue.IssueChangeMapper; import org.sonar.db.issue.IssueDto; import org.sonar.db.issue.IssueMapper; import org.sonar.db.issue.ShortBranchIssueDto; +import org.sonar.db.measure.LiveMeasureMapper; import org.sonar.db.measure.MeasureDto; import org.sonar.db.measure.MeasureMapper; import org.sonar.db.measure.custom.CustomMeasureDto; @@ -207,6 +208,7 @@ public class MyBatis implements Startable { ComponentKeyUpdaterMapper.class, ComponentLinkMapper.class, ComponentMapper.class, + LiveMeasureMapper.class, CustomMeasureMapper.class, DefaultQProfileMapper.class, DuplicationMapper.class, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java index 9e69d55f2d9..65e14a2d8a8 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java @@ -138,7 +138,6 @@ public class ComponentDto { private String moduleUuid; private String moduleUuidPath; private String copyComponentUuid; - private String developerUuid; private String scope; private String qualifier; private String path; @@ -401,16 +400,6 @@ public class ComponentDto { return this; } - @CheckForNull - public String getDeveloperUuid() { - return developerUuid; - } - - public ComponentDto setDeveloperUuid(@Nullable String developerUuid) { - this.developerUuid = developerUuid; - return this; - } - public Date getCreatedAt() { return createdAt; } @@ -488,7 +477,6 @@ public class ComponentDto { .append("rootUuid", rootUuid) .append("mainBranchProjectUuid", mainBranchProjectUuid) .append("copyComponentUuid", copyComponentUuid) - .append("developerUuid", developerUuid) .append("path", path) .append("deprecatedKey", deprecatedKey) .append("name", name) @@ -513,7 +501,6 @@ public class ComponentDto { copy.moduleUuid = moduleUuid; copy.moduleUuidPath = moduleUuidPath; copy.copyComponentUuid = copyComponentUuid; - copy.developerUuid = developerUuid; copy.scope = scope; copy.qualifier = qualifier; copy.path = path; diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ResourceDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ResourceDto.java index b8cc1cf53f6..46b06fe7a48 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ResourceDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ResourceDto.java @@ -45,7 +45,6 @@ public class ResourceDto { private String description; private String language; private String copyComponentUuid; - private String developerUuid; private Date createdAt; public Long getId() { @@ -202,16 +201,6 @@ public class ResourceDto { return this; } - @CheckForNull - public String getDeveloperUuid() { - return developerUuid; - } - - public ResourceDto setDeveloperUuid(@Nullable String developerUuid) { - this.developerUuid = developerUuid; - return this; - } - public Date getCreatedAt() { return createdAt; } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDao.java new file mode 100644 index 00000000000..353e1297327 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDao.java @@ -0,0 +1,100 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.db.measure; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import javax.annotation.Nullable; +import org.apache.ibatis.session.ResultHandler; +import org.sonar.api.utils.System2; +import org.sonar.core.util.Uuids; +import org.sonar.db.Dao; +import org.sonar.db.DbSession; +import org.sonar.db.component.ComponentDto; + +import static java.util.Collections.singletonList; +import static org.sonar.db.DatabaseUtils.executeLargeInputs; + +public class LiveMeasureDao implements Dao { + + private final System2 system2; + + public LiveMeasureDao(System2 system2) { + this.system2 = system2; + } + + public List selectByComponentUuids(DbSession dbSession, Collection largeComponentUuids, Collection metricIds) { + if (largeComponentUuids.isEmpty() || metricIds.isEmpty()) { + return Collections.emptyList(); + } + + return executeLargeInputs( + largeComponentUuids, + componentUuids -> mapper(dbSession).selectByComponentUuidsAndMetricIds(componentUuids, metricIds)); + } + + public List selectByComponentUuidsAndMetricKeys(DbSession dbSession, Collection largeComponentUuids, Collection metricKeys) { + if (largeComponentUuids.isEmpty() || metricKeys.isEmpty()) { + return Collections.emptyList(); + } + + return executeLargeInputs( + largeComponentUuids, + componentUuids -> mapper(dbSession).selectByComponentUuidsAndMetricKeys(componentUuids, metricKeys)); + } + + public Optional selectMeasure(DbSession dbSession, String componentUuid, String metricKey) { + List measures = selectByComponentUuidsAndMetricKeys(dbSession, singletonList(componentUuid), singletonList(metricKey)); + // couple of columns [component_uuid, metric_id] is unique. List can't have more than 1 item. + if (measures.size() == 1) { + return Optional.of(measures.get(0)); + } + return Optional.empty(); + } + + public void selectTreeByQuery(DbSession dbSession, ComponentDto baseComponent, MeasureTreeQuery query, ResultHandler resultHandler) { + if (query.returnsEmpty()) { + return; + } + mapper(dbSession).selectTreeByQuery(query, baseComponent.uuid(), query.getUuidPath(baseComponent), resultHandler); + } + + public void insert(DbSession dbSession, LiveMeasureDto dto) { + mapper(dbSession).insert(dto, Uuids.create(), null, system2.now()); + } + + public void insertOrUpdate(DbSession dbSession, LiveMeasureDto dto, @Nullable String marker) { + LiveMeasureMapper mapper = mapper(dbSession); + long now = system2.now(); + if (mapper.update(dto, marker, now) == 0) { + mapper.insert(dto, Uuids.create(), marker, now); + } + } + + public void deleteByProjectUuidExcludingMarker(DbSession dbSession, String projectUuid, String marker) { + mapper(dbSession).deleteByProjectUuidExcludingMarker(projectUuid, marker); + } + + private static LiveMeasureMapper mapper(DbSession dbSession) { + return dbSession.getMapper(LiveMeasureMapper.class); + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDto.java new file mode 100644 index 00000000000..b8b9d78aa49 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDto.java @@ -0,0 +1,141 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.db.measure; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public class LiveMeasureDto { + + private static final int MAX_TEXT_VALUE_LENGTH = 4000; + + private String componentUuid; + private String projectUuid; + private int metricId; + @Nullable + private Double value; + @Nullable + private String textValue; + @Nullable + private byte[] data; + @Nullable + private Double variation; + + public String getComponentUuid() { + return componentUuid; + } + + public LiveMeasureDto setComponentUuid(String s) { + this.componentUuid = s; + return this; + } + + public String getProjectUuid() { + return projectUuid; + } + + public LiveMeasureDto setProjectUuid(String s) { + this.projectUuid = s; + return this; + } + + public int getMetricId() { + return metricId; + } + + public LiveMeasureDto setMetricId(int i) { + this.metricId = i; + return this; + } + + @CheckForNull + public Double getValue() { + return value; + } + + public LiveMeasureDto setValue(@Nullable Double value) { + this.value = value; + return this; + } + + @CheckForNull + public String getTextValue() { + return textValue; + } + + @CheckForNull + public byte[] getData() { + return data; + } + + @CheckForNull + public String getDataAsString() { + if (data != null) { + return new String(data, StandardCharsets.UTF_8); + } + return textValue; + } + + public LiveMeasureDto setData(@Nullable String data) { + if (data == null) { + this.textValue = null; + this.data = null; + } else if (data.length() > MAX_TEXT_VALUE_LENGTH) { + this.textValue = null; + this.data = data.getBytes(StandardCharsets.UTF_8); + } else { + this.textValue = data; + this.data = null; + } + return this; + } + + public LiveMeasureDto setData(@Nullable byte[] data) { + this.textValue = null; + this.data = data; + return this; + } + + @CheckForNull + public Double getVariation() { + return variation; + } + + public LiveMeasureDto setVariation(@Nullable Double variation) { + this.variation = variation; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("LiveMeasureDto{"); + sb.append("componentUuid='").append(componentUuid).append('\''); + sb.append(", projectUuid='").append(projectUuid).append('\''); + sb.append(", metricId=").append(metricId); + sb.append(", value=").append(value); + sb.append(", variation=").append(variation); + sb.append(", textValue='").append(textValue).append('\''); + sb.append(", data=").append(Arrays.toString(data)); + sb.append('}'); + return sb.toString(); + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureMapper.java new file mode 100644 index 00000000000..4cdfa215381 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureMapper.java @@ -0,0 +1,58 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.db.measure; + +import java.util.Collection; +import java.util.List; +import javax.annotation.Nullable; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.session.ResultHandler; + +public interface LiveMeasureMapper { + + List selectByComponentUuidsAndMetricIds( + @Param("componentUuids") List componentUuids, + @Param("metricIds") Collection metricIds); + + List selectByComponentUuidsAndMetricKeys( + @Param("componentUuids") List componentUuids, + @Param("metricKeys") Collection metricKeys); + + void selectTreeByQuery( + @Param("query") MeasureTreeQuery measureQuery, + @Param("baseUuid") String baseUuid, + @Param("baseUuidPath") String baseUuidPath, + ResultHandler resultHandler); + + void insert( + @Param("dto") LiveMeasureDto dto, + @Param("uuid") String uuid, + @Nullable @Param("marker") String marker, + @Param("now") long now); + + int update( + @Param("dto") LiveMeasureDto dto, + @Nullable @Param("marker") String marker, + @Param("now") long now); + + void deleteByProjectUuidExcludingMarker( + @Param("projectUuid") String projectUuid, + @Param("marker") String marker); +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureDao.java index 1656c3229a2..f6ba91fde59 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureDao.java @@ -19,24 +19,24 @@ */ package org.sonar.db.measure; -import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import java.util.Collection; import java.util.List; import java.util.Optional; -import org.apache.ibatis.session.ResultHandler; import org.sonar.db.Dao; import org.sonar.db.DbSession; -import org.sonar.db.component.ComponentDto; import static java.util.Collections.emptyList; import static org.sonar.db.DatabaseUtils.executeLargeInputs; public class MeasureDao implements Dao { - public Optional selectSingle(DbSession dbSession, MeasureQuery query) { - List measures = selectByQuery(dbSession, query); - return Optional.ofNullable(Iterables.getOnlyElement(measures, null)); + public Optional selectLastMeasure(DbSession dbSession, String componentUuid, String metricKey) { + return Optional.ofNullable(mapper(dbSession).selectLastMeasure(componentUuid, metricKey)); + } + + public Optional selectMeasure(DbSession dbSession, String analysisUuid, String componentUuid, String metricKey) { + return Optional.ofNullable(mapper(dbSession).selectMeasure(analysisUuid, componentUuid, metricKey)); } /** @@ -47,10 +47,6 @@ public class MeasureDao implements Dao { * - A list of components in {@link MeasureQuery#componentUuids} with one mandatory project in {@link MeasureQuery#projectUuids} * - One single component in {@link MeasureQuery#componentUuids} *

- * In addition, this method returns measures which are not associated to any developer, unless one is specified in - * {@link MeasureQuery#personId}. - *

- *

* Returned measure can optionally be filtered metric (either by specifying {@link MeasureQuery#metricIds} * or {@link MeasureQuery#metricKeys}). *

@@ -78,13 +74,6 @@ public class MeasureDao implements Dao { return mapper(dbSession).selectByQueryOnSingleComponent(query); } - public void selectTreeByQuery(DbSession dbSession, ComponentDto baseComponent, MeasureTreeQuery query, ResultHandler resultHandler) { - if (query.returnsEmpty()) { - return; - } - mapper(dbSession).selectTreeByQuery(query, baseComponent.uuid(), query.getUuidPath(baseComponent), resultHandler); - } - public List selectPastMeasures(DbSession dbSession, String componentUuid, String analysisUuid, Collection metricIds) { if (metricIds.isEmpty()) { return emptyList(); @@ -107,19 +96,6 @@ public class MeasureDao implements Dao { return mapper(dbSession).selectPastMeasuresOnSeveralAnalyses(query); } - /** - * Used by developer cockpit. - */ - public List selectProjectMeasuresOfDeveloper(DbSession dbSession, long developerId, Collection metricIds) { - return executeLargeInputs( - metricIds, - ids -> mapper(dbSession).selectProjectMeasuresOfDeveloper(developerId, metricIds)); - } - - public List selectByComponentsAndMetrics(DbSession dbSession, Collection componentUuids, Collection metricIds) { - return executeLargeInputs(componentUuids, partitionComponentUuids -> mapper(dbSession).selectByComponentsAndMetrics(partitionComponentUuids, metricIds)); - } - public void insert(DbSession session, MeasureDto measureDto) { mapper(session).insert(measureDto); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureDto.java index fbf5fae3594..d5161b1a117 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureDto.java @@ -36,7 +36,6 @@ public class MeasureDto { private String componentUuid; private String analysisUuid; private int metricId; - private Long developerId; @CheckForNull public Double getValue() { @@ -128,16 +127,6 @@ public class MeasureDto { return this; } - @CheckForNull - public Long getDeveloperId() { - return developerId; - } - - public MeasureDto setDeveloperId(@Nullable Long developerId) { - this.developerId = developerId; - return this; - } - @Override public String toString() { return MoreObjects.toStringHelper(this) @@ -150,7 +139,6 @@ public class MeasureDto { .add("componentUuid", componentUuid) .add("analysisUuid", analysisUuid) .add("metricId", metricId) - .add("developerId", developerId) .toString(); } } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureMapper.java index 4250ac4f067..c059728466e 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureMapper.java @@ -19,31 +19,35 @@ */ package org.sonar.db.measure; -import java.util.Collection; import java.util.List; +import javax.annotation.CheckForNull; import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.session.ResultHandler; public interface MeasureMapper { + @CheckForNull + MeasureDto selectLastMeasure( + @Param("componentUuid") String componentUuid, + @Param("metricKey") String metricKey + ); + + @CheckForNull + MeasureDto selectMeasure( + @Param("analysisUuid") String analysisUuid, + @Param("componentUuid") String componentUuid, + @Param("metricKey") String metricKey + ); + List selectByQueryOnProjects(@Param("query") MeasureQuery query); List selectByQueryOnComponents(@Param("query") MeasureQuery query); List selectByQueryOnSingleComponent(@Param("query") MeasureQuery query); - void selectTreeByQuery(@Param("query") MeasureTreeQuery measureQuery, @Param("baseUuid") String baseUuid, @Param("baseUuidPath") String baseUuidPath, - ResultHandler resultHandler); - - List selectPastMeasuresOnSingleAnalysis(@Param("componentUuid") String componentUuid, @Param("analysisUuid") String analysisUuid, @Param("metricIds") List metricIds); List selectPastMeasuresOnSeveralAnalyses(@Param("query") PastMeasureQuery query); - List selectProjectMeasuresOfDeveloper(@Param("developerId") long developerId, @Param("metricIds") Collection metricIds); - - List selectByComponentsAndMetrics(@Param("componentUuids") List componentUuids, @Param("metricIds") Collection metricIds); - void insert(MeasureDto measureDto); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureQuery.java b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureQuery.java index 0b2685ebbe5..5d074d93da7 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureQuery.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureQuery.java @@ -44,19 +44,15 @@ public class MeasureQuery { @CheckForNull private final Collection metricKeys; - @CheckForNull - private final Long personId; - private MeasureQuery(Builder builder) { - this(builder.analysisUuid, builder.projectUuids, builder.componentUuids, builder.metricIds, builder.metricKeys, builder.personId); + this(builder.analysisUuid, builder.projectUuids, builder.componentUuids, builder.metricIds, builder.metricKeys); } private MeasureQuery(@Nullable String analysisUuid, @Nullable Collection projectUuids, @Nullable Collection componentUuids, @Nullable Collection metricIds, - @Nullable Collection metricKeys, - @Nullable Long personId) { + @Nullable Collection metricKeys) { checkArgument(metricIds == null || metricKeys == null, "Metric IDs and keys must not be set both"); checkArgument(projectUuids != null || componentUuids != null, "At least one filter on component UUID is expected"); checkArgument(componentUuids == null || componentUuids.size() == 1 || (projectUuids != null && projectUuids.size() == 1), @@ -67,7 +63,6 @@ public class MeasureQuery { this.componentUuids = componentUuids; this.metricIds = metricIds; this.metricKeys = metricKeys; - this.personId = personId; } public String getAnalysisUuid() { @@ -104,11 +99,6 @@ public class MeasureQuery { return metricKeys; } - @CheckForNull - public Long getPersonId() { - return personId; - } - public boolean returnsEmpty() { return (projectUuids != null && projectUuids.isEmpty()) || (componentUuids != null && componentUuids.isEmpty()) @@ -141,13 +131,12 @@ public class MeasureQuery { Objects.equals(projectUuids, that.projectUuids) && Objects.equals(componentUuids, that.componentUuids) && Objects.equals(metricIds, that.metricIds) && - Objects.equals(metricKeys, that.metricKeys) && - Objects.equals(personId, that.personId); + Objects.equals(metricKeys, that.metricKeys); } @Override public int hashCode() { - return Objects.hash(analysisUuid, componentUuids, metricIds, metricKeys, personId); + return Objects.hash(analysisUuid, componentUuids, metricIds, metricKeys); } public static Builder builder() { @@ -155,11 +144,11 @@ public class MeasureQuery { } static MeasureQuery copyWithSubsetOfProjectUuids(MeasureQuery query, Collection projectUuids) { - return new MeasureQuery(query.analysisUuid, projectUuids, query.componentUuids, query.metricIds, query.metricKeys, query.personId); + return new MeasureQuery(query.analysisUuid, projectUuids, query.componentUuids, query.metricIds, query.metricKeys); } static MeasureQuery copyWithSubsetOfComponentUuids(MeasureQuery query, Collection componentUuids) { - return new MeasureQuery(query.analysisUuid, query.projectUuids, componentUuids, query.metricIds, query.metricKeys, query.personId); + return new MeasureQuery(query.analysisUuid, query.projectUuids, componentUuids, query.metricIds, query.metricKeys); } public static final class Builder { @@ -168,7 +157,6 @@ public class MeasureQuery { private Collection componentUuids; private Collection metricIds; private Collection metricKeys; - private Long personId; private Builder() { // see MeasureQuery#builder() @@ -230,11 +218,6 @@ public class MeasureQuery { return this; } - public Builder setPersonId(@Nullable Long l) { - this.personId = l; - return this; - } - public MeasureQuery build() { return new MeasureQuery(this); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureTreeQuery.java b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureTreeQuery.java index 04df1a38f17..0f368911b78 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureTreeQuery.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureTreeQuery.java @@ -47,15 +47,11 @@ public class MeasureTreeQuery { @CheckForNull private final Collection metricIds; - @CheckForNull - private final Long personId; - private MeasureTreeQuery(Builder builder) { this.nameOrKeyQuery = builder.nameOrKeyQuery; this.qualifiers = builder.qualifiers == null ? null : newArrayList(builder.qualifiers); this.strategy = requireNonNull(builder.strategy); this.metricIds = builder.metricIds; - this.personId = builder.personId; } @CheckForNull @@ -85,11 +81,6 @@ public class MeasureTreeQuery { return metricIds; } - @CheckForNull - public Long getPersonId() { - return personId; - } - public String getUuidPath(ComponentDto component) { switch (strategy) { case CHILDREN: @@ -120,9 +111,6 @@ public class MeasureTreeQuery { @CheckForNull private Collection metricIds; - @CheckForNull - private Long personId; - private Builder() { } @@ -149,11 +137,6 @@ public class MeasureTreeQuery { return this; } - public Builder setPersonId(@Nullable Long personId) { - this.personId = personId; - return this; - } - public MeasureTreeQuery build() { return new MeasureTreeQuery(this); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/PastMeasureDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/PastMeasureDto.java index 0e5ccfb91aa..324c24a29ff 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/PastMeasureDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/PastMeasureDto.java @@ -31,9 +31,6 @@ public class PastMeasureDto { @CheckForNull private Double value; - @CheckForNull - private Long personId; - public double getValue() { requireNonNull(value); return value; @@ -56,15 +53,4 @@ public class PastMeasureDto { this.metricId = i; return this; } - - @CheckForNull - public Long getPersonId() { - return personId; - } - - PastMeasureDto setPersonId(@Nullable Long l) { - this.personId = l; - return this; - } - } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/ProjectMeasuresIndexerIterator.java b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/ProjectMeasuresIndexerIterator.java index d6cd39815a9..b922e5a9ba2 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/ProjectMeasuresIndexerIterator.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/ProjectMeasuresIndexerIterator.java @@ -69,24 +69,23 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator tags = readDbTags(DatabaseUtils.getString(rs, 7)); - Project project = new Project(orgUuid, uuid, key, name, tags, analysisUuid, analysisDate); + Long analysisDate = DatabaseUtils.getLong(rs, 5); + List tags = readDbTags(DatabaseUtils.getString(rs, 6)); + Project project = new Project(orgUuid, uuid, key, name, tags, analysisDate); projects.add(project); } return projects; @@ -165,20 +163,16 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator value = metricKey.startsWith("new_") ? getDouble(rs, FIELD_MEASURE_VARIATION_VALUE_1) : getDouble(rs, FIELD_MEASURE_VALUE); + Optional value = metricKey.startsWith("new_") ? getDouble(rs, FIELD_MEASURE_VARIATION) : getDouble(rs, FIELD_MEASURE_VALUE); if (value.isPresent()) { measures.addNumericMeasure(metricKey, value.get()); return; @@ -239,17 +233,15 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator tags; - public Project(String organizationUuid, String uuid, String key, String name, List tags, @Nullable String analysisUuid, @Nullable Long analysisDate) { + public Project(String organizationUuid, String uuid, String key, String name, List tags, @Nullable Long analysisDate) { this.organizationUuid = organizationUuid; this.uuid = uuid; this.key = key; this.name = name; this.tags = tags; - this.analysisUuid = analysisUuid; this.analysisDate = analysisDate; } @@ -273,11 +265,6 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator> analysisUuidsPartitions = Lists.partition(IdUuidPairs.uuids(purgeMapper.selectAnalysisIdsAndUuids(new PurgeSnapshotQuery().setComponentUuid(rootUuid))), - MAX_SNAPSHOTS_PER_QUERY); + List> analysisUuidsPartitions = Lists.partition(IdUuidPairs.uuids( + purgeMapper.selectAnalysisIdsAndUuids(new PurgeSnapshotQuery().setComponentUuid(rootComponentUuid))), MAX_SNAPSHOTS_PER_QUERY); deleteAnalysisDuplications(analysisUuidsPartitions); @@ -84,9 +82,10 @@ class PurgeCommands { } void deleteAnalyses(PurgeSnapshotQuery... queries) { - List snapshotIds = from(asList(queries)) - .transformAndConcat(purgeMapper::selectAnalysisIdsAndUuids) - .toList(); + List snapshotIds = Arrays.stream(queries) + .flatMap(q -> purgeMapper.selectAnalysisIdsAndUuids(q).stream()) + .collect(Collectors.toList()); + deleteAnalyses(snapshotIds); } @@ -124,9 +123,11 @@ class PurgeCommands { profiler.start("deleteSnapshotWastedMeasures (project_measures)"); List metricIdsWithoutHistoricalData = purgeMapper.selectMetricIdsWithoutHistoricalData(); - analysisUuidsPartitions - .forEach(analysisUuidsPartition -> purgeMapper.deleteAnalysisWastedMeasures(analysisUuidsPartition, metricIdsWithoutHistoricalData)); - session.commit(); + if (!metricIdsWithoutHistoricalData.isEmpty()) { + analysisUuidsPartitions + .forEach(analysisUuidsPartition -> purgeMapper.deleteAnalysisWastedMeasures(analysisUuidsPartition, metricIdsWithoutHistoricalData)); + session.commit(); + } profiler.stop(); profiler.start("updatePurgeStatusToOne (snapshots)"); @@ -272,4 +273,11 @@ class PurgeCommands { session.commit(); profiler.stop(); } + + void deleteLiveMeasures(String rootUuid) { + profiler.start("deleteLiveMeasures (live_measures)"); + purgeMapper.deleteLiveMeasuresByProjectUuid(rootUuid); + session.commit(); + profiler.stop(); + } } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeConfiguration.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeConfiguration.java index c9d903f883f..245c23c7c6a 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeConfiguration.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeConfiguration.java @@ -49,12 +49,12 @@ public class PurgeConfiguration { this.maxAgeInDaysOfInactiveShortLivingBranches = maxAgeInDaysOfInactiveShortLivingBranches; } - public static PurgeConfiguration newDefaultPurgeConfiguration(Configuration config, IdUuidPair idUuidPair, Collection disabledComponentUuids) { + public static PurgeConfiguration newDefaultPurgeConfiguration(Configuration config, IdUuidPair rootId, Collection disabledComponentUuids) { String[] scopes = new String[] {Scopes.FILE}; if (config.getBoolean(PurgeConstants.PROPERTY_CLEAN_DIRECTORY).orElse(false)) { scopes = new String[] {Scopes.DIRECTORY, Scopes.FILE}; } - return new PurgeConfiguration(idUuidPair, scopes, config.getInt(PurgeConstants.DAYS_BEFORE_DELETING_CLOSED_ISSUES).get(), + return new PurgeConfiguration(rootId, scopes, config.getInt(PurgeConstants.DAYS_BEFORE_DELETING_CLOSED_ISSUES).get(), config.getInt(PurgeConstants.DAYS_BEFORE_DELETING_INACTIVE_SHORT_LIVING_BRANCHES), System2.INSTANCE, disabledComponentUuids); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java index de29166e268..cad8376dc39 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java @@ -204,6 +204,7 @@ public class PurgeDao implements Dao { commands.deleteCeQueue(rootUuid); commands.deleteWebhookDeliveries(rootUuid); commands.deleteBranch(rootUuid); + commands.deleteLiveMeasures(rootUuid); } /** diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java index 3961aa6a335..c6393dc4e86 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java @@ -97,4 +97,6 @@ public interface PurgeMapper { void deleteWebhookDeliveriesByProjectUuid(@Param("projectUuid") String projectUuid); void deleteBranchByUuid(@Param("uuid") String uuid); + + void deleteLiveMeasuresByProjectUuid(@Param("projectUuid") String projectUuid); } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml index 5d7e051032f..22991a65e29 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml @@ -24,7 +24,6 @@ p.path as path, p.enabled as enabled, p.copy_component_uuid as copyComponentUuid, - p.developer_uuid as developerUuid, p.private as isPrivate, p.created_at as createdAt @@ -512,7 +511,6 @@ root_uuid, path, copy_component_uuid, - developer_uuid, enabled, created_at, b_changed, @@ -548,7 +546,6 @@ #{rootUuid,jdbcType=VARCHAR}, #{path,jdbcType=VARCHAR}, #{copyComponentUuid,jdbcType=VARCHAR}, - #{developerUuid,jdbcType=VARCHAR}, #{enabled,jdbcType=BOOLEAN}, #{createdAt,jdbcType=TIMESTAMP}, ${_false}, diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/measure/LiveMeasureMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/measure/LiveMeasureMapper.xml new file mode 100644 index 00000000000..57eb8f87008 --- /dev/null +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/measure/LiveMeasureMapper.xml @@ -0,0 +1,110 @@ + + + + + + + lm.component_uuid as componentUuid, + lm.project_uuid as projectUuid, + lm.metric_id as metricId, + lm.value as value, + lm.text_value as textValue, + lm.measure_data as data, + lm.variation as variation + + + + + + + + insert into live_measures ( + uuid, + component_uuid, + project_uuid, + metric_id, + value, + text_value, + variation, + measure_data, + update_marker, + created_at, + updated_at + ) values ( + #{uuid, jdbcType=VARCHAR}, + #{dto.componentUuid, jdbcType=VARCHAR}, + #{dto.projectUuid, jdbcType=VARCHAR}, + #{dto.metricId, jdbcType=INTEGER}, + #{dto.value, jdbcType=DOUBLE}, + #{dto.textValue, jdbcType=VARCHAR}, + #{dto.variation, jdbcType=DOUBLE}, + #{dto.data, jdbcType=BINARY}, + #{marker, jdbcType=VARCHAR}, + #{now, jdbcType=BIGINT}, + #{now, jdbcType=BIGINT} + ) + + + + update live_measures set + value = #{dto.value, jdbcType=DOUBLE}, + variation = #{dto.variation, jdbcType=DOUBLE}, + text_value = #{dto.textValue, jdbcType=VARCHAR}, + measure_data = #{dto.data, jdbcType=BINARY}, + update_marker = #{marker, jdbcType=VARCHAR}, + updated_at = #{now, jdbcType=BIGINT} + where + component_uuid = #{dto.componentUuid, jdbcType=VARCHAR} + and metric_id = #{dto.metricId, jdbcType=INTEGER} + + + + delete from live_measures + where + project_uuid = #{projectUuid, jdbcType=VARCHAR} and + (update_marker != #{marker, jdbcType=VARCHAR} or update_marker is null) + + + + diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/measure/MeasureMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/measure/MeasureMapper.xml index 860b4e73f47..4dd4ecd7504 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/measure/MeasureMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/measure/MeasureMapper.xml @@ -5,7 +5,6 @@ pm.metric_id as metricId, - pm.person_id as developerId, pm.component_uuid as componentUuid, pm.analysis_uuid as analysisUuid, pm.value as value, @@ -16,10 +15,27 @@ pm.variation_value_1 as variation - - , - metric.name as metricKey - + + + - select from project_measures pm - inner join snapshots analysis on analysis.uuid = pm.analysis_uuid - inner join projects p on p.project_uuid=analysis.component_uuid and p.uuid=pm.component_uuid - - - - - -- Add measures of base component - union all - select from project_measures pm - inner join snapshots analysis on analysis.uuid = pm.analysis_uuid - inner join projects p on p.project_uuid=analysis.component_uuid and p.uuid=pm.component_uuid and pm.component_uuid=#{baseUuid} - - - - - - - and analysis.islast=${_true} - - and pm.metric_id in - #{metricId} - - - - and pm.person_id = #{query.personId,jdbcType=BIGINT} - - - and pm.person_id is null - - - - - - - insert into project_measures ( value, @@ -191,7 +132,6 @@ text_value, alert_status, alert_text, - person_id, variation_value_1, measure_data) VALUES ( @@ -202,7 +142,6 @@ #{textValue, jdbcType=VARCHAR}, #{alertStatus, jdbcType=VARCHAR}, #{alertText, jdbcType=VARCHAR}, - #{developerId, jdbcType=INTEGER}, #{variation, jdbcType=DOUBLE}, #{dataValue, jdbcType=BINARY} ) diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml index 36f668e5626..471bd59dbb1 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml @@ -151,14 +151,10 @@ #{analysisUuid} - and (person_id is not null - - or metric_id in - - #{metricId} - - - ) + and metric_id in + + #{metricId,jdbcType=INTEGER} + @@ -332,5 +328,8 @@ delete from project_branches where uuid=#{uuid,jdbcType=VARCHAR} + + delete from live_measures where project_uuid = #{projectUuid,jdbcType=VARCHAR} + diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java index d79b8f289e8..5ff22c1e2fd 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java @@ -30,6 +30,6 @@ public class DaoModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new DaoModule().configure(container); - assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 51); + assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 52); } } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java index 140a3a0bfd3..7ffbbb8371e 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java @@ -111,7 +111,6 @@ public class ComponentDaoTest { assertThat(result.scope()).isEqualTo("PRJ"); assertThat(result.language()).isNull(); assertThat(result.getCopyResourceUuid()).isNull(); - assertThat(result.getDeveloperUuid()).isNull(); assertThat(result.isPrivate()).isTrue(); assertThat(underTest.selectByUuid(dbSession, "UNKNOWN")).isAbsent(); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDtoTest.java index 143f2abdf43..f79ea9c4e70 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDtoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDtoTest.java @@ -42,8 +42,7 @@ public class ComponentDtoTest { .setDescription("desc") .setPath("src/org/struts/RequestContext.java") .setCopyComponentUuid("uuid_5") - .setRootUuid("uuid_3") - .setDeveloperUuid("uuid_6"); + .setRootUuid("uuid_3"); assertThat(componentDto.getId()).isEqualTo(1L); assertThat(componentDto.getDbKey()).isEqualTo("org.struts:struts-core:src/org/struts/RequestContext.java"); @@ -58,7 +57,6 @@ public class ComponentDtoTest { assertThat(componentDto.description()).isEqualTo("desc"); assertThat(componentDto.getRootUuid()).isEqualTo("uuid_3"); assertThat(componentDto.getCopyResourceUuid()).isEqualTo("uuid_5"); - assertThat(componentDto.getDeveloperUuid()).isEqualTo("uuid_6"); assertThat(componentDto.isPrivate()).isFalse(); } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/LiveMeasureDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/LiveMeasureDaoTest.java new file mode 100644 index 00000000000..c4ab865c43f --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/LiveMeasureDaoTest.java @@ -0,0 +1,146 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.db.measure; + +import java.util.List; +import org.assertj.core.groups.Tuple; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.db.DbTester; +import org.sonar.db.metric.MetricDto; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.db.measure.MeasureTesting.newLiveMeasure; + +public class LiveMeasureDaoTest { + + private static final int A_METRIC_ID = 42; + + @Rule + public DbTester db = DbTester.create(System2.INSTANCE); + + private LiveMeasureDao underTest = db.getDbClient().liveMeasureDao(); + + @Test + public void test_selectByComponentUuids() { + LiveMeasureDto measure1 = newLiveMeasure().setMetricId(A_METRIC_ID); + LiveMeasureDto measure2 = newLiveMeasure().setMetricId(A_METRIC_ID); + underTest.insert(db.getSession(), measure1); + underTest.insert(db.getSession(), measure2); + + List selected = underTest.selectByComponentUuids(db.getSession(), asList(measure1.getComponentUuid(), measure2.getComponentUuid()), singletonList(A_METRIC_ID)); + assertThat(selected) + .extracting(LiveMeasureDto::getComponentUuid, LiveMeasureDto::getProjectUuid, LiveMeasureDto::getMetricId, LiveMeasureDto::getValue, LiveMeasureDto::getDataAsString) + .containsExactlyInAnyOrder( + Tuple.tuple(measure1.getComponentUuid(), measure1.getProjectUuid(), measure1.getMetricId(), measure1.getValue(), measure1.getDataAsString()), + Tuple.tuple(measure2.getComponentUuid(), measure2.getProjectUuid(), measure2.getMetricId(), measure2.getValue(), measure2.getDataAsString())); + } + + @Test + public void selectByComponentUuids_returns_empty_list_if_metric_does_not_match() { + LiveMeasureDto measure = newLiveMeasure().setMetricId(10); + underTest.insert(db.getSession(), measure); + + List selected = underTest.selectByComponentUuids(db.getSession(), singletonList(measure.getComponentUuid()), singletonList(222)); + + assertThat(selected).isEmpty(); + } + + @Test + public void selectByComponentUuids_returns_empty_list_if_component_does_not_match() { + LiveMeasureDto measure = newLiveMeasure(); + underTest.insert(db.getSession(), measure); + + List selected = underTest.selectByComponentUuids(db.getSession(), singletonList("_missing_"), singletonList(measure.getMetricId())); + + assertThat(selected).isEmpty(); + } + + @Test + public void test_selectMeasure() { + MetricDto metric = db.measures().insertMetric(); + LiveMeasureDto stored = newLiveMeasure().setMetricId(metric.getId()); + underTest.insert(db.getSession(), stored); + + // metric exists but not component + assertThat(underTest.selectMeasure(db.getSession(), "_missing_", metric.getKey())).isEmpty(); + + // component exists but not metric + assertThat(underTest.selectMeasure(db.getSession(), stored.getComponentUuid(), "_missing_")).isEmpty(); + + // component and metric don't match + assertThat(underTest.selectMeasure(db.getSession(), "_missing_", "_missing_")).isEmpty(); + + // matches + assertThat(underTest.selectMeasure(db.getSession(), stored.getComponentUuid(), metric.getKey()).get()) + .isEqualToComparingFieldByField(stored); + } + + @Test + public void test_insertOrUpdate() { + // insert + LiveMeasureDto dto = newLiveMeasure(); + underTest.insertOrUpdate(db.getSession(), dto, "foo"); + verifyPersisted(dto); + verifyTableSize(1); + + // update + dto.setValue(dto.getValue() + 1); + dto.setVariation(dto.getVariation() + 10); + dto.setData(dto.getDataAsString() + "_new"); + underTest.insertOrUpdate(db.getSession(), dto, "foo"); + verifyPersisted(dto); + verifyTableSize(1); + } + + @Test + public void deleteByProjectUuidExcludingMarker() { + LiveMeasureDto measure1 = newLiveMeasure().setProjectUuid("P1"); + LiveMeasureDto measure2 = newLiveMeasure().setProjectUuid("P1"); + LiveMeasureDto measure3DifferentMarker = newLiveMeasure().setProjectUuid("P1"); + LiveMeasureDto measure4NoMarker = newLiveMeasure().setProjectUuid("P1"); + LiveMeasureDto measure5OtherProject = newLiveMeasure().setProjectUuid("P2"); + underTest.insertOrUpdate(db.getSession(), measure1, "foo"); + underTest.insertOrUpdate(db.getSession(), measure2, "foo"); + underTest.insertOrUpdate(db.getSession(), measure3DifferentMarker, "bar"); + underTest.insertOrUpdate(db.getSession(), measure4NoMarker, null); + underTest.insertOrUpdate(db.getSession(), measure5OtherProject, "foo"); + + underTest.deleteByProjectUuidExcludingMarker(db.getSession(), "P1", "foo"); + + verifyTableSize(3); + verifyPersisted(measure1); + verifyPersisted(measure2); + verifyPersisted(measure5OtherProject); + } + + private void verifyTableSize(int expectedSize) { + assertThat(db.countRowsOfTable(db.getSession(), "live_measures")).isEqualTo(expectedSize); + } + + private void verifyPersisted(LiveMeasureDto dto) { + List selected = underTest.selectByComponentUuids(db.getSession(), singletonList(dto.getComponentUuid()), singletonList(dto.getMetricId())); + assertThat(selected).hasSize(1); + assertThat(selected.get(0)).isEqualToComparingFieldByField(dto); + } +} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureDaoTest.java index 40067b96321..05d9444c1af 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureDaoTest.java @@ -19,45 +19,34 @@ */ package org.sonar.db.measure; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.Optional; -import javax.annotation.Nullable; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.utils.System2; -import org.sonar.core.util.UuidFactoryImpl; +import org.sonar.core.util.Uuids; 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.metric.MetricDto; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; -import static org.sonar.api.resources.Qualifiers.FILE; -import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE; import static org.sonar.api.utils.DateUtils.parseDate; 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; public class MeasureDaoTest { private static final int COVERAGE_METRIC_ID = 10; private static final int COMPLEXITY_METRIC_ID = 11; private static final int NCLOC_METRIC_ID = 12; - private static final long A_PERSON_ID = 444L; - private static final String LAST_ANALYSIS_UUID = "A1"; - private static final String OTHER_ANALYSIS_UUID = "A2"; - private static final String PREVIOUS_ANALYSIS_UUID = "previous analysis UUID"; @Rule public ExpectedException expectedException = ExpectedException.none(); @@ -70,37 +59,48 @@ public class MeasureDaoTest { private MeasureDao underTest = db.getDbClient().measureDao(); @Test - public void test_inserted_and_selected_columns() { + public void test_selectLastMeasure() { + MetricDto metric = db.measures().insertMetric(); ComponentDto project = db.components().insertPrivateProject(); - insertAnalysis(LAST_ANALYSIS_UUID, project.uuid(), true); - db.components().insertComponent(newFileDto(project).setUuid("C4")); - - MeasureDto inserted = new MeasureDto() - .setAnalysisUuid(LAST_ANALYSIS_UUID) - .setMetricId(2) - .setDeveloperId(3L) - .setComponentUuid("C4") - .setValue(5.0d) - .setData("data") - .setVariation(1d) - .setAlertStatus("alert") - .setAlertText("alert-text"); - underTest.insert(db.getSession(), inserted); - db.commit(); + ComponentDto file = db.components().insertComponent(newFileDto(project)); + SnapshotDto lastAnalysis = insertAnalysis(project.uuid(), true); + SnapshotDto pastAnalysis = insertAnalysis(project.uuid(), false); + + MeasureDto pastMeasure = MeasureTesting.newMeasureDto(metric, file, pastAnalysis); + MeasureDto lastMeasure = MeasureTesting.newMeasureDto(metric, file, lastAnalysis); + underTest.insert(db.getSession(), pastMeasure); + underTest.insert(db.getSession(), lastMeasure); + + MeasureDto selected = underTest.selectLastMeasure(db.getSession(), file.uuid(), metric.getKey()).get(); + assertThat(selected).isEqualToComparingFieldByField(lastMeasure); + + assertThat(underTest.selectLastMeasure(dbSession, "_missing_", metric.getKey())).isEmpty(); + assertThat(underTest.selectLastMeasure(dbSession, file.uuid(), "_missing_")).isEmpty(); + assertThat(underTest.selectLastMeasure(dbSession, "_missing_", "_missing_")).isEmpty(); + } + + @Test + public void test_selectMeasure() { + MetricDto metric = db.measures().insertMetric(); + ComponentDto project = db.components().insertPrivateProject(); + ComponentDto file = db.components().insertComponent(newFileDto(project)); + SnapshotDto lastAnalysis = insertAnalysis(project.uuid(), true); + SnapshotDto pastAnalysis = insertAnalysis(project.uuid(), false); + + MeasureDto pastMeasure = MeasureTesting.newMeasureDto(metric, file, pastAnalysis); + MeasureDto lastMeasure = MeasureTesting.newMeasureDto(metric, file, lastAnalysis); + underTest.insert(db.getSession(), pastMeasure); + underTest.insert(db.getSession(), lastMeasure); + + assertThat(underTest.selectMeasure(db.getSession(), lastAnalysis.getUuid(), file.uuid(), metric.getKey()).get()) + .isEqualToComparingFieldByField(lastMeasure); + + assertThat(underTest.selectMeasure(db.getSession(), pastAnalysis.getUuid(), file.uuid(), metric.getKey()).get()) + .isEqualToComparingFieldByField(pastMeasure); - MeasureDto selected = underTest.selectSingle(db.getSession(), MeasureQuery.builder() - .setComponentUuid(inserted.getComponentUuid()) - .setPersonId(inserted.getDeveloperId()) - .build()).get(); - assertThat(selected.getAnalysisUuid()).isEqualTo(inserted.getAnalysisUuid()); - assertThat(selected.getMetricId()).isEqualTo(inserted.getMetricId()); - assertThat(selected.getDeveloperId()).isEqualTo(inserted.getDeveloperId()); - assertThat(selected.getComponentUuid()).isEqualTo(inserted.getComponentUuid()); - assertThat(selected.getValue()).isEqualTo(inserted.getValue()); - assertThat(selected.getData()).isEqualTo(inserted.getData()); - assertThat(selected.getVariation()).isEqualTo(inserted.getVariation()); - assertThat(selected.getAlertStatus()).isEqualTo(inserted.getAlertStatus()); - assertThat(selected.getAlertText()).isEqualTo(inserted.getAlertText()); + assertThat(underTest.selectMeasure(db.getSession(), "_missing_", file.uuid(), metric.getKey())).isEmpty(); + assertThat(underTest.selectMeasure(db.getSession(), pastAnalysis.getUuid(), "_missing_", metric.getKey())).isEmpty(); + assertThat(underTest.selectMeasure(db.getSession(), pastAnalysis.getUuid(), file.uuid(), "_missing_")).isEmpty(); } @Test @@ -109,28 +109,25 @@ public class MeasureDaoTest { ComponentDto module = db.components().insertComponent(newModuleDto(project1)); db.components().insertComponent(newFileDto(module).setUuid("C1")); db.components().insertComponent(newFileDto(module).setUuid("C2")); - insertAnalysis(LAST_ANALYSIS_UUID, project1.uuid(), true); - insertAnalysis(OTHER_ANALYSIS_UUID, project1.uuid(), false); + SnapshotDto lastAnalysis = insertAnalysis(project1.uuid(), true); + SnapshotDto pastAnalysis = insertAnalysis(project1.uuid(), false); - String project2LastAnalysisUuid = "P2_LAST_ANALYSIS"; ComponentDto project2 = db.components().insertPrivateProject(); - insertAnalysis(project2LastAnalysisUuid, project2.uuid(), true); + SnapshotDto project2LastAnalysis = insertAnalysis(project2.uuid(), true); // project 1 - insertMeasure("P1_M1", LAST_ANALYSIS_UUID, project1.uuid(), NCLOC_METRIC_ID); - insertMeasure("P1_M2", LAST_ANALYSIS_UUID, project1.uuid(), COVERAGE_METRIC_ID); - insertMeasure("P1_M3", OTHER_ANALYSIS_UUID, project1.uuid(), NCLOC_METRIC_ID); + insertMeasure("P1_M1", lastAnalysis.getUuid(), project1.uuid(), NCLOC_METRIC_ID); + insertMeasure("P1_M2", lastAnalysis.getUuid(), project1.uuid(), COVERAGE_METRIC_ID); + insertMeasure("P1_M3", pastAnalysis.getUuid(), project1.uuid(), NCLOC_METRIC_ID); // project 2 - insertMeasure("P2_M1", project2LastAnalysisUuid, project2.uuid(), NCLOC_METRIC_ID); - insertMeasure("P2_M2", project2LastAnalysisUuid, project2.uuid(), COVERAGE_METRIC_ID); + insertMeasure("P2_M1", project2LastAnalysis.getUuid(), project2.uuid(), NCLOC_METRIC_ID); + insertMeasure("P2_M2", project2LastAnalysis.getUuid(), project2.uuid(), COVERAGE_METRIC_ID); // component C1 - insertMeasure("M1", OTHER_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID); - insertMeasure("M2", LAST_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID); - insertMeasure("M3", LAST_ANALYSIS_UUID, "C1", COVERAGE_METRIC_ID); - insertMeasureOnPerson("M4", LAST_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID, A_PERSON_ID); - insertMeasureOnPerson("M5", OTHER_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID, 123L); + insertMeasure("M1", pastAnalysis.getUuid(), "C1", NCLOC_METRIC_ID); + insertMeasure("M2", lastAnalysis.getUuid(), "C1", NCLOC_METRIC_ID); + insertMeasure("M3", lastAnalysis.getUuid(), "C1", COVERAGE_METRIC_ID); // component C2 - insertMeasure("M6", LAST_ANALYSIS_UUID, "C2", NCLOC_METRIC_ID); + insertMeasure("M6", lastAnalysis.getUuid(), "C2", NCLOC_METRIC_ID); db.commit(); verifyZeroMeasures(MeasureQuery.builder().setComponentUuids(project1.uuid(), emptyList())); @@ -141,51 +138,37 @@ public class MeasureDaoTest { // all measures of component C1 of last analysis verifyMeasures(MeasureQuery.builder().setComponentUuid("C1"), "M2", "M3"); // all measures of component C1 of non last analysis - verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID), "M1"); + verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(pastAnalysis.getUuid()), "M1"); // all measures of component C1 of last analysis by UUID - verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID), "M2", "M3"); + verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(lastAnalysis.getUuid()), "M2", "M3"); // ncloc measure of component C1 of last analysis verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setMetricId(NCLOC_METRIC_ID), "M2"); // ncloc measure of component C1 of non last analysis - verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID).setMetricId(NCLOC_METRIC_ID), "M1"); + verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(pastAnalysis.getUuid()).setMetricId(NCLOC_METRIC_ID), "M1"); // ncloc measure of component C1 of last analysis by UUID - verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID).setMetricId(NCLOC_METRIC_ID), "M2"); + verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(lastAnalysis.getUuid()).setMetricId(NCLOC_METRIC_ID), "M2"); // multiple measures of component C1 of last analysis verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setMetricIds(asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID)), "M2", "M3"); // multiple measures of component C1 of non last analysis - verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID).setMetricIds(asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID)), "M1"); + verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(pastAnalysis.getUuid()).setMetricIds(asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID)), "M1"); // multiple measures of component C1 of last analysis by UUID - verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID).setMetricIds(asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID)), "M2", "M3"); + verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(lastAnalysis.getUuid()).setMetricIds(asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID)), "M2", "M3"); // missing measure of component C1 of last analysis verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setMetricId(COMPLEXITY_METRIC_ID)); // missing measure of component C1 of non last analysis - verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID).setMetricId(COMPLEXITY_METRIC_ID)); + verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(pastAnalysis.getUuid()).setMetricId(COMPLEXITY_METRIC_ID)); // missing measure of component C1 of last analysis by UUID - verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID).setMetricId(COMPLEXITY_METRIC_ID)); + verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(lastAnalysis.getUuid()).setMetricId(COMPLEXITY_METRIC_ID)); // ncloc measures of components C1, C2 and C3 (which does not exist) of last analysis verifyMeasures(MeasureQuery.builder().setComponentUuids(project1.uuid(), asList("C1", "C2", "C3")), "M2", "M3", "M6"); // ncloc measures of components C1, C2 and C3 (which does not exist) of non last analysis - verifyMeasures(MeasureQuery.builder().setComponentUuids(project1.uuid(), asList("C1", "C2", "C3")).setAnalysisUuid(OTHER_ANALYSIS_UUID), "M1"); + verifyMeasures(MeasureQuery.builder().setComponentUuids(project1.uuid(), asList("C1", "C2", "C3")).setAnalysisUuid(pastAnalysis.getUuid()), "M1"); // ncloc measures of components C1, C2 and C3 (which does not exist) of last analysis by UUID - verifyMeasures(MeasureQuery.builder().setComponentUuids(project1.uuid(), asList("C1", "C2", "C3")).setAnalysisUuid(LAST_ANALYSIS_UUID), "M2", "M3", "M6"); - - // measures of missing developer of component C1 of last analysis - verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setPersonId(123L)); - // measures of missing developer of component C1 of non last analysis - verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID).setPersonId(123L), "M5"); - // measures of missing developer of component C1 of last analysis by UUID - verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID).setPersonId(123L)); - - // developer measures of component C1 of last analysis - verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setPersonId(A_PERSON_ID), "M4"); - // developer measures of component C1 of non last analysis - verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID).setPersonId(A_PERSON_ID)); - // developer measures of component C1 of last analysis by UUID - verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID).setPersonId(A_PERSON_ID), "M4"); + verifyMeasures(MeasureQuery.builder().setComponentUuids(project1.uuid(), asList("C1", "C2", "C3")).setAnalysisUuid(lastAnalysis.getUuid()), "M2", "M3", "M6"); // projects measures of last analysis verifyMeasures(MeasureQuery.builder().setProjectUuids(singletonList(project1.uuid())).setMetricId(NCLOC_METRIC_ID), "P1_M1"); @@ -194,113 +177,8 @@ public class MeasureDaoTest { verifyMeasures(MeasureQuery.builder().setProjectUuids(asList(project1.uuid(), project2.uuid(), "UNKNOWN")).setMetricId(NCLOC_METRIC_ID), "P1_M1", "P2_M1"); // projects measures of none last analysis - verifyMeasures(MeasureQuery.builder().setProjectUuids(singletonList(project1.uuid())).setMetricId(NCLOC_METRIC_ID).setAnalysisUuid(OTHER_ANALYSIS_UUID), "P1_M3"); - verifyMeasures(MeasureQuery.builder().setProjectUuids(asList(project1.uuid(), project2.uuid())).setMetricId(NCLOC_METRIC_ID).setAnalysisUuid(OTHER_ANALYSIS_UUID), "P1_M3"); - } - - @Test - public void selectSingle() { - ComponentDto project = db.components().insertPrivateProject(); - db.components().insertComponent(newFileDto(project).setUuid("C1")); - insertAnalysis(LAST_ANALYSIS_UUID, project.uuid(), true); - insertMeasure("M1", LAST_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID); - insertMeasure("M2", LAST_ANALYSIS_UUID, "C1", COMPLEXITY_METRIC_ID); - db.commit(); - - assertThat(selectSingle(MeasureQuery.builder().setComponentUuids(project.uuid(), emptyList()))).isNotPresent(); - assertThat(selectSingle(MeasureQuery.builder().setComponentUuid("MISSING_COMPONENT"))).isNotPresent(); - - // select a single measure - assertThat(selectSingle(MeasureQuery.builder().setComponentUuid("C1").setMetricId(NCLOC_METRIC_ID))).isPresent(); - - // select multiple measures -> fail - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("expected one element"); - selectSingle(MeasureQuery.builder().setComponentUuid("C1")); - } - - @Test - public void select_tree_by_query() { - ComponentDto project = db.components().insertPrivateProject(); - ComponentDto module1 = db.components().insertComponent(newModuleDto(project)); - ComponentDto module2 = db.components().insertComponent(newModuleDto(project)); - ComponentDto file1 = db.components().insertComponent(newFileDto(module1).setUuid("C1").setName("File One")); - db.components().insertComponent(newFileDto(module2).setUuid("C2").setName("File Two").setQualifier(UNIT_TEST_FILE)); - insertAnalysis(LAST_ANALYSIS_UUID, project.uuid(), true); - - // project - insertMeasure("PROJECT_M1", LAST_ANALYSIS_UUID, project.uuid(), NCLOC_METRIC_ID); - // module 1 - insertMeasure("MODULE_M1", LAST_ANALYSIS_UUID, module1.uuid(), NCLOC_METRIC_ID); - // component C1 - insertMeasure("M2", LAST_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID); - insertMeasure("M3", LAST_ANALYSIS_UUID, "C1", COVERAGE_METRIC_ID); - insertMeasureOnPerson("M4", LAST_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID, A_PERSON_ID); - // component C2 - insertMeasure("M6", LAST_ANALYSIS_UUID, "C2", NCLOC_METRIC_ID); - db.commit(); - - // Children measures of project - verifyMeasures(project, MeasureTreeQuery.builder().setStrategy(CHILDREN), "PROJECT_M1", "MODULE_M1"); - - // Children measures of module 1 - verifyMeasures(module1, MeasureTreeQuery.builder().setStrategy(CHILDREN), "M2", "M3", "MODULE_M1"); - - // Children measure on file => only measures from itself - verifyMeasures(file1, MeasureTreeQuery.builder().setStrategy(CHILDREN), "M2", "M3"); - - // Leaves measures of project - verifyMeasures(project, MeasureTreeQuery.builder().setStrategy(LEAVES), "PROJECT_M1", "MODULE_M1", "M2", "M3", "M6"); - - // Leaves measures of module 1 - verifyMeasures(module1, MeasureTreeQuery.builder().setStrategy(LEAVES), "MODULE_M1", "M2", "M3"); - - // Leaves measures of project by metric ids - verifyMeasures(project, MeasureTreeQuery.builder().setMetricIds(asList(NCLOC_METRIC_ID)).setStrategy(LEAVES), "PROJECT_M1", "MODULE_M1", "M2", - "M6"); - - // Leaves measure on file - verifyMeasures(file1, MeasureTreeQuery.builder().setStrategy(LEAVES), "M2", "M3"); - - // Leaves measures of project matching name - verifyMeasures(project, MeasureTreeQuery.builder().setNameOrKeyQuery("OnE").setStrategy(LEAVES), "M2", "M3"); - - // Leaves measures of project matching qualifiers - verifyMeasures(project, MeasureTreeQuery.builder().setQualifiers(asList(FILE)).setStrategy(LEAVES), "M2", "M3"); - verifyMeasures(project, MeasureTreeQuery.builder().setQualifiers(asList(FILE, UNIT_TEST_FILE)).setStrategy(LEAVES), "M2", "M3", "M6"); - } - - @Test - public void select_tree_by_query_use_only_latest_analysis() { - ComponentDto project = db.components().insertPrivateProject(); - ComponentDto file1 = db.components().insertComponent(newFileDto(project).setUuid("C1").setName("File One")); - db.components().insertComponent(newFileDto(project).setUuid("C2").setName("File Two").setQualifier(UNIT_TEST_FILE)); - insertAnalysis(LAST_ANALYSIS_UUID, project.uuid(), true); - insertAnalysis(OTHER_ANALYSIS_UUID, project.uuid(), false); - - // project - insertMeasure("PROJECT_M1", LAST_ANALYSIS_UUID, project.uuid(), NCLOC_METRIC_ID); - insertMeasure("PROJECT_M2", OTHER_ANALYSIS_UUID, project.uuid(), NCLOC_METRIC_ID); - // component C1 - insertMeasure("M2", LAST_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID); - insertMeasure("M3", LAST_ANALYSIS_UUID, "C1", COVERAGE_METRIC_ID); - insertMeasure("M4", OTHER_ANALYSIS_UUID, "C1", COVERAGE_METRIC_ID); - // component C2 - insertMeasure("M5", LAST_ANALYSIS_UUID, "C2", NCLOC_METRIC_ID); - insertMeasure("M6", OTHER_ANALYSIS_UUID, "C2", NCLOC_METRIC_ID); - db.commit(); - - // Children measures of project - verifyMeasures(project, MeasureTreeQuery.builder().setStrategy(CHILDREN), "PROJECT_M1", "M2", "M3", "M5"); - - // Children measure on file => only measures from itself - verifyMeasures(file1, MeasureTreeQuery.builder().setStrategy(CHILDREN), "M2", "M3"); - - // Leaves measures of project - verifyMeasures(project, MeasureTreeQuery.builder().setStrategy(LEAVES), "PROJECT_M1", "M2", "M3", "M5"); - - // Leaves measure on file - verifyMeasures(file1, MeasureTreeQuery.builder().setStrategy(LEAVES), "M2", "M3"); + verifyMeasures(MeasureQuery.builder().setProjectUuids(singletonList(project1.uuid())).setMetricId(NCLOC_METRIC_ID).setAnalysisUuid(pastAnalysis.getUuid()), "P1_M3"); + verifyMeasures(MeasureQuery.builder().setProjectUuids(asList(project1.uuid(), project2.uuid())).setMetricId(NCLOC_METRIC_ID).setAnalysisUuid(pastAnalysis.getUuid()), "P1_M3"); } @Test @@ -309,14 +187,14 @@ public class MeasureDaoTest { 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)); + SnapshotDto lastAnalysis = dbClient.snapshotDao().insert(dbSession, newAnalysis(project).setCreatedAt(lastAnalysisDate)); + SnapshotDto pastAnalysis = dbClient.snapshotDao().insert(dbSession, newAnalysis(project).setCreatedAt(previousAnalysisDate).setLast(false)); + dbClient.snapshotDao().insert(dbSession, newAnalysis(project).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_M1", lastAnalysis.getUuid(), project.uuid(), NCLOC_METRIC_ID); + insertMeasure("PROJECT_M2", pastAnalysis.getUuid(), project.uuid(), NCLOC_METRIC_ID); insertMeasure("PROJECT_M3", "OLD_ANALYSIS_UUID", project.uuid(), NCLOC_METRIC_ID); db.commit(); @@ -327,58 +205,6 @@ public class MeasureDaoTest { assertThat(result).hasSize(2).extracting(MeasureDto::getData).containsOnly("PROJECT_M1", "PROJECT_M2"); } - @Test - public void selectByComponentsAndMetrics() { - ComponentDto project1 = db.components().insertPrivateProject(db.getDefaultOrganization(), "P1"); - ComponentDto module = db.components().insertComponent(newModuleDto(project1)); - db.components().insertComponent(newFileDto(module).setUuid("C1")); - db.components().insertComponent(newFileDto(module).setUuid("C2")); - insertAnalysis(LAST_ANALYSIS_UUID, project1.uuid(), true); - insertAnalysis(OTHER_ANALYSIS_UUID, project1.uuid(), false); - - String project2LastAnalysisUuid = "P2_LAST_ANALYSIS"; - ComponentDto project2 = db.components().insertPrivateProject(db.getDefaultOrganization(), "P2"); - insertAnalysis(project2LastAnalysisUuid, project2.uuid(), true); - - // project 1 - insertMeasure("P1_M1", LAST_ANALYSIS_UUID, project1.uuid(), NCLOC_METRIC_ID); - insertMeasure("P1_M2", LAST_ANALYSIS_UUID, project1.uuid(), COVERAGE_METRIC_ID); - insertMeasure("P1_M3", OTHER_ANALYSIS_UUID, project1.uuid(), NCLOC_METRIC_ID); - // project 2 - insertMeasure("P2_M1", project2LastAnalysisUuid, project2.uuid(), NCLOC_METRIC_ID); - insertMeasure("P2_M2", project2LastAnalysisUuid, project2.uuid(), COVERAGE_METRIC_ID); - // component C1 - insertMeasure("M1", OTHER_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID); - insertMeasure("M2", LAST_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID); - insertMeasure("M3", LAST_ANALYSIS_UUID, "C1", COVERAGE_METRIC_ID); - insertMeasureOnPerson("M4", LAST_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID, A_PERSON_ID); - insertMeasureOnPerson("M5", OTHER_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID, 123L); - // component C2 - insertMeasure("M6", LAST_ANALYSIS_UUID, "C2", NCLOC_METRIC_ID); - db.commit(); - - assertThat(underTest.selectByComponentsAndMetrics(db.getSession(), Collections.emptyList(), Collections.emptyList())).isEmpty(); - - // Measures of component C1 - assertThat(underTest.selectByComponentsAndMetrics(db.getSession(), singletonList("C1"), singletonList(NCLOC_METRIC_ID))).extracting(MeasureDto::getData).containsOnly("M2"); - assertThat(underTest.selectByComponentsAndMetrics(db.getSession(), singletonList("C1"), asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID))).extracting(MeasureDto::getData) - .containsOnly("M2", "M3"); - - // ncloc measures of components C1, C2 - assertThat(underTest.selectByComponentsAndMetrics(db.getSession(), asList("C1", "C2"), asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID))).extracting(MeasureDto::getData) - .containsOnly("M2", "M3", "M6"); - - // projects measures of last analysis - assertThat(underTest.selectByComponentsAndMetrics(db.getSession(), singletonList("P1"), singletonList(NCLOC_METRIC_ID))).extracting(MeasureDto::getData) - .containsOnly("P1_M1"); - assertThat(underTest.selectByComponentsAndMetrics(db.getSession(), asList("P1", "P2"), asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID))).extracting(MeasureDto::getData) - .containsOnly("P1_M1", "P1_M2", "P2_M1", "P2_M2"); - } - - private Optional selectSingle(MeasureQuery.Builder query) { - return underTest.selectSingle(db.getSession(), query.build()); - } - private void verifyMeasures(MeasureQuery.Builder query, String... expectedIds) { List measures = underTest.selectByQuery(db.getSession(), query.build()); assertThat(measures).extracting(MeasureDto::getData).containsOnly(expectedIds); @@ -388,63 +214,22 @@ public class MeasureDaoTest { assertThat(underTest.selectByQuery(db.getSession(), query.build())).isEmpty(); } - private void verifyMeasures(ComponentDto baseComponent, MeasureTreeQuery.Builder measureQuery, String... expectedIds) { - List measures = new ArrayList<>(); - underTest.selectTreeByQuery(db.getSession(), baseComponent, measureQuery.build(), result -> measures.add(result.getResultObject())); - assertThat(measures).extracting(MeasureDto::getData).containsOnly(expectedIds); - } - private void insertMeasure(String id, String analysisUuid, String componentUuid, int metricId) { - insertMeasure(id, analysisUuid, componentUuid, null, metricId); - } - - private void insertMeasure(String id, String analysisUuid, String componentUuid, @Nullable Long developerId, int metricId) { MeasureDto measure = MeasureTesting.newMeasure() .setAnalysisUuid(analysisUuid) .setComponentUuid(componentUuid) .setMetricId(metricId) // as ids can't be forced when inserting measures, the field "data" // is used to store a virtual id. It is used then in assertions. - .setData(id) - .setDeveloperId(developerId); - db.getDbClient().measureDao().insert(db.getSession(), measure); - } - - private String insertComponent(String scope, String qualifier, boolean enabled) { - String uuid = UuidFactoryImpl.INSTANCE.create(); - ComponentDto componentDto = new ComponentDto() - .setOrganizationUuid("org1") - .setUuid(uuid) - .setScope(scope) - .setQualifier(qualifier) - .setProjectUuid("don't care") - .setRootUuid("don't care") - .setUuidPath("don't care") - .setDbKey("kee_" + uuid) - .setEnabled(enabled); - db.getDbClient().componentDao().insert(db.getSession(), componentDto); - return uuid; - } - - private void insertMeasureOnPerson(String id, String analysisUuid, String componentUuid, int metricId, long personId) { - MeasureDto measure = MeasureTesting.newMeasure() - .setAnalysisUuid(analysisUuid) - .setComponentUuid(componentUuid) - .setMetricId(metricId) - .setDeveloperId(personId) - // as ids can't be forced when inserting measures, the field "data" - // is used to store a virtual id. It is used then in assertions. .setData(id); db.getDbClient().measureDao().insert(db.getSession(), measure); } - private SnapshotDto insertAnalysis(String uuid, String projectUuid, boolean isLast) { + private SnapshotDto insertAnalysis(String projectUuid, boolean isLast) { return db.getDbClient().snapshotDao().insert(db.getSession(), SnapshotTesting.newSnapshot() - .setUuid(uuid) + .setUuid(Uuids.createFast()) .setComponentUuid(projectUuid) .setLast(isLast)); } - // TODO test selectPastMeasures - } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureDbTester.java b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureDbTester.java index b1d45914ec3..30ba72b4a30 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureDbTester.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureDbTester.java @@ -28,16 +28,15 @@ import org.sonar.db.component.ComponentDto; import org.sonar.db.component.SnapshotDto; import org.sonar.db.metric.MetricDto; +import static org.sonar.db.measure.MeasureTesting.newLiveMeasure; import static org.sonar.db.measure.MeasureTesting.newMeasureDto; import static org.sonar.db.metric.MetricTesting.newMetricDto; public class MeasureDbTester { - private final DbTester db; private final DbClient dbClient; private final DbSession dbSession; public MeasureDbTester(DbTester db) { - this.db = db; this.dbClient = db.getDbClient(); this.dbSession = db.getSession(); } @@ -51,6 +50,15 @@ public class MeasureDbTester { return measureDto; } + @SafeVarargs + public final LiveMeasureDto insertLiveMeasure(ComponentDto component, MetricDto metric, Consumer... consumers){ + LiveMeasureDto dto = newLiveMeasure(component, metric); + Arrays.stream(consumers).forEach(c -> c.accept(dto)); + dbClient.liveMeasureDao().insert(dbSession, dto); + dbSession.commit(); + return dto; + } + @SafeVarargs public final MetricDto insertMetric(Consumer... consumers){ MetricDto metricDto = newMetricDto(); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureQueryTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureQueryTest.java index 3e083c415bb..959edae309c 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureQueryTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureQueryTest.java @@ -81,13 +81,6 @@ public class MeasureQueryTest { assertThat(query.getMetricIds()).isNull(); } - @Test - public void create_query_from_person_id() { - MeasureQuery query = MeasureQuery.builder().setProjectUuids(asList("PROJECT_1", "PROJECT_2")).setPersonId(100L).build(); - - assertThat(query.getPersonId()).isEqualTo(100L); - } - @Test public void return_empty_when_metrics_are_empty() { assertThat(MeasureQuery.builder() diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureTesting.java b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureTesting.java index 39e7671a281..34f8ba2a927 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureTesting.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureTesting.java @@ -50,10 +50,29 @@ public class MeasureTesting { .setMetricId(cursor++) .setComponentUuid(String.valueOf(cursor++)) .setAnalysisUuid(String.valueOf(cursor++)) - .setDeveloperId(null) .setData(String.valueOf(cursor++)) .setAlertStatus(String.valueOf(cursor++)) .setAlertText(String.valueOf(cursor++)) - .setValue((double)cursor++); + .setValue((double) cursor++); + } + + public static LiveMeasureDto newLiveMeasure() { + return new LiveMeasureDto() + .setMetricId(cursor++) + .setComponentUuid(String.valueOf(cursor++)) + .setProjectUuid(String.valueOf(cursor++)) + .setData(String.valueOf(cursor++)) + .setValue((double) cursor++) + .setVariation((double) cursor++); + } + + public static LiveMeasureDto newLiveMeasure(ComponentDto component, MetricDto metric) { + return new LiveMeasureDto() + .setMetricId(metric.getId()) + .setComponentUuid(component.uuid()) + .setProjectUuid(component.projectUuid()) + .setData(String.valueOf(cursor++)) + .setValue((double) cursor++) + .setVariation((double) cursor++); } } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureTreeQueryTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureTreeQueryTest.java index 925f0765c95..54dbde3ea54 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureTreeQueryTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureTreeQueryTest.java @@ -43,14 +43,12 @@ public class MeasureTreeQueryTest { .setQualifiers(asList("FIL", "DIR")) .setNameOrKeyQuery("teSt") .setMetricIds(asList(10, 11)) - .setPersonId(100L) .build(); assertThat(query.getStrategy()).isEqualTo(CHILDREN); assertThat(query.getQualifiers()).containsOnly("FIL", "DIR"); assertThat(query.getNameOrKeyQuery()).isEqualTo("teSt"); assertThat(query.getMetricIds()).containsOnly(10, 11); - assertThat(query.getPersonId()).isEqualTo(100L); } @Test @@ -63,7 +61,6 @@ public class MeasureTreeQueryTest { assertThat(query.getQualifiers()).isNull(); assertThat(query.getNameOrKeyQuery()).isNull(); assertThat(query.getMetricIds()).isNull(); - assertThat(query.getPersonId()).isNull(); } @Test diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/PastMeasureDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/PastMeasureDtoTest.java index 07a630ad5f7..02fe87598bc 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/PastMeasureDtoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/PastMeasureDtoTest.java @@ -29,26 +29,22 @@ public class PastMeasureDtoTest { public void test_getter_and_setter() throws Exception { PastMeasureDto dto = new PastMeasureDto() .setValue(1d) - .setMetricId(2) - .setPersonId(5L); + .setMetricId(2); assertThat(dto.hasValue()).isTrue(); assertThat(dto.getValue()).isEqualTo(1d); assertThat(dto.getMetricId()).isEqualTo(2); - assertThat(dto.getPersonId()).isEqualTo(5L); } @Test public void test_has_value() throws Exception { PastMeasureDto measureWithValue = new PastMeasureDto() .setValue(1d) - .setMetricId(2) - .setPersonId(5L); + .setMetricId(2); assertThat(measureWithValue.hasValue()).isTrue(); PastMeasureDto measureWithoutValue = new PastMeasureDto() - .setMetricId(2) - .setPersonId(5L); + .setMetricId(2); assertThat(measureWithoutValue.hasValue()).isFalse(); } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/ProjectMeasuresIndexerIteratorTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/ProjectMeasuresIndexerIteratorTest.java index 5c475437ed3..2d78a96a857 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/ProjectMeasuresIndexerIteratorTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/ProjectMeasuresIndexerIteratorTest.java @@ -25,7 +25,6 @@ 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; @@ -35,7 +34,6 @@ import org.sonar.db.component.ComponentTesting; import org.sonar.db.component.SnapshotDto; import org.sonar.db.measure.ProjectMeasuresIndexerIterator.ProjectMeasures; import org.sonar.db.metric.MetricDto; -import org.sonar.db.metric.MetricTesting; import org.sonar.db.organization.OrganizationDto; import static org.assertj.core.api.Assertions.assertThat; @@ -48,7 +46,6 @@ import static org.sonar.api.measures.Metric.ValueType.DISTRIB; import static org.sonar.api.measures.Metric.ValueType.INT; import static org.sonar.api.measures.Metric.ValueType.LEVEL; import static org.sonar.api.measures.Metric.ValueType.STRING; -import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; import static org.sonar.db.component.ComponentTesting.newView; import static org.sonar.db.component.SnapshotTesting.newAnalysis; @@ -64,12 +61,13 @@ public class ProjectMeasuresIndexerIteratorTest { @Test public void return_project_measure() { - MetricDto metric1 = insertIntMetric("ncloc"); - MetricDto metric2 = insertIntMetric("coverage"); - ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()).setDbKey("Project-Key").setName("Project Name").setTagsString("platform,java"); - SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project); - insertMeasure(project, analysis, metric1, 10d); - insertMeasure(project, analysis, metric2, 20d); + OrganizationDto organization = dbTester.organizations().insert(); + ComponentDto project = dbTester.components().insertPrivateProject(organization, p -> p.setDbKey("Project-Key").setName("Project Name").setTagsString("platform,java")); + SnapshotDto analysis = dbTester.components().insertSnapshot(project); + MetricDto metric1 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("ncloc")); + MetricDto metric2 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("coverage")); + dbTester.measures().insertLiveMeasure(project, metric1, m -> m.setValue(10d)); + dbTester.measures().insertLiveMeasure(project, metric2, m -> m.setValue(20d)); Map docsById = createResultSetAndReturnDocsById(); @@ -81,15 +79,15 @@ public class ProjectMeasuresIndexerIteratorTest { assertThat(doc.getProject().getName()).isEqualTo("Project Name"); assertThat(doc.getProject().getTags()).containsExactly("platform", "java"); assertThat(doc.getProject().getAnalysisDate()).isNotNull().isEqualTo(analysis.getCreatedAt()); - assertThat(doc.getMeasures().getNumericMeasures()).containsOnly(entry("ncloc", 10d), entry("coverage", 20d)); + assertThat(doc.getMeasures().getNumericMeasures()).containsOnly(entry(metric1.getKey(), 10d), entry(metric2.getKey(), 20d)); } @Test public void return_project_measure_having_leak() throws Exception { - MetricDto metric = insertIntMetric("new_lines"); - ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()); - SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project); - insertMeasureOnLeak(project, analysis, metric, 10d); + OrganizationDto organization = dbTester.organizations().insert(); + ComponentDto project = dbTester.components().insertPrivateProject(organization, p -> p.setDbKey("Project-Key").setName("Project Name").setTagsString("platform,java")); + MetricDto metric = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("new_lines")); + dbTester.measures().insertLiveMeasure(project, metric, m -> m.setVariation(10d)); Map docsById = createResultSetAndReturnDocsById(); @@ -98,34 +96,40 @@ public class ProjectMeasuresIndexerIteratorTest { @Test public void return_quality_gate_status_measure() throws Exception { - MetricDto metric = insertMetric("alert_status", LEVEL); - insertProjectAndMeasure("project1", metric, WARN.name()); - insertProjectAndMeasure("project2", metric, OK.name()); - insertProjectAndMeasure("project3", metric, ERROR.name()); + OrganizationDto organization = dbTester.organizations().insert(); + ComponentDto project1 = dbTester.components().insertPrivateProject(organization); + ComponentDto project2 = dbTester.components().insertPrivateProject(organization); + ComponentDto project3 = dbTester.components().insertPrivateProject(organization); + MetricDto metric = dbTester.measures().insertMetric(m -> m.setValueType(LEVEL.name()).setKey("alert_status")); + dbTester.measures().insertLiveMeasure(project1, metric, m -> m.setValue(null).setData(WARN.name())); + dbTester.measures().insertLiveMeasure(project2, metric, m -> m.setValue(null).setData(OK.name())); + dbTester.measures().insertLiveMeasure(project3, metric, m -> m.setValue(null).setData(ERROR.name())); Map docsById = createResultSetAndReturnDocsById(); - assertThat(docsById.get("project1").getMeasures().getQualityGateStatus()).isEqualTo("WARN"); - assertThat(docsById.get("project2").getMeasures().getQualityGateStatus()).isEqualTo("OK"); - assertThat(docsById.get("project3").getMeasures().getQualityGateStatus()).isEqualTo("ERROR"); + assertThat(docsById.get(project1.uuid()).getMeasures().getQualityGateStatus()).isEqualTo("WARN"); + assertThat(docsById.get(project2.uuid()).getMeasures().getQualityGateStatus()).isEqualTo("OK"); + assertThat(docsById.get(project3.uuid()).getMeasures().getQualityGateStatus()).isEqualTo("ERROR"); } @Test public void does_not_fail_when_quality_gate_has_no_value() throws Exception { - MetricDto metric = insertMetric("alert_status", LEVEL); - insertProjectAndMeasure("project", metric, null); + OrganizationDto organization = dbTester.organizations().insert(); + ComponentDto project = dbTester.components().insertPrivateProject(organization); + MetricDto metric = dbTester.measures().insertMetric(m -> m.setValueType(LEVEL.name()).setKey("alert_status")); + dbTester.measures().insertLiveMeasure(project, metric, m -> m.setValue(null).setVariation(null).setData((String) null)); Map docsById = createResultSetAndReturnDocsById(); - assertThat(docsById.get("project").getMeasures().getNumericMeasures()).isEmpty(); + assertThat(docsById.get(project.uuid()).getMeasures().getNumericMeasures()).isEmpty(); } @Test public void return_language_distribution_measure() throws Exception { - MetricDto metric = insertMetric("ncloc_language_distribution", DATA); - ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()); - SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project); - insertMeasure(project, analysis, metric, "=2;java=6;xoo=18"); + OrganizationDto organization = dbTester.organizations().insert(); + ComponentDto project = dbTester.components().insertPrivateProject(organization); + MetricDto metric = dbTester.measures().insertMetric(m -> m.setValueType(DATA.name()).setKey("ncloc_language_distribution")); + dbTester.measures().insertLiveMeasure(project, metric, m -> m.setValue(null).setData("=2;java=6;xoo=18")); Map docsById = createResultSetAndReturnDocsById(); @@ -135,14 +139,14 @@ public class ProjectMeasuresIndexerIteratorTest { @Test public void does_not_return_none_numeric_metrics() throws Exception { - MetricDto dataMetric = insertMetric("data", DATA); - MetricDto distribMetric = insertMetric("distrib", DISTRIB); - MetricDto stringMetric = insertMetric("string", STRING); - ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()); - SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project); - insertMeasure(project, analysis, dataMetric, "dat"); - insertMeasure(project, analysis, distribMetric, "dis"); - insertMeasure(project, analysis, stringMetric, "str"); + OrganizationDto organization = dbTester.organizations().insert(); + ComponentDto project = dbTester.components().insertPrivateProject(organization); + MetricDto dataMetric = dbTester.measures().insertMetric(m -> m.setValueType(DATA.name()).setKey("data")); + MetricDto distribMetric = dbTester.measures().insertMetric(m -> m.setValueType(DISTRIB.name()).setKey("distrib")); + MetricDto stringMetric = dbTester.measures().insertMetric(m -> m.setValueType(STRING.name()).setKey("string")); + dbTester.measures().insertLiveMeasure(project, dataMetric, m -> m.setData("dat")); + dbTester.measures().insertLiveMeasure(project, distribMetric, m -> m.setData("dis")); + dbTester.measures().insertLiveMeasure(project, stringMetric, m -> m.setData("str")); Map docsById = createResultSetAndReturnDocsById(); @@ -151,10 +155,10 @@ public class ProjectMeasuresIndexerIteratorTest { @Test public void does_not_return_disabled_metrics() throws Exception { - MetricDto disabledMetric = insertMetric("disabled", false, false, INT); - ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()); - SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project); - insertMeasure(project, analysis, disabledMetric, 10d); + OrganizationDto organization = dbTester.organizations().insert(); + ComponentDto project = dbTester.components().insertPrivateProject(organization); + MetricDto disabledMetric = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setEnabled(false).setHidden(false).setKey("disabled")); + dbTester.measures().insertLiveMeasure(project, disabledMetric, m -> m.setValue(10d)); Map docsById = createResultSetAndReturnDocsById(); @@ -163,29 +167,28 @@ public class ProjectMeasuresIndexerIteratorTest { @Test public void ignore_measure_that_does_not_have_value() throws Exception { - MetricDto metric1 = insertIntMetric("coverage"); - MetricDto metric2 = insertIntMetric("ncloc"); - MetricDto leakMetric = insertIntMetric("new_lines"); - ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()); - SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project); + OrganizationDto organization = dbTester.organizations().insert(); + MetricDto metric1 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("coverage")); + MetricDto metric2 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("ncloc")); + MetricDto leakMetric = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("new_lines")); + ComponentDto project = dbTester.components().insertPrivateProject(organization); - MeasureDto withValue = insertMeasure(project, analysis, metric1, 10d); - MeasureDto withLeakValue = insertMeasure(project, analysis, leakMetric, null, 20d); - MeasureDto withoutValue = insertMeasure(project, analysis, metric2, null, null); + dbTester.measures().insertLiveMeasure(project, metric1, m -> m.setValue(10d)); + dbTester.measures().insertLiveMeasure(project, leakMetric, m -> m.setValue(null).setVariation(20d)); + dbTester.measures().insertLiveMeasure(project, metric2, m -> m.setValue(null).setVariation(null)); Map numericMeasures = createResultSetAndReturnDocsById().get(project.uuid()).getMeasures().getNumericMeasures(); - assertThat(numericMeasures).containsOnly(entry("coverage", 10d), entry("new_lines", 20d)); + assertThat(numericMeasures).containsOnly(entry(metric1.getKey(), 10d), entry(leakMetric.getKey(), 20d)); } @Test public void ignore_numeric_measure_that_has_text_value_but_not_numeric_value() throws Exception { - MetricDto metric1 = insertIntMetric("coverage"); - MetricDto metric2 = insertIntMetric("ncloc"); - ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()); - SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project); - - MeasureDto withNumericValue = insertMeasure(project, analysis, metric1, 10d); - MeasureDto withTextValue = insertMeasure(project, analysis, metric2, "foo"); + OrganizationDto organization = dbTester.organizations().insert(); + MetricDto metric1 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("coverage")); + MetricDto metric2 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("ncloc")); + ComponentDto project = dbTester.components().insertPrivateProject(organization); + dbTester.measures().insertLiveMeasure(project, metric1, m -> m.setValue(10d).setData((String) null)); + dbTester.measures().insertLiveMeasure(project, metric2, m -> m.setValue(null).setData("foo")); Map numericMeasures = createResultSetAndReturnDocsById().get(project.uuid()).getMeasures().getNumericMeasures(); assertThat(numericMeasures).containsOnly(entry("coverage", 10d)); @@ -193,9 +196,13 @@ public class ProjectMeasuresIndexerIteratorTest { @Test public void return_many_project_measures() { - dbTester.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization())); - dbTester.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization())); - dbTester.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization())); + OrganizationDto organization = dbTester.organizations().insert(); + ComponentDto project1 = dbTester.components().insertPrivateProject(organization); + ComponentDto project2 = dbTester.components().insertPrivateProject(organization); + ComponentDto project3 = dbTester.components().insertPrivateProject(organization); + dbTester.components().insertSnapshot(project1); + dbTester.components().insertSnapshot(project2); + dbTester.components().insertSnapshot(project3); assertThat(createResultSetAndReturnDocsById()).hasSize(3); } @@ -215,42 +222,47 @@ public class ProjectMeasuresIndexerIteratorTest { @Test public void does_not_return_non_active_projects() throws Exception { + OrganizationDto organization = dbTester.organizations().insert(); // Disabled project - dbTester.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()).setEnabled(false)); + dbTester.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(organization).setEnabled(false)); // Disabled project with analysis - ComponentDto project = dbTester.components().insertComponent(ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()).setEnabled(false)); + ComponentDto project = dbTester.components().insertComponent(ComponentTesting.newPrivateProjectDto(organization).setEnabled(false)); dbClient.snapshotDao().insert(dbSession, newAnalysis(project)); // A view - dbTester.components().insertProjectAndSnapshot(newView(dbTester.getDefaultOrganization())); + dbTester.components().insertProjectAndSnapshot(newView(organization)); dbSession.commit(); - assertResultSetIsEmpty(); + assertThat(createResultSetAndReturnDocsById()).isEmpty(); } @Test public void return_only_docs_from_given_project() throws Exception { - OrganizationDto organizationDto = dbTester.organizations().insert(); - ComponentDto project = ComponentTesting.newPrivateProjectDto(organizationDto); - SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project); - dbTester.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(organizationDto)); - dbTester.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(organizationDto)); + OrganizationDto organization = dbTester.organizations().insert(); + ComponentDto project1 = dbTester.components().insertPrivateProject(organization); + ComponentDto project2 = dbTester.components().insertPrivateProject(organization); + ComponentDto project3 = dbTester.components().insertPrivateProject(organization); + SnapshotDto analysis1 = dbTester.components().insertSnapshot(project1); + SnapshotDto analysis2 = dbTester.components().insertSnapshot(project2); + SnapshotDto analysis3 = dbTester.components().insertSnapshot(project3); - Map docsById = createResultSetAndReturnDocsById(project.uuid()); + Map docsById = createResultSetAndReturnDocsById(project1.uuid()); assertThat(docsById).hasSize(1); - ProjectMeasures doc = docsById.get(project.uuid()); + ProjectMeasures doc = docsById.get(project1.uuid()); assertThat(doc).isNotNull(); - assertThat(doc.getProject().getUuid()).isEqualTo(project.uuid()); - assertThat(doc.getProject().getKey()).isNotNull().isEqualTo(project.getDbKey()); - assertThat(doc.getProject().getName()).isNotNull().isEqualTo(project.name()); - assertThat(doc.getProject().getAnalysisDate()).isNotNull().isEqualTo(analysis.getCreatedAt()); + assertThat(doc.getProject().getUuid()).isEqualTo(project1.uuid()); + assertThat(doc.getProject().getKey()).isNotNull().isEqualTo(project1.getDbKey()); + assertThat(doc.getProject().getName()).isNotNull().isEqualTo(project1.name()); + assertThat(doc.getProject().getAnalysisDate()).isNotNull().isEqualTo(analysis1.getCreatedAt()); } @Test public void return_nothing_on_unknown_project() throws Exception { - dbTester.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization())); + OrganizationDto organization = dbTester.organizations().insert(); + ComponentDto project = dbTester.components().insertPrivateProject(organization); + dbTester.components().insertSnapshot(project); Map docsById = createResultSetAndReturnDocsById("UNKNOWN"); @@ -259,14 +271,13 @@ public class ProjectMeasuresIndexerIteratorTest { @Test public void non_main_branches_are_not_indexed() { - MetricDto metric = insertIntMetric("ncloc"); - ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()); - SnapshotDto projectAnalysis = dbTester.components().insertProjectAndSnapshot(project); - insertMeasure(project, projectAnalysis, metric, 10d); + OrganizationDto organization = dbTester.organizations().insert(); + ComponentDto project = dbTester.components().insertPrivateProject(organization); + MetricDto metric = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("ncloc")); + dbTester.measures().insertLiveMeasure(project, metric, m -> m.setValue(10d)); ComponentDto branch = dbTester.components().insertProjectBranch(project, b -> b.setKey("feature/foo")); - SnapshotDto branchAnalysis = dbTester.components().insertSnapshot(branch); - insertMeasure(branch, branchAnalysis, metric, 20d); + dbTester.measures().insertLiveMeasure(branch, metric, m -> m.setValue(20d)); Map docsById = createResultSetAndReturnDocsById(); @@ -284,56 +295,4 @@ public class ProjectMeasuresIndexerIteratorTest { it.close(); return docsById; } - - private void assertResultSetIsEmpty() { - 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 insertProjectAndMeasure(String projectUuid, MetricDto metric, String value) { - ComponentDto project = newPrivateProjectDto(dbTester.getDefaultOrganization(), projectUuid); - SnapshotDto analysis1 = dbTester.components().insertProjectAndSnapshot(project); - return insertMeasure(project, analysis1, metric, value); - } - - 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, String value) { - return insertMeasure(MeasureTesting.newMeasureDto(metric, project, analysis).setData(value)); - } - - private MeasureDto insertMeasure(ComponentDto project, SnapshotDto analysis, MetricDto metric, @Nullable Double value, @Nullable Double leakValue) { - return insertMeasure(MeasureTesting.newMeasureDto(metric, project, analysis).setValue(value).setVariation(leakValue)); - } - - private MeasureDto insertMeasure(MeasureDto measure) { - dbClient.measureDao().insert(dbSession, measure); - dbSession.commit(); - return measure; - } - } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeCommandsTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeCommandsTest.java index d6b7f01f9f9..ba0126ffbdb 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeCommandsTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeCommandsTest.java @@ -164,7 +164,7 @@ public class PurgeCommandsTest { @Test public void deletePermissions_deletes_permissions_of_view() { OrganizationDto organization = dbTester.organizations().insert(); - ComponentDto project = dbTester.components().insertView(organization); + ComponentDto project = dbTester.components().insertPublicPortfolio(organization); addPermissions(organization, project); PurgeCommands purgeCommands = new PurgeCommands(dbTester.getSession(), profiler); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java index e48f5c90b70..f618a131fa4 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java @@ -50,6 +50,7 @@ import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; import org.sonar.db.measure.MeasureDto; import org.sonar.db.measure.custom.CustomMeasureDto; +import org.sonar.db.metric.MetricDto; import org.sonar.db.property.PropertyDto; import org.sonar.db.rule.RuleDefinitionDto; @@ -86,7 +87,7 @@ public class PurgeDaoTest { private PurgeDao underTest = dbTester.getDbClient().purgeDao(); @Test - public void shouldDeleteAbortedBuilds() { + public void purge_failed_ce_tasks() { dbTester.prepareDbUnit(getClass(), "shouldDeleteAbortedBuilds.xml"); underTest.purge(dbSession, newConfigurationWith30Days(), PurgeListener.EMPTY, new PurgeProfiler()); @@ -96,7 +97,7 @@ public class PurgeDaoTest { } @Test - public void should_purge_project() { + public void purge_history_of_project() { dbTester.prepareDbUnit(getClass(), "shouldPurgeProject.xml"); underTest.purge(dbSession, newConfigurationWith30Days(), PurgeListener.EMPTY, new PurgeProfiler()); dbSession.commit(); @@ -104,7 +105,7 @@ public class PurgeDaoTest { } @Test - public void should_purge_inactive_short_living_branches() { + public void purge_inactive_short_living_branches() { when(system2.now()).thenReturn(new Date().getTime()); RuleDefinitionDto rule = dbTester.rules().insert(); ComponentDto project = dbTester.components().insertMainBranch(); @@ -394,6 +395,26 @@ public class PurgeDaoTest { verifyNoEffect(componentDbTester.insertView(), componentDbTester.insertPrivateProject(), componentDbTester.insertPublicProject()); } + @Test + public void delete_live_measures_when_deleting_project() { + MetricDto metric = dbTester.measures().insertMetric(); + + ComponentDto project1 = dbTester.components().insertPublicProject(); + ComponentDto module1 = dbTester.components().insertComponent(ComponentTesting.newModuleDto(project1)); + dbTester.measures().insertLiveMeasure(project1, metric); + dbTester.measures().insertLiveMeasure(module1, metric); + + ComponentDto project2 = dbTester.components().insertPublicProject(); + ComponentDto module2 = dbTester.components().insertComponent(ComponentTesting.newModuleDto(project2)); + dbTester.measures().insertLiveMeasure(project2, metric); + dbTester.measures().insertLiveMeasure(module2, metric); + + underTest.deleteProject(dbSession, project1.uuid()); + + assertThat(dbClient.liveMeasureDao().selectByComponentUuids(dbSession, asList(project1.uuid(), module1.uuid()), asList(metric.getId()))).isEmpty(); + assertThat(dbClient.liveMeasureDao().selectByComponentUuids(dbSession, asList(project2.uuid(), module2.uuid()), asList(metric.getId()))).hasSize(2); + } + private void verifyNoEffect(ComponentDto firstRoot, ComponentDto... otherRoots) { DbSession dbSession = mock(DbSession.class); diff --git a/server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/insert-result.xml b/server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/insert-result.xml index 692539f175b..2af5e30c370 100644 --- a/server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/insert-result.xml +++ b/server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/insert-result.xml @@ -4,7 +4,7 @@ analysis_uuid="u2" metric_id="3" component_uuid="FILE1" - person_id="23" + person_id="[null]" value="2.0" text_value="measure-value" measure_data="[null]" diff --git a/server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/past_measures_with_person_id.xml b/server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/past_measures_with_person_id.xml deleted file mode 100644 index d1886556bef..00000000000 --- a/server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/past_measures_with_person_id.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/with_some_measures_for_developer.xml b/server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/with_some_measures_for_developer.xml deleted file mode 100644 index 67736536768..00000000000 --- a/server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/with_some_measures_for_developer.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/server/sonar-db-dao/src/test/resources/org/sonar/db/purge/PurgeCommandsTest/shouldDeleteWastedMeasuresWhenPurgingAnalysis.xml b/server/sonar-db-dao/src/test/resources/org/sonar/db/purge/PurgeCommandsTest/shouldDeleteWastedMeasuresWhenPurgingAnalysis.xml index 2c0a7c2df25..d683a6acfe6 100644 --- a/server/sonar-db-dao/src/test/resources/org/sonar/db/purge/PurgeCommandsTest/shouldDeleteWastedMeasuresWhenPurgingAnalysis.xml +++ b/server/sonar-db-dao/src/test/resources/org/sonar/db/purge/PurgeCommandsTest/shouldDeleteWastedMeasuresWhenPurgingAnalysis.xml @@ -90,23 +90,4 @@ description="[null]" measure_data="[null]"/> - - diff --git a/server/sonar-db-dao/src/test/resources/org/sonar/db/purge/PurgeCommandsTest/shouldPurgeAnalysis.xml b/server/sonar-db-dao/src/test/resources/org/sonar/db/purge/PurgeCommandsTest/shouldPurgeAnalysis.xml index 70b5a8db7c1..5e2e4c4e819 100644 --- a/server/sonar-db-dao/src/test/resources/org/sonar/db/purge/PurgeCommandsTest/shouldPurgeAnalysis.xml +++ b/server/sonar-db-dao/src/test/resources/org/sonar/db/purge/PurgeCommandsTest/shouldPurgeAnalysis.xml @@ -111,7 +111,6 @@ implements SqlStatement extends AutoCloseable { CHILD setBoolean(int columnIndex, @Nullable Boolean value) throws SQLException; + CHILD setBytes(int columnIndex, @Nullable byte[] value) throws SQLException; + CHILD setDate(int columnIndex, @Nullable Date value) throws SQLException; CHILD setDouble(int columnIndex, @Nullable Double value) throws SQLException; diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasures.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasures.java new file mode 100644 index 00000000000..bc37773d96c --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasures.java @@ -0,0 +1,125 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.platform.db.migration.version.v70; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.def.VarcharColumnDef; +import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder; +import org.sonar.server.platform.db.migration.sql.CreateTableBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static org.sonar.server.platform.db.migration.def.BigIntegerColumnDef.newBigIntegerColumnDefBuilder; +import static org.sonar.server.platform.db.migration.def.BlobColumnDef.newBlobColumnDefBuilder; +import static org.sonar.server.platform.db.migration.def.DecimalColumnDef.newDecimalColumnDefBuilder; +import static org.sonar.server.platform.db.migration.def.IntegerColumnDef.newIntegerColumnDefBuilder; +import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE; +import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder; + +public class CreateTableLiveMeasures extends DdlChange { + + private static final String TABLE_NAME = "live_measures"; + + public CreateTableLiveMeasures(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + context.execute(new CreateTableBuilder(getDialect(), TABLE_NAME) + .addPkColumn(newVarcharColumnDefBuilder() + .setColumnName("uuid") + .setIsNullable(false) + .setLimit(VarcharColumnDef.UUID_SIZE) + .build()) + .addColumn(newVarcharColumnDefBuilder() + .setColumnName("project_uuid") + .setIsNullable(false) + .setLimit(VarcharColumnDef.UUID_VARCHAR_SIZE) + .build()) + .addColumn(newVarcharColumnDefBuilder() + .setColumnName("component_uuid") + .setIsNullable(false) + .setLimit(VarcharColumnDef.UUID_VARCHAR_SIZE) + .build()) + .addColumn(newIntegerColumnDefBuilder() + .setColumnName("metric_id") + .setIsNullable(false) + .build()) + .addColumn(newDecimalColumnDefBuilder() + .setColumnName("value") + .setPrecision(38) + .setScale(20) + .build()) + .addColumn(newVarcharColumnDefBuilder() + .setColumnName("text_value") + .setIsNullable(true) + .setLimit(4_000) + .build()) + .addColumn(newDecimalColumnDefBuilder() + .setColumnName("variation") + .setPrecision(38) + .setScale(20) + .build()) + .addColumn(newBlobColumnDefBuilder() + .setColumnName("measure_data") + .build()) + .addColumn(newVarcharColumnDefBuilder() + .setColumnName("update_marker") + .setIsNullable(true) + .setLimit(UUID_SIZE) + .build()) + .addColumn(newBigIntegerColumnDefBuilder() + .setColumnName("created_at") + .setIsNullable(false) + .build()) + .addColumn(newBigIntegerColumnDefBuilder() + .setColumnName("updated_at") + .setIsNullable(false) + .build()) + .build()); + + context.execute(new CreateIndexBuilder(getDialect()) + .addColumn(newVarcharColumnDefBuilder() + .setColumnName("project_uuid") + .setIsNullable(false) + .setLimit(VarcharColumnDef.UUID_VARCHAR_SIZE) + .build()) + .setUnique(false) + .setTable(TABLE_NAME) + .setName("live_measures_project") + .build()); + + context.execute(new CreateIndexBuilder(getDialect()) + .addColumn(newVarcharColumnDefBuilder() + .setColumnName("component_uuid") + .setIsNullable(false) + .setLimit(VarcharColumnDef.UUID_VARCHAR_SIZE) + .build()) + .addColumn(newIntegerColumnDefBuilder() + .setColumnName("metric_id") + .setIsNullable(false) + .build()) + .setUnique(true) + .setTable(TABLE_NAME) + .setName("live_measures_component") + .build()); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java index f03ca93038b..e51e2426a93 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java @@ -32,7 +32,11 @@ public class DbVersion70 implements DbVersion { .add(1902, "Make QUALITY_GATES.IS_BUILT_IN not null", MakeQualityGatesIsBuiltInNotNullable.class) .add(1903, "Remove quality gates loaded templates", RemoveQualityGateLoadedTemplates.class) .add(1904, "Rename quality gate \"SonarQube way\" to \"Sonar way\"", RenameOldSonarQubeWayQualityGate.class) - .add(1905, "Drop LOADED_TEMPLATES table", DropLoadedTemplatesTable.class); + .add(1905, "Drop LOADED_TEMPLATES table", DropLoadedTemplatesTable.class) + .add(1906, "Create table live_measures", CreateTableLiveMeasures.class) + .add(1907, "Populate table live_measures", PopulateLiveMeasures.class) + .add(1908, "Delete person and file measures", DeletePersonAndFileMeasures.class) + .add(1909, "Drop index on project_measures.person_id", DropIndexOnPersonMeasures.class) + ; } - } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasures.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasures.java new file mode 100644 index 00000000000..92ba449f7cd --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasures.java @@ -0,0 +1,84 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.platform.db.migration.version.v70; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.dialect.H2; +import org.sonar.db.dialect.MsSql; +import org.sonar.db.dialect.MySql; +import org.sonar.db.dialect.Oracle; +import org.sonar.db.dialect.PostgreSql; +import org.sonar.server.platform.db.migration.step.DataChange; +import org.sonar.server.platform.db.migration.step.MassUpdate; + +public class DeletePersonAndFileMeasures extends DataChange { + public DeletePersonAndFileMeasures(Database db) { + super(db); + } + + @Override + protected void execute(Context context) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("select uuid from snapshots"); + massUpdate.rowPluralName("snapshots"); + massUpdate.update(getDeleteSql()); + + massUpdate.execute((row, update) -> { + update.setString(1, row.getString(1)); + return true; + }); + } + + private String getDeleteSql() { + switch (getDialect().getId()) { + case MySql.ID: + case MsSql.ID: + return "delete pm from project_measures pm " + + "inner join projects c on c.uuid = pm.component_uuid " + + "where pm.analysis_uuid = ? " + + "and (c.qualifier in ('UTS', 'FIL') or pm.person_id is not null)"; + case H2.ID: + return "delete from project_measures " + + "where id in ( " + + " select pm.id from project_measures pm " + + " inner join projects c on c.uuid = pm.component_uuid " + + " where pm.analysis_uuid = ? " + + " and (c.qualifier in ('UTS', 'FIL') or pm.person_id is not null) " + + ")"; + case PostgreSql.ID: + return "delete from project_measures pm " + + "using projects c " + + "where pm.analysis_uuid = ? " + + "and c.uuid = pm.component_uuid " + + "and (c.qualifier in ('UTS', 'FIL') or pm.person_id is not null) "; + case Oracle.ID: + return "delete from project_measures pm where exists (" + + " select 1 from project_measures pm2 " + + " inner join projects c on c.uuid = pm2.component_uuid " + + " where pm2.analysis_uuid = ? " + + " and (c.qualifier in ('UTS', 'FIL') or pm.person_id is not null) " + + " and pm.id = pm2.id" + + ")"; + default: + throw new IllegalStateException("Unsupported DB dialect: " + getDialect()); + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasures.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasures.java new file mode 100644 index 00000000000..c11e4e48c8b --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasures.java @@ -0,0 +1,40 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.platform.db.migration.version.v70; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.sql.DropIndexBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +public class DropIndexOnPersonMeasures extends DdlChange { + + public DropIndexOnPersonMeasures(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + context.execute(new DropIndexBuilder(getDialect()) + .setTable("project_measures") + .setName("measures_person") + .build()); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasures.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasures.java new file mode 100644 index 00000000000..1d226fca4f1 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasures.java @@ -0,0 +1,72 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.platform.db.migration.version.v70; + +import java.sql.SQLException; +import org.sonar.api.utils.System2; +import org.sonar.core.util.Uuids; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.DataChange; +import org.sonar.server.platform.db.migration.step.MassUpdate; + +public class PopulateLiveMeasures extends DataChange { + + private final System2 system2; + + public PopulateLiveMeasures(Database db, System2 system2) { + super(db); + this.system2 = system2; + } + + @Override + protected void execute(Context context) throws SQLException { + long now = system2.now(); + // reentrancy of migration + context.prepareUpsert("TRUNCATE TABLE live_measures").execute(); + + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("SELECT p.uuid, p.project_uuid, pm.metric_id, pm.value, pm.text_value, pm.variation_value_1, pm.measure_data " + + "FROM project_measures pm " + + "INNER JOIN projects p on p.uuid = pm.component_uuid " + + "INNER JOIN snapshots s on s.uuid = pm.analysis_uuid " + + "WHERE s.islast = ?") + .setBoolean(1, true); + + massUpdate.update("INSERT INTO live_measures " + + "(uuid, component_uuid, project_uuid, metric_id, value, text_value, variation, measure_data, created_at, updated_at) " + + "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + + massUpdate.rowPluralName("live measures"); + massUpdate.execute((row, update) -> { + update.setString(1, Uuids.create()); + update.setString(2, row.getString(1)); + update.setString(3, row.getString(2)); + update.setInt(4, row.getInt(3)); + update.setDouble(5, row.getNullableDouble(4)); + update.setString(6, row.getString(5)); + update.setDouble(7, row.getNullableDouble(6)); + update.setBytes(8, row.getNullableBytes(7)); + update.setLong(9, now); + update.setLong(10, now); + return true; + }); + } + +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasuresTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasuresTest.java new file mode 100644 index 00000000000..806e0ea00e8 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasuresTest.java @@ -0,0 +1,59 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.platform.db.migration.version.v70; + +import java.sql.SQLException; +import java.sql.Types; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.db.CoreDbTester; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CreateTableLiveMeasuresTest { + private static final String TABLE = "live_measures"; + + @Rule + public final CoreDbTester db = CoreDbTester.createForSchema(CreateTableLiveMeasuresTest.class, "empty.sql"); + + private CreateTableLiveMeasures underTest = new CreateTableLiveMeasures(db.database()); + + @Test + public void creates_table_on_empty_db() throws SQLException { + underTest.execute(); + + assertThat(db.countRowsOfTable(TABLE)).isEqualTo(0); + + db.assertColumnDefinition(TABLE, "uuid", Types.VARCHAR, 40, false); + db.assertColumnDefinition(TABLE, "project_uuid", Types.VARCHAR, 50, false); + db.assertColumnDefinition(TABLE, "component_uuid", Types.VARCHAR, 50, false); + db.assertColumnDefinition(TABLE, "metric_id", Types.INTEGER, null, false); + db.assertColumnDefinition(TABLE, "value", Types.DOUBLE, null, true); + db.assertColumnDefinition(TABLE, "text_value", Types.VARCHAR, 4_000, true); + db.assertColumnDefinition(TABLE, "variation", Types.DOUBLE, null, true); + db.assertColumnDefinition(TABLE, "measure_data", Types.BLOB, null, true); + db.assertColumnDefinition(TABLE, "update_marker", Types.VARCHAR, 40, true); + db.assertColumnDefinition(TABLE, "created_at", Types.BIGINT, null, false); + db.assertColumnDefinition(TABLE, "updated_at", Types.BIGINT, null, false); + + db.assertIndex(TABLE, "live_measures_project", "project_uuid"); + db.assertUniqueIndex(TABLE, "live_measures_component", "component_uuid", "metric_id"); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java index ece11091b41..fa47fed02ed 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java @@ -35,7 +35,7 @@ public class DbVersion70Test { @Test public void verify_migration_count() { - verifyMigrationCount(underTest, 6); + verifyMigrationCount(underTest, 10); } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasuresTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasuresTest.java new file mode 100644 index 00000000000..2019003f8e0 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasuresTest.java @@ -0,0 +1,125 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.platform.db.migration.version.v70; + +import java.sql.SQLException; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.commons.lang.math.RandomUtils; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.db.CoreDbTester; +import org.sonar.server.platform.db.migration.step.DataChange; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DeletePersonAndFileMeasuresTest { + private static final AtomicInteger GENERATOR = new AtomicInteger(); + @Rule + public CoreDbTester db = CoreDbTester.createForSchema(DeletePersonAndFileMeasuresTest.class, "initial.sql"); + + private DataChange underTest = new DeletePersonAndFileMeasures(db.database()); + + @Test + public void delete_file_and_person_measures() throws SQLException { + insertComponent("P1", "PRJ", "TRK"); + insertComponent("D1", "DIR", "DIR"); + insertComponent("F1", "FIL", "FIL"); + insertComponent("F2", "FIL", "UTS"); + insertSnapshot("S1", "P1", false); + insertSnapshot("S2", "P1", true); + // past measures + long m1 = insertMeasure("P1", "S1"); + long m2 = insertMeasure("D1", "S1"); + long m3 = insertMeasure("F1", "S1"); + long m4 = insertMeasure("F2", "S1"); + long m5 = insertPersonMeasure("P1", "S1"); + long m6 = insertPersonMeasure("F1", "S1"); + // last measures + long m7 = insertMeasure("P1", "S2"); + long m8 = insertMeasure("D1", "S2"); + long m9 = insertMeasure("F1", "S2"); + long m10 = insertMeasure("F2", "S2"); + long m11 = insertPersonMeasure("P1", "S2"); + long m12 = insertPersonMeasure("F1", "S2"); + + underTest.execute(); + + assertThat(db.countRowsOfTable("PROJECTS")).isEqualTo(4); + assertThat(db.countRowsOfTable("SNAPSHOTS")).isEqualTo(2); + assertThatMeasuresAreExactly(m1, m2, m7, m8); + + // migration is re-entrant + underTest.execute(); + assertThat(db.countRowsOfTable("PROJECTS")).isEqualTo(4); + assertThat(db.countRowsOfTable("SNAPSHOTS")).isEqualTo(2); + assertThatMeasuresAreExactly(m1, m2, m7, m8); + } + + private void assertThatMeasuresAreExactly(long... expectedMeasureIds) { + long[] ids = db.select("select id as \"id\" from project_measures") + .stream() + .mapToLong(m -> (Long) m.get("id")) + .toArray(); + assertThat(ids).containsOnly(expectedMeasureIds); + } + + private void insertComponent(String uuid, String scope, String qualifier) { + db.executeInsert("PROJECTS", + "ORGANIZATION_UUID", "O1", + "KEE", "" + GENERATOR.incrementAndGet(), + "UUID", uuid, + "PROJECT_UUID", "" + GENERATOR.incrementAndGet(), + "MAIN_BRANCH_PROJECT_UUID", "" + GENERATOR.incrementAndGet(), + "UUID_PATH", ".", + "ROOT_UUID", "" + GENERATOR.incrementAndGet(), + "PRIVATE", "true", + "QUALIFIER", qualifier, + "SCOPE", scope); + } + + private void insertSnapshot(String uuid, String projectUuid, boolean last) { + db.executeInsert("SNAPSHOTS", + "UUID", uuid, + "COMPONENT_UUID", projectUuid, + "STATUS", "P", + "ISLAST", last); + } + + private long insertMeasure(String componentUuid, String analysisUuid) { + long id = GENERATOR.incrementAndGet(); + db.executeInsert("PROJECT_MEASURES", + "ID", id, + "METRIC_ID", "42", + "COMPONENT_UUID", componentUuid, + "ANALYSIS_UUID", analysisUuid); + return id; + } + + private long insertPersonMeasure(String componentUuid, String analysisUuid) { + long id = GENERATOR.incrementAndGet(); + db.executeInsert("PROJECT_MEASURES", + "ID", id, + "METRIC_ID", "42", + "COMPONENT_UUID", componentUuid, + "ANALYSIS_UUID", analysisUuid, + "PERSON_ID", RandomUtils.nextInt(100)); + return id; + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasuresTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasuresTest.java new file mode 100644 index 00000000000..08bf2079eec --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasuresTest.java @@ -0,0 +1,48 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.platform.db.migration.version.v70; + +import java.sql.SQLException; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.db.CoreDbTester; +import org.sonar.server.platform.db.migration.step.DdlChange; + +public class DropIndexOnPersonMeasuresTest { + + private static final String TABLE = "project_measures"; + private static final String INDEX = "measures_person"; + + @Rule + public CoreDbTester db = CoreDbTester.createForSchema(DropIndexOnPersonMeasuresTest.class, "initial.sql"); + + private DdlChange underTest = new DropIndexOnPersonMeasures(db.database()); + + @Test + public void drop_index() throws SQLException { + db.assertIndex(TABLE, INDEX, "person_id"); + + underTest.execute(); + + db.assertIndexDoesNotExist(TABLE, INDEX); + } +} + + diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest.java new file mode 100644 index 00000000000..7a2fc7e9e2c --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest.java @@ -0,0 +1,140 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.platform.db.migration.version.v70; + +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import org.assertj.core.groups.Tuple; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.internal.TestSystem2; +import org.sonar.db.CoreDbTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +public class PopulateLiveMeasuresTest { + + private System2 system2 = new TestSystem2().setNow(1_500_000_000_000L); + + @Rule + public CoreDbTester db = CoreDbTester.createForSchema(PopulateLiveMeasuresTest.class, "initial.sql"); + + private PopulateLiveMeasures underTest = new PopulateLiveMeasures(db.database(), system2); + + @Test + public void do_nothing_when_no_data() throws SQLException { + assertThat(db.countRowsOfTable("PROJECT_MEASURES")).isEqualTo(0); + underTest.execute(); + assertThat(db.countRowsOfTable("LIVE_MEASURES")).isEqualTo(0); + } + + @Test + public void execute_must_update_database() throws SQLException { + generateProjectMeasures(); + + underTest.execute(); + + assertThat(getLiveMeasures()).extracting( + field("COMPONENT_UUID"), + field("PROJECT_UUID"), + field("METRIC_ID"), + field("VALUE"), + field("TEXT_VALUE"), + field("VARIATION"), + field("MEASURE_DATA") + ).containsExactlyInAnyOrder(generateLiveMeasures()); + } + + private Function, Object> field(String name) { + return m -> m.get(name); + } + + @Test + public void migration_is_reentrant() throws SQLException { + generateProjectMeasures(); + + underTest.execute(); + underTest.execute(); + + assertThat(getLiveMeasures()).extracting( + field("COMPONENT_UUID"), + field("PROJECT_UUID"), + field("METRIC_ID"), + field("VALUE"), + field("TEXT_VALUE"), + field("VARIATION"), + field("MEASURE_DATA") + ).containsExactlyInAnyOrder(generateLiveMeasures()); + } + + private void generateProjectMeasures() { + Map project = new HashMap<>(); + project.put("UUID", "PRJ1"); + project.put("ORGANIZATION_UUID", "ORG1"); + project.put("UUID_PATH", "X"); + project.put("ROOT_UUID", "X"); + project.put("PROJECT_UUID", "PRJ1"); + project.put("PRIVATE", "FALSE"); + db.executeInsert("PROJECTS", project); + + Map analysis1 = new HashMap<>(); + analysis1.put("UUID", "A1"); + analysis1.put("ISLAST", "FALSE"); + analysis1.put("COMPONENT_UUID", "PRJ1"); + db.executeInsert("SNAPSHOTS", analysis1); + + Map analysis2 = new HashMap<>(); + analysis2.put("UUID", "A2"); + analysis2.put("ISLAST", "TRUE"); + analysis2.put("COMPONENT_UUID", "PRJ1"); + db.executeInsert("SNAPSHOTS", analysis2); + + Map measure1 = new HashMap<>(); + measure1.put("COMPONENT_UUID", "PRJ1"); + measure1.put("ANALYSIS_UUID", "A1"); + measure1.put("METRIC_ID", "123"); + db.executeInsert("PROJECT_MEASURES", measure1); + + Map measure2 = new HashMap<>(); + measure2.put("COMPONENT_UUID", "PRJ1"); + measure2.put("ANALYSIS_UUID", "A2"); + measure2.put("METRIC_ID", "123"); + measure2.put("VALUE", "234"); + measure2.put("TEXT_VALUE", "TEXT_VALUEx"); + measure2.put("VARIATION_VALUE_1", "345"); + measure2.put("MEASURE_DATA", "FFFF"); + db.executeInsert("PROJECT_MEASURES", measure2); + } + + private List> getLiveMeasures() { + return db.select("SELECT * FROM LIVE_MEASURES"); + } + + private Tuple[] generateLiveMeasures() { + return new Tuple[] { + tuple("PRJ1", "PRJ1", 123L, 234.0, "TEXT_VALUEx", 345.0, new byte[] {-1, -1}) + }; + } +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasuresTest/empty.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasuresTest/empty.sql new file mode 100644 index 00000000000..e69de29bb2d diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasuresTest/initial.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasuresTest/initial.sql new file mode 100644 index 00000000000..be55d6f14e8 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasuresTest/initial.sql @@ -0,0 +1,98 @@ +CREATE TABLE "PROJECTS" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "ORGANIZATION_UUID" VARCHAR(40) NOT NULL, + "KEE" VARCHAR(400), + "UUID" VARCHAR(50) NOT NULL, + "UUID_PATH" VARCHAR(1500) NOT NULL, + "ROOT_UUID" VARCHAR(50) NOT NULL, + "PROJECT_UUID" VARCHAR(50) NOT NULL, + "MODULE_UUID" VARCHAR(50), + "MODULE_UUID_PATH" VARCHAR(1500), + "MAIN_BRANCH_PROJECT_UUID" VARCHAR(50), + "NAME" VARCHAR(2000), + "DESCRIPTION" VARCHAR(2000), + "PRIVATE" BOOLEAN NOT NULL, + "TAGS" VARCHAR(500), + "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE, + "SCOPE" VARCHAR(3), + "QUALIFIER" VARCHAR(10), + "DEPRECATED_KEE" VARCHAR(400), + "PATH" VARCHAR(2000), + "LANGUAGE" VARCHAR(20), + "COPY_COMPONENT_UUID" VARCHAR(50), + "LONG_NAME" VARCHAR(2000), + "DEVELOPER_UUID" VARCHAR(50), + "CREATED_AT" TIMESTAMP, + "AUTHORIZATION_UPDATED_AT" BIGINT, + "B_CHANGED" BOOLEAN, + "B_COPY_COMPONENT_UUID" VARCHAR(50), + "B_DESCRIPTION" VARCHAR(2000), + "B_ENABLED" BOOLEAN, + "B_UUID_PATH" VARCHAR(1500), + "B_LANGUAGE" VARCHAR(20), + "B_LONG_NAME" VARCHAR(500), + "B_MODULE_UUID" VARCHAR(50), + "B_MODULE_UUID_PATH" VARCHAR(1500), + "B_NAME" VARCHAR(500), + "B_PATH" VARCHAR(2000), + "B_QUALIFIER" VARCHAR(10) +); +CREATE INDEX "PROJECTS_ORGANIZATION" ON "PROJECTS" ("ORGANIZATION_UUID"); +CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE"); +CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID"); +CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID"); +CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID"); +CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID"); +CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER"); + + +CREATE TABLE "SNAPSHOTS" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "UUID" VARCHAR(50) NOT NULL, + "CREATED_AT" BIGINT, + "BUILD_DATE" BIGINT, + "COMPONENT_UUID" VARCHAR(50) NOT NULL, + "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U', + "PURGE_STATUS" INTEGER, + "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE, + "VERSION" VARCHAR(500), + "PERIOD1_MODE" VARCHAR(100), + "PERIOD1_PARAM" VARCHAR(100), + "PERIOD1_DATE" BIGINT, + "PERIOD2_MODE" VARCHAR(100), + "PERIOD2_PARAM" VARCHAR(100), + "PERIOD2_DATE" BIGINT, + "PERIOD3_MODE" VARCHAR(100), + "PERIOD3_PARAM" VARCHAR(100), + "PERIOD3_DATE" BIGINT, + "PERIOD4_MODE" VARCHAR(100), + "PERIOD4_PARAM" VARCHAR(100), + "PERIOD4_DATE" BIGINT, + "PERIOD5_MODE" VARCHAR(100), + "PERIOD5_PARAM" VARCHAR(100), + "PERIOD5_DATE" BIGINT +); +CREATE INDEX "SNAPSHOT_COMPONENT" ON "SNAPSHOTS" ("COMPONENT_UUID"); +CREATE UNIQUE INDEX "ANALYSES_UUID" ON "SNAPSHOTS" ("UUID"); + + +CREATE TABLE "PROJECT_MEASURES" ( + "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "VALUE" DOUBLE, + "METRIC_ID" INTEGER NOT NULL, + "COMPONENT_UUID" VARCHAR(50) NOT NULL, + "ANALYSIS_UUID" VARCHAR(50) NOT NULL, + "TEXT_VALUE" VARCHAR(4000), + "ALERT_STATUS" VARCHAR(5), + "ALERT_TEXT" VARCHAR(4000), + "DESCRIPTION" VARCHAR(4000), + "PERSON_ID" INTEGER, + "VARIATION_VALUE_1" DOUBLE, + "VARIATION_VALUE_2" DOUBLE, + "VARIATION_VALUE_3" DOUBLE, + "VARIATION_VALUE_4" DOUBLE, + "VARIATION_VALUE_5" DOUBLE, + "MEASURE_DATA" BINARY +); +CREATE INDEX "MEASURES_COMPONENT_UUID" ON "PROJECT_MEASURES" ("COMPONENT_UUID"); +CREATE INDEX "MEASURES_ANALYSIS_METRIC" ON "PROJECT_MEASURES" ("ANALYSIS_UUID", "METRIC_ID"); diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasuresTest/initial.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasuresTest/initial.sql new file mode 100644 index 00000000000..00ef5e22307 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasuresTest/initial.sql @@ -0,0 +1,21 @@ +CREATE TABLE "PROJECT_MEASURES" ( + "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "VALUE" DOUBLE, + "METRIC_ID" INTEGER NOT NULL, + "COMPONENT_UUID" VARCHAR(50) NOT NULL, + "ANALYSIS_UUID" VARCHAR(50) NOT NULL, + "TEXT_VALUE" VARCHAR(4000), + "ALERT_STATUS" VARCHAR(5), + "ALERT_TEXT" VARCHAR(4000), + "DESCRIPTION" VARCHAR(4000), + "PERSON_ID" INTEGER, + "VARIATION_VALUE_1" DOUBLE, + "VARIATION_VALUE_2" DOUBLE, + "VARIATION_VALUE_3" DOUBLE, + "VARIATION_VALUE_4" DOUBLE, + "VARIATION_VALUE_5" DOUBLE, + "MEASURE_DATA" BINARY +); +CREATE INDEX "MEASURES_COMPONENT_UUID" ON "PROJECT_MEASURES" ("COMPONENT_UUID"); +CREATE INDEX "MEASURES_ANALYSIS_METRIC" ON "PROJECT_MEASURES" ("ANALYSIS_UUID", "METRIC_ID"); +CREATE INDEX "MEASURES_PERSON" ON "PROJECT_MEASURES" ("PERSON_ID"); diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest/initial.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest/initial.sql new file mode 100644 index 00000000000..b3dc4557dbb --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest/initial.sql @@ -0,0 +1,116 @@ +CREATE TABLE "PROJECTS" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "ORGANIZATION_UUID" VARCHAR(40) NOT NULL, + "KEE" VARCHAR(400), + "UUID" VARCHAR(50) NOT NULL, + "UUID_PATH" VARCHAR(1500) NOT NULL, + "ROOT_UUID" VARCHAR(50) NOT NULL, + "PROJECT_UUID" VARCHAR(50) NOT NULL, + "MODULE_UUID" VARCHAR(50), + "MODULE_UUID_PATH" VARCHAR(1500), + "MAIN_BRANCH_PROJECT_UUID" VARCHAR(50), + "NAME" VARCHAR(2000), + "DESCRIPTION" VARCHAR(2000), + "PRIVATE" BOOLEAN NOT NULL, + "TAGS" VARCHAR(500), + "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE, + "SCOPE" VARCHAR(3), + "QUALIFIER" VARCHAR(10), + "DEPRECATED_KEE" VARCHAR(400), + "PATH" VARCHAR(2000), + "LANGUAGE" VARCHAR(20), + "COPY_COMPONENT_UUID" VARCHAR(50), + "LONG_NAME" VARCHAR(2000), + "DEVELOPER_UUID" VARCHAR(50), + "CREATED_AT" TIMESTAMP, + "AUTHORIZATION_UPDATED_AT" BIGINT, + "B_CHANGED" BOOLEAN, + "B_COPY_COMPONENT_UUID" VARCHAR(50), + "B_DESCRIPTION" VARCHAR(2000), + "B_ENABLED" BOOLEAN, + "B_UUID_PATH" VARCHAR(1500), + "B_LANGUAGE" VARCHAR(20), + "B_LONG_NAME" VARCHAR(500), + "B_MODULE_UUID" VARCHAR(50), + "B_MODULE_UUID_PATH" VARCHAR(1500), + "B_NAME" VARCHAR(500), + "B_PATH" VARCHAR(2000), + "B_QUALIFIER" VARCHAR(10) +); +CREATE INDEX "PROJECTS_ORGANIZATION" ON "PROJECTS" ("ORGANIZATION_UUID"); +CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE"); +CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID"); +CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID"); +CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID"); +CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID"); +CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER"); + +CREATE TABLE "SNAPSHOTS" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "UUID" VARCHAR(50) NOT NULL, + "CREATED_AT" BIGINT, + "BUILD_DATE" BIGINT, + "COMPONENT_UUID" VARCHAR(50) NOT NULL, + "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U', + "PURGE_STATUS" INTEGER, + "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE, + "VERSION" VARCHAR(500), + "PERIOD1_MODE" VARCHAR(100), + "PERIOD1_PARAM" VARCHAR(100), + "PERIOD1_DATE" BIGINT, + "PERIOD2_MODE" VARCHAR(100), + "PERIOD2_PARAM" VARCHAR(100), + "PERIOD2_DATE" BIGINT, + "PERIOD3_MODE" VARCHAR(100), + "PERIOD3_PARAM" VARCHAR(100), + "PERIOD3_DATE" BIGINT, + "PERIOD4_MODE" VARCHAR(100), + "PERIOD4_PARAM" VARCHAR(100), + "PERIOD4_DATE" BIGINT, + "PERIOD5_MODE" VARCHAR(100), + "PERIOD5_PARAM" VARCHAR(100), + "PERIOD5_DATE" BIGINT +); +CREATE INDEX "SNAPSHOT_COMPONENT" ON "SNAPSHOTS" ("COMPONENT_UUID"); +CREATE UNIQUE INDEX "ANALYSES_UUID" ON "SNAPSHOTS" ("UUID"); + + +CREATE TABLE "LIVE_MEASURES" ( + "UUID" VARCHAR(40) NOT NULL PRIMARY KEY, + "PROJECT_UUID" VARCHAR(50) NOT NULL, + "COMPONENT_UUID" VARCHAR(50) NOT NULL, + "METRIC_ID" INTEGER NOT NULL, + "VALUE" DOUBLE, + "TEXT_VALUE" VARCHAR(4000), + "VARIATION" DOUBLE, + "MEASURE_DATA" BINARY, + "UPDATE_MARKER" VARCHAR(40), + "CREATED_AT" BIGINT NOT NULL, + "UPDATED_AT" BIGINT NOT NULL +); +CREATE INDEX "LIVE_MEASURES_PROJECT" ON "LIVE_MEASURES" ("PROJECT_UUID"); +CREATE UNIQUE INDEX "LIVE_MEASURES_COMPONENT" ON "LIVE_MEASURES" ("COMPONENT_UUID", "METRIC_ID"); + + +CREATE TABLE "PROJECT_MEASURES" ( + "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "VALUE" DOUBLE, + "METRIC_ID" INTEGER NOT NULL, + "COMPONENT_UUID" VARCHAR(50) NOT NULL, + "ANALYSIS_UUID" VARCHAR(50) NOT NULL, + "TEXT_VALUE" VARCHAR(4000), + "ALERT_STATUS" VARCHAR(5), + "ALERT_TEXT" VARCHAR(4000), + "DESCRIPTION" VARCHAR(4000), + "PERSON_ID" INTEGER, + "VARIATION_VALUE_1" DOUBLE, + "VARIATION_VALUE_2" DOUBLE, + "VARIATION_VALUE_3" DOUBLE, + "VARIATION_VALUE_4" DOUBLE, + "VARIATION_VALUE_5" DOUBLE, + "MEASURE_DATA" BINARY +); +CREATE INDEX "MEASURES_COMPONENT_UUID" ON "PROJECT_MEASURES" ("COMPONENT_UUID"); +CREATE INDEX "MEASURES_ANALYSIS_METRIC" ON "PROJECT_MEASURES" ("ANALYSIS_UUID", "METRIC_ID"); +CREATE INDEX "MEASURES_PERSON" ON "PROJECT_MEASURES" ("PERSON_ID"); + diff --git a/server/sonar-server/src/main/java/org/sonar/server/branch/ws/ListAction.java b/server/sonar-server/src/main/java/org/sonar/server/branch/ws/ListAction.java index d337d8ca5fd..65af52a7b87 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/branch/ws/ListAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/branch/ws/ListAction.java @@ -31,14 +31,14 @@ import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; import org.sonar.api.web.UserRole; +import org.sonar.core.util.Protobuf; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.BranchDto; import org.sonar.db.component.BranchType; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.SnapshotDto; -import org.sonar.db.measure.MeasureDto; -import org.sonar.db.metric.MetricDto; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.server.component.ComponentFinder; import org.sonar.server.issue.index.BranchStatistics; import org.sonar.server.issue.index.IssueIndex; @@ -97,13 +97,12 @@ public class ListAction implements BranchWsAction { checkArgument(project.isEnabled() && PROJECT.equals(project.qualifier()), "Invalid project key"); Collection branches = dbClient.branchDao().selectByComponent(dbSession, project); - MetricDto qualityGateMetric = dbClient.metricDao().selectOrFailByKey(dbSession, ALERT_STATUS_KEY); Map mergeBranchesByUuid = dbClient.branchDao() .selectByUuids(dbSession, branches.stream().map(BranchDto::getMergeBranchUuid).filter(Objects::nonNull).collect(toList())) .stream().collect(uniqueIndex(BranchDto::getUuid)); - Map qualityGateMeasuresByComponentUuids = dbClient.measureDao() - .selectByComponentsAndMetrics(dbSession, branches.stream().map(BranchDto::getUuid).collect(toList()), singletonList(qualityGateMetric.getId())) - .stream().collect(uniqueIndex(MeasureDto::getComponentUuid)); + Map qualityGateMeasuresByComponentUuids = dbClient.liveMeasureDao() + .selectByComponentUuidsAndMetricKeys(dbSession, branches.stream().map(BranchDto::getUuid).collect(toList()), singletonList(ALERT_STATUS_KEY)) + .stream().collect(uniqueIndex(LiveMeasureDto::getComponentUuid)); Map branchStatisticsByBranchUuid = issueIndex.searchBranchStatistics(project.uuid(), branches.stream() .filter(b -> b.getBranchType().equals(SHORT)) .map(BranchDto::getUuid).collect(toList())) @@ -113,15 +112,14 @@ public class ListAction implements BranchWsAction { .stream().collect(uniqueIndex(SnapshotDto::getComponentUuid, s -> formatDateTime(s.getCreatedAt()))); ProjectBranches.ListWsResponse.Builder protobufResponse = ProjectBranches.ListWsResponse.newBuilder(); - branches.stream() - .forEach(b -> addBranch(protobufResponse, b, mergeBranchesByUuid, qualityGateMeasuresByComponentUuids.get(b.getUuid()), branchStatisticsByBranchUuid.get(b.getUuid()), + branches.forEach(b -> addBranch(protobufResponse, b, mergeBranchesByUuid, qualityGateMeasuresByComponentUuids.get(b.getUuid()), branchStatisticsByBranchUuid.get(b.getUuid()), analysisDateByBranchUuid.get(b.getUuid()))); WsUtils.writeProtobuf(protobufResponse.build(), request, response); } } private static void addBranch(ProjectBranches.ListWsResponse.Builder response, BranchDto branch, Map mergeBranchesByUuid, - @Nullable MeasureDto qualityGateMeasure, BranchStatistics branchStatistics, @Nullable String analysisDate) { + @Nullable LiveMeasureDto qualityGateMeasure, BranchStatistics branchStatistics, @Nullable String analysisDate) { ProjectBranches.Branch.Builder builder = toBranchBuilder(branch, Optional.ofNullable(mergeBranchesByUuid.get(branch.getMergeBranchUuid()))); setBranchStatus(builder, branch, qualityGateMeasure, branchStatistics); if (analysisDate != null) { @@ -147,11 +145,11 @@ public class ListAction implements BranchWsAction { return builder; } - private static void setBranchStatus(ProjectBranches.Branch.Builder builder, BranchDto branch, @Nullable MeasureDto qualityGateMeasure, + private static void setBranchStatus(ProjectBranches.Branch.Builder builder, BranchDto branch, @Nullable LiveMeasureDto qualityGateMeasure, @Nullable BranchStatistics branchStatistics) { ProjectBranches.Branch.Status.Builder statusBuilder = ProjectBranches.Branch.Status.newBuilder(); if (branch.getBranchType() == LONG && qualityGateMeasure != null) { - statusBuilder.setQualityGateStatus(qualityGateMeasure.getData()); + Protobuf.setNullable(qualityGateMeasure.getDataAsString(), statusBuilder::setQualityGateStatus); } if (branch.getBranchType() == BranchType.SHORT) { statusBuilder.setBugs(branchStatistics == null ? 0L : branchStatistics.getBugs()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/AppAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/AppAction.java index 56d4f4aa89f..901da41760b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/AppAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/AppAction.java @@ -19,12 +19,10 @@ */ package org.sonar.server.component.ws; -import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; +import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.apache.commons.lang.BooleanUtils; @@ -37,8 +35,7 @@ import org.sonar.api.web.UserRole; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; -import org.sonar.db.measure.MeasureDto; -import org.sonar.db.measure.MeasureQuery; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.metric.MetricDto; import org.sonar.db.property.PropertyDto; import org.sonar.db.property.PropertyQuery; @@ -46,6 +43,8 @@ import org.sonar.server.component.ComponentFinder; import org.sonar.server.user.UserSession; import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableList; import static org.sonar.api.measures.CoreMetrics.COVERAGE; import static org.sonar.api.measures.CoreMetrics.COVERAGE_KEY; import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY; @@ -66,12 +65,12 @@ public class AppAction implements ComponentsWsAction { private static final String PARAM_COMPONENT_ID = "componentId"; private static final String PARAM_COMPONENT = "component"; - private static final List METRIC_KEYS = ImmutableList.of( + private static final List METRIC_KEYS = unmodifiableList(asList( LINES_KEY, VIOLATIONS_KEY, COVERAGE_KEY, DUPLICATED_LINES_DENSITY_KEY, - TESTS_KEY); + TESTS_KEY)); private final DbClient dbClient; @@ -121,7 +120,7 @@ public class AppAction implements ComponentsWsAction { JsonWriter json = response.newJsonWriter(); json.beginObject(); - Map measuresByMetricKey = measuresByMetricKey(component, session); + Map measuresByMetricKey = loadMeasuresGroupedByMetricKey(component, session); appendComponent(json, component, userSession, session); appendPermissions(json, userSession); appendMeasures(json, measuresByMetricKey); @@ -177,7 +176,7 @@ public class AppAction implements ComponentsWsAction { json.prop("canMarkAsFavorite", userSession.isLoggedIn()); } - private static void appendMeasures(JsonWriter json, Map measuresByMetricKey) { + private static void appendMeasures(JsonWriter json, Map measuresByMetricKey) { json.name("measures").beginObject(); json.prop("lines", formatMeasure(measuresByMetricKey, LINES)); json.prop("coverage", formatMeasure(measuresByMetricKey, COVERAGE)); @@ -187,12 +186,11 @@ public class AppAction implements ComponentsWsAction { json.endObject(); } - private Map measuresByMetricKey(ComponentDto component, DbSession session) { - MeasureQuery query = MeasureQuery.builder().setComponentUuid(component.uuid()).setMetricKeys(METRIC_KEYS).build(); - List measures = dbClient.measureDao().selectByQuery(session, query); - Set metricIds = measures.stream().map(MeasureDto::getMetricId).collect(Collectors.toSet()); - List metrics = dbClient.metricDao().selectByIds(session, metricIds); + private Map loadMeasuresGroupedByMetricKey(ComponentDto component, DbSession dbSession) { + List metrics = dbClient.metricDao().selectByKeys(dbSession, METRIC_KEYS); Map metricsById = Maps.uniqueIndex(metrics, MetricDto::getId); + List measures = dbClient.liveMeasureDao() + .selectByComponentUuids(dbSession, Collections.singletonList(component.uuid()), metricsById.keySet()); return Maps.uniqueIndex(measures, m -> metricsById.get(m.getMetricId()).getKey()); } @@ -205,12 +203,12 @@ public class AppAction implements ComponentsWsAction { } @CheckForNull - private static String formatMeasure(Map measuresByMetricKey, Metric metric) { - MeasureDto measure = measuresByMetricKey.get(metric.getKey()); + private static String formatMeasure(Map measuresByMetricKey, Metric metric) { + LiveMeasureDto measure = measuresByMetricKey.get(metric.getKey()); return formatMeasure(measure, metric); } - private static String formatMeasure(@Nullable MeasureDto measure, Metric metric) { + private static String formatMeasure(@Nullable LiveMeasureDto measure, Metric metric) { if (measure == null) { return null; } @@ -222,7 +220,7 @@ public class AppAction implements ComponentsWsAction { } @CheckForNull - private static Double getDoubleValue(MeasureDto measure, Metric metric) { + private static Double getDoubleValue(LiveMeasureDto measure, Metric metric) { Double value = measure.getValue(); if (BooleanUtils.isTrue(metric.isOptimizedBestValue()) && value == null) { value = metric.getBestValue(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/dbcleaner/ProjectCleaner.java b/server/sonar-server/src/main/java/org/sonar/server/computation/dbcleaner/ProjectCleaner.java index 1def57fd52b..455bfbcb8c7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/dbcleaner/ProjectCleaner.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/dbcleaner/ProjectCleaner.java @@ -54,11 +54,11 @@ public class ProjectCleaner { this.purgeListener = purgeListener; } - public ProjectCleaner purge(DbSession session, IdUuidPair idUuidPair, Configuration projectConfig, Collection disabledComponentUuids) { + public ProjectCleaner purge(DbSession session, IdUuidPair rootId, Configuration projectConfig, Collection disabledComponentUuids) { long start = System.currentTimeMillis(); profiler.reset(); - PurgeConfiguration configuration = newDefaultPurgeConfiguration(projectConfig, idUuidPair, disabledComponentUuids); + PurgeConfiguration configuration = newDefaultPurgeConfiguration(projectConfig, rootId, disabledComponentUuids); periodCleaner.clean(session, configuration.rootProjectIdUuid().getUuid(), projectConfig); purgeDao.purge(session, configuration, purgeListener, profiler); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/BestValueOptimization.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/BestValueOptimization.java index bce3c7c57a7..52f30c83cb1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/BestValueOptimization.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/BestValueOptimization.java @@ -19,8 +19,7 @@ */ package org.sonar.server.computation.task.projectanalysis.measure; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; +import java.util.function.Predicate; import javax.annotation.Nonnull; import org.sonar.server.computation.task.projectanalysis.component.Component; import org.sonar.server.computation.task.projectanalysis.metric.Metric; @@ -40,7 +39,7 @@ public class BestValueOptimization implements Predicate { if (isBestValueOptimized(metric) && isBestValueOptimized(component)) { return new BestValueOptimization(metric); } - return Predicates.alwaysFalse(); + return x -> false; } private static boolean isBestValueOptimized(Metric metric) { @@ -52,7 +51,7 @@ public class BestValueOptimization implements Predicate { } @Override - public boolean apply(@Nonnull Measure measure) { + public boolean test(@Nonnull Measure measure) { return isBestValueOptimized(measure); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MapBasedRawMeasureRepository.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MapBasedRawMeasureRepository.java index 751a962bf20..8410b834599 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MapBasedRawMeasureRepository.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MapBasedRawMeasureRepository.java @@ -24,7 +24,6 @@ import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.SetMultimap; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -60,11 +59,6 @@ public final class MapBasedRawMeasureRepository implements MeasureRepository throw new UnsupportedOperationException("This implementation of MeasureRepository supports only raw measures"); } - @Override - public int loadAsRawMeasures(Collection components, Collection metrics) { - throw new UnsupportedOperationException("This implementation of MeasureRepository supports only raw measures"); - } - @Override public Optional getRawMeasure(final Component component, final Metric metric) { // fail fast diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepository.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepository.java index ecc86ce8c5e..c5acb1d03c2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepository.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepository.java @@ -19,16 +19,13 @@ */ package org.sonar.server.computation.task.projectanalysis.measure; -import java.util.Collection; +import com.google.common.base.Optional; +import com.google.common.collect.SetMultimap; import java.util.Set; - import org.sonar.server.computation.task.projectanalysis.component.Component; import org.sonar.server.computation.task.projectanalysis.metric.Metric; import org.sonar.server.computation.task.projectanalysis.metric.MetricImpl; -import com.google.common.base.Optional; -import com.google.common.collect.SetMultimap; - public interface MeasureRepository { /** @@ -43,8 +40,6 @@ public interface MeasureRepository { */ Optional getBaseMeasure(Component component, Metric metric); - int loadAsRawMeasures(Collection components, Collection metrics); - /** * Retrieves the measure created during the current analysis for the specified {@link Component} for the specified * {@link Metric} if it exists (ie. one created by the Compute Engine or the Batch) and which is not diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepositoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepositoryImpl.java index 9a0942003b3..5755fb91cde 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepositoryImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepositoryImpl.java @@ -19,21 +19,14 @@ */ package org.sonar.server.computation.task.projectanalysis.measure; -import static java.util.Objects.requireNonNull; -import static org.sonar.server.computation.task.projectanalysis.component.ComponentFunctions.toReportRef; - -import java.util.Collection; +import com.google.common.base.Optional; +import com.google.common.collect.SetMultimap; import java.util.HashSet; -import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; - import org.sonar.core.util.CloseableIterator; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.measure.MeasureDto; -import org.sonar.db.measure.MeasureQuery; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReader; import org.sonar.server.computation.task.projectanalysis.component.Component; @@ -42,8 +35,8 @@ import org.sonar.server.computation.task.projectanalysis.metric.Metric; import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository; import org.sonar.server.computation.task.projectanalysis.metric.ReportMetricValidator; -import com.google.common.base.Optional; -import com.google.common.collect.SetMultimap; +import static java.util.Objects.requireNonNull; +import static org.sonar.server.computation.task.projectanalysis.component.ComponentFunctions.toReportRef; public class MeasureRepositoryImpl implements MeasureRepository { private final MapBasedRawMeasureRepository delegate = new MapBasedRawMeasureRepository<>(toReportRef()); @@ -72,8 +65,7 @@ public class MeasureRepositoryImpl implements MeasureRepository { requireNonNull(metric); try (DbSession dbSession = dbClient.openSession(false)) { - MeasureQuery query = MeasureQuery.builder().setComponentUuid(component.getUuid()).setMetricKey(metric.getKey()).build(); - java.util.Optional measureDto = dbClient.measureDao().selectSingle(dbSession, query); + java.util.Optional measureDto = dbClient.measureDao().selectLastMeasure(dbSession, component.getUuid(), metric.getKey()); if (measureDto.isPresent()) { return measureTransformer.toMeasure(measureDto.get(), metric); } @@ -81,32 +73,6 @@ public class MeasureRepositoryImpl implements MeasureRepository { } } - @Override - public int loadAsRawMeasures(Collection components, Collection metrics) { - requireNonNull(components); - requireNonNull(metrics); - - Map componentsByUuid = components.stream() - .collect(Collectors.toMap(Component::getUuid, c -> c)); - Map metricsById = metrics.stream() - .collect(Collectors.toMap(Metric::getId, m -> m)); - - List measuresDto; - try (DbSession dbSession = dbClient.openSession(false)) { - measuresDto = dbClient.measureDao().selectByComponentsAndMetrics(dbSession, componentsByUuid.keySet(), metricsById.keySet()); - } - - for (MeasureDto dto : measuresDto) { - - Metric metric = metricsById.get(dto.getMetricId()); - Component component = componentsByUuid.get(dto.getComponentUuid()); - Measure measure = measureTransformer.toMeasure(dto, metric).get(); - - delegate.add(component, metric, measure); - } - return measuresDto.size(); - } - @Override public Optional getRawMeasure(Component component, Metric metric) { Optional local = delegate.getRawMeasure(component, metric); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureToMeasureDto.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureToMeasureDto.java index cc1935fe7b2..1970b18db37 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureToMeasureDto.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureToMeasureDto.java @@ -20,25 +20,23 @@ package org.sonar.server.computation.task.projectanalysis.measure; import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.measure.MeasureDto; import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder; import org.sonar.server.computation.task.projectanalysis.component.Component; -import org.sonar.server.computation.task.projectanalysis.component.DbIdsRepository; -import org.sonar.server.computation.task.projectanalysis.component.Developer; +import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder; import org.sonar.server.computation.task.projectanalysis.metric.Metric; public class MeasureToMeasureDto { - private final DbIdsRepository dbIdsRepository; private final AnalysisMetadataHolder analysisMetadataHolder; + private final TreeRootHolder treeRootHolder; - public MeasureToMeasureDto(DbIdsRepository dbIdsRepository, AnalysisMetadataHolder analysisMetadataHolder) { - this.dbIdsRepository = dbIdsRepository; + public MeasureToMeasureDto(AnalysisMetadataHolder analysisMetadataHolder, TreeRootHolder treeRootHolder) { this.analysisMetadataHolder = analysisMetadataHolder; + this.treeRootHolder = treeRootHolder; } - @Nonnull public MeasureDto toMeasureDto(Measure measure, Metric metric, Component component) { MeasureDto out = new MeasureDto(); out.setMetricId(metric.getId()); @@ -50,9 +48,18 @@ public class MeasureToMeasureDto { if (measure.hasQualityGateStatus()) { setAlert(out, measure.getQualityGateStatus()); } - Developer developer = measure.getDeveloper(); - if (developer != null) { - out.setDeveloperId(dbIdsRepository.getDeveloperId(developer)); + out.setValue(valueAsDouble(measure)); + out.setData(data(measure)); + return out; + } + + public LiveMeasureDto toLiveMeasureDto(Measure measure, Metric metric, Component component) { + LiveMeasureDto out = new LiveMeasureDto(); + out.setMetricId(metric.getId()); + out.setComponentUuid(component.getUuid()); + out.setProjectUuid(treeRootHolder.getRoot().getUuid()); + if (measure.hasVariation()) { + out.setVariation(measure.getVariation()); } out.setValue(valueAsDouble(measure)); out.setData(data(measure)); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistLiveMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistLiveMeasuresStep.java new file mode 100644 index 00000000000..507f152f347 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistLiveMeasuresStep.java @@ -0,0 +1,133 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.computation.task.projectanalysis.step; + +import com.google.common.collect.Multimap; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; +import javax.annotation.Nonnull; +import org.sonar.core.util.Uuids; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.measure.LiveMeasureDao; +import org.sonar.server.computation.task.projectanalysis.component.Component; +import org.sonar.server.computation.task.projectanalysis.component.CrawlerDepthLimit; +import org.sonar.server.computation.task.projectanalysis.component.DepthTraversalTypeAwareCrawler; +import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder; +import org.sonar.server.computation.task.projectanalysis.component.TypeAwareVisitorAdapter; +import org.sonar.server.computation.task.projectanalysis.measure.BestValueOptimization; +import org.sonar.server.computation.task.projectanalysis.measure.Measure; +import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepository; +import org.sonar.server.computation.task.projectanalysis.measure.MeasureToMeasureDto; +import org.sonar.server.computation.task.projectanalysis.metric.Metric; +import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository; +import org.sonar.server.computation.task.step.ComputationStep; + +import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableSet; +import static org.sonar.api.measures.CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION_KEY; +import static org.sonar.api.measures.CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION_KEY; +import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY; +import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER; + +public class PersistLiveMeasuresStep implements ComputationStep { + + /** + * List of metrics that should not be persisted on file measure. + */ + private static final Set NOT_TO_PERSIST_ON_FILE_METRIC_KEYS = unmodifiableSet(new HashSet<>(asList( + FILE_COMPLEXITY_DISTRIBUTION_KEY, + FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, + CLASS_COMPLEXITY_DISTRIBUTION_KEY))); + + private final DbClient dbClient; + private final MetricRepository metricRepository; + private final MeasureToMeasureDto measureToMeasureDto; + private final TreeRootHolder treeRootHolder; + private final MeasureRepository measureRepository; + + public PersistLiveMeasuresStep(DbClient dbClient, MetricRepository metricRepository, MeasureToMeasureDto measureToMeasureDto, + TreeRootHolder treeRootHolder, MeasureRepository measureRepository) { + this.dbClient = dbClient; + this.metricRepository = metricRepository; + this.measureToMeasureDto = measureToMeasureDto; + this.treeRootHolder = treeRootHolder; + this.measureRepository = measureRepository; + } + + @Override + public String getDescription() { + return "Persist live measures"; + } + + @Override + public void execute() { + try (DbSession dbSession = dbClient.openSession(false)) { + String marker = Uuids.create(); + Component root = treeRootHolder.getRoot(); + new DepthTraversalTypeAwareCrawler(new MeasureVisitor(dbSession, marker)).visit(root); + dbClient.liveMeasureDao().deleteByProjectUuidExcludingMarker(dbSession, root.getUuid(), marker); + dbSession.commit(); + } + } + + private class MeasureVisitor extends TypeAwareVisitorAdapter { + private final DbSession dbSession; + private final String marker; + + private MeasureVisitor(DbSession dbSession, String marker) { + super(CrawlerDepthLimit.LEAVES, PRE_ORDER); + this.dbSession = dbSession; + this.marker = marker; + } + + @Override + public void visitAny(Component component) { + LiveMeasureDao dao = dbClient.liveMeasureDao(); + Multimap measures = measureRepository.getRawMeasures(component); + for (Map.Entry> measuresByMetricKey : measures.asMap().entrySet()) { + String metricKey = measuresByMetricKey.getKey(); + if (NOT_TO_PERSIST_ON_FILE_METRIC_KEYS.contains(metricKey) && component.getType() == Component.Type.FILE) { + continue; + } + Metric metric = metricRepository.getByKey(metricKey); + Predicate notBestValueOptimized = BestValueOptimization.from(metric, component).negate(); + measuresByMetricKey.getValue().stream() + .filter(NonEmptyMeasure.INSTANCE) + .filter(notBestValueOptimized) + .map(measure -> measureToMeasureDto.toLiveMeasureDto(measure, metric, component)) + .forEach(dto -> dao.insertOrUpdate(dbSession, dto, marker)); + } + } + } + + private enum NonEmptyMeasure implements Predicate { + INSTANCE; + + @Override + public boolean test(@Nonnull Measure input) { + return input.getValueType() != Measure.ValueType.NO_VALUE || input.hasVariation() || input.getData() != null; + } + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistMeasuresStep.java index 69d10747645..6c9f03a5427 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistMeasuresStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistMeasuresStep.java @@ -19,24 +19,23 @@ */ package org.sonar.server.computation.task.projectanalysis.step; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableList; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Multimap; import java.util.Collection; -import java.util.List; import java.util.Map; +import java.util.function.Predicate; import javax.annotation.Nonnull; +import org.sonar.core.config.PurgeConstants; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.measure.MeasureDao; import org.sonar.db.measure.MeasureDto; import org.sonar.server.computation.task.projectanalysis.component.Component; +import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepository; import org.sonar.server.computation.task.projectanalysis.component.CrawlerDepthLimit; import org.sonar.server.computation.task.projectanalysis.component.DepthTraversalTypeAwareCrawler; import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder; import org.sonar.server.computation.task.projectanalysis.component.TypeAwareVisitorAdapter; -import org.sonar.server.computation.task.projectanalysis.measure.BestValueOptimization; import org.sonar.server.computation.task.projectanalysis.measure.Measure; import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepository; import org.sonar.server.computation.task.projectanalysis.measure.MeasureToMeasureDto; @@ -44,35 +43,32 @@ import org.sonar.server.computation.task.projectanalysis.metric.Metric; import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository; import org.sonar.server.computation.task.step.ComputationStep; -import static com.google.common.collect.FluentIterable.from; -import static org.sonar.api.measures.CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION_KEY; -import static org.sonar.api.measures.CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION_KEY; -import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY; import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER; public class PersistMeasuresStep implements ComputationStep { - /** - * List of metrics that should not be persisted on file measure (Waiting for SONAR-6688 to be implemented) - */ - private static final List NOT_TO_PERSIST_ON_FILE_METRIC_KEYS = ImmutableList.of( - FILE_COMPLEXITY_DISTRIBUTION_KEY, - FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, - CLASS_COMPLEXITY_DISTRIBUTION_KEY); - private final DbClient dbClient; private final MetricRepository metricRepository; private final MeasureToMeasureDto measureToMeasureDto; private final TreeRootHolder treeRootHolder; private final MeasureRepository measureRepository; + private final boolean persistDirectories; public PersistMeasuresStep(DbClient dbClient, MetricRepository metricRepository, MeasureToMeasureDto measureToMeasureDto, - TreeRootHolder treeRootHolder, MeasureRepository measureRepository) { + TreeRootHolder treeRootHolder, MeasureRepository measureRepository, ConfigurationRepository settings) { + this(dbClient, metricRepository, measureToMeasureDto, treeRootHolder,measureRepository, + !settings.getConfiguration().getBoolean(PurgeConstants.PROPERTY_CLEAN_DIRECTORY).orElseThrow(() -> new IllegalStateException("Missing default value"))); + } + + @VisibleForTesting + PersistMeasuresStep(DbClient dbClient, MetricRepository metricRepository, MeasureToMeasureDto measureToMeasureDto, TreeRootHolder treeRootHolder, + MeasureRepository measureRepository, boolean persistDirectories) { this.dbClient = dbClient; this.metricRepository = metricRepository; this.measureToMeasureDto = measureToMeasureDto; this.treeRootHolder = treeRootHolder; this.measureRepository = measureRepository; + this.persistDirectories = persistDirectories; } @Override @@ -97,25 +93,47 @@ public class PersistMeasuresStep implements ComputationStep { } @Override - public void visitAny(Component component) { - Multimap measures = measureRepository.getRawMeasures(component); - persistMeasures(component, measures); + public void visitProject(Component project) { + persistMeasures(project); + } + + @Override + public void visitModule(Component module) { + persistMeasures(module); + } + + @Override + public void visitDirectory(Component directory) { + if (persistDirectories) { + persistMeasures(directory); + } + } + + @Override + public void visitView(Component view) { + persistMeasures(view); + } + + @Override + public void visitSubView(Component subView) { + persistMeasures(subView); } - private void persistMeasures(Component component, Multimap batchReportMeasures) { - for (Map.Entry> measures : batchReportMeasures.asMap().entrySet()) { - String metricKey = measures.getKey(); - if (NOT_TO_PERSIST_ON_FILE_METRIC_KEYS.contains(metricKey) && component.getType() == Component.Type.FILE) { - continue; - } + @Override + public void visitProjectView(Component projectView) { + persistMeasures(projectView); + } + private void persistMeasures(Component component) { + Multimap measures = measureRepository.getRawMeasures(component); + for (Map.Entry> measuresByMetricKey : measures.asMap().entrySet()) { + String metricKey = measuresByMetricKey.getKey(); Metric metric = metricRepository.getByKey(metricKey); - Predicate notBestValueOptimized = Predicates.not(BestValueOptimization.from(metric, component)); MeasureDao measureDao = dbClient.measureDao(); - for (Measure measure : from(measures.getValue()).filter(NonEmptyMeasure.INSTANCE).filter(notBestValueOptimized)) { + measuresByMetricKey.getValue().stream().filter(NonEmptyMeasure.INSTANCE).forEach(measure -> { MeasureDto measureDto = measureToMeasureDto.toMeasureDto(measure, metric, component); measureDao.insert(session, measureDto); - } + }); } } @@ -125,7 +143,7 @@ public class PersistMeasuresStep implements ComputationStep { INSTANCE; @Override - public boolean apply(@Nonnull Measure input) { + public boolean test(@Nonnull Measure input) { return input.getValueType() != Measure.ValueType.NO_VALUE || input.hasVariation() || input.getData() != null; } } 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 108ca3b9d84..d9b4a87e366 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 @@ -88,6 +88,7 @@ public class ReportComputationSteps extends AbstractComputationSteps { PersistAnalysisStep.class, PersistAnalysisPropertiesStep.class, PersistMeasuresStep.class, + PersistLiveMeasuresStep.class, PersistIssuesStep.class, PersistProjectLinksStep.class, PersistEventsStep.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowAction.java b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowAction.java index 73b428f2345..6136a64d95f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowAction.java @@ -30,8 +30,7 @@ import org.sonar.api.web.UserRole; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; -import org.sonar.db.measure.MeasureDto; -import org.sonar.db.measure.MeasureQuery; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.server.component.ComponentFinder; import org.sonar.server.user.UserSession; @@ -110,12 +109,8 @@ public class ShowAction implements DuplicationsWsAction { @CheckForNull private String findDataFromComponent(DbSession dbSession, ComponentDto component) { - MeasureQuery query = MeasureQuery.builder() - .setComponentUuid(component.uuid()) - .setMetricKey(CoreMetrics.DUPLICATIONS_DATA_KEY) - .build(); - return dbClient.measureDao().selectSingle(dbSession, query) - .map(MeasureDto::getData) + return dbClient.liveMeasureDao().selectMeasure(dbSession, component.uuid(), CoreMetrics.DUPLICATIONS_DATA_KEY) + .map(LiveMeasureDto::getDataAsString) .orElse(null); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentAction.java index 59633ac3d74..37fbe9a40f2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentAction.java @@ -43,25 +43,22 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.SnapshotDto; -import org.sonar.db.measure.MeasureDto; -import org.sonar.db.measure.MeasureQuery; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.metric.MetricDto; import org.sonar.db.metric.MetricDtoFunctions; import org.sonar.server.component.ComponentFinder; import org.sonar.server.exceptions.NotFoundException; -import org.sonar.server.measure.ws.MetricDtoWithBestValue.MetricDtoToMetricDtoWithBestValueFunction; import org.sonar.server.user.UserSession; import org.sonarqube.ws.Measures; import org.sonarqube.ws.Measures.ComponentWsResponse; import static com.google.common.base.Preconditions.checkArgument; import static java.lang.String.format; -import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01; import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_COMPONENT; -import static org.sonar.server.component.ComponentFinder.ParamNames.DEVELOPER_ID_AND_KEY; import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.componentDtoToWsComponent; import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAdditionalFieldsParameter; import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createDeveloperParameters; @@ -133,6 +130,10 @@ public class ComponentAction implements MeasuresWsAction { @Override public void handle(Request request, Response response) throws Exception { + if (request.param(PARAM_DEVELOPER_ID) != null || request.param(PARAM_DEVELOPER_KEY) != null) { + throw new NotFoundException("The Developer Cockpit feature has been dropped. The specified developer cannot be found."); + } + ComponentWsResponse componentWsResponse = doHandle(toComponentWsRequest(request)); writeProtobuf(componentWsResponse, request, response); } @@ -140,13 +141,12 @@ public class ComponentAction implements MeasuresWsAction { private ComponentWsResponse doHandle(ComponentRequest request) { try (DbSession dbSession = dbClient.openSession(false)) { ComponentDto component = loadComponent(dbSession, request); - Long developerId = searchDeveloperId(dbSession, request); Optional refComponent = getReferenceComponent(dbSession, component); checkPermissions(component); SnapshotDto analysis = dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, component.projectUuid()).orElse(null); List metrics = searchMetrics(dbSession, request); List periods = snapshotToWsPeriods(analysis); - List measures = searchMeasures(dbSession, component, analysis, metrics, developerId); + List measures = searchMeasures(dbSession, component, metrics); return buildResponse(request, component, refComponent, measures, metrics, periods); } @@ -162,15 +162,6 @@ public class ComponentAction implements MeasuresWsAction { : componentFinder.getByKeyAndBranch(dbSession, componentKey, branch); } - @CheckForNull - private Long searchDeveloperId(DbSession dbSession, ComponentRequest request) { - if (request.getDeveloperId() == null && request.getDeveloperKey() == null) { - return null; - } - - return componentFinder.getByUuidOrKey(dbSession, request.getDeveloperId(), request.getDeveloperKey(), DEVELOPER_ID_AND_KEY).getId(); - } - private Optional getReferenceComponent(DbSession dbSession, ComponentDto component) { if (component.getCopyResourceUuid() == null) { return Optional.absent(); @@ -179,12 +170,12 @@ public class ComponentAction implements MeasuresWsAction { return dbClient.componentDao().selectByUuid(dbSession, component.getCopyResourceUuid()); } - private static ComponentWsResponse buildResponse(ComponentRequest request, ComponentDto component, Optional refComponent, List measures, + private static ComponentWsResponse buildResponse(ComponentRequest request, ComponentDto component, Optional refComponent, List measures, List metrics, List periods) { ComponentWsResponse.Builder response = ComponentWsResponse.newBuilder(); Map metricsById = Maps.uniqueIndex(metrics, MetricDto::getId); - Map measuresByMetric = new HashMap<>(); - for (MeasureDto measure : measures) { + Map measuresByMetric = new HashMap<>(); + for (LiveMeasureDto measure : measures) { MetricDto metric = metricsById.get(measure.getMetricId()); measuresByMetric.put(metric, measure); } @@ -223,20 +214,10 @@ public class ComponentAction implements MeasuresWsAction { return metrics; } - private List searchMeasures(DbSession dbSession, ComponentDto component, @Nullable SnapshotDto analysis, List metrics, @Nullable Long developerId) { - if (analysis == null) { - return emptyList(); - } - + private List searchMeasures(DbSession dbSession, ComponentDto component, List metrics) { List metricIds = Lists.transform(metrics, MetricDto::getId); - MeasureQuery query = MeasureQuery.builder() - .setPersonId(developerId) - .setMetricIds(metricIds) - .setComponentUuid(component.uuid()) - .build(); - List measures = dbClient.measureDao().selectByQuery(dbSession, query); + List measures = dbClient.liveMeasureDao().selectByComponentUuids(dbSession, singletonList(component.uuid()), metricIds); addBestValuesToMeasures(measures, component, metrics); - return measures; } @@ -247,16 +228,16 @@ public class ComponentAction implements MeasuresWsAction { *
  • metric is optimized for best value
  • * */ - private static void addBestValuesToMeasures(List measures, ComponentDto component, List metrics) { + private static void addBestValuesToMeasures(List measures, ComponentDto component, List metrics) { if (!QUALIFIERS_ELIGIBLE_FOR_BEST_VALUE.contains(component.qualifier())) { return; } List metricWithBestValueList = metrics.stream() .filter(MetricDtoFunctions.isOptimizedForBestValue()) - .map(new MetricDtoToMetricDtoWithBestValueFunction()) + .map(MetricDtoWithBestValue::new) .collect(MoreCollectors.toList(metrics.size())); - Map measuresByMetricId = Maps.uniqueIndex(measures, MeasureDto::getMetricId); + Map measuresByMetricId = Maps.uniqueIndex(measures, LiveMeasureDto::getMetricId); for (MetricDtoWithBestValue metricWithBestValue : metricWithBestValueList) { if (measuresByMetricId.get(metricWithBestValue.getMetric().getId()) == null) { @@ -271,9 +252,7 @@ public class ComponentAction implements MeasuresWsAction { .setComponent(request.param(PARAM_COMPONENT)) .setBranch(request.param(PARAM_BRANCH)) .setAdditionalFields(request.paramAsStrings(PARAM_ADDITIONAL_FIELDS)) - .setMetricKeys(request.mandatoryParamAsStrings(PARAM_METRIC_KEYS)) - .setDeveloperId(request.param(PARAM_DEVELOPER_ID)) - .setDeveloperKey(request.param(PARAM_DEVELOPER_KEY)); + .setMetricKeys(request.mandatoryParamAsStrings(PARAM_METRIC_KEYS)); checkRequest(!componentRequest.getMetricKeys().isEmpty(), "At least one metric key must be provided"); return componentRequest; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java index 959d707acdf..66470ef3f8c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java @@ -22,7 +22,7 @@ package org.sonar.server.measure.ws; import java.util.Map; import org.sonar.core.util.Protobuf; import org.sonar.db.component.ComponentDto; -import org.sonar.db.measure.MeasureDto; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.metric.MetricDto; import org.sonarqube.ws.Measures; import org.sonarqube.ws.Measures.Component; @@ -32,7 +32,7 @@ class ComponentDtoToWsComponent { // static methods only } - static Component.Builder componentDtoToWsComponent(ComponentDto component, Map measuresByMetric, + static Component.Builder componentDtoToWsComponent(ComponentDto component, Map measuresByMetric, Map referenceComponentsByUuid) { Component.Builder wsComponent = componentDtoToWsComponent(component); @@ -43,7 +43,7 @@ class ComponentDtoToWsComponent { } Measures.Measure.Builder measureBuilder = Measures.Measure.newBuilder(); - for (Map.Entry entry : measuresByMetric.entrySet()) { + for (Map.Entry entry : measuresByMetric.entrySet()) { MeasureDtoToWsMeasure.updateMeasureBuilder(measureBuilder, entry.getKey(), entry.getValue()); wsComponent.addMeasures(measureBuilder); measureBuilder.clear(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java index e47c71bb1a6..4a1b94aa3e7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java @@ -61,7 +61,7 @@ import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTreeQuery; import org.sonar.db.component.ComponentTreeQuery.Strategy; import org.sonar.db.component.SnapshotDto; -import org.sonar.db.measure.MeasureDto; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.measure.MeasureTreeQuery; import org.sonar.db.metric.MetricDto; import org.sonar.db.metric.MetricDtoFunctions; @@ -94,8 +94,8 @@ import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetri import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriods.snapshotToWsPeriods; import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; -import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext; import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter; +import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext; import static org.sonar.server.ws.WsUtils.checkRequest; import static org.sonar.server.ws.WsUtils.writeProtobuf; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ACTION_COMPONENT_TREE; @@ -261,6 +261,10 @@ public class ComponentTreeAction implements MeasuresWsAction { } private ComponentTreeWsResponse doHandle(ComponentTreeRequest request) { + if (request.getDeveloperId() != null || request.getDeveloperKey() != null) { + return emptyResponse(null, request); + } + ComponentTreeData data = load(request); if (data.getComponents() == null) { return emptyResponse(data.getBaseComponent(), request); @@ -320,13 +324,15 @@ public class ComponentTreeAction implements MeasuresWsAction { return additionalFields != null && additionalFields.contains(ADDITIONAL_PERIODS); } - private static ComponentTreeWsResponse emptyResponse(ComponentDto baseComponent, ComponentTreeRequest request) { + private static ComponentTreeWsResponse emptyResponse(@Nullable ComponentDto baseComponent, ComponentTreeRequest request) { ComponentTreeWsResponse.Builder response = ComponentTreeWsResponse.newBuilder(); response.getPagingBuilder() .setPageIndex(request.getPage()) .setPageSize(request.getPageSize()) .setTotal(0); - response.setBaseComponent(componentDtoToWsComponent(baseComponent)); + if (baseComponent != null) { + response.setBaseComponent(componentDtoToWsComponent(baseComponent)); + } return response.build(); } @@ -395,14 +401,13 @@ public class ComponentTreeAction implements MeasuresWsAction { .setBaseComponent(baseComponent) .build(); } - Long developerId = searchDeveloperId(dbSession, wsRequest); ComponentTreeQuery componentTreeQuery = toComponentTreeQuery(wsRequest, baseComponent); List components = searchComponents(dbSession, componentTreeQuery); List metrics = searchMetrics(dbSession, wsRequest); Table measuresByComponentUuidAndMetric = searchMeasuresByComponentUuidAndMetric(dbSession, baseComponent, componentTreeQuery, components, - metrics, developerId); + metrics); components = filterComponents(components, measuresByComponentUuidAndMetric, metrics, wsRequest); components = sortComponents(components, wsRequest, metrics, measuresByComponentUuidAndMetric); @@ -432,15 +437,6 @@ public class ComponentTreeAction implements MeasuresWsAction { : componentFinder.getByKeyAndBranch(dbSession, componentKey, branch); } - @CheckForNull - private Long searchDeveloperId(DbSession dbSession, ComponentTreeRequest wsRequest) { - if (wsRequest.getDeveloperId() == null && wsRequest.getDeveloperKey() == null) { - return null; - } - - return componentFinder.getByUuidOrKey(dbSession, wsRequest.getDeveloperId(), wsRequest.getDeveloperKey(), DEVELOPER_ID_AND_KEY).getId(); - } - private Map searchReferenceComponentsById(DbSession dbSession, List components) { List referenceComponentUUids = components.stream() .map(ComponentDto::getCopyResourceUuid) @@ -483,20 +479,19 @@ public class ComponentTreeAction implements MeasuresWsAction { } private Table searchMeasuresByComponentUuidAndMetric(DbSession dbSession, ComponentDto baseComponent, - ComponentTreeQuery componentTreeQuery, List components, List metrics, @Nullable Long developerId) { + ComponentTreeQuery componentTreeQuery, List components, List metrics) { Map metricsById = Maps.uniqueIndex(metrics, MetricDto::getId); MeasureTreeQuery measureQuery = MeasureTreeQuery.builder() .setStrategy(MeasureTreeQuery.Strategy.valueOf(componentTreeQuery.getStrategy().name())) .setNameOrKeyQuery(componentTreeQuery.getNameOrKeyQuery()) .setQualifiers(componentTreeQuery.getQualifiers()) - .setPersonId(developerId) .setMetricIds(new ArrayList<>(metricsById.keySet())) .build(); Table measuresByComponentUuidAndMetric = HashBasedTable.create(components.size(), metrics.size()); - dbClient.measureDao().selectTreeByQuery(dbSession, baseComponent, measureQuery, result -> { - MeasureDto measureDto = result.getResultObject(); + dbClient.liveMeasureDao().selectTreeByQuery(dbSession, baseComponent, measureQuery, result -> { + LiveMeasureDto measureDto = result.getResultObject(); measuresByComponentUuidAndMetric.put( measureDto.getComponentUuid(), metricsById.get(measureDto.getMetricId()), diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeData.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeData.java index 16a7d522dbc..5a871110bb8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeData.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeData.java @@ -25,7 +25,7 @@ import java.util.Map; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.sonar.db.component.ComponentDto; -import org.sonar.db.measure.MeasureDto; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.metric.MetricDto; import org.sonarqube.ws.Measures; @@ -149,9 +149,9 @@ class ComponentTreeData { private String data; private double variation; - private Measure(MeasureDto measureDto) { + private Measure(LiveMeasureDto measureDto) { this.value = toPrimitive(measureDto.getValue()); - this.data = measureDto.getData(); + this.data = measureDto.getDataAsString(); this.variation = toPrimitive(measureDto.getVariation()); } @@ -176,7 +176,7 @@ class ComponentTreeData { return !isNaN(variation); } - static Measure createFromMeasureDto(MeasureDto measureDto) { + static Measure createFromMeasureDto(LiveMeasureDto measureDto) { return new Measure(measureDto); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeRequest.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeRequest.java index 909a133e1bc..79ed3e383dd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeRequest.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeRequest.java @@ -26,7 +26,6 @@ import javax.annotation.Nullable; class ComponentTreeRequest { private String baseComponentId; - private String baseComponentKey; private String component; private String branch; private String strategy; diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/MeasureDtoToWsMeasure.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/MeasureDtoToWsMeasure.java index 3764c46b903..c0bbad0037a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/MeasureDtoToWsMeasure.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/MeasureDtoToWsMeasure.java @@ -20,6 +20,7 @@ package org.sonar.server.measure.ws; import javax.annotation.Nullable; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.measure.MeasureDto; import org.sonar.db.metric.MetricDto; import org.sonarqube.ws.Measures; @@ -40,6 +41,12 @@ class MeasureDtoToWsMeasure { updateMeasureBuilder(measureBuilder, metricDto, value == null ? Double.NaN : value, measureDto.getData(), variation == null ? Double.NaN : variation); } + static void updateMeasureBuilder(Measure.Builder measureBuilder, MetricDto metricDto, LiveMeasureDto measureDto) { + Double value = measureDto.getValue(); + Double variation = measureDto.getVariation(); + updateMeasureBuilder(measureBuilder, metricDto, value == null ? Double.NaN : value, measureDto.getDataAsString(), variation == null ? Double.NaN : variation); + } + static void updateMeasureBuilder(Measure.Builder measureBuilder, MetricDto metric, double doubleValue, @Nullable String stringValue, double variation) { measureBuilder.setMetric(metric.getKey()); // a measure value can be null, new_violations metric for example diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/MetricDtoWithBestValue.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/MetricDtoWithBestValue.java index f4d920f1b30..a76ee049fd5 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/MetricDtoWithBestValue.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/MetricDtoWithBestValue.java @@ -22,12 +22,10 @@ package org.sonar.server.measure.ws; import com.google.common.collect.ImmutableSortedSet; import java.util.Locale; import java.util.Set; -import java.util.function.Function; import java.util.function.Predicate; -import javax.annotation.Nonnull; import org.sonar.api.resources.Qualifiers; import org.sonar.db.component.ComponentDto; -import org.sonar.db.measure.MeasureDto; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.metric.MetricDto; class MetricDtoWithBestValue { @@ -35,11 +33,11 @@ class MetricDtoWithBestValue { private static final Set QUALIFIERS_ELIGIBLE_FOR_BEST_VALUE = ImmutableSortedSet.of(Qualifiers.FILE, Qualifiers.UNIT_TEST_FILE); private final MetricDto metric; - private final MeasureDto bestValue; + private final LiveMeasureDto bestValue; MetricDtoWithBestValue(MetricDto metric) { this.metric = metric; - MeasureDto measure = new MeasureDto().setMetricId(metric.getId()); + LiveMeasureDto measure = new LiveMeasureDto().setMetricId(metric.getId()); boolean isNewTypeMetric = metric.getKey().toLowerCase(Locale.ENGLISH).startsWith(LOWER_CASE_NEW_METRIC_PREFIX); if (isNewTypeMetric) { measure.setVariation(metric.getBestValue()); @@ -54,19 +52,11 @@ class MetricDtoWithBestValue { return metric; } - MeasureDto getBestValue() { + LiveMeasureDto getBestValue() { return bestValue; } static Predicate isEligibleForBestValue() { return component -> QUALIFIERS_ELIGIBLE_FOR_BEST_VALUE.contains(component.qualifier()); } - - static class MetricDtoToMetricDtoWithBestValueFunction implements Function { - - @Override - public MetricDtoWithBestValue apply(@Nonnull MetricDto input) { - return new MetricDtoWithBestValue(input); - } - } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchAction.java index dbbda02517f..bdd6d5b2738 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchAction.java @@ -34,7 +34,7 @@ import org.sonar.core.util.stream.MoreCollectors; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; -import org.sonar.db.measure.MeasureDto; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.metric.MetricDto; import org.sonar.server.user.UserSession; import org.sonarqube.ws.Measures.Measure; @@ -105,7 +105,7 @@ public class SearchAction implements MeasuresWsAction { private SearchRequest request; private List projects; private List metrics; - private List measures; + private List measures; ResponseBuilder(Request httpRequest, DbSession dbSession) { this.dbSession = dbSession; @@ -161,10 +161,10 @@ public class SearchAction implements MeasuresWsAction { .collect(toList()); } - private List searchMeasures() { - return dbClient.measureDao().selectByComponentsAndMetrics(dbSession, - projects.stream().map(ComponentDto::uuid).collect(toList()), - metrics.stream().map(MetricDto::getId).collect(toList())); + private List searchMeasures() { + return dbClient.liveMeasureDao().selectByComponentUuids(dbSession, + projects.stream().map(ComponentDto::uuid).collect(MoreCollectors.toArrayList(projects.size())), + metrics.stream().map(MetricDto::getId).collect(MoreCollectors.toArrayList(metrics.size()))); } private SearchWsResponse buildResponse() { @@ -179,7 +179,7 @@ public class SearchAction implements MeasuresWsAction { Map componentNamesByKey = projects.stream().collect(toMap(ComponentDto::getDbKey, ComponentDto::name)); Map metricsById = metrics.stream().collect(toMap(MetricDto::getId, identity())); - Function dbMeasureToDbMetric = dbMeasure -> metricsById.get(dbMeasure.getMetricId()); + Function dbMeasureToDbMetric = dbMeasure -> metricsById.get(dbMeasure.getMetricId()); Function byMetricKey = Measure::getMetric; Function byComponentName = wsMeasure -> componentNamesByKey.get(wsMeasure.getComponent()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsAction.java index 37867d57e77..74f41d8fc9d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsAction.java @@ -19,12 +19,13 @@ */ package org.sonar.server.project.ws; -import java.util.List; -import java.util.function.Function; - import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; +import java.util.List; +import java.util.function.Function; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.resources.Qualifiers; import org.sonar.api.server.ws.Change; @@ -39,19 +40,15 @@ import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentLinkDto; import org.sonar.db.component.ComponentQuery; import org.sonar.db.component.SnapshotDto; -import org.sonar.db.measure.MeasureDto; -import org.sonar.db.measure.MeasureQuery; -import org.sonar.db.metric.MetricDto; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.server.user.UserSession; import org.sonarqube.ws.Projects.SearchMyProjectsWsResponse; import org.sonarqube.ws.Projects.SearchMyProjectsWsResponse.Link; import org.sonarqube.ws.Projects.SearchMyProjectsWsResponse.Project; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - import static com.google.common.base.Strings.emptyToNull; import static com.google.common.base.Strings.isNullOrEmpty; +import static java.util.Collections.singletonList; import static java.util.Objects.requireNonNull; import static org.sonar.api.utils.Paging.offset; import static org.sonar.core.util.Protobuf.setNullable; @@ -177,12 +174,8 @@ public class SearchMyProjectsAction implements ProjectsWsAction { List projectUuids = Lists.transform(projects, ComponentDto::projectUuid); List projectLinks = dbClient.componentLinkDao().selectByComponentUuids(dbSession, projectUuids); List snapshots = dbClient.snapshotDao().selectLastAnalysesByRootComponentUuids(dbSession, projectUuids); - MetricDto gateStatusMetric = dbClient.metricDao().selectOrFailByKey(dbSession, CoreMetrics.ALERT_STATUS_KEY); - MeasureQuery measureQuery = MeasureQuery.builder() - .setProjectUuids(projectUuids) - .setMetricId(gateStatusMetric.getId()) - .build(); - List qualityGates = dbClient.measureDao().selectByQuery(dbSession, measureQuery); + List qualityGates = dbClient.liveMeasureDao() + .selectByComponentUuidsAndMetricKeys(dbSession, projectUuids, singletonList(CoreMetrics.ALERT_STATUS_KEY)); data.setProjects(projects) .setProjectLinks(projectLinks) diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsData.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsData.java index 9e389e76aef..49f746d3a8c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsData.java +++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsData.java @@ -29,7 +29,7 @@ import java.util.stream.Collectors; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentLinkDto; import org.sonar.db.component.SnapshotDto; -import org.sonar.db.measure.MeasureDto; +import org.sonar.db.measure.LiveMeasureDto; import static com.google.common.collect.ImmutableList.copyOf; import static java.util.Objects.requireNonNull; @@ -86,16 +86,16 @@ class SearchMyProjectsData { snapshot -> formatDateTime(snapshot.getCreatedAt())))); } - private static Map buildQualityGateStatuses(List measures) { + private static Map buildQualityGateStatuses(List measures) { return ImmutableMap.copyOf(measures.stream() - .collect(Collectors.toMap(MeasureDto::getComponentUuid, MeasureDto::getData))); + .collect(Collectors.toMap(LiveMeasureDto::getComponentUuid, LiveMeasureDto::getDataAsString))); } static class Builder { private List projects; private List projectLinks; private List snapshots; - private List qualityGates; + private List qualityGates; private Integer totalNbOfProjects; private Builder() { @@ -117,7 +117,7 @@ class SearchMyProjectsData { return this; } - public Builder setQualityGates(List qGateStatuses) { + public Builder setQualityGates(List qGateStatuses) { this.qualityGates = qGateStatuses; return this; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ProjectStatusAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ProjectStatusAction.java index 53c9b817282..7bd685e3203 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ProjectStatusAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ProjectStatusAction.java @@ -19,11 +19,11 @@ */ package org.sonar.server.qualitygate.ws; -import com.google.common.base.Optional; import java.util.Arrays; -import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.server.ws.Change; import org.sonar.api.server.ws.Request; @@ -35,8 +35,8 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.SnapshotDto; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.measure.MeasureDto; -import org.sonar.db.measure.MeasureQuery; import org.sonar.server.component.ComponentFinder; import org.sonar.server.component.ComponentFinder.ParamNames; import org.sonar.server.exceptions.BadRequestException; @@ -45,7 +45,6 @@ import org.sonar.server.ws.KeyExamples; import org.sonarqube.ws.Qualitygates.ProjectStatusResponse; import static com.google.common.base.Strings.isNullOrEmpty; -import static java.util.Collections.singletonList; import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException; import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; import static org.sonar.server.ws.WsUtils.checkRequest; @@ -88,8 +87,7 @@ public class ProjectStatusAction implements QualityGatesWsAction { .setSince("5.3") .setHandler(this) .setChangelog( - new Change("6.4", "The field 'ignoredConditions' is added to the response") - ); + new Change("6.4", "The field 'ignoredConditions' is added to the response")); action.createParam(PARAM_ANALYSIS_ID) .setDescription("Analysis id") @@ -120,11 +118,11 @@ public class ProjectStatusAction implements QualityGatesWsAction { writeProtobuf(projectStatusResponse, request, response); } - private ProjectStatusResponse doHandle(String analysisId, String projectId, String projectKey) { + private ProjectStatusResponse doHandle(@Nullable String analysisId, @Nullable String projectId, @Nullable String projectKey) { try (DbSession dbSession = dbClient.openSession(false)) { ProjectAndSnapshot projectAndSnapshot = getProjectAndSnapshot(dbSession, analysisId, projectId, projectKey); checkPermission(projectAndSnapshot.project); - Optional measureData = getQualityGateDetailsMeasureData(dbSession, projectAndSnapshot.project); + Optional measureData = loadQualityGateDetails(dbSession, projectAndSnapshot, analysisId != null); return ProjectStatusResponse.newBuilder() .setProjectStatus(new QualityGateDetailsFormatter(measureData, projectAndSnapshot.snapshotDto).format()) @@ -132,10 +130,11 @@ public class ProjectStatusAction implements QualityGatesWsAction { } } - private ProjectAndSnapshot getProjectAndSnapshot(DbSession dbSession, String analysisId, String projectId, String projectKey) { + private ProjectAndSnapshot getProjectAndSnapshot(DbSession dbSession, @Nullable String analysisId, @Nullable String projectId, @Nullable String projectKey) { if (!isNullOrEmpty(analysisId)) { return getSnapshotThenProject(dbSession, analysisId); - } else if (!isNullOrEmpty(projectId) ^ !isNullOrEmpty(projectKey)) { + } + if (!isNullOrEmpty(projectId) ^ !isNullOrEmpty(projectKey)) { return getProjectThenSnapshot(dbSession, projectId, projectKey); } @@ -144,7 +143,7 @@ public class ProjectStatusAction implements QualityGatesWsAction { private ProjectAndSnapshot getProjectThenSnapshot(DbSession dbSession, String projectId, String projectKey) { ComponentDto projectDto = componentFinder.getByUuidOrKey(dbSession, projectId, projectKey, ParamNames.PROJECT_ID_AND_KEY); - java.util.Optional snapshot = dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, projectDto.projectUuid()); + Optional snapshot = dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, projectDto.projectUuid()); return new ProjectAndSnapshot(projectDto, snapshot.orElse(null)); } @@ -155,20 +154,24 @@ public class ProjectStatusAction implements QualityGatesWsAction { } private SnapshotDto getSnapshot(DbSession dbSession, String analysisUuid) { - java.util.Optional snapshotDto = dbClient.snapshotDao().selectByUuid(dbSession, analysisUuid); + Optional snapshotDto = dbClient.snapshotDao().selectByUuid(dbSession, analysisUuid); return checkFoundWithOptional(snapshotDto, "Analysis with id '%s' is not found", analysisUuid); } - private Optional getQualityGateDetailsMeasureData(DbSession dbSession, ComponentDto project) { - MeasureQuery measureQuery = MeasureQuery.builder() - .setProjectUuids(singletonList(project.projectUuid())) - .setMetricKey(CoreMetrics.QUALITY_GATE_DETAILS_KEY) - .build(); - List measures = dbClient.measureDao().selectByQuery(dbSession, measureQuery); + private Optional loadQualityGateDetails(DbSession dbSession, ProjectAndSnapshot projectAndSnapshot, boolean onAnalysis) { + if (onAnalysis) { + if (!projectAndSnapshot.snapshotDto.isPresent()) { + return Optional.empty(); + } + // get the gate status as it was computed during the specified analysis + String analysisUuid = projectAndSnapshot.snapshotDto.get().getUuid(); + return dbClient.measureDao().selectMeasure(dbSession, analysisUuid, projectAndSnapshot.project.projectUuid(), CoreMetrics.QUALITY_GATE_DETAILS_KEY) + .map(MeasureDto::getData); + } - return measures.isEmpty() - ? Optional.absent() - : Optional.fromNullable(measures.get(0).getData()); + // do not restrict to a specified analysis, use the live measure + Optional measure = dbClient.liveMeasureDao().selectMeasure(dbSession, projectAndSnapshot.project.projectUuid(), CoreMetrics.QUALITY_GATE_DETAILS_KEY); + return measure.map(LiveMeasureDto::getDataAsString); } private void checkPermission(ComponentDto project) { @@ -178,13 +181,14 @@ public class ProjectStatusAction implements QualityGatesWsAction { } } + @Immutable private static class ProjectAndSnapshot { private final ComponentDto project; private final Optional snapshotDto; private ProjectAndSnapshot(ComponentDto project, @Nullable SnapshotDto snapshotDto) { this.project = project; - this.snapshotDto = Optional.fromNullable(snapshotDto); + this.snapshotDto = Optional.ofNullable(snapshotDto); } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java index 84c902f8729..10dfbb4831d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java @@ -19,11 +19,11 @@ */ package org.sonar.server.qualitygate.ws; -import com.google.common.base.Optional; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import java.util.Optional; import java.util.function.Predicate; import java.util.stream.StreamSupport; import javax.annotation.Nullable; diff --git a/server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentAction.java b/server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentAction.java index a55d2ebcfe8..34156db7759 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentAction.java @@ -26,9 +26,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; -import java.util.function.Function; import java.util.function.Predicate; -import java.util.stream.Stream; import javax.annotation.Nullable; import org.sonar.api.resources.Qualifiers; import org.sonar.api.resources.ResourceType; @@ -45,8 +43,7 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.SnapshotDto; -import org.sonar.db.measure.MeasureDto; -import org.sonar.db.measure.MeasureQuery; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.permission.OrganizationPermission; import org.sonar.db.property.PropertyDto; @@ -166,10 +163,6 @@ public class ComponentAction implements NavigationWsAction { .endObject(); } - private static Function> toQualityProfiles() { - return dbMeasure -> QPMeasureData.fromJson(dbMeasure.getData()).getProfiles().stream(); - } - private static void writePage(JsonWriter json, Page page) { json.beginObject() .prop("key", page.getKey()) @@ -205,12 +198,11 @@ public class ComponentAction implements NavigationWsAction { return componentFavourites.size() == 1; } - private void writeProfiles(JsonWriter json, DbSession session, ComponentDto component) { + private void writeProfiles(JsonWriter json, DbSession dbSession, ComponentDto component) { json.name("qualityProfiles").beginArray(); - dbClient.measureDao().selectSingle(session, MeasureQuery.builder().setComponentUuid(component.projectUuid()).setMetricKey(QUALITY_PROFILES_KEY).build()) - .ifPresent(dbMeasure -> Stream.of(dbMeasure) - .flatMap(toQualityProfiles()) - .forEach(writeToJson(json))); + dbClient.liveMeasureDao().selectMeasure(dbSession, component.projectUuid(), QUALITY_PROFILES_KEY) + .map(LiveMeasureDto::getDataAsString) + .ifPresent(data -> QPMeasureData.fromJson(data).getProfiles().forEach(writeToJson(json))); json.endArray(); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/branch/ws/ListActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/branch/ws/ListActionTest.java index bb8a93ae72b..2415c9cc93a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/branch/ws/ListActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/branch/ws/ListActionTest.java @@ -34,7 +34,6 @@ import org.sonar.db.component.BranchType; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; import org.sonar.db.component.ResourceTypesRule; -import org.sonar.db.component.SnapshotDto; import org.sonar.db.component.SnapshotTesting; import org.sonar.db.metric.MetricDto; import org.sonar.db.organization.OrganizationDto; @@ -250,8 +249,7 @@ public class ListActionTest { ComponentDto project = db.components().insertMainBranch(); userSession.logIn().addProjectPermission(UserRole.USER, project); ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG)); - SnapshotDto branchAnalysis = db.components().insertSnapshot(branch); - db.measures().insertMeasure(branch, branchAnalysis, qualityGateStatus, m -> m.setData("OK")); + db.measures().insertLiveMeasure(branch, qualityGateStatus, m -> m.setData("OK")); ListWsResponse response = ws.newRequest() .setParam("project", project.getKey()) diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/AppActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/AppActionTest.java index 7eb2c50a074..be66db8cebe 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/AppActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/AppActionTest.java @@ -25,7 +25,6 @@ import org.junit.rules.ExpectedException; import org.sonar.api.server.ws.WebService; import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; -import org.sonar.db.component.SnapshotDto; import org.sonar.db.metric.MetricDto; import org.sonar.server.component.TestComponentFinder; import org.sonar.server.exceptions.ForbiddenException; @@ -135,19 +134,18 @@ public class AppActionTest { ComponentDto project = db.components().insertPrivateProject(); ComponentDto directory = db.components().insertComponent(newDirectory(project, "src")); ComponentDto file = db.components().insertComponent(newFileDto(project, directory)); - SnapshotDto analysis = db.components().insertSnapshot(project); MetricDto lines = db.measures().insertMetric(m -> m.setKey(LINES_KEY)); - db.measures().insertMeasure(file, analysis, lines, m -> m.setValue(200d)); + db.measures().insertLiveMeasure(file, lines, m -> m.setValue(200d)); MetricDto duplicatedLines = db.measures().insertMetric(m -> m.setKey(DUPLICATED_LINES_DENSITY_KEY)); - db.measures().insertMeasure(file, analysis, duplicatedLines, m -> m.setValue(7.4)); + db.measures().insertLiveMeasure(file, duplicatedLines, m -> m.setValue(7.4)); MetricDto tests = db.measures().insertMetric(m -> m.setKey(TESTS_KEY)); - db.measures().insertMeasure(file, analysis, tests, m -> m.setValue(3d)); + db.measures().insertLiveMeasure(file, tests, m -> m.setValue(3d)); MetricDto technicalDebt = db.measures().insertMetric(m -> m.setKey(TECHNICAL_DEBT_KEY)); - db.measures().insertMeasure(file, analysis, technicalDebt, m -> m.setValue(182d)); + db.measures().insertLiveMeasure(file, technicalDebt, m -> m.setValue(182d)); MetricDto issues = db.measures().insertMetric(m -> m.setKey(VIOLATIONS_KEY)); - db.measures().insertMeasure(file, analysis, issues, m -> m.setValue(231d)); + db.measures().insertLiveMeasure(file, issues, m -> m.setValue(231d)); MetricDto coverage = db.measures().insertMetric(m -> m.setKey(COVERAGE_KEY)); - db.measures().insertMeasure(file, analysis, coverage, m -> m.setValue(95.4d)); + db.measures().insertLiveMeasure(file, coverage, m -> m.setValue(95.4d)); userSession.logIn("john").addProjectPermission(USER, project); String result = ws.newRequest() @@ -170,9 +168,8 @@ public class AppActionTest { public void get_by_uuid() { ComponentDto project = db.components().insertPrivateProject(); ComponentDto file = db.components().insertComponent(newFileDto(project, project)); - SnapshotDto analysis = db.components().insertSnapshot(project); MetricDto coverage = db.measures().insertMetric(m -> m.setKey(COVERAGE_KEY)); - db.measures().insertMeasure(file, analysis, coverage, m -> m.setValue(95.4d)); + db.measures().insertLiveMeasure(file, coverage, m -> m.setValue(95.4d)); userSession.logIn("john").addProjectPermission(USER, project); String result = ws.newRequest() @@ -258,9 +255,8 @@ public class AppActionTest { ComponentDto module = db.components().insertComponent(newModuleDto(branch)); ComponentDto directory = db.components().insertComponent(newDirectory(module, "src")); ComponentDto file = db.components().insertComponent(newFileDto(module, directory)); - SnapshotDto analysis = db.components().insertSnapshot(branch); MetricDto coverage = db.measures().insertMetric(m -> m.setKey(COVERAGE_KEY)); - db.measures().insertMeasure(file, analysis, coverage, m -> m.setValue(95.4d)); + db.measures().insertLiveMeasure(file, coverage, m -> m.setValue(95.4d)); String result = ws.newRequest() .setParam("component", file.getKey()) diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java index 77ba51d4a7e..afddc8ae32c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java @@ -41,8 +41,7 @@ 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.measure.MeasureDto; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.metric.MetricDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.property.PropertyDto; @@ -316,9 +315,9 @@ public class SearchProjectsActionTest { userSession.logIn(); OrganizationDto organizationDto = db.organizations().insert(); MetricDto qualityGateStatus = db.measures().insertMetric(c -> c.setKey(QUALITY_GATE_STATUS).setValueType(LEVEL.name())); - ComponentDto project1 = insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setData("OK"))); - ComponentDto project2 = insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setData("OK"))); - ComponentDto project3 = insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setData("ERROR"))); + ComponentDto project1 = insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK"))); + ComponentDto project2 = insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK"))); + ComponentDto project3 = insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setValue(null).setData("ERROR"))); SearchProjectsWsResponse result = call(request.setFilter("alert_status = OK")); @@ -332,10 +331,10 @@ public class SearchProjectsActionTest { userSession.logIn(); OrganizationDto organizationDto = db.organizations().insert(); MetricDto languagesDistribution = db.measures().insertMetric(c -> c.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY).setValueType(DATA.name())); - ComponentDto project1 = insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("=2;java=6;xoo=18"))); - ComponentDto project2 = insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("java=3;xoo=9"))); - ComponentDto project3 = insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("xoo=1"))); - ComponentDto project4 = insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("=1;java=5;xoo=13"))); + ComponentDto project1 = insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("=2;java=6;xoo=18"))); + ComponentDto project2 = insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("java=3;xoo=9"))); + ComponentDto project3 = insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("xoo=1"))); + ComponentDto project4 = insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("=1;java=5;xoo=13"))); SearchProjectsWsResponse result = call(request.setFilter("languages IN (java, js, )")); @@ -628,10 +627,10 @@ public class SearchProjectsActionTest { userSession.logIn(); OrganizationDto organizationDto = db.organizations().insert(); MetricDto languagesDistribution = db.measures().insertMetric(c -> c.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY).setValueType(DATA.name())); - insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("=2;java=6;xoo=18"))); - insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("java=5;xoo=19"))); - insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("xoo=1"))); - insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("=1;java=3;xoo=8"))); + insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("=2;java=6;xoo=18"))); + insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("java=5;xoo=19"))); + insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("xoo=1"))); + insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("=1;java=3;xoo=8"))); SearchProjectsWsResponse result = call(request.setFacets(singletonList(FILTER_LANGUAGES))); @@ -651,8 +650,8 @@ public class SearchProjectsActionTest { userSession.logIn(); OrganizationDto organizationDto = db.organizations().insert(); MetricDto languagesDistribution = db.measures().insertMetric(c -> c.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY).setValueType(DATA.name())); - insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("=2;java=6"))); - insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("java=5"))); + insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("=2;java=6"))); + insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("java=5"))); SearchProjectsWsResponse result = call(request.setFilter("languages = xoo").setFacets(singletonList(FILTER_LANGUAGES))); @@ -964,10 +963,10 @@ public class SearchProjectsActionTest { userSession.logIn(); OrganizationDto organization = db.organizations().insert(); MetricDto qualityGateStatus = db.measures().insertMetric(c -> c.setKey(QUALITY_GATE_STATUS).setValueType(LEVEL.name())); - ComponentDto project1 = insertProject(organization, c -> c.setName("Sonar Java"), new Measure(qualityGateStatus, c -> c.setData("ERROR"))); - ComponentDto project2 = insertProject(organization, c -> c.setName("Sonar Groovy"), new Measure(qualityGateStatus, c -> c.setData("WARN"))); - ComponentDto project3 = insertProject(organization, c -> c.setName("Sonar Markdown"), new Measure(qualityGateStatus, c -> c.setData("OK"))); - ComponentDto project4 = insertProject(organization, c -> c.setName("Sonar Qube"), new Measure(qualityGateStatus, c -> c.setData("OK"))); + ComponentDto project1 = insertProject(organization, c -> c.setName("Sonar Java"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("ERROR"))); + ComponentDto project2 = insertProject(organization, c -> c.setName("Sonar Groovy"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("WARN"))); + ComponentDto project3 = insertProject(organization, c -> c.setName("Sonar Markdown"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK"))); + ComponentDto project4 = insertProject(organization, c -> c.setName("Sonar Qube"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK"))); assertThat(call(request.setSort(QUALITY_GATE_STATUS).setAsc(true)).getComponentsList()).extracting(Component::getKey) .containsExactly(project3.getDbKey(), project4.getDbKey(), project2.getDbKey(), project1.getDbKey()); @@ -1138,8 +1137,7 @@ public class SearchProjectsActionTest { private ComponentDto insertProject(OrganizationDto organizationDto, Consumer projectConsumer, Measure... measures) { ComponentDto project = db.components().insertPublicProject(organizationDto, projectConsumer); - SnapshotDto analysis = db.components().insertSnapshot(project); - Arrays.stream(measures).forEach(m -> db.measures().insertMeasure(project, analysis, m.metric, m.consumer)); + Arrays.stream(measures).forEach(m -> db.measures().insertLiveMeasure(project, m.metric, m.consumer)); authorizationIndexerTester.allowOnlyAnyone(project); projectMeasuresIndexer.indexOnAnalysis(project.uuid()); return project; @@ -1147,9 +1145,9 @@ public class SearchProjectsActionTest { private static class Measure { private final MetricDto metric; - private final Consumer consumer; + private final Consumer consumer; - public Measure(MetricDto metric, Consumer consumer) { + public Measure(MetricDto metric, Consumer consumer) { this.metric = metric; this.consumer = consumer; } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/BestValueOptimizationTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/BestValueOptimizationTest.java index 6ad0fbe2841..76d170ac3bd 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/BestValueOptimizationTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/BestValueOptimizationTest.java @@ -19,7 +19,7 @@ */ package org.sonar.server.computation.task.projectanalysis.measure; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import org.junit.Test; import org.sonar.server.computation.task.projectanalysis.component.Component; import org.sonar.server.computation.task.projectanalysis.component.ReportComponent; @@ -56,18 +56,18 @@ public class BestValueOptimizationTest { public void apply_returns_true_for_value_true_for_Boolean_Metric_and_best_value_1() { Predicate underTest = BestValueOptimization.from(METRIC_BOOLEAN_TRUE, FILE_COMPONENT); - assertThat(underTest.apply(newMeasureBuilder().create(true))).isTrue(); - assertThat(underTest.apply(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(true))).isTrue(); - assertThat(underTest.apply(newMeasureBuilder().create(false))).isFalse(); - assertThat(underTest.apply(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(false))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().create(true))).isTrue(); + assertThat(underTest.test(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(true))).isTrue(); + assertThat(underTest.test(newMeasureBuilder().create(false))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(false))).isFalse(); } @Test public void apply_returns_false_if_component_is_not_a_FILE_for_Boolean_Metric_and_best_value_1() { Predicate underTest = BestValueOptimization.from(METRIC_BOOLEAN_TRUE, SOME_NON_FILE_COMPONENT); - assertThat(underTest.apply(newMeasureBuilder().create(true))).isFalse(); - assertThat(underTest.apply(newMeasureBuilder().create(false))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().create(true))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().create(false))).isFalse(); } @Test @@ -75,8 +75,8 @@ public class BestValueOptimizationTest { Predicate underTest = BestValueOptimization.from(METRIC_BOOLEAN_TRUE, FILE_COMPONENT); for (Measure.NewMeasureBuilder builder : builders_of_non_bestValueOptimized_measures()) { - assertThat(underTest.apply(builder.create(true))).isFalse(); - assertThat(underTest.apply(builder.create(false))).isFalse(); + assertThat(underTest.test(builder.create(true))).isFalse(); + assertThat(underTest.test(builder.create(false))).isFalse(); } } @@ -84,25 +84,25 @@ public class BestValueOptimizationTest { public void apply_returns_false_if_measure_has_data_for_Boolean_Metric_and_best_value_1() { Predicate underTest = BestValueOptimization.from(METRIC_BOOLEAN_TRUE, FILE_COMPONENT); - assertThat(underTest.apply(newMeasureBuilder().create(true, SOME_DATA))).isFalse(); - assertThat(underTest.apply(newMeasureBuilder().create(false, SOME_DATA))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().create(true, SOME_DATA))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().create(false, SOME_DATA))).isFalse(); } @Test public void apply_returns_true_for_value_false_for_Boolean_Metric_and_best_value_not_1() { Predicate underTest = BestValueOptimization.from(METRIC_BOOLEAN_FALSE, FILE_COMPONENT); - assertThat(underTest.apply(newMeasureBuilder().create(true))).isFalse(); - assertThat(underTest.apply(newMeasureBuilder().create(false))).isTrue(); - assertThat(underTest.apply(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(false))).isTrue(); + assertThat(underTest.test(newMeasureBuilder().create(true))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().create(false))).isTrue(); + assertThat(underTest.test(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(false))).isTrue(); } @Test public void apply_returns_false_if_component_is_not_a_FILE_for_Boolean_Metric_and_best_value_not_1() { Predicate underTest = BestValueOptimization.from(METRIC_BOOLEAN_FALSE, SOME_NON_FILE_COMPONENT); - assertThat(underTest.apply(newMeasureBuilder().create(true))).isFalse(); - assertThat(underTest.apply(newMeasureBuilder().create(false))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().create(true))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().create(false))).isFalse(); } @Test @@ -110,8 +110,8 @@ public class BestValueOptimizationTest { Predicate underTest = BestValueOptimization.from(METRIC_BOOLEAN_FALSE, FILE_COMPONENT); for (Measure.NewMeasureBuilder builder : builders_of_non_bestValueOptimized_measures()) { - assertThat(underTest.apply(builder.create(true))).isFalse(); - assertThat(underTest.apply(builder.create(false))).isFalse(); + assertThat(underTest.test(builder.create(true))).isFalse(); + assertThat(underTest.test(builder.create(false))).isFalse(); } } @@ -119,59 +119,59 @@ public class BestValueOptimizationTest { public void apply_returns_false_if_measure_has_data_for_Boolean_Metric_and_best_value_not_1() { Predicate underTest = BestValueOptimization.from(METRIC_BOOLEAN_FALSE, FILE_COMPONENT); - assertThat(underTest.apply(newMeasureBuilder().create(true, SOME_DATA))).isFalse(); - assertThat(underTest.apply(newMeasureBuilder().create(false, SOME_DATA))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().create(true, SOME_DATA))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().create(false, SOME_DATA))).isFalse(); } @Test public void verify_value_comparison_for_int_metric() { Predicate underTest = BestValueOptimization.from(createMetric(Metric.MetricType.INT, 10), FILE_COMPONENT); - assertThat(underTest.apply(newMeasureBuilder().create(10))).isTrue(); - assertThat(underTest.apply(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(10))).isTrue(); - assertThat(underTest.apply(newMeasureBuilder().create(11))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().create(10))).isTrue(); + assertThat(underTest.test(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(10))).isTrue(); + assertThat(underTest.test(newMeasureBuilder().create(11))).isFalse(); } @Test public void verify_value_comparison_for_long_metric() { Predicate underTest = BestValueOptimization.from(createMetric(Metric.MetricType.WORK_DUR, 9511L), FILE_COMPONENT); - assertThat(underTest.apply(newMeasureBuilder().create(9511L))).isTrue(); - assertThat(underTest.apply(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(9511L))).isTrue(); - assertThat(underTest.apply(newMeasureBuilder().create(963L))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().create(9511L))).isTrue(); + assertThat(underTest.test(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(9511L))).isTrue(); + assertThat(underTest.test(newMeasureBuilder().create(963L))).isFalse(); } @Test public void verify_value_comparison_for_rating_metric() { Predicate underTest = BestValueOptimization.from(createMetric(Metric.MetricType.RATING, A.getIndex()), FILE_COMPONENT); - assertThat(underTest.apply(newMeasureBuilder().create(A.getIndex()))).isTrue(); - assertThat(underTest.apply(newMeasureBuilder().setVariation((double) A.getIndex()).createNoValue())).isTrue(); - assertThat(underTest.apply(newMeasureBuilder().create(B.getIndex()))).isFalse(); - assertThat(underTest.apply(newMeasureBuilder().setVariation((double) B.getIndex()).createNoValue())).isFalse(); + assertThat(underTest.test(newMeasureBuilder().create(A.getIndex()))).isTrue(); + assertThat(underTest.test(newMeasureBuilder().setVariation((double) A.getIndex()).createNoValue())).isTrue(); + assertThat(underTest.test(newMeasureBuilder().create(B.getIndex()))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().setVariation((double) B.getIndex()).createNoValue())).isFalse(); } @Test public void verify_value_comparison_for_double_metric() { Predicate underTest = BestValueOptimization.from(createMetric(Metric.MetricType.FLOAT, 36.5d), FILE_COMPONENT); - assertThat(underTest.apply(newMeasureBuilder().create(36.5d, 1))).isTrue(); - assertThat(underTest.apply(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(36.5d, 1))).isTrue(); - assertThat(underTest.apply(newMeasureBuilder().create(36.6d, 1))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().create(36.5d, 1))).isTrue(); + assertThat(underTest.test(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(36.5d, 1))).isTrue(); + assertThat(underTest.test(newMeasureBuilder().create(36.6d, 1))).isFalse(); } @Test public void apply_returns_false_for_String_measure() { Predicate underTest = BestValueOptimization.from(createMetric(Metric.MetricType.FLOAT, 36.5d), FILE_COMPONENT); - assertThat(underTest.apply(newMeasureBuilder().create("aaa"))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().create("aaa"))).isFalse(); } @Test public void apply_returns_false_for_LEVEL_measure() { Predicate underTest = BestValueOptimization.from(createMetric(Metric.MetricType.STRING, 36.5d), FILE_COMPONENT); - assertThat(underTest.apply(newMeasureBuilder().create(Measure.Level.OK))).isFalse(); + assertThat(underTest.test(newMeasureBuilder().create(Measure.Level.OK))).isFalse(); } private static MetricImpl createMetric(Metric.MetricType metricType, double bestValue) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepositoryRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepositoryRule.java index 976ea71ae07..a8333648e6d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepositoryRule.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepositoryRule.java @@ -19,23 +19,19 @@ */ package org.sonar.server.computation.task.projectanalysis.measure; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.FluentIterable.from; -import static com.google.common.collect.Maps.filterKeys; -import static java.lang.String.format; -import static java.util.Objects.requireNonNull; - +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.SetMultimap; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Set; - import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import javax.annotation.Nullable; - import org.junit.rules.ExternalResource; import org.sonar.server.computation.task.projectanalysis.component.Component; import org.sonar.server.computation.task.projectanalysis.component.ComponentProvider; @@ -48,11 +44,12 @@ import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolde import org.sonar.server.computation.task.projectanalysis.metric.Metric; import org.sonar.server.computation.task.projectanalysis.metric.MetricRepositoryRule; -import com.google.common.base.Function; -import com.google.common.base.Optional; -import com.google.common.base.Predicate; -import com.google.common.collect.ImmutableSetMultimap; -import com.google.common.collect.SetMultimap; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.FluentIterable.from; +import static com.google.common.collect.Maps.filterKeys; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; /** * An implementation of MeasureRepository as a JUnit rule which provides add methods for raw measures and extra add @@ -192,13 +189,6 @@ public class MeasureRepositoryRule extends ExternalResource implements MeasureRe return Optional.fromNullable(baseMeasures.get(new InternalKey(component, metric))); } - @Override - public int loadAsRawMeasures(Collection components, Collection metrics) { - this.loadedAsRawComponents = components; - this.loadedAsRawMetrics = metrics; - return 0; - } - public Collection getComponentsLoadedAsRaw() { return loadedAsRawComponents; } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureToMeasureDtoTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureToMeasureDtoTest.java index 809e3d7c3ec..e3adbf59c28 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureToMeasureDtoTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureToMeasureDtoTest.java @@ -26,12 +26,11 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.measure.MeasureDto; import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysisMetadataHolderRule; import org.sonar.server.computation.task.projectanalysis.component.Component; -import org.sonar.server.computation.task.projectanalysis.component.Developer; -import org.sonar.server.computation.task.projectanalysis.component.DumbDeveloper; -import org.sonar.server.computation.task.projectanalysis.component.MutableDbIdsRepositoryRule; +import org.sonar.server.computation.task.projectanalysis.component.MutableTreeRootHolderRule; import org.sonar.server.computation.task.projectanalysis.component.ReportComponent; import org.sonar.server.computation.task.projectanalysis.metric.Metric; import org.sonar.server.computation.task.projectanalysis.metric.MetricImpl; @@ -41,7 +40,6 @@ import static org.assertj.core.api.Assertions.assertThat; @RunWith(DataProviderRunner.class) public class MeasureToMeasureDtoTest { private static final MetricImpl SOME_METRIC = new MetricImpl(42, "metric_key", "metric_name", Metric.MetricType.STRING); - private static final int SOME_COMPONENT_ID = 951; private static final String SOME_DATA = "some_data"; private static final String SOME_STRING = "some_string"; private static final double SOME_VARIATIONS = 1d; @@ -55,16 +53,15 @@ public class MeasureToMeasureDtoTest { private static final Component SOME_COMPONENT = ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("uuid_1").build(); @Rule - public MutableDbIdsRepositoryRule dbIdsRepository = MutableDbIdsRepositoryRule.create(SOME_COMPONENT); + public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule(); @Rule - public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule(); + public MutableTreeRootHolderRule treeRootHolder = new MutableTreeRootHolderRule(); - MeasureToMeasureDto underTest = new MeasureToMeasureDto(dbIdsRepository, analysisMetadataHolder); + private MeasureToMeasureDto underTest = new MeasureToMeasureDto(analysisMetadataHolder, treeRootHolder); @Before public void setUp() throws Exception { - dbIdsRepository.setComponentId(SOME_COMPONENT, SOME_COMPONENT_ID); analysisMetadataHolder.setUuid(ANALYSIS_UUID); } @@ -130,22 +127,6 @@ public class MeasureToMeasureDtoTest { MeasureDto measureDto = underTest.toMeasureDto(measure, metric, SOME_COMPONENT); assertThat(measureDto.getComponentUuid()).isEqualTo(SOME_COMPONENT.getUuid()); - // assertThat(measureDto.getSnapshotId()).isEqualTo(SOME_SNAPSHOT_ID); - } - - @Test - @UseDataProvider("all_types_Measures") - public void toMeasureDto_does_no_set_developerId_if_not_set_in_Measure(Measure measure, Metric metric) { - assertThat(underTest.toMeasureDto(measure, metric, SOME_COMPONENT).getDeveloperId()).isNull(); - } - - @Test - public void toMeasureDto_sets_developerId_if_set_in_Measure() { - Developer developer = new DumbDeveloper("DEV1"); - dbIdsRepository.setDeveloperId(developer, 42); - Measure measure = Measure.newMeasureBuilder().forDeveloper(developer).createNoValue(); - - assertThat(underTest.toMeasureDto(measure, SOME_BOOLEAN_METRIC, SOME_COMPONENT).getDeveloperId()).isEqualTo(42); } @Test @@ -200,4 +181,16 @@ public class MeasureToMeasureDtoTest { assertThat(trueMeasureDto.getValue()).isNull(); assertThat(trueMeasureDto.getData()).isEqualTo(Measure.Level.OK.name()); } + + @Test + public void toLiveMeasureDto() { + treeRootHolder.setRoot(SOME_COMPONENT); + + LiveMeasureDto liveMeasureDto = underTest.toLiveMeasureDto( + Measure.newMeasureBuilder().create(Measure.Level.OK), + SOME_LEVEL_METRIC, + SOME_COMPONENT); + + assertThat(liveMeasureDto.getTextValue()).isEqualTo(Measure.Level.OK.name()); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistLiveMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistLiveMeasuresStepTest.java new file mode 100644 index 00000000000..1a0f07d076a --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistLiveMeasuresStepTest.java @@ -0,0 +1,290 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.computation.task.projectanalysis.step; + +import java.util.Optional; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.measures.Metric; +import org.sonar.api.utils.System2; +import org.sonar.db.DbClient; +import org.sonar.db.DbTester; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.measure.LiveMeasureDto; +import org.sonar.db.metric.MetricDto; +import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysisMetadataHolderRule; +import org.sonar.server.computation.task.projectanalysis.analysis.Project; +import org.sonar.server.computation.task.projectanalysis.component.Component; +import org.sonar.server.computation.task.projectanalysis.component.ReportComponent; +import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule; +import org.sonar.server.computation.task.projectanalysis.component.ViewsComponent; +import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepositoryRule; +import org.sonar.server.computation.task.projectanalysis.measure.MeasureToMeasureDto; +import org.sonar.server.computation.task.projectanalysis.metric.MetricRepositoryRule; +import org.sonar.server.computation.task.step.ComputationStep; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.db.measure.MeasureTesting.newLiveMeasure; +import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.DIRECTORY; +import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.FILE; +import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.MODULE; +import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.PROJECT; +import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.PROJECT_VIEW; +import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.SUBVIEW; +import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.VIEW; +import static org.sonar.server.computation.task.projectanalysis.measure.Measure.newMeasureBuilder; + +public class PersistLiveMeasuresStepTest extends BaseStepTest { + + private static final Metric STRING_METRIC = new Metric.Builder("string-metric", "String metric", Metric.ValueType.STRING).create(); + private static final Metric INT_METRIC = new Metric.Builder("int-metric", "int metric", Metric.ValueType.INT).create(); + private static final Metric METRIC_WITH_BEST_VALUE = new Metric.Builder("best-value-metric", "best value metric", Metric.ValueType.INT) + .setBestValue(0.0) + .setOptimizedBestValue(true) + .create(); + + private static final int REF_1 = 1; + private static final int REF_2 = 2; + private static final int REF_3 = 3; + private static final int REF_4 = 4; + + @Rule + public DbTester db = DbTester.create(System2.INSTANCE); + @Rule + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + @Rule + public MetricRepositoryRule metricRepository = new MetricRepositoryRule(); + @Rule + public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); + @Rule + public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule(); + + private DbClient dbClient = db.getDbClient(); + + @Before + public void setUp() { + MetricDto stringMetricDto = db.measures().insertMetric(m -> m.setKey(STRING_METRIC.getKey()).setValueType(Metric.ValueType.STRING.name())); + MetricDto intMetricDto = db.measures().insertMetric(m -> m.setKey(INT_METRIC.getKey()).setValueType(Metric.ValueType.INT.name())); + MetricDto bestValueMMetricDto = db.measures().insertMetric(m -> m.setKey(METRIC_WITH_BEST_VALUE.getKey()).setValueType(Metric.ValueType.INT.name()).setOptimizedBestValue(true).setBestValue(0.0)); + metricRepository.add(stringMetricDto.getId(), STRING_METRIC); + metricRepository.add(intMetricDto.getId(), INT_METRIC); + metricRepository.add(bestValueMMetricDto.getId(), METRIC_WITH_BEST_VALUE); + } + + @Test + public void persist_live_measures_of_project_analysis() { + prepareProject(); + + // the computed measures + measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().create("project-value")); + measureRepository.addRawMeasure(REF_2, STRING_METRIC.getKey(), newMeasureBuilder().create("module-value")); + measureRepository.addRawMeasure(REF_3, STRING_METRIC.getKey(), newMeasureBuilder().create("dir-value")); + measureRepository.addRawMeasure(REF_4, STRING_METRIC.getKey(), newMeasureBuilder().create("file-value")); + + step().execute(); + + // all measures are persisted, from project to file + assertThat(db.countRowsOfTable("live_measures")).isEqualTo(4); + assertThat(selectMeasure("project-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("project-value"); + assertThat(selectMeasure("module-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("module-value"); + assertThat(selectMeasure("dir-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("dir-value"); + assertThat(selectMeasure("file-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("file-value"); + } + + @Test + public void measures_without_value_are_not_persisted() { + prepareProject(); + measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().createNoValue()); + measureRepository.addRawMeasure(REF_1, INT_METRIC.getKey(), newMeasureBuilder().createNoValue()); + + step().execute(); + + assertThatMeasureIsNotPersisted("project-uuid", STRING_METRIC); + assertThatMeasureIsNotPersisted("project-uuid", INT_METRIC); + } + + @Test + public void measures_on_leak_period_are_persisted() { + prepareProject(); + measureRepository.addRawMeasure(REF_1, INT_METRIC.getKey(), newMeasureBuilder().setVariation(42.0).createNoValue()); + + step().execute(); + + LiveMeasureDto persistedMeasure = selectMeasure("project-uuid", INT_METRIC).get(); + assertThat(persistedMeasure.getValue()).isNull(); + assertThat(persistedMeasure.getVariation()).isEqualTo(42.0); + } + + @Test + public void delete_measures_from_db_if_no_more_computed() { + prepareProject(); + // measure to be updated + LiveMeasureDto measureOnFileInProject = insertMeasure("file-uuid", "project-uuid", INT_METRIC); + // measure to be deleted because on a file that has been deleted + LiveMeasureDto measureOnDeletedFileInProject = insertMeasure("deleted-file-in-project", "project-uuid", INT_METRIC); + // measure to be deleted because not computed anymore + LiveMeasureDto otherMeasureOnFileInProject = insertMeasure("file-uuid", "project-uuid", STRING_METRIC); + // measure in another project, not touched + LiveMeasureDto measureInOtherProject = insertMeasure("other-file-uuid", "other-project-uuid", INT_METRIC); + db.commit(); + + measureRepository.addRawMeasure(REF_4, INT_METRIC.getKey(), newMeasureBuilder().create(42)); + + step().execute(); + + assertThatMeasureHasValue(measureOnFileInProject, 42); + assertThatMeasureDoesNotExist(measureOnDeletedFileInProject); + assertThatMeasureDoesNotExist(otherMeasureOnFileInProject); + assertThatMeasureHasValue(measureInOtherProject, (int)measureInOtherProject.getValue().doubleValue()); + } + + @Test + public void do_not_persist_file_measures_with_best_value() { + prepareProject(); + // measure to be deleted because new value matches the metric best value + LiveMeasureDto oldMeasure = insertMeasure("file-uuid", "project-uuid", INT_METRIC); + db.commit(); + + // project measure with metric best value -> persist with value 0 + measureRepository.addRawMeasure(REF_1, METRIC_WITH_BEST_VALUE.getKey(), newMeasureBuilder().create(0)); + // file measure with metric best value -> do not persist + measureRepository.addRawMeasure(REF_4, METRIC_WITH_BEST_VALUE.getKey(), newMeasureBuilder().create(0)); + + step().execute(); + + assertThatMeasureDoesNotExist(oldMeasure); + assertThatMeasureHasValue("project-uuid", METRIC_WITH_BEST_VALUE, 0); + } + + @Test + public void persist_live_measures_of_portfolio_analysis() { + preparePortfolio(); + + // the computed measures + measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().create("view-value")); + measureRepository.addRawMeasure(REF_2, STRING_METRIC.getKey(), newMeasureBuilder().create("subview-value")); + measureRepository.addRawMeasure(REF_3, STRING_METRIC.getKey(), newMeasureBuilder().create("project-value")); + + step().execute(); + + assertThat(db.countRowsOfTable("live_measures")).isEqualTo(3); + assertThat(selectMeasure("view-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("view-value"); + assertThat(selectMeasure("subview-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("subview-value"); + assertThat(selectMeasure("project-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("project-value"); + } + + private LiveMeasureDto insertMeasure(String componentUuid, String projectUuid, Metric metric) { + LiveMeasureDto measure = newLiveMeasure() + .setComponentUuid(componentUuid) + .setProjectUuid(projectUuid) + .setMetricId(metricRepository.getByKey(metric.getKey()).getId()); + dbClient.liveMeasureDao().insertOrUpdate(db.getSession(), measure, null); + return measure; + } + + private void assertThatMeasureHasValue(LiveMeasureDto template, int expectedValue) { + Optional persisted = dbClient.liveMeasureDao().selectMeasure(db.getSession(), + template.getComponentUuid(), metricRepository.getById(template.getMetricId()).getKey()); + assertThat(persisted).isPresent(); + assertThat(persisted.get().getValue()).isEqualTo((double) expectedValue); + } + + private void assertThatMeasureHasValue(String componentUuid, Metric metric, int expectedValue) { + Optional persisted = dbClient.liveMeasureDao().selectMeasure(db.getSession(), + componentUuid, metric.getKey()); + assertThat(persisted).isPresent(); + assertThat(persisted.get().getValue()).isEqualTo((double) expectedValue); + } + + private void assertThatMeasureDoesNotExist(LiveMeasureDto template) { + assertThat(dbClient.liveMeasureDao().selectMeasure(db.getSession(), + template.getComponentUuid(), metricRepository.getById(template.getMetricId()).getKey())) + .isEmpty(); + } + + private void prepareProject() { + // tree of components as defined by scanner report + Component project = ReportComponent.builder(PROJECT, REF_1).setUuid("project-uuid") + .addChildren( + ReportComponent.builder(MODULE, REF_2).setUuid("module-uuid") + .addChildren( + ReportComponent.builder(DIRECTORY, REF_3).setUuid("dir-uuid") + .addChildren( + ReportComponent.builder(FILE, REF_4).setUuid("file-uuid") + .build()) + .build()) + .build()) + .build(); + treeRootHolder.setRoot(project); + analysisMetadataHolder.setProject(Project.copyOf(project)); + + // components as persisted in db + ComponentDto projectDto = insertComponent("project-key", "project-uuid"); + ComponentDto moduleDto = insertComponent("module-key", "module-uuid"); + ComponentDto dirDto = insertComponent("dir-key", "dir-uuid"); + ComponentDto fileDto = insertComponent("file-key", "file-uuid"); + } + + private void preparePortfolio() { + // tree of components + Component portfolio = ViewsComponent.builder(VIEW, REF_1).setUuid("view-uuid") + .addChildren( + ViewsComponent.builder(SUBVIEW, REF_2).setUuid("subview-uuid") + .addChildren( + ViewsComponent.builder(PROJECT_VIEW, REF_3).setUuid("project-uuid") + .build()) + .build()) + .build(); + treeRootHolder.setRoot(portfolio); + + // components as persisted in db + ComponentDto portfolioDto = insertComponent("view-key", "view-uuid"); + ComponentDto subViewDto = insertComponent("subview-key", "subview-uuid"); + ComponentDto projectDto = insertComponent("project-key", "project-uuid"); + analysisMetadataHolder.setProject(Project.copyOf(portfolioDto)); + } + + private void assertThatMeasureIsNotPersisted(String componentUuid, Metric metric) { + assertThat(selectMeasure(componentUuid, metric)).isEmpty(); + } + + private Optional selectMeasure(String componentUuid, Metric metric) { + return dbClient.liveMeasureDao().selectMeasure(db.getSession(), componentUuid, metric.getKey()); + } + + private ComponentDto insertComponent(String key, String uuid) { + ComponentDto componentDto = new ComponentDto() + .setOrganizationUuid("org1") + .setDbKey(key) + .setUuid(uuid) + .setUuidPath(uuid + ".") + .setRootUuid(uuid) + .setProjectUuid(uuid); + dbClient.componentDao().insert(db.getSession(), componentDto); + return componentDto; + } + + @Override + protected ComputationStep step() { + return new PersistLiveMeasuresStep(dbClient, metricRepository, new MeasureToMeasureDto(analysisMetadataHolder, treeRootHolder), treeRootHolder, measureRepository); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistMeasuresStepTest.java index 61d0099b157..36be1d480af 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistMeasuresStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistMeasuresStepTest.java @@ -19,8 +19,7 @@ */ package org.sonar.server.computation.task.projectanalysis.step; -import java.util.List; -import java.util.Map; +import java.util.Optional; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -29,12 +28,10 @@ import org.sonar.api.utils.System2; import org.sonar.db.DbClient; import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; -import org.sonar.db.rule.RuleDto; +import org.sonar.db.measure.MeasureDto; +import org.sonar.db.metric.MetricDto; import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysisMetadataHolderRule; import org.sonar.server.computation.task.projectanalysis.component.Component; -import org.sonar.server.computation.task.projectanalysis.component.Developer; -import org.sonar.server.computation.task.projectanalysis.component.DumbDeveloper; -import org.sonar.server.computation.task.projectanalysis.component.MutableDbIdsRepositoryRule; import org.sonar.server.computation.task.projectanalysis.component.ReportComponent; import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule; import org.sonar.server.computation.task.projectanalysis.component.ViewsComponent; @@ -44,12 +41,6 @@ import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository import org.sonar.server.computation.task.step.ComputationStep; import static org.assertj.core.api.Assertions.assertThat; -import static org.sonar.api.measures.CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION; -import static org.sonar.api.measures.CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION_KEY; -import static org.sonar.api.measures.CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION; -import static org.sonar.api.measures.CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION_KEY; -import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION; -import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY; import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.DIRECTORY; import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.FILE; import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.MODULE; @@ -61,325 +52,180 @@ import static org.sonar.server.computation.task.projectanalysis.measure.Measure. public class PersistMeasuresStepTest extends BaseStepTest { - private static final String STRING_METRIC_KEY = "string-metric-key"; - private static final String DOUBLE_METRIC_KEY = "double-metric-key"; - private static final String INT_METRIC_KEY = "int-metric-key"; - private static final String LONG_METRIC_KEY = "long-metric-key"; - private static final String OPTIMIZED_METRIC_KEY = "optimized-metric-key"; - - private static final Metric STRING_METRIC = new Metric.Builder(STRING_METRIC_KEY, "String metric", Metric.ValueType.STRING).create(); - private static final Metric DOUBLE_METRIC = new Metric.Builder(DOUBLE_METRIC_KEY, "Double metric", Metric.ValueType.FLOAT).create(); - private static final Metric INT_METRIC = new Metric.Builder(INT_METRIC_KEY, "int metric", Metric.ValueType.INT).create(); - private static final Metric LONG_METRIC = new Metric.Builder(LONG_METRIC_KEY, "long metric", Metric.ValueType.WORK_DUR).create(); - - private static final int ROOT_REF = 1; - private static final int INTERMEDIATE_1_REF = 2; - private static final int INTERMEDIATE_2_REF = 3; - private static final int LEAF_REF = 4; + private static final Metric STRING_METRIC = new Metric.Builder("string-metric", "String metric", Metric.ValueType.STRING).create(); + private static final Metric INT_METRIC = new Metric.Builder("int-metric", "int metric", Metric.ValueType.INT).create(); + private static final String ANALYSIS_UUID = "a1"; + private static final int REF_1 = 1; + private static final int REF_2 = 2; + private static final int REF_3 = 3; + private static final int REF_4 = 4; + @Rule - public DbTester dbTester = DbTester.create(System2.INSTANCE); + public DbTester db = DbTester.create(System2.INSTANCE); @Rule public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); @Rule public MetricRepositoryRule metricRepository = new MetricRepositoryRule(); @Rule public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); - @Rule - public MutableDbIdsRepositoryRule dbIdsRepository = MutableDbIdsRepositoryRule.create(treeRootHolder); - @Rule public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule(); - DbClient dbClient = dbTester.getDbClient(); - RuleDto rule; - ComponentDto rootDto; - ComponentDto intermediate1Dto; - ComponentDto intermediate2Dto; - ComponentDto leafDto; - - PersistMeasuresStep underTest; + private DbClient dbClient = db.getDbClient(); @Before public void setUp() { - underTest = new PersistMeasuresStep(dbClient, metricRepository, new MeasureToMeasureDto(dbIdsRepository, analysisMetadataHolder), treeRootHolder, measureRepository); analysisMetadataHolder.setUuid(ANALYSIS_UUID); - } - - private void setupReportComponents() { - Component project = ReportComponent.builder(PROJECT, ROOT_REF).setUuid("root-uuid") - .addChildren( - ReportComponent.builder(MODULE, INTERMEDIATE_1_REF).setUuid("intermediate1-uuid") - .addChildren( - ReportComponent.builder(DIRECTORY, INTERMEDIATE_2_REF).setUuid("intermediate2-uuid") - .addChildren( - ReportComponent.builder(FILE, LEAF_REF).setUuid("leaf-uuid") - .build()) - .build()) - .build()) - .build(); - treeRootHolder.setRoot(project); - - setupDbIds(); - } - - private void setupViewsComponents() { - Component view = ViewsComponent.builder(VIEW, ROOT_REF).setUuid("root-uuid") - .addChildren( - ViewsComponent.builder(SUBVIEW, INTERMEDIATE_1_REF).setUuid("intermediate1-uuid") - .addChildren( - ViewsComponent.builder(SUBVIEW, INTERMEDIATE_2_REF).setUuid("intermediate2-uuid") - .addChildren( - ViewsComponent.builder(PROJECT_VIEW, LEAF_REF).setUuid("leaf-uuid") - .build()) - .build()) - .build()) - .build(); - treeRootHolder.setRoot(view); - - setupDbIds(); - } - - private void setupDbIds() { - rootDto = addComponent("root-key", "root-uuid"); - intermediate1Dto = addComponent("intermediate1-key", "intermediate1-uuid"); - intermediate2Dto = addComponent("intermediate2-key", "intermediate2-uuid"); - leafDto = addComponent("leaf-key", "leaf-uuid"); - - setDbIds(ROOT_REF, rootDto.getId()); - setDbIds(INTERMEDIATE_1_REF, intermediate1Dto.getId()); - setDbIds(INTERMEDIATE_2_REF, intermediate2Dto.getId()); - setDbIds(LEAF_REF, leafDto.getId()); - } - - private void setDbIds(int componentRef, Long dbId) { - dbIdsRepository.setComponentId(componentRef, dbId); + MetricDto stringMetricDto = db.measures().insertMetric(m -> m.setKey(STRING_METRIC.getKey()).setValueType(Metric.ValueType.STRING.name())); + MetricDto intMetricDto = db.measures().insertMetric(m -> m.setKey(INT_METRIC.getKey()).setValueType(Metric.ValueType.INT.name())); + metricRepository.add(stringMetricDto.getId(), STRING_METRIC); + metricRepository.add(intMetricDto.getId(), INT_METRIC); } @Test - public void insert_measures_from_report() { - setupReportComponents(); + public void persist_measures_of_project_analysis() { + prepareProject(); - insertMeasures(); - } + // the computed measures + measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().create("project-value")); + measureRepository.addRawMeasure(REF_2, STRING_METRIC.getKey(), newMeasureBuilder().create("module-value")); + measureRepository.addRawMeasure(REF_3, STRING_METRIC.getKey(), newMeasureBuilder().create("dir-value")); + measureRepository.addRawMeasure(REF_4, STRING_METRIC.getKey(), newMeasureBuilder().create("file-value")); - @Test - public void insert_measures_from_views() { - setupViewsComponents(); - - insertMeasures(); - } - - private void insertMeasures() { - int stringMetricId = 1; - int doubleMetricId = 2; - int intMetricId = 3; - int longMetricId = 4; - metricRepository.add(stringMetricId, STRING_METRIC); - metricRepository.add(doubleMetricId, DOUBLE_METRIC); - metricRepository.add(intMetricId, INT_METRIC); - metricRepository.add(longMetricId, LONG_METRIC); - - measureRepository.addRawMeasure(ROOT_REF, STRING_METRIC_KEY, newMeasureBuilder().create("measure-data")); - measureRepository.addRawMeasure(INTERMEDIATE_1_REF, INT_METRIC_KEY, newMeasureBuilder().create(12)); - measureRepository.addRawMeasure(INTERMEDIATE_2_REF, LONG_METRIC_KEY, newMeasureBuilder().create(9635L)); - measureRepository.addRawMeasure(LEAF_REF, DOUBLE_METRIC_KEY, newMeasureBuilder().create(123.123d, 1)); - - underTest.execute(); - - assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(4); - - List> dtos = selectSnapshots(); - - Map dto = dtos.get(0); - assertThat(dto.get("analysisUuid")).isEqualTo(ANALYSIS_UUID); - assertThat(dto.get("componentUuid")).isEqualTo(rootDto.uuid()); - assertThat(dto.get("metricId")).isEqualTo((long) stringMetricId); - assertThat(dto.get("value")).isNull(); - assertThat(dto.get("textValue")).isEqualTo("measure-data"); - assertThat(dto.get("severity")).isNull(); - - dto = dtos.get(1); - assertThat(dto.get("analysisUuid")).isEqualTo(ANALYSIS_UUID); - assertThat(dto.get("componentUuid")).isEqualTo(intermediate1Dto.uuid()); - assertThat(dto.get("metricId")).isEqualTo((long) intMetricId); - assertValue(dto, 12d); - assertThat(dto.get("textValue")).isNull(); - assertThat(dto.get("severity")).isNull(); - - dto = dtos.get(2); - assertThat(dto.get("analysisUuid")).isEqualTo(ANALYSIS_UUID); - assertThat(dto.get("componentUuid")).isEqualTo(intermediate2Dto.uuid()); - assertThat(dto.get("metricId")).isEqualTo((long) longMetricId); - assertValue(dto, 9635d); - assertThat(dto.get("textValue")).isNull(); - assertThat(dto.get("severity")).isNull(); - - dto = dtos.get(3); - assertThat(dto.get("analysisUuid")).isEqualTo(ANALYSIS_UUID); - assertThat(dto.get("componentUuid")).isEqualTo(leafDto.uuid()); - assertThat(dto.get("metricId")).isEqualTo((long) doubleMetricId); - assertValue(dto, 123.1d); - assertThat(dto.get("textValue")).isNull(); - assertThat(dto.get("severity")).isNull(); - } + execute(true); - /** - * Horrible trick to support oracle retuning number as BigDecimal and DbTester#select converting BigDecimal with no - * scale to Long instead of Double when all other DBs will return a Double anyway. - */ - private static void assertValue(Map dto, double expected) { - Object actual = dto.get("value"); - if (expected % 1 == 0d && actual instanceof Long) { - assertThat(actual).isEqualTo((long) expected); - } else { - assertThat(actual).isEqualTo(expected); - } + // project, module and dir measures are persisted, but not file measures + assertThat(db.countRowsOfTable("project_measures")).isEqualTo(3); + assertThat(selectMeasure("project-uuid", STRING_METRIC).get().getData()).isEqualTo("project-value"); + assertThat(selectMeasure("module-uuid", STRING_METRIC).get().getData()).isEqualTo("module-value"); + assertThat(selectMeasure("dir-uuid", STRING_METRIC).get().getData()).isEqualTo("dir-value"); + assertThatMeasuresAreNotPersisted("file-uuid"); } @Test - public void insert_measure_with_variations_from_report() { - setupReportComponents(); - - insertMeasureWithVariations(); - } - - @Test - public void insert_measure_with_variations_from_views() { - setupViewsComponents(); - - insertMeasureWithVariations(); - } - - private void insertMeasureWithVariations() { - metricRepository.add(1, DOUBLE_METRIC); + public void persist_measures_of_project_analysis_excluding_directories() { + prepareProject(); - measureRepository.addRawMeasure(ROOT_REF, DOUBLE_METRIC_KEY, - newMeasureBuilder() - .setVariation(1.1d) - .create(10d, 1)); + // the computed measures + measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().create("project-value")); + measureRepository.addRawMeasure(REF_2, STRING_METRIC.getKey(), newMeasureBuilder().create("module-value")); + measureRepository.addRawMeasure(REF_3, STRING_METRIC.getKey(), newMeasureBuilder().create("dir-value")); + measureRepository.addRawMeasure(REF_4, STRING_METRIC.getKey(), newMeasureBuilder().create("file-value")); - underTest.execute(); + execute(false); - assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1); - List> dtos = selectSnapshots(); - Map dto = dtos.get(0); - assertThat(dto.get("variation_value")).isEqualTo(1.1d); + // project, module and dir measures are persisted, but not file measures + assertThat(db.countRowsOfTable("project_measures")).isEqualTo(2); + assertThat(selectMeasure("project-uuid", STRING_METRIC).get().getData()).isEqualTo("project-value"); + assertThat(selectMeasure("module-uuid", STRING_METRIC).get().getData()).isEqualTo("module-value"); + assertThatMeasuresAreNotPersisted("dir-uuid"); + assertThatMeasuresAreNotPersisted("file-uuid"); } @Test - public void bestValue_measure_of_bestValueOptimized_metrics_are_not_persisted() { - setupReportComponents(); - - metricRepository.add(1, new Metric.Builder(OPTIMIZED_METRIC_KEY, "Optimized metric", Metric.ValueType.BOOL).setOptimizedBestValue(true).setBestValue(1d).create()); + public void measures_without_value_are_not_persisted() { + prepareProject(); + measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().createNoValue()); + measureRepository.addRawMeasure(REF_1, INT_METRIC.getKey(), newMeasureBuilder().createNoValue()); - measureRepository.addRawMeasure(LEAF_REF, OPTIMIZED_METRIC_KEY, newMeasureBuilder().create(true)); + execute(false); - underTest.execute(); - - assertThat(selectSnapshots()).isEmpty(); + assertThatMeasureIsNotPersisted("project-uuid", STRING_METRIC); + assertThatMeasureIsNotPersisted("project-uuid", INT_METRIC); } @Test - public void empty_values_are_not_persisted() { - setupReportComponents(); - - metricRepository.add(1, STRING_METRIC); - metricRepository.add(2, DOUBLE_METRIC); - - measureRepository.addRawMeasure(LEAF_REF, STRING_METRIC_KEY, newMeasureBuilder().createNoValue()); - measureRepository.addRawMeasure(LEAF_REF, DOUBLE_METRIC_KEY, newMeasureBuilder().createNoValue()); + public void measures_on_leak_period_are_persisted() { + prepareProject(); + measureRepository.addRawMeasure(REF_1, INT_METRIC.getKey(), newMeasureBuilder().setVariation(42.0).createNoValue()); - underTest.execute(); + execute(false); - assertThat(selectSnapshots()).isEmpty(); + MeasureDto persistedMeasure = selectMeasure("project-uuid", INT_METRIC).get(); + assertThat(persistedMeasure.getValue()).isNull(); + assertThat(persistedMeasure.getVariation()).isEqualTo(42.0); } @Test - public void do_not_insert_file_complexity_distribution_metric_on_files() { - setupReportComponents(); + public void persist_all_measures_of_portfolio_analysis() { + preparePortfolio(); - metricRepository.add(1, FILE_COMPLEXITY_DISTRIBUTION); + // the computed measures + measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().create("view-value")); + measureRepository.addRawMeasure(REF_2, STRING_METRIC.getKey(), newMeasureBuilder().create("subview-value")); + measureRepository.addRawMeasure(REF_3, STRING_METRIC.getKey(), newMeasureBuilder().create("project-value")); - measureRepository.addRawMeasure(ROOT_REF, FILE_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0=1;2=10")); - measureRepository.addRawMeasure(LEAF_REF, FILE_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0=1;2=10")); + execute(true); - underTest.execute(); - - assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1); - - List> dtos = selectSnapshots(); - - Map dto = dtos.get(0); - assertThat(dto.get("analysisUuid")).isEqualTo(ANALYSIS_UUID); - assertThat(dto.get("componentUuid")).isEqualTo(rootDto.uuid()); - assertThat(dto.get("textValue")).isEqualTo("0=1;2=10"); + assertThat(db.countRowsOfTable("project_measures")).isEqualTo(3); + assertThat(selectMeasure("view-uuid", STRING_METRIC).get().getData()).isEqualTo("view-value"); + assertThat(selectMeasure("subview-uuid", STRING_METRIC).get().getData()).isEqualTo("subview-value"); + assertThat(selectMeasure("project-uuid", STRING_METRIC).get().getData()).isEqualTo("project-value"); } - @Test - public void do_not_insert_function_complexity_distribution_metric_on_files() { - setupReportComponents(); - - metricRepository.add(1, FUNCTION_COMPLEXITY_DISTRIBUTION); - - measureRepository.addRawMeasure(ROOT_REF, FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0=1;2=10")); - measureRepository.addRawMeasure(LEAF_REF, FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0=1;2=10")); - - underTest.execute(); - - assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1); - - List> dtos = selectSnapshots(); + private void prepareProject() { + // tree of components as defined by scanner report + Component project = ReportComponent.builder(PROJECT, REF_1).setUuid("project-uuid") + .addChildren( + ReportComponent.builder(MODULE, REF_2).setUuid("module-uuid") + .addChildren( + ReportComponent.builder(DIRECTORY, REF_3).setUuid("dir-uuid") + .addChildren( + ReportComponent.builder(FILE, REF_4).setUuid("file-uuid") + .build()) + .build()) + .build()) + .build(); + treeRootHolder.setRoot(project); - Map dto = dtos.get(0); - assertThat(dto.get("analysisUuid")).isEqualTo(ANALYSIS_UUID); - assertThat(dto.get("componentUuid")).isEqualTo(rootDto.uuid()); - assertThat(dto.get("textValue")).isEqualTo("0=1;2=10"); + // components as persisted in db + ComponentDto projectDto = insertComponent("project-key", "project-uuid"); + ComponentDto moduleDto = insertComponent("module-key", "module-uuid"); + ComponentDto dirDto = insertComponent("dir-key", "dir-uuid"); + ComponentDto fileDto = insertComponent("file-key", "file-uuid"); + db.components().insertSnapshot(projectDto, s -> s.setUuid(ANALYSIS_UUID)); } - @Test - public void do_not_insert_class_complexity_distribution_metric_on_files() { - setupReportComponents(); - - metricRepository.add(1, CLASS_COMPLEXITY_DISTRIBUTION); - - measureRepository.addRawMeasure(ROOT_REF, CLASS_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0=1;2=10")); - measureRepository.addRawMeasure(LEAF_REF, CLASS_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0=1;2=10")); - - underTest.execute(); - - assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1); - - List> dtos = selectSnapshots(); + private void preparePortfolio() { + // tree of components + Component portfolio = ViewsComponent.builder(VIEW, REF_1).setUuid("view-uuid") + .addChildren( + ViewsComponent.builder(SUBVIEW, REF_2).setUuid("subview-uuid") + .addChildren( + ViewsComponent.builder(PROJECT_VIEW, REF_3).setUuid("project-uuid") + .build()) + .build()) + .build(); + treeRootHolder.setRoot(portfolio); - Map dto = dtos.get(0); - assertThat(dto.get("analysisUuid")).isEqualTo(ANALYSIS_UUID); - assertThat(dto.get("componentUuid")).isEqualTo(rootDto.uuid()); - assertThat(dto.get("textValue")).isEqualTo("0=1;2=10"); + // components as persisted in db + ComponentDto viewDto = insertComponent("view-key", "view-uuid"); + ComponentDto subViewDto = insertComponent("subview-key", "subview-uuid"); + ComponentDto projectDto = insertComponent("project-key", "project-uuid"); + db.components().insertSnapshot(viewDto, s -> s.setUuid(ANALYSIS_UUID)); } - @Test - public void insert_developer_measure_from_report() { - setupReportComponents(); - - metricRepository.add(1, INT_METRIC); - - Developer developer = new DumbDeveloper("DEV1"); - dbIdsRepository.setDeveloperId(developer, 10); - measureRepository.addRawMeasure(ROOT_REF, INT_METRIC_KEY, newMeasureBuilder().forDeveloper(developer).create(1)); + private void assertThatMeasureIsNotPersisted(String componentUuid, Metric metric) { + assertThat(selectMeasure(componentUuid, metric)).isEmpty(); + } - underTest.execute(); + private void assertThatMeasuresAreNotPersisted(String componentUuid) { + assertThatMeasureIsNotPersisted(componentUuid, STRING_METRIC); + assertThatMeasureIsNotPersisted(componentUuid, INT_METRIC); + } - assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1); - List> dtos = selectSnapshots(); - Map dto = dtos.get(0); + private void execute(boolean persistDirectories) { + new PersistMeasuresStep(dbClient, metricRepository, new MeasureToMeasureDto(analysisMetadataHolder, treeRootHolder), treeRootHolder, measureRepository, persistDirectories) + .execute(); + } - assertValue(dto, 1d); - assertThat(dto.get("developerId")).isEqualTo(10L); + private Optional selectMeasure(String componentUuid, Metric metric) { + return dbClient.measureDao().selectMeasure(db.getSession(), ANALYSIS_UUID, componentUuid, metric.getKey()); } - private ComponentDto addComponent(String key, String uuid) { + private ComponentDto insertComponent(String key, String uuid) { ComponentDto componentDto = new ComponentDto() .setOrganizationUuid("org1") .setDbKey(key) @@ -387,23 +233,12 @@ public class PersistMeasuresStepTest extends BaseStepTest { .setUuidPath(uuid + ".") .setRootUuid(uuid) .setProjectUuid(uuid); - dbClient.componentDao().insert(dbTester.getSession(), componentDto); + dbClient.componentDao().insert(db.getSession(), componentDto); return componentDto; } - private List> selectSnapshots() { - return dbTester - .select( - "SELECT analysis_uuid as \"analysisUuid\", component_uuid as \"componentUuid\", metric_id as \"metricId\", person_id as \"developerId\", " - + - "value as \"value\", text_value as \"textValue\", " + - "variation_value_1 as \"variation_value\"" + - "FROM project_measures " + - "ORDER by id asc"); - } - @Override protected ComputationStep step() { - return underTest; + return new PersistMeasuresStep(dbClient, metricRepository, new MeasureToMeasureDto(analysisMetadataHolder, treeRootHolder), treeRootHolder, measureRepository, true); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowActionTest.java index c00e6f7f18b..b337a109258 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowActionTest.java @@ -29,7 +29,6 @@ import org.sonar.api.server.ws.WebService; import org.sonar.api.web.UserRole; import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; -import org.sonar.db.component.SnapshotDto; import org.sonar.db.metric.MetricDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.server.component.TestComponentFinder; @@ -113,9 +112,8 @@ public class ShowActionTest { ComponentDto project = db.components().insertMainBranch(); userSessionRule.addProjectPermission(UserRole.CODEVIEWER, project); ComponentDto branch = db.components().insertProjectBranch(project); - SnapshotDto analysis = db.components().insertSnapshot(newAnalysis(branch)); ComponentDto file = db.components().insertComponent(newFileDto(branch)); - db.measures().insertMeasure(file, analysis, dataMetric, m -> m.setData(format("\n" + + db.measures().insertLiveMeasure(file, dataMetric, m -> m.setData(format("\n" + " \n" + " \n" + " \n" + @@ -224,14 +222,13 @@ public class ShowActionTest { ComponentDto project = db.components().insertPrivateProject(); userSessionRule.addProjectPermission(UserRole.CODEVIEWER, project); ComponentDto file = db.components().insertComponent(newFileDto(project).setDbKey("foo.js")); - SnapshotDto snapshot = db.components().insertSnapshot(newAnalysis(project)); String xml = "\n" + " \n" + " \n" + " \n" + " \n" + "\n"; - db.measures().insertMeasure(file, snapshot, dataMetric, m -> m.setData(xml)); + db.measures().insertLiveMeasure(file, dataMetric, m -> m.setData(xml)); TestRequest request = requestFactory.apply(file); TestResponse result = request.execute(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentActionTest.java index 82468f874c4..026a92eb616 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentActionTest.java @@ -29,7 +29,7 @@ import org.sonar.api.web.UserRole; import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.SnapshotDto; -import org.sonar.db.measure.MeasureDto; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.metric.MetricDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.server.component.TestComponentFinder; @@ -52,7 +52,7 @@ import static org.sonar.api.utils.DateUtils.parseDateTime; import static org.sonar.api.web.UserRole.USER; import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonar.db.component.ComponentTesting.newProjectCopy; -import static org.sonar.db.component.SnapshotTesting.newAnalysis; +import static org.sonar.server.computation.task.projectanalysis.metric.Metric.MetricType.INT; import static org.sonar.test.JsonAssert.assertJson; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.DEPRECATED_PARAM_COMPONENT_ID; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS; @@ -92,10 +92,10 @@ public class ComponentActionTest { @Test public void provided_project() { ComponentDto project = db.components().insertPrivateProject(); - logAsUser(project); - insertNclocMetric(); + userSession.addProjectPermission(UserRole.USER, project); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name())); - ComponentWsResponse response = newRequest(project.getKey(), "ncloc"); + ComponentWsResponse response = newRequest(project.getKey(), metric.getKey()); assertThat(response.getMetrics().getMetricsCount()).isEqualTo(1); assertThat(response.getPeriods().getPeriodsCount()).isEqualTo(0); @@ -105,13 +105,13 @@ public class ComponentActionTest { @Test public void without_additional_fields() { ComponentDto project = db.components().insertPrivateProject(); - logAsUser(project); + userSession.addProjectPermission(UserRole.USER, project); db.components().insertSnapshot(project); - insertNclocMetric(); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name())); String response = ws.newRequest() .setParam(PARAM_COMPONENT, project.getKey()) - .setParam(PARAM_METRIC_KEYS, "ncloc") + .setParam(PARAM_METRIC_KEYS, metric.getKey()) .execute().getInput(); assertThat(response) @@ -122,12 +122,12 @@ public class ComponentActionTest { @Test public void branch() { ComponentDto project = db.components().insertPrivateProject(); - logAsUser(project); + userSession.addProjectPermission(UserRole.USER, project); ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch")); SnapshotDto analysis = db.components().insertSnapshot(branch); ComponentDto file = db.components().insertComponent(newFileDto(branch)); - MetricDto complexity = insertComplexityMetric(); - MeasureDto measure = db.measures().insertMeasure(file, analysis, complexity, m -> m.setValue(12.0d).setVariation(2.0d)); + MetricDto complexity = db.measures().insertMetric(m1 -> m1.setKey("complexity").setValueType(INT.name())); + LiveMeasureDto measure = db.measures().insertLiveMeasure(file, complexity, m -> m.setValue(12.0d).setVariation(2.0d)); ComponentWsResponse response = ws.newRequest() .setParam(PARAM_COMPONENT, file.getKey()) @@ -149,9 +149,9 @@ public class ComponentActionTest { ComponentDto view = db.components().insertView(); db.components().insertSnapshot(view); ComponentDto projectCopy = db.components().insertComponent(newProjectCopy("project-uuid-copy", project, view)); - insertNclocMetric(); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name())); - ComponentWsResponse response = newRequest(projectCopy.getKey(), "ncloc"); + ComponentWsResponse response = newRequest(projectCopy.getKey(), metric.getKey()); assertThat(response.getComponent().getRefId()).isEqualTo(project.uuid()); assertThat(response.getComponent().getRefKey()).isEqualTo(project.getKey()); @@ -160,11 +160,11 @@ public class ComponentActionTest { @Test public void return_deprecated_id_in_the_response() { ComponentDto project = db.components().insertPrivateProject(); - logAsUser(project); + userSession.addProjectPermission(UserRole.USER, project); db.components().insertSnapshot(project); - insertNclocMetric(); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name())); - ComponentWsResponse response = newRequest(project.getKey(), "ncloc"); + ComponentWsResponse response = newRequest(project.getKey(), metric.getKey()); assertThat(response.getComponent().getId()).isEqualTo(project.uuid()); } @@ -172,13 +172,13 @@ public class ComponentActionTest { @Test public void use_deprecated_component_id_parameter() { ComponentDto project = db.components().insertPrivateProject(); - logAsUser(project); + userSession.addProjectPermission(UserRole.USER, project); userSession.addProjectPermission(USER, project); - insertNclocMetric(); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name())); ComponentWsResponse response = ws.newRequest() .setParam("componentId", project.uuid()) - .setParam(PARAM_METRIC_KEYS, "ncloc") + .setParam(PARAM_METRIC_KEYS, metric.getKey()) .executeProtobuf(ComponentWsResponse.class); assertThat(response.getComponent().getKey()).isEqualTo(project.getDbKey()); @@ -187,13 +187,13 @@ public class ComponentActionTest { @Test public void use_deprecated_component_key_parameter() { ComponentDto project = db.components().insertPrivateProject(); - logAsUser(project); + userSession.addProjectPermission(UserRole.USER, project); userSession.addProjectPermission(USER, project); - insertNclocMetric(); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name())); ComponentWsResponse response = ws.newRequest() .setParam("componentKey", project.getKey()) - .setParam(PARAM_METRIC_KEYS, "ncloc") + .setParam(PARAM_METRIC_KEYS, metric.getKey()) .executeProtobuf(ComponentWsResponse.class); assertThat(response.getComponent().getKey()).isEqualTo(project.getDbKey()); @@ -202,12 +202,11 @@ public class ComponentActionTest { @Test public void metric_without_a_domain() { ComponentDto project = db.components().insertPrivateProject(); - logAsUser(project); - SnapshotDto analysis = db.getDbClient().snapshotDao().insert(db.getSession(), newAnalysis(project)); + userSession.addProjectPermission(UserRole.USER, project); MetricDto metricWithoutDomain = db.measures().insertMetric(m -> m .setValueType(Measure.ValueType.INT.name()) .setDomain(null)); - db.measures().insertMeasure(project, analysis, metricWithoutDomain); + db.measures().insertLiveMeasure(project, metricWithoutDomain); ComponentWsResponse response = ws.newRequest() .setParam(PARAM_COMPONENT, project.getKey()) @@ -215,36 +214,58 @@ public class ComponentActionTest { .setParam(PARAM_ADDITIONAL_FIELDS, "metrics") .executeProtobuf(ComponentWsResponse.class); - assertThat(response.getComponent().getMeasures(0).getMetric()).isEqualTo(metricWithoutDomain.getKey()); + assertThat(response.getComponent().getMeasuresList()).extracting(Measures.Measure::getMetric).containsExactly(metricWithoutDomain.getKey()); Common.Metric responseMetric = response.getMetrics().getMetrics(0); assertThat(responseMetric.getKey()).isEqualTo(metricWithoutDomain.getKey()); assertThat(responseMetric.hasDomain()).isFalse(); } + @Test + public void use_best_values() { + ComponentDto project = db.components().insertPrivateProject(); + ComponentDto file = db.components().insertComponent(newFileDto(project)); + userSession.addProjectPermission(UserRole.USER, project); + MetricDto metric = db.measures().insertMetric(m -> m + .setValueType(Measure.ValueType.INT.name()) + .setBestValue(7.0d) + .setOptimizedBestValue(true) + .setDomain(null)); + + ComponentWsResponse response = ws.newRequest() + .setParam(PARAM_COMPONENT, file.getKey()) + .setParam(PARAM_METRIC_KEYS, metric.getKey()) + .setParam(PARAM_ADDITIONAL_FIELDS, "metrics") + .executeProtobuf(ComponentWsResponse.class); + + assertThat(response.getComponent().getMeasuresList()) + .extracting(Measures.Measure::getMetric, Measures.Measure::getValue) + .containsExactly(tuple(metric.getKey(), "7")); + } + @Test public void fail_when_developer_is_not_found() { ComponentDto project = db.components().insertPrivateProject(); - logAsUser(project); + userSession.addProjectPermission(UserRole.USER, project); db.components().insertSnapshot(project); - insertNclocMetric(); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name())); expectedException.expect(NotFoundException.class); - expectedException.expectMessage("Component id 'unknown-developer-id' not found"); + expectedException.expectMessage("The Developer Cockpit feature has been dropped. The specified developer cannot be found."); ws.newRequest() .setParam(PARAM_COMPONENT, project.getKey()) - .setParam(PARAM_METRIC_KEYS, "ncloc") + .setParam(PARAM_METRIC_KEYS, metric.getKey()) .setParam(PARAM_DEVELOPER_ID, "unknown-developer-id").executeProtobuf(ComponentWsResponse.class); } @Test public void fail_when_a_metric_is_not_found() { ComponentDto project = db.components().insertPrivateProject(); - logAsUser(project); + userSession.addProjectPermission(UserRole.USER, project); db.components().insertSnapshot(project); - insertNclocMetric(); - insertComplexityMetric(); + db.measures().insertMetric(m -> m.setKey("ncloc").setValueType(INT.name())); + db.measures().insertMetric(m -> m.setKey("complexity").setValueType(INT.name())); expectedException.expect(NotFoundException.class); expectedException.expectMessage("The following metric keys are not found: unknown-metric, another-unknown-metric"); @@ -255,7 +276,7 @@ public class ComponentActionTest { @Test public void fail_when_empty_metric_keys_parameter() { ComponentDto project = db.components().insertPrivateProject(); - logAsUser(project); + userSession.addProjectPermission(UserRole.USER, project); db.components().insertSnapshot(project); expectedException.expect(BadRequestException.class); @@ -269,39 +290,39 @@ public class ComponentActionTest { userSession.logIn(); ComponentDto project = db.components().insertPrivateProject(); db.components().insertSnapshot(project); - insertNclocMetric(); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name())); expectedException.expect(ForbiddenException.class); - newRequest(project.getKey(), "ncloc"); + newRequest(project.getKey(), metric.getKey()); } @Test public void fail_when_component_does_not_exist() { - insertNclocMetric(); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name())); expectedException.expect(NotFoundException.class); expectedException.expectMessage("Component key 'project-key' not found"); ws.newRequest() .setParam(PARAM_COMPONENT, "project-key") - .setParam(PARAM_METRIC_KEYS, "ncloc") + .setParam(PARAM_METRIC_KEYS, metric.getKey()) .execute(); } @Test public void fail_when_component_is_removed() { ComponentDto project = db.components().insertPrivateProject(p -> p.setEnabled(false)); - logAsUser(project); + userSession.addProjectPermission(UserRole.USER, project); userSession.addProjectPermission(USER, project); - insertNclocMetric(); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name())); expectedException.expect(NotFoundException.class); expectedException.expectMessage(String.format("Component key '%s' not found", project.getKey())); ws.newRequest() .setParam(PARAM_COMPONENT, project.getKey()) - .setParam(PARAM_METRIC_KEYS, "ncloc") + .setParam(PARAM_METRIC_KEYS, metric.getKey()) .execute(); } @@ -345,14 +366,14 @@ public class ComponentActionTest { ComponentDto project = db.components().insertMainBranch(organization); userSession.logIn().addProjectPermission(UserRole.USER, project); ComponentDto branch = db.components().insertProjectBranch(project); - insertNclocMetric(); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name())); expectedException.expect(NotFoundException.class); expectedException.expectMessage(format("Component key '%s' not found", branch.getDbKey())); ws.newRequest() .setParam(PARAM_COMPONENT, branch.getDbKey()) - .setParam(PARAM_METRIC_KEYS, "ncloc") + .setParam(PARAM_METRIC_KEYS, metric.getKey()) .execute(); } @@ -362,21 +383,21 @@ public class ComponentActionTest { ComponentDto project = db.components().insertMainBranch(organization); userSession.logIn().addProjectPermission(UserRole.USER, project); ComponentDto branch = db.components().insertProjectBranch(project); - insertNclocMetric(); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name())); expectedException.expect(NotFoundException.class); expectedException.expectMessage(format("Component id '%s' not found", branch.uuid())); ws.newRequest() .setParam(DEPRECATED_PARAM_COMPONENT_ID, branch.uuid()) - .setParam(PARAM_METRIC_KEYS, "ncloc") + .setParam(PARAM_METRIC_KEYS, metric.getKey()) .execute(); } @Test public void json_example() { ComponentDto project = db.components().insertPrivateProject(); - logAsUser(project); + userSession.addProjectPermission(UserRole.USER, project); SnapshotDto analysis = db.components().insertSnapshot(project, s -> s.setPeriodDate(parseDateTime("2016-01-11T10:49:50+0100").getTime()) .setPeriodMode("previous_version") @@ -386,66 +407,36 @@ public class ComponentActionTest { .setName("ElementImpl.java") .setLanguage("java") .setPath("src/main/java/com/sonarsource/markdown/impl/ElementImpl.java")); - MetricDto complexity = insertComplexityMetric(); - db.measures().insertMeasure(file, analysis, complexity, - m -> m.setValue(12.0d) - .setVariation(2.0d) - .setData(null)); - MetricDto ncloc = insertNclocMetric(); - db.measures().insertMeasure(file, analysis, ncloc, - m -> m.setValue(114.0d) - .setVariation(3.0d) - .setData(null)); - MetricDto newViolations = insertNewViolationMetric(); - db.measures().insertMeasure(file, analysis, newViolations, - m -> m.setVariation(25.0d) - .setValue(null) - .setData(null)); - - String response = ws.newRequest() - .setParam(PARAM_COMPONENT, file.getKey()) - .setParam(PARAM_METRIC_KEYS, "ncloc, complexity, new_violations") - .setParam(PARAM_ADDITIONAL_FIELDS, "metrics,periods") - .execute() - .getInput(); - assertJson(response).isSimilarTo(getClass().getResource("component-example.json")); - } - - private ComponentWsResponse newRequest(String componentKey, String metricKeys) { - return ws.newRequest() - .setParam(PARAM_COMPONENT, componentKey) - .setParam(PARAM_METRIC_KEYS, metricKeys) - .setParam(PARAM_ADDITIONAL_FIELDS, "metrics,periods") - .executeProtobuf(ComponentWsResponse.class); - } - - private MetricDto insertNclocMetric() { - return db.measures().insertMetric(m -> m.setKey("ncloc") - .setShortName("Lines of code") - .setDescription("Non Commenting Lines of Code") - .setDomain("Size") + MetricDto complexity = db.measures().insertMetric(m -> m.setKey("complexity") + .setShortName("Complexity") + .setDescription("Cyclomatic complexity") + .setDomain("Complexity") .setValueType("INT") .setDirection(-1) .setQualitative(false) .setHidden(false) .setUserManaged(false)); - } + db.measures().insertLiveMeasure(file, complexity, + m -> m.setValue(12.0d) + .setVariation(2.0d) + .setData((String) null)); - private MetricDto insertComplexityMetric() { - return db.measures().insertMetric(m -> m.setKey("complexity") - .setShortName("Complexity") - .setDescription("Cyclomatic complexity") - .setDomain("Complexity") + MetricDto ncloc = db.measures().insertMetric(m1 -> m1.setKey("ncloc") + .setShortName("Lines of code") + .setDescription("Non Commenting Lines of Code") + .setDomain("Size") .setValueType("INT") .setDirection(-1) .setQualitative(false) .setHidden(false) .setUserManaged(false)); - } + db.measures().insertLiveMeasure(file, ncloc, + m -> m.setValue(114.0d) + .setVariation(3.0d) + .setData((String) null)); - private MetricDto insertNewViolationMetric() { - return db.measures().insertMetric(m -> m.setKey("new_violations") + MetricDto newViolations = db.measures().insertMetric(m -> m.setKey("new_violations") .setShortName("New issues") .setDescription("New Issues") .setDomain("Issues") @@ -454,10 +445,26 @@ public class ComponentActionTest { .setQualitative(true) .setHidden(false) .setUserManaged(false)); - } + db.measures().insertLiveMeasure(file, newViolations, + m -> m.setVariation(25.0d) + .setValue(null) + .setData((String) null)); - private void logAsUser(ComponentDto project) { - userSession.addProjectPermission(UserRole.USER, project); + String response = ws.newRequest() + .setParam(PARAM_COMPONENT, file.getKey()) + .setParam(PARAM_METRIC_KEYS, "ncloc, complexity, new_violations") + .setParam(PARAM_ADDITIONAL_FIELDS, "metrics,periods") + .execute() + .getInput(); + + assertJson(response).isSimilarTo(getClass().getResource("component-example.json")); } + private ComponentWsResponse newRequest(String componentKey, String metricKeys) { + return ws.newRequest() + .setParam(PARAM_COMPONENT, componentKey) + .setParam(PARAM_METRIC_KEYS, metricKeys) + .setParam(PARAM_ADDITIONAL_FIELDS, "metrics,periods") + .executeProtobuf(ComponentWsResponse.class); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java index c5ab5886cda..8909b85dd04 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java @@ -37,7 +37,7 @@ import org.sonar.db.component.ComponentDbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ResourceTypesRule; import org.sonar.db.component.SnapshotDto; -import org.sonar.db.measure.MeasureDto; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.metric.MetricDto; import org.sonar.db.metric.MetricTesting; import org.sonar.db.organization.OrganizationDto; @@ -73,7 +73,6 @@ import static org.sonar.db.component.ComponentTesting.newDirectory; import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonar.db.component.ComponentTesting.newProjectCopy; import static org.sonar.db.component.SnapshotTesting.newAnalysis; -import static org.sonar.db.measure.MeasureTesting.newMeasureDto; import static org.sonar.server.measure.ws.ComponentTreeAction.LEAVES_STRATEGY; import static org.sonar.server.measure.ws.ComponentTreeAction.METRIC_PERIOD_SORT; import static org.sonar.server.measure.ws.ComponentTreeAction.METRIC_SORT; @@ -142,33 +141,19 @@ public class ComponentTreeActionTest { .setQualifier(DIRECTORY)); MetricDto complexity = insertComplexityMetric(); - dbClient.measureDao().insert(dbSession, - newMeasureDto(complexity, file1, analysis) - .setValue(12.0d), - newMeasureDto(complexity, dir, analysis) - .setValue(35.0d) - .setVariation(0.0d), - newMeasureDto(complexity, project, analysis) - .setValue(42.0d)); + db.measures().insertLiveMeasure(file1, complexity, m -> m.setValue(12.0d)); + db.measures().insertLiveMeasure(dir, complexity, m -> m.setValue(35.0d).setVariation(0.0d)); + db.measures().insertLiveMeasure(project, complexity, m -> m.setValue(42.0d)); MetricDto ncloc = insertNclocMetric(); - dbClient.measureDao().insert(dbSession, - newMeasureDto(ncloc, file1, analysis) - .setValue(114.0d), - newMeasureDto(ncloc, dir, analysis) - .setValue(217.0d) - .setVariation(0.0d), - newMeasureDto(ncloc, project, analysis) - .setValue(1984.0d)); + db.measures().insertLiveMeasure(file1, ncloc, m -> m.setValue(114.0d)); + db.measures().insertLiveMeasure(dir, ncloc, m -> m.setValue(217.0d).setVariation(0.0d)); + db.measures().insertLiveMeasure(project, ncloc, m -> m.setValue(1984.0d)); MetricDto newViolations = insertNewViolationsMetric(); - dbClient.measureDao().insert(dbSession, - newMeasureDto(newViolations, file1, analysis) - .setVariation(25.0d), - newMeasureDto(newViolations, dir, analysis) - .setVariation(25.0d), - newMeasureDto(newViolations, project, analysis) - .setVariation(255.0d)); + db.measures().insertLiveMeasure(file1, newViolations, m -> m.setVariation(25.0d)); + db.measures().insertLiveMeasure(dir, newViolations, m -> m.setVariation(25.0d)); + db.measures().insertLiveMeasure(project, newViolations, m -> m.setVariation(255.0d)); db.commit(); @@ -206,17 +191,16 @@ public class ComponentTreeActionTest { .setPeriodMode("last_version") .setPeriodDate(System.currentTimeMillis())); userSession.anonymous().addProjectPermission(UserRole.USER, project); - ComponentDto directoryDto = newDirectory(project, "directory-uuid", "path/to/directory").setName("directory-1"); - componentDb.insertComponent(directoryDto); - ComponentDto file = newFileDto(directoryDto, null, "file-uuid").setName("file-1"); + ComponentDto directory = newDirectory(project, "directory-uuid", "path/to/directory").setName("directory-1"); + componentDb.insertComponent(directory); + ComponentDto file = newFileDto(directory, null, "file-uuid").setName("file-1"); componentDb.insertComponent(file); MetricDto ncloc = insertNclocMetric(); MetricDto coverage = insertCoverageMetric(); - dbClient.measureDao().insert(dbSession, - newMeasureDto(ncloc, file, projectSnapshot).setValue(5.0d).setVariation(4.0d), - newMeasureDto(coverage, file, projectSnapshot).setValue(15.5d), - newMeasureDto(coverage, directoryDto, projectSnapshot).setValue(15.0d)); db.commit(); + db.measures().insertLiveMeasure(file, ncloc, m -> m.setValue(5.0d).setVariation(4.0d)); + db.measures().insertLiveMeasure(file, coverage, m -> m.setValue(15.5d)); + db.measures().insertLiveMeasure(directory, coverage, m -> m.setValue(15.5d)); ComponentTreeWsResponse response = ws.newRequest() .setParam(PARAM_COMPONENT, project.getKey()) @@ -237,9 +221,9 @@ public class ComponentTreeActionTest { ComponentDto project = db.components().insertPrivateProject(); SnapshotDto projectSnapshot = db.components().insertSnapshot(project); userSession.anonymous().addProjectPermission(UserRole.USER, project); - ComponentDto directoryDto = newDirectory(project, "directory-uuid", "path/to/directory").setName("directory-1"); - componentDb.insertComponent(directoryDto); - ComponentDto file = newFileDto(directoryDto, null, "file-uuid").setName("file-1"); + ComponentDto directory = newDirectory(project, "directory-uuid", "path/to/directory").setName("directory-1"); + componentDb.insertComponent(directory); + ComponentDto file = newFileDto(directory, null, "file-uuid").setName("file-1"); componentDb.insertComponent(file); MetricDto coverage = insertCoverageMetric(); dbClient.metricDao().insert(dbSession, MetricTesting.newMetricDto() @@ -253,10 +237,9 @@ public class ComponentTreeActionTest { .setOptimizedBestValue(true) .setBestValue(1984.0d) .setValueType(INT.name())); - dbClient.measureDao().insert(dbSession, - newMeasureDto(coverage, file, projectSnapshot).setValue(15.5d), - newMeasureDto(coverage, directoryDto, projectSnapshot).setValue(42.0d)); db.commit(); + db.measures().insertLiveMeasure(file, coverage, m -> m.setValue(15.5d)); + db.measures().insertLiveMeasure(directory, coverage, m -> m.setValue(42.0d)); ComponentTreeWsResponse response = ws.newRequest() .setParam(PARAM_COMPONENT, project.getKey()) @@ -284,17 +267,17 @@ public class ComponentTreeActionTest { .setPeriodDate(parseDateTime("2016-01-11T10:49:50+0100").getTime()) .setPeriodMode("previous_version") .setPeriodParam("1.0-SNAPSHOT")); - ComponentDto directoryDto = newDirectory(project, "directory-uuid", "path/to/directory").setName("directory-1"); - componentDb.insertComponent(directoryDto); - ComponentDto file = newFileDto(directoryDto, null, "file-uuid").setName("file-1"); + ComponentDto directory = newDirectory(project, "directory-uuid", "path/to/directory").setName("directory-1"); + componentDb.insertComponent(directory); + ComponentDto file = newFileDto(directory, null, "file-uuid").setName("file-1"); componentDb.insertComponent(file); MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDto() .setKey(NEW_SECURITY_RATING_KEY) .setOptimizedBestValue(true) .setBestValue(1d) .setValueType(RATING.name())); - dbClient.measureDao().insert(dbSession, newMeasureDto(metric, directoryDto, projectSnapshot).setVariation(2d)); db.commit(); + db.measures().insertLiveMeasure(directory, metric, m -> m.setVariation(2d)); ComponentTreeWsResponse response = ws.newRequest() .setParam(PARAM_COMPONENT, project.getKey()) @@ -322,17 +305,16 @@ public class ComponentTreeActionTest { ComponentDto file2 = componentDb.insertComponent(newFileDto(project, null, "file-uuid-2").setName("file-1")); ComponentDto file1 = componentDb.insertComponent(newFileDto(project, null, "file-uuid-1").setName("file-1")); MetricDto coverage = insertCoverageMetric(); - dbClient.measureDao().insert(dbSession, - newMeasureDto(coverage, file1, projectSnapshot).setValue(1.0d), - newMeasureDto(coverage, file2, projectSnapshot).setValue(2.0d), - newMeasureDto(coverage, file3, projectSnapshot).setValue(3.0d), - newMeasureDto(coverage, file4, projectSnapshot).setValue(4.0d), - newMeasureDto(coverage, file5, projectSnapshot).setValue(5.0d), - newMeasureDto(coverage, file6, projectSnapshot).setValue(6.0d), - newMeasureDto(coverage, file7, projectSnapshot).setValue(7.0d), - newMeasureDto(coverage, file8, projectSnapshot).setValue(8.0d), - newMeasureDto(coverage, file9, projectSnapshot).setValue(9.0d)); db.commit(); + db.measures().insertLiveMeasure(file1, coverage, m -> m.setValue(1.0d)); + db.measures().insertLiveMeasure(file2, coverage, m -> m.setValue(2.0d)); + db.measures().insertLiveMeasure(file3, coverage, m -> m.setValue(3.0d)); + db.measures().insertLiveMeasure(file4, coverage, m -> m.setValue(4.0d)); + db.measures().insertLiveMeasure(file5, coverage, m -> m.setValue(5.0d)); + db.measures().insertLiveMeasure(file6, coverage, m -> m.setValue(6.0d)); + db.measures().insertLiveMeasure(file7, coverage, m -> m.setValue(7.0d)); + db.measures().insertLiveMeasure(file8, coverage, m -> m.setValue(8.0d)); + db.measures().insertLiveMeasure(file9, coverage, m -> m.setValue(9.0d)); ComponentTreeWsResponse response = ws.newRequest() .setParam(PARAM_COMPONENT, project.getKey()) @@ -361,11 +343,10 @@ public class ComponentTreeActionTest { ComponentDto file2 = componentDb.insertComponent(newFileDto(project, null, "file-uuid-2")); MetricDto ncloc = newMetricDto().setKey("ncloc").setValueType(INT.name()).setDirection(1); dbClient.metricDao().insert(dbSession, ncloc); - dbClient.measureDao().insert(dbSession, - newMeasureDto(ncloc, file1, projectSnapshot).setValue(1.0d), - newMeasureDto(ncloc, file2, projectSnapshot).setValue(2.0d), - newMeasureDto(ncloc, file3, projectSnapshot).setValue(3.0d)); db.commit(); + db.measures().insertLiveMeasure(file1, ncloc, m -> m.setValue(1.0d)); + db.measures().insertLiveMeasure(file2, ncloc, m -> m.setValue(2.0d)); + db.measures().insertLiveMeasure(file3, ncloc, m -> m.setValue(3.0d)); ComponentTreeWsResponse response = ws.newRequest() .setParam(PARAM_COMPONENT, project.getKey()) @@ -392,12 +373,11 @@ public class ComponentTreeActionTest { componentDb.insertComponent(file4); MetricDto ncloc = newMetricDto().setKey("ncloc").setValueType(INT.name()).setDirection(1); dbClient.metricDao().insert(dbSession, ncloc); - dbClient.measureDao().insert(dbSession, - newMeasureDto(ncloc, file1, projectSnapshot).setValue(1.0d), - newMeasureDto(ncloc, file2, projectSnapshot).setValue(2.0d), - newMeasureDto(ncloc, file3, projectSnapshot).setValue(3.0d), - // measure on period 1 - newMeasureDto(ncloc, file4, projectSnapshot).setVariation(4.0d)); + db.measures().insertLiveMeasure(file1, ncloc, m -> m.setData((String) null).setValue(1.0d).setVariation(null)); + db.measures().insertLiveMeasure(file2, ncloc, m -> m.setData((String) null).setValue(2.0d).setVariation(null)); + db.measures().insertLiveMeasure(file3, ncloc, m -> m.setData((String) null).setValue(3.0d).setVariation(null)); + // measure on period 1 + db.measures().insertLiveMeasure(file4, ncloc, m -> m.setData((String) null).setValue(null).setVariation(4.0d)); db.commit(); ComponentTreeWsResponse response = ws.newRequest() @@ -423,11 +403,10 @@ public class ComponentTreeActionTest { ComponentDto file2 = componentDb.insertComponent(newFileDto(project, null, "file-uuid-2")); MetricDto ncloc = newMetricDto().setKey("ncloc").setValueType(INT.name()).setDirection(1); dbClient.metricDao().insert(dbSession, ncloc); - dbClient.measureDao().insert(dbSession, - newMeasureDto(ncloc, file1, projectSnapshot).setVariation(1.0d), - newMeasureDto(ncloc, file2, projectSnapshot).setVariation(2.0d), - newMeasureDto(ncloc, file3, projectSnapshot).setVariation(3.0d)); db.commit(); + db.measures().insertLiveMeasure(file1, ncloc, m -> m.setVariation(1.0d)); + db.measures().insertLiveMeasure(file2, ncloc, m -> m.setVariation(2.0d)); + db.measures().insertLiveMeasure(file3, ncloc, m -> m.setVariation(3.0d)); ComponentTreeWsResponse response = ws.newRequest() .setParam(PARAM_COMPONENT, project.getKey()) @@ -450,12 +429,11 @@ public class ComponentTreeActionTest { ComponentDto file1 = componentDb.insertComponent(newFileDto(project, null, "file-uuid-1")); MetricDto ncloc = newMetricDto().setKey("new_ncloc").setValueType(INT.name()).setDirection(1); dbClient.metricDao().insert(dbSession, ncloc); - dbClient.measureDao().insert(dbSession, - newMeasureDto(ncloc, file1, projectSnapshot).setVariation(1.0d), - newMeasureDto(ncloc, file2, projectSnapshot).setVariation(2.0d), - newMeasureDto(ncloc, file3, projectSnapshot).setVariation(3.0d), - // file 4 measure is on absolute value - newMeasureDto(ncloc, file4, projectSnapshot).setValue(4.0d)); + db.measures().insertLiveMeasure(file1, ncloc, m -> m.setData((String) null).setValue(null).setVariation(1.0d)); + db.measures().insertLiveMeasure(file2, ncloc, m -> m.setData((String) null).setValue(null).setVariation(2.0d)); + db.measures().insertLiveMeasure(file3, ncloc, m -> m.setData((String) null).setValue(null).setVariation(3.0d)); + // file 4 measure is on absolute value + db.measures().insertLiveMeasure(file4, ncloc, m -> m.setData((String) null).setValue(4.0d).setVariation(null)); db.commit(); ComponentTreeWsResponse response = ws.newRequest() @@ -492,12 +470,13 @@ public class ComponentTreeActionTest { @Test public void branch() { - ComponentDto project = db.components().insertPrivateProject(); + OrganizationDto organization = db.organizations().insert(); + ComponentDto project = db.components().insertPrivateProject(organization); ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch")); SnapshotDto analysis = db.components().insertSnapshot(branch); ComponentDto file = db.components().insertComponent(newFileDto(branch)); - MetricDto complexity = insertComplexityMetric(); - MeasureDto measure = db.measures().insertMeasure(file, analysis, complexity, m -> m.setValue(12.0d)); + MetricDto complexity = db.measures().insertMetric(m -> m.setValueType(INT.name())); + LiveMeasureDto measure = db.measures().insertLiveMeasure(file, complexity, m -> m.setValue(12.0d)); ComponentTreeWsResponse response = ws.newRequest() .setParam(PARAM_COMPONENT, file.getKey()) @@ -518,7 +497,7 @@ public class ComponentTreeActionTest { SnapshotDto analysis = db.components().insertSnapshot(project); ComponentDto file = componentDb.insertComponent(newFileDto(project)); MetricDto ncloc = insertNclocMetric(); - db.measures().insertMeasure(file, analysis, ncloc, m -> m.setValue(2d)); + db.measures().insertLiveMeasure(file, ncloc, m -> m.setValue(2d)); ComponentTreeWsResponse response = ws.newRequest() .setParam(PARAM_COMPONENT, project.getKey()) @@ -565,7 +544,7 @@ public class ComponentTreeActionTest { MetricDto metricWithoutDomain = db.measures().insertMetric(m -> m .setValueType(Metric.ValueType.INT.name()) .setDomain(null)); - db.measures().insertMeasure(project, analysis, metricWithoutDomain); + db.measures().insertLiveMeasure(project, metricWithoutDomain); ComponentTreeWsResponse result = ws.newRequest() .setParam(PARAM_COMPONENT, project.getKey()) @@ -582,11 +561,11 @@ public class ComponentTreeActionTest { @Test public void reference_component() { ComponentDto project = db.components().insertPrivateProject(); - ComponentDto view = db.components().insertView(); + ComponentDto view = db.components().insertPrivatePortfolio(db.getDefaultOrganization()); SnapshotDto viewAnalysis = db.components().insertSnapshot(view); ComponentDto projectCopy = db.components().insertComponent(newProjectCopy(project, view)); MetricDto ncloc = insertNclocMetric(); - db.measures().insertMeasure(projectCopy, viewAnalysis, ncloc, m -> m.setValue(5d)); + db.measures().insertLiveMeasure(projectCopy, ncloc, m -> m.setValue(5d)); ComponentTreeWsResponse result = ws.newRequest() .setParam(PARAM_COMPONENT, view.getKey()) @@ -903,7 +882,7 @@ public class ComponentTreeActionTest { .setShortName("Lines of code") .setDescription("Non Commenting Lines of Code") .setDomain("Size") - .setValueType("INT") + .setValueType(INT.name()) .setDirection(-1) .setQualitative(false) .setHidden(false) @@ -918,7 +897,7 @@ public class ComponentTreeActionTest { .setShortName("Complexity") .setDescription("Cyclomatic complexity") .setDomain("Complexity") - .setValueType("INT") + .setValueType(INT.name()) .setDirection(-1) .setQualitative(false) .setHidden(false) diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeSortTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeSortTest.java index 839a9c05864..438ca643ee6 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeSortTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeSortTest.java @@ -30,7 +30,7 @@ import org.sonar.api.measures.Metric.ValueType; import org.sonar.api.resources.Qualifiers; import org.sonar.core.util.Uuids; import org.sonar.db.component.ComponentDto; -import org.sonar.db.measure.MeasureDto; +import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.metric.MetricDto; import static com.google.common.collect.Lists.newArrayList; @@ -78,9 +78,9 @@ public class ComponentTreeSortTest { // same number than path field double currentValue = 9; for (ComponentDto component : components) { - measuresByComponentUuidAndMetric.put(component.uuid(), violationsMetric, createFromMeasureDto(new MeasureDto().setValue(currentValue) + measuresByComponentUuidAndMetric.put(component.uuid(), violationsMetric, createFromMeasureDto(new LiveMeasureDto().setValue(currentValue) .setVariation(-currentValue))); - measuresByComponentUuidAndMetric.put(component.uuid(), sqaleIndexMetric, createFromMeasureDto(new MeasureDto().setData(String.valueOf(currentValue)))); + measuresByComponentUuidAndMetric.put(component.uuid(), sqaleIndexMetric, createFromMeasureDto(new LiveMeasureDto().setData(String.valueOf(currentValue)))); currentValue--; } } @@ -167,7 +167,7 @@ public class ComponentTreeSortTest { for (int i = 0; i < components.size(); i++) { ComponentDto component = components.get(i); String alertStatus = statuses.get(i % 3); - measuresByComponentUuidAndMetric.put(component.uuid(), metrics.get(0), createFromMeasureDto(new MeasureDto().setData(alertStatus))); + measuresByComponentUuidAndMetric.put(component.uuid(), metrics.get(0), createFromMeasureDto(new LiveMeasureDto().setData(alertStatus))); } ComponentTreeRequest wsRequest = newRequest(newArrayList(METRIC_SORT, NAME_SORT), true, CoreMetrics.ALERT_STATUS_KEY); diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchActionTest.java index 535eeb75696..0ed7e63c25c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchActionTest.java @@ -20,29 +20,21 @@ package org.sonar.server.measure.ws; import com.google.common.base.Joiner; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; import javax.annotation.Nullable; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.sonar.api.measures.Metric; import org.sonar.api.server.ws.WebService; 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.ComponentDto; -import org.sonar.db.component.ComponentTesting; -import org.sonar.db.component.SnapshotDto; import org.sonar.db.metric.MetricDto; import org.sonar.db.organization.OrganizationDto; -import org.sonar.db.user.UserDto; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestRequest; @@ -57,16 +49,12 @@ import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; -import static org.sonar.api.utils.DateUtils.parseDateTime; -import static org.sonar.db.component.ComponentTesting.newApplication; +import static org.sonar.api.measures.Metric.ValueType.FLOAT; +import static org.sonar.api.measures.Metric.ValueType.INT; import static org.sonar.db.component.ComponentTesting.newDirectory; import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonar.db.component.ComponentTesting.newModuleDto; import static org.sonar.db.component.ComponentTesting.newSubView; -import static org.sonar.db.component.ComponentTesting.newView; -import static org.sonar.db.component.SnapshotTesting.newAnalysis; -import static org.sonar.db.measure.MeasureTesting.newMeasureDto; -import static org.sonar.db.metric.MetricTesting.newMetricDto; import static org.sonar.test.JsonAssert.assertJson; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_KEYS; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_PROJECT_KEYS; @@ -77,25 +65,41 @@ public class SearchActionTest { public ExpectedException expectedException = ExpectedException.none(); @Rule - public UserSessionRule userSession = UserSessionRule.standalone(); + public UserSessionRule userSession = UserSessionRule.standalone().logIn(); @Rule public DbTester db = DbTester.create(System2.INSTANCE); - private DbClient dbClient = db.getDbClient(); - private DbSession dbSession = db.getSession(); - private UserDto user; - private WsActionTester ws = new WsActionTester(new SearchAction(userSession, dbClient)); - - @Before - public void setUp() throws Exception { - user = db.users().insertUser("john"); - userSession.logIn(user); - } + private WsActionTester ws = new WsActionTester(new SearchAction(userSession, db.getDbClient())); @Test public void json_example() { - List projectKeys = insertJsonExampleData(); + OrganizationDto organization = db.organizations().insert(); + + ComponentDto project1 = db.components().insertPrivateProject(organization, p -> p.setDbKey("MY_PROJECT_1").setName("Project 1")); + ComponentDto project2 = db.components().insertPrivateProject(organization, p -> p.setDbKey("MY_PROJECT_2").setName("Project 2")); + ComponentDto project3 = db.components().insertPrivateProject(organization, p -> p.setDbKey("MY_PROJECT_3").setName("Project 3")); + + userSession.addProjectPermission(UserRole.USER, project1); + userSession.addProjectPermission(UserRole.USER, project2); + userSession.addProjectPermission(UserRole.USER, project3); + + MetricDto complexity = db.measures().insertMetric(m -> m.setKey("complexity").setValueType(INT.name())); + db.measures().insertLiveMeasure(project1, complexity, m -> m.setValue(12.0d)); + db.measures().insertLiveMeasure(project2, complexity, m -> m.setValue(35.0d).setVariation(0.0d)); + db.measures().insertLiveMeasure(project3, complexity, m -> m.setValue(42.0d)); + + MetricDto ncloc = db.measures().insertMetric(m -> m.setKey("ncloc").setValueType(INT.name())); + db.measures().insertLiveMeasure(project1, ncloc, m -> m.setValue(114.0d)); + db.measures().insertLiveMeasure(project2, ncloc, m -> m.setValue(217.0d).setVariation(0.0d)); + db.measures().insertLiveMeasure(project3, ncloc, m -> m.setValue(1984.0d)); + + MetricDto newViolations = db.measures().insertMetric(m -> m.setKey("new_violations").setValueType(INT.name())); + db.measures().insertLiveMeasure(project1, newViolations, m -> m.setVariation(25.0d)); + db.measures().insertLiveMeasure(project2, newViolations, m -> m.setVariation(25.0d)); + db.measures().insertLiveMeasure(project3, newViolations, m -> m.setVariation(255.0d)); + + List projectKeys = Arrays.asList(project1.getDbKey(), project2.getDbKey(), project3.getDbKey()); String result = ws.newRequest() .setParam(PARAM_PROJECT_KEYS, Joiner.on(",").join(projectKeys)) @@ -108,40 +112,34 @@ public class SearchActionTest { @Test public void return_measures() throws Exception { - ComponentDto project = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()); - SnapshotDto projectSnapshot = db.components().insertProjectAndSnapshot(project); - setBrowsePermissionOnUser(project); - MetricDto coverage = insertCoverageMetric(); - dbClient.measureDao().insert(dbSession, newMeasureDto(coverage, project, projectSnapshot).setValue(15.5d)); - db.commit(); + ComponentDto project = db.components().insertPrivateProject(db.getDefaultOrganization()); + userSession.addProjectPermission(UserRole.USER, project); + MetricDto coverage = db.measures().insertMetric(m -> m.setValueType(FLOAT.name())); + db.measures().insertLiveMeasure(project, coverage, m -> m.setValue(15.5d)); - SearchWsResponse result = call(singletonList(project.getDbKey()), singletonList("coverage")); + SearchWsResponse result = call(singletonList(project.getDbKey()), singletonList(coverage.getKey())); List measures = result.getMeasuresList(); assertThat(measures).hasSize(1); Measure measure = measures.get(0); - assertThat(measure.getMetric()).isEqualTo("coverage"); + assertThat(measure.getMetric()).isEqualTo(coverage.getKey()); assertThat(measure.getValue()).isEqualTo("15.5"); } @Test public void return_measures_on_leak_period() throws Exception { - ComponentDto project = ComponentTesting.newPrivateProjectDto(db.organizations().insert()); - SnapshotDto projectSnapshot = db.components().insertProjectAndSnapshot(project); - setBrowsePermissionOnUser(project); - MetricDto coverage = insertCoverageMetric(); - dbClient.measureDao().insert(dbSession, - newMeasureDto(coverage, project, projectSnapshot) - .setValue(15.5d) - .setVariation(10d)); - db.commit(); - - SearchWsResponse result = call(singletonList(project.getDbKey()), singletonList("coverage")); + OrganizationDto organization = db.organizations().insert(); + ComponentDto project = db.components().insertPrivateProject(organization); + userSession.addProjectPermission(UserRole.USER, project); + MetricDto coverage = db.measures().insertMetric(m -> m.setValueType(FLOAT.name())); + db.measures().insertLiveMeasure(project, coverage, m -> m.setValue(15.5d).setVariation(10d)); + + SearchWsResponse result = call(singletonList(project.getDbKey()), singletonList(coverage.getKey())); List measures = result.getMeasuresList(); assertThat(measures).hasSize(1); Measure measure = measures.get(0); - assertThat(measure.getMetric()).isEqualTo("coverage"); + assertThat(measure.getMetric()).isEqualTo(coverage.getKey()); assertThat(measure.getValue()).isEqualTo("15.5"); assertThat(measure.getPeriods().getPeriodsValueList()) .extracting(Measures.PeriodValue::getIndex, Measures.PeriodValue::getValue) @@ -150,130 +148,121 @@ public class SearchActionTest { @Test public void sort_by_metric_key_then_project_name() throws Exception { - MetricDto coverage = insertCoverageMetric(); - MetricDto complexity = insertComplexityMetric(); - OrganizationDto organizationDto = db.organizations().insert(); - ComponentDto project1 = ComponentTesting.newPrivateProjectDto(organizationDto).setName("C"); - SnapshotDto projectSnapshot1 = db.components().insertProjectAndSnapshot(project1); - ComponentDto project2 = ComponentTesting.newPrivateProjectDto(organizationDto).setName("A"); - SnapshotDto projectSnapshot2 = db.components().insertProjectAndSnapshot(project2); - ComponentDto project3 = ComponentTesting.newPrivateProjectDto(organizationDto).setName("B"); - SnapshotDto projectSnapshot3 = db.components().insertProjectAndSnapshot(project3); - setBrowsePermissionOnUser(project1, project2, project3); - dbClient.measureDao().insert(dbSession, newMeasureDto(coverage, project1, projectSnapshot1).setValue(5.5d)); - dbClient.measureDao().insert(dbSession, newMeasureDto(coverage, project2, projectSnapshot2).setValue(6.5d)); - dbClient.measureDao().insert(dbSession, newMeasureDto(coverage, project3, projectSnapshot3).setValue(7.5d)); - dbClient.measureDao().insert(dbSession, newMeasureDto(complexity, project1, projectSnapshot1).setValue(10d)); - dbClient.measureDao().insert(dbSession, newMeasureDto(complexity, project2, projectSnapshot2).setValue(15d)); - dbClient.measureDao().insert(dbSession, newMeasureDto(complexity, project3, projectSnapshot3).setValue(20d)); - db.commit(); - - SearchWsResponse result = call(asList(project1.getDbKey(), project2.getDbKey(), project3.getDbKey()), asList("coverage", "complexity")); + MetricDto coverage = db.measures().insertMetric(m -> m.setKey("coverage").setValueType(FLOAT.name())); + MetricDto complexity = db.measures().insertMetric(m -> m.setKey("complexity").setValueType(INT.name())); + OrganizationDto organization = db.organizations().insert(); + ComponentDto project1 = db.components().insertPrivateProject(organization, p -> p.setName("C")); + ComponentDto project2 = db.components().insertPrivateProject(organization, p -> p.setName("A")); + ComponentDto project3 = db.components().insertPrivateProject(organization, p -> p.setName("B")); + userSession.addProjectPermission(UserRole.USER, project1); + userSession.addProjectPermission(UserRole.USER, project2); + userSession.addProjectPermission(UserRole.USER, project3); + db.measures().insertLiveMeasure(project1, coverage, m -> m.setValue(5.5d)); + db.measures().insertLiveMeasure(project2, coverage, m -> m.setValue(6.5d)); + db.measures().insertLiveMeasure(project3, coverage, m -> m.setValue(7.5d)); + db.measures().insertLiveMeasure(project1, complexity, m -> m.setValue(10d)); + db.measures().insertLiveMeasure(project2, complexity, m -> m.setValue(15d)); + db.measures().insertLiveMeasure(project3, complexity, m -> m.setValue(20d)); + + SearchWsResponse result = call(asList(project1.getDbKey(), project2.getDbKey(), project3.getDbKey()), asList(coverage.getKey(), complexity.getKey())); assertThat(result.getMeasuresList()).extracting(Measure::getMetric, Measure::getComponent) .containsExactly( - tuple("complexity", project2.getDbKey()), tuple("complexity", project3.getDbKey()), tuple("complexity", project1.getDbKey()), - tuple("coverage", project2.getDbKey()), tuple("coverage", project3.getDbKey()), tuple("coverage", project1.getDbKey())); + tuple(complexity.getKey(), project2.getDbKey()), tuple(complexity.getKey(), project3.getDbKey()), tuple(complexity.getKey(), project1.getDbKey()), + tuple(coverage.getKey(), project2.getDbKey()), tuple(coverage.getKey(), project3.getDbKey()), tuple(coverage.getKey(), project1.getDbKey())); } @Test public void return_measures_on_view() throws Exception { - ComponentDto view = newView(db.getDefaultOrganization()); - SnapshotDto viewSnapshot = db.components().insertProjectAndSnapshot(view); - MetricDto coverage = insertCoverageMetric(); - dbClient.measureDao().insert(dbSession, newMeasureDto(coverage, view, viewSnapshot).setValue(15.5d)); - db.commit(); + OrganizationDto organization = db.organizations().insert(); + ComponentDto view = db.components().insertPrivatePortfolio(organization); + userSession.addProjectPermission(UserRole.USER, view); + MetricDto coverage = db.measures().insertMetric(m -> m.setValueType(FLOAT.name())); + db.measures().insertLiveMeasure(view, coverage, m -> m.setValue(15.5d)); - SearchWsResponse result = call(singletonList(view.getDbKey()), singletonList("coverage")); + SearchWsResponse result = call(singletonList(view.getDbKey()), singletonList(coverage.getKey())); List measures = result.getMeasuresList(); assertThat(measures).hasSize(1); Measure measure = measures.get(0); - assertThat(measure.getMetric()).isEqualTo("coverage"); + assertThat(measure.getMetric()).isEqualTo(coverage.getKey()); assertThat(measure.getValue()).isEqualTo("15.5"); } @Test public void return_measures_on_application() throws Exception { - ComponentDto application = newApplication(db.getDefaultOrganization()); - SnapshotDto viewSnapshot = db.components().insertProjectAndSnapshot(application); - MetricDto coverage = insertCoverageMetric(); - dbClient.measureDao().insert(dbSession, newMeasureDto(coverage, application, viewSnapshot).setValue(15.5d)); - db.commit(); + OrganizationDto organization = db.organizations().insert(); + ComponentDto application = db.components().insertPrivateApplication(organization); + userSession.addProjectPermission(UserRole.USER, application); + MetricDto coverage = db.measures().insertMetric(m -> m.setValueType(FLOAT.name())); + db.measures().insertLiveMeasure(application, coverage, m -> m.setValue(15.5d)); - SearchWsResponse result = call(singletonList(application.getDbKey()), singletonList("coverage")); + SearchWsResponse result = call(singletonList(application.getDbKey()), singletonList(coverage.getKey())); List measures = result.getMeasuresList(); assertThat(measures).hasSize(1); Measure measure = measures.get(0); - assertThat(measure.getMetric()).isEqualTo("coverage"); + assertThat(measure.getMetric()).isEqualTo(coverage.getKey()); assertThat(measure.getValue()).isEqualTo("15.5"); } @Test public void return_measures_on_sub_view() throws Exception { - ComponentDto view = newView(db.getDefaultOrganization()); - SnapshotDto viewSnapshot = db.components().insertProjectAndSnapshot(view); + OrganizationDto organization = db.organizations().insert(); + ComponentDto view = db.components().insertPrivatePortfolio(organization); ComponentDto subView = db.components().insertComponent(newSubView(view)); - MetricDto coverage = insertCoverageMetric(); - dbClient.measureDao().insert(dbSession, newMeasureDto(coverage, subView, viewSnapshot).setValue(15.5d)); - db.commit(); + userSession.addProjectPermission(UserRole.USER, subView); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(FLOAT.name())); + db.measures().insertLiveMeasure(subView, metric, m -> m.setValue(15.5d)); - SearchWsResponse result = call(singletonList(subView.getDbKey()), singletonList("coverage")); + SearchWsResponse result = call(singletonList(subView.getDbKey()), singletonList(metric.getKey())); List measures = result.getMeasuresList(); assertThat(measures).hasSize(1); Measure measure = measures.get(0); - assertThat(measure.getMetric()).isEqualTo("coverage"); + assertThat(measure.getMetric()).isEqualTo(metric.getKey()); assertThat(measure.getValue()).isEqualTo("15.5"); } @Test public void only_returns_authorized_projects() { - MetricDto metricDto = insertComplexityMetric(); - ComponentDto project1 = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()); - SnapshotDto projectSnapshot1 = db.components().insertProjectAndSnapshot(project1); - ComponentDto project2 = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()); - SnapshotDto projectSnapshot2 = db.components().insertProjectAndSnapshot(project2); - dbClient.measureDao().insert(dbSession, - newMeasureDto(metricDto, project1, projectSnapshot1).setValue(15.5d), - newMeasureDto(metricDto, project2, projectSnapshot2).setValue(42.0d)); - db.commit(); - setBrowsePermissionOnUser(project1); - - SearchWsResponse result = call(asList(project1.getDbKey(), project2.getDbKey()), singletonList("complexity")); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(FLOAT.name())); + ComponentDto project1 = db.components().insertPrivateProject(db.getDefaultOrganization()); + ComponentDto project2 = db.components().insertPrivateProject(db.getDefaultOrganization()); + db.measures().insertLiveMeasure(project1, metric, m -> m.setValue(15.5d)); + db.measures().insertLiveMeasure(project2, metric, m -> m.setValue(42.0d)); + Arrays.stream(new ComponentDto[]{project1}).forEach(p -> userSession.addProjectPermission(UserRole.USER, p)); + + SearchWsResponse result = call(asList(project1.getDbKey(), project2.getDbKey()), singletonList(metric.getKey())); assertThat(result.getMeasuresList()).extracting(Measure::getComponent).containsOnly(project1.getDbKey()); } @Test public void do_not_verify_permissions_if_user_is_root() { - MetricDto metricDto = insertComplexityMetric(); - ComponentDto project1 = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()); - SnapshotDto projectSnapshot1 = db.components().insertProjectAndSnapshot(project1); - dbClient.measureDao().insert(dbSession, newMeasureDto(metricDto, project1, projectSnapshot1).setValue(15.5d)); - db.commit(); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(FLOAT.name())); + ComponentDto project1 = db.components().insertPrivateProject(db.getDefaultOrganization()); + db.measures().insertLiveMeasure(project1, metric, m -> m.setValue(15.5d)); userSession.setNonRoot(); - SearchWsResponse result = call(asList(project1.getDbKey()), singletonList("complexity")); + SearchWsResponse result = call(singletonList(project1.getDbKey()), singletonList(metric.getKey())); assertThat(result.getMeasuresCount()).isEqualTo(0); userSession.setRoot(); - result = call(asList(project1.getDbKey()), singletonList("complexity")); + result = call(singletonList(project1.getDbKey()), singletonList(metric.getKey())); assertThat(result.getMeasuresCount()).isEqualTo(1); } @Test public void does_not_return_branch_when_using_db_key() { - MetricDto coverage = insertCoverageMetric(); + MetricDto coverage = db.measures().insertMetric(m -> m.setValueType(FLOAT.name())); ComponentDto project = db.components().insertMainBranch(); ComponentDto branch = db.components().insertProjectBranch(project); - SnapshotDto analysis = db.components().insertSnapshot(branch); - db.measures().insertMeasure(branch, analysis, coverage, m -> m.setValue(10d)); - setBrowsePermissionOnUser(project); + db.measures().insertLiveMeasure(branch, coverage, m -> m.setValue(10d)); + userSession.addProjectPermission(UserRole.USER, project); - SearchWsResponse result = call(asList(branch.getDbKey()), singletonList(coverage.getKey())); + SearchWsResponse result = call(singletonList(branch.getDbKey()), singletonList(coverage.getKey())); assertThat(result.getMeasuresList()).isEmpty(); } @@ -281,7 +270,7 @@ public class SearchActionTest { @Test public void fail_if_no_metric() { ComponentDto project = db.components().insertPrivateProject(); - setBrowsePermissionOnUser(project); + userSession.addProjectPermission(UserRole.USER, project); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("The 'metricKeys' parameter is missing"); @@ -292,7 +281,7 @@ public class SearchActionTest { @Test public void fail_if_empty_metric() { ComponentDto project = db.components().insertPrivateProject(); - setBrowsePermissionOnUser(project); + userSession.addProjectPermission(UserRole.USER, project); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Metric keys must be provided"); @@ -303,33 +292,33 @@ public class SearchActionTest { @Test public void fail_if_unknown_metric() { ComponentDto project = db.components().insertPrivateProject(); - setBrowsePermissionOnUser(project); - insertComplexityMetric(); + userSession.addProjectPermission(UserRole.USER, project); + MetricDto metric = db.measures().insertMetric(); expectedException.expect(BadRequestException.class); expectedException.expectMessage("The following metrics are not found: ncloc, violations"); - call(singletonList(project.getDbKey()), newArrayList("violations", "complexity", "ncloc")); + call(singletonList(project.getDbKey()), newArrayList("violations", metric.getKey(), "ncloc")); } @Test public void fail_if_no_project() { - insertComplexityMetric(); + MetricDto metric = db.measures().insertMetric(); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Project keys must be provided"); - call(null, singletonList("complexity")); + call(null, singletonList(metric.getKey())); } @Test public void fail_if_empty_project_key() { - insertComplexityMetric(); + MetricDto metric = db.measures().insertMetric(); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Project keys must be provided"); - call(emptyList(), singletonList("complexity")); + call(emptyList(), singletonList(metric.getKey())); } @Test @@ -338,12 +327,12 @@ public class SearchActionTest { .mapToObj(i -> db.components().insertPrivateProject()) .map(ComponentDto::getDbKey) .collect(Collectors.toList()); - insertComplexityMetric(); + MetricDto metric = db.measures().insertMetric(); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("101 projects provided, more than maximum authorized (100)"); - call(keys, singletonList("complexity")); + call(keys, singletonList(metric.getKey())); } @Test @@ -352,48 +341,48 @@ public class SearchActionTest { .mapToObj(i -> db.components().insertPrivateProject()) .map(ComponentDto::getDbKey) .collect(Collectors.toList()); - insertComplexityMetric(); + MetricDto metric = db.measures().insertMetric(); - call(keys, singletonList("complexity")); + call(keys, singletonList(metric.getKey())); } @Test public void fail_if_module() { ComponentDto project = db.components().insertPrivateProject(); ComponentDto module = db.components().insertComponent(newModuleDto(project)); - setBrowsePermissionOnUser(project); - insertComplexityMetric(); + userSession.addProjectPermission(UserRole.USER, project); + MetricDto metric = db.measures().insertMetric(); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Only component of qualifiers [TRK, APP, VW, SVW] are allowed"); - call(singletonList(module.getDbKey()), singletonList("complexity")); + call(singletonList(module.getDbKey()), singletonList(metric.getKey())); } @Test public void fail_if_directory() { ComponentDto project = db.components().insertPrivateProject(); ComponentDto dir = db.components().insertComponent(newDirectory(project, "dir")); - setBrowsePermissionOnUser(project); - insertComplexityMetric(); + userSession.addProjectPermission(UserRole.USER, project); + MetricDto metric = db.measures().insertMetric(); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Only component of qualifiers [TRK, APP, VW, SVW] are allowed"); - call(singletonList(dir.getDbKey()), singletonList("complexity")); + call(singletonList(dir.getDbKey()), singletonList(metric.getKey())); } @Test public void fail_if_file() { ComponentDto project = db.components().insertPrivateProject(); ComponentDto file = db.components().insertComponent(newFileDto(project)); - setBrowsePermissionOnUser(project); - insertComplexityMetric(); + userSession.addProjectPermission(UserRole.USER, project); + MetricDto metric = db.measures().insertMetric(); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Only component of qualifiers [TRK, APP, VW, SVW] are allowed"); - call(singletonList(file.getDbKey()), singletonList("complexity")); + call(singletonList(file.getDbKey()), singletonList(metric.getKey())); } @Test @@ -418,132 +407,4 @@ public class SearchActionTest { } return request.executeProtobuf(SearchWsResponse.class); } - - private static MetricDto newMetricDtoWithoutOptimization() { - return newMetricDto() - .setWorstValue(null) - .setBestValue(null) - .setOptimizedBestValue(false) - .setUserManaged(false); - } - - private MetricDto insertNewViolationsMetric() { - MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDtoWithoutOptimization() - .setKey("new_violations") - .setShortName("New issues") - .setDescription("New Issues") - .setDomain("Issues") - .setValueType("INT") - .setDirection(-1) - .setQualitative(true) - .setHidden(false) - .setUserManaged(false) - .setOptimizedBestValue(true) - .setBestValue(0.0d)); - db.commit(); - return metric; - } - - private MetricDto insertNclocMetric() { - MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDtoWithoutOptimization() - .setKey("ncloc") - .setShortName("Lines of code") - .setDescription("Non Commenting Lines of Code") - .setDomain("Size") - .setValueType("INT") - .setDirection(-1) - .setQualitative(false) - .setHidden(false) - .setUserManaged(false)); - db.commit(); - return metric; - } - - private MetricDto insertComplexityMetric() { - MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDtoWithoutOptimization() - .setKey("complexity") - .setShortName("Complexity") - .setDescription("Cyclomatic complexity") - .setDomain("Complexity") - .setValueType("INT") - .setDirection(-1) - .setQualitative(false) - .setHidden(false) - .setUserManaged(false)); - db.commit(); - return metric; - } - - private MetricDto insertCoverageMetric() { - MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDtoWithoutOptimization() - .setKey("coverage") - .setShortName("Coverage") - .setDescription("Code Coverage") - .setDomain("Coverage") - .setValueType(Metric.ValueType.FLOAT.name()) - .setDirection(1) - .setQualitative(false) - .setHidden(false) - .setUserManaged(false)); - db.commit(); - return metric; - } - - private List insertJsonExampleData() { - List projectKeys = new ArrayList<>(); - OrganizationDto organizationDto = db.organizations().insert(); - ComponentDto project1 = ComponentTesting.newPrivateProjectDto(organizationDto).setDbKey("MY_PROJECT_1").setName("Project 1"); - ComponentDto project2 = ComponentTesting.newPrivateProjectDto(organizationDto).setDbKey("MY_PROJECT_2").setName("Project 2"); - ComponentDto project3 = ComponentTesting.newPrivateProjectDto(organizationDto).setDbKey("MY_PROJECT_3").setName("Project 3"); - projectKeys.addAll(asList(project1.getDbKey(), project2.getDbKey(), project3.getDbKey())); - db.components().insertComponents(project1, project2, project3); - SnapshotDto projectSnapshot1 = dbClient.snapshotDao().insert(dbSession, newAnalysis(project1) - .setPeriodDate(parseDateTime("2016-01-11T10:49:50+0100").getTime()) - .setPeriodMode("previous_version") - .setPeriodParam("1.0-SNAPSHOT")); - SnapshotDto projectSnapshot2 = dbClient.snapshotDao().insert(dbSession, newAnalysis(project2) - .setPeriodDate(parseDateTime("2016-01-11T10:49:50+0100").getTime()) - .setPeriodMode("previous_version") - .setPeriodParam("1.0-SNAPSHOT")); - SnapshotDto projectSnapshot3 = dbClient.snapshotDao().insert(dbSession, newAnalysis(project3) - .setPeriodDate(parseDateTime("2016-01-11T10:49:50+0100").getTime()) - .setPeriodMode("previous_version") - .setPeriodParam("1.0-SNAPSHOT")); - - MetricDto complexity = insertComplexityMetric(); - dbClient.measureDao().insert(dbSession, - newMeasureDto(complexity, project1, projectSnapshot1) - .setValue(12.0d), - newMeasureDto(complexity, project2, projectSnapshot2) - .setValue(35.0d) - .setVariation(0.0d), - newMeasureDto(complexity, project3, projectSnapshot3) - .setValue(42.0d)); - - MetricDto ncloc = insertNclocMetric(); - dbClient.measureDao().insert(dbSession, - newMeasureDto(ncloc, project1, projectSnapshot1) - .setValue(114.0d), - newMeasureDto(ncloc, project2, projectSnapshot2) - .setValue(217.0d) - .setVariation(0.0d), - newMeasureDto(ncloc, project3, projectSnapshot3) - .setValue(1984.0d)); - - MetricDto newViolations = insertNewViolationsMetric(); - dbClient.measureDao().insert(dbSession, - newMeasureDto(newViolations, project1, projectSnapshot1) - .setVariation(25.0d), - newMeasureDto(newViolations, project2, projectSnapshot2) - .setVariation(25.0d), - newMeasureDto(newViolations, project3, projectSnapshot3) - .setVariation(255.0d)); - db.commit(); - setBrowsePermissionOnUser(project1, project2, project3); - return projectKeys; - } - - private void setBrowsePermissionOnUser(ComponentDto... projects) { - Arrays.stream(projects).forEach(p -> userSession.addProjectPermission(UserRole.USER, p)); - } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchHistoryActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchHistoryActionTest.java index f778f3fdac0..1544e8a77b3 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchHistoryActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchHistoryActionTest.java @@ -264,20 +264,6 @@ public class SearchHistoryActionTest { assertThat(result.getPaging().getTotal()).isEqualTo(1); } - @Test - public void do_not_return_developer_measures() { - wsRequest.setMetrics(singletonList(complexityMetric.getKey())); - dbClient.measureDao().insert(dbSession, newMeasureDto(complexityMetric, project, analysis).setDeveloperId(42L)); - db.commit(); - - SearchHistoryResponse result = call(); - - assertThat(result.getMeasuresCount()).isEqualTo(1); - assertThat(result.getMeasures(0).getHistoryCount()).isEqualTo(1); - assertThat(result.getMeasures(0).getHistory(0).hasDate()).isTrue(); - assertThat(result.getMeasures(0).getHistory(0).hasValue()).isFalse(); - } - @Test public void branch() { ComponentDto project = db.components().insertPrivateProject(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchMyProjectsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchMyProjectsActionTest.java index d0d986e8b0e..14148994fce 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchMyProjectsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchMyProjectsActionTest.java @@ -52,7 +52,7 @@ import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY; import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; import static org.sonar.db.component.ComponentTesting.newView; import static org.sonar.db.component.SnapshotTesting.newAnalysis; -import static org.sonar.db.measure.MeasureTesting.newMeasureDto; +import static org.sonar.db.measure.MeasureTesting.newLiveMeasure; import static org.sonar.db.metric.MetricTesting.newMetricDto; import static org.sonar.db.user.UserTesting.newUserDto; import static org.sonar.test.JsonAssert.assertJson; @@ -95,8 +95,8 @@ public class SearchMyProjectsActionTest { long anotherTime = DateUtils.parseDateTime("2016-06-11T14:25:53+0000").getTime(); SnapshotDto jdk7Snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(jdk7).setCreatedAt(oneTime)); SnapshotDto cLangSnapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(cLang).setCreatedAt(anotherTime)); - dbClient.measureDao().insert(dbSession, newMeasureDto(alertStatusMetric, jdk7, jdk7Snapshot).setData(Level.ERROR.name())); - dbClient.measureDao().insert(dbSession, newMeasureDto(alertStatusMetric, cLang, cLangSnapshot).setData(Level.OK.name())); + dbClient.liveMeasureDao().insert(dbSession, newLiveMeasure(jdk7, alertStatusMetric).setData(Level.ERROR.name())); + dbClient.liveMeasureDao().insert(dbSession, newLiveMeasure(cLang, alertStatusMetric).setData(Level.OK.name())); db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, jdk7); db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, cLang); db.commit(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ProjectStatusActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ProjectStatusActionTest.java index 2bf2c728243..242bbc6b553 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ProjectStatusActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ProjectStatusActionTest.java @@ -51,6 +51,7 @@ import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; import static org.sonar.db.component.SnapshotTesting.newAnalysis; +import static org.sonar.db.measure.MeasureTesting.newLiveMeasure; import static org.sonar.db.measure.MeasureTesting.newMeasureDto; import static org.sonar.db.metric.MetricTesting.newMetricDto; import static org.sonar.test.JsonAssert.assertJson; @@ -71,17 +72,22 @@ public class ProjectStatusActionTest { private WsActionTester ws; private DbClient dbClient; private DbSession dbSession; + private MetricDto gateDetailsMetric; @Before public void setUp() { dbClient = db.getDbClient(); dbSession = db.getSession(); + gateDetailsMetric = dbClient.metricDao().insert(dbSession, newMetricDto() + .setEnabled(true) + .setKey(CoreMetrics.QUALITY_GATE_DETAILS_KEY)); + ws = new WsActionTester(new ProjectStatusAction(dbClient, TestComponentFinder.from(db), userSession)); } @Test - public void definition() throws Exception { + public void test_definition() throws Exception { WebService.Action def = ws.getDef(); assertThat(def.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("analysisId", "projectKey", "projectId"); assertThat(def.changelog()).extracting(Change::getVersion, Change::getDescription).containsExactly( @@ -90,7 +96,7 @@ public class ProjectStatusActionTest { } @Test - public void json_example() throws IOException { + public void test_json_example() throws IOException { ComponentDto project = db.components().insertPrivateProject(db.organizations().insert()); userSession.addProjectPermission(UserRole.USER, project); @@ -98,11 +104,8 @@ public class ProjectStatusActionTest { .setPeriodMode("last_version") .setPeriodParam("2015-12-07") .setPeriodDate(956789123987L)); - MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDto() - .setEnabled(true) - .setKey(CoreMetrics.QUALITY_GATE_DETAILS_KEY)); dbClient.measureDao().insert(dbSession, - newMeasureDto(metric, project, snapshot) + newMeasureDto(gateDetailsMetric, project, snapshot) .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionTest/measure_data.json")))); dbSession.commit(); @@ -114,17 +117,43 @@ public class ProjectStatusActionTest { } @Test - public void return_status_by_project_id() throws IOException { + public void return_past_status_when_project_is_referenced_by_past_analysis_id() throws IOException { ComponentDto project = db.components().insertPrivateProject(db.organizations().insert()); - SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(project) + SnapshotDto pastAnalysis = dbClient.snapshotDao().insert(dbSession, newAnalysis(project) + .setLast(false) .setPeriodMode("last_version") .setPeriodParam("2015-12-07") .setPeriodDate(956789123987L)); - MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDto() - .setEnabled(true) - .setKey(CoreMetrics.QUALITY_GATE_DETAILS_KEY)); + SnapshotDto lastAnalysis = dbClient.snapshotDao().insert(dbSession, newAnalysis(project) + .setLast(true) + .setPeriodMode("last_version") + .setPeriodParam("2016-12-07") + .setPeriodDate(1_500L)); dbClient.measureDao().insert(dbSession, - newMeasureDto(metric, project, snapshot) + newMeasureDto(gateDetailsMetric, project, pastAnalysis) + .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionTest/measure_data.json")))); + dbClient.measureDao().insert(dbSession, + newMeasureDto(gateDetailsMetric, project, lastAnalysis) + .setData("not_used")); + dbSession.commit(); + userSession.addProjectPermission(UserRole.USER, project); + + String response = ws.newRequest() + .setParam(PARAM_ANALYSIS_ID, pastAnalysis.getUuid()) + .execute().getInput(); + + assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json")); + } + + @Test + public void return_live_status_when_project_is_referenced_by_its_id() throws IOException { + ComponentDto project = db.components().insertPrivateProject(db.organizations().insert()); + dbClient.snapshotDao().insert(dbSession, newAnalysis(project) + .setPeriodMode("last_version") + .setPeriodParam("2015-12-07") + .setPeriodDate(956789123987L)); + dbClient.liveMeasureDao().insert(dbSession, + newLiveMeasure(project, gateDetailsMetric) .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionTest/measure_data.json")))); dbSession.commit(); userSession.addProjectPermission(UserRole.USER, project); @@ -137,17 +166,14 @@ public class ProjectStatusActionTest { } @Test - public void return_status_by_project_key() throws IOException { + public void return_live_status_when_project_is_referenced_by_its_key() throws IOException { ComponentDto project = db.components().insertComponent(ComponentTesting.newPrivateProjectDto(db.organizations().insert()).setDbKey("project-key")); - SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(project) + dbClient.snapshotDao().insert(dbSession, newAnalysis(project) .setPeriodMode("last_version") .setPeriodParam("2015-12-07") .setPeriodDate(956789123987L)); - MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDto() - .setEnabled(true) - .setKey(CoreMetrics.QUALITY_GATE_DETAILS_KEY)); - dbClient.measureDao().insert(dbSession, - newMeasureDto(metric, project, snapshot) + dbClient.liveMeasureDao().insert(dbSession, + newLiveMeasure(project, gateDetailsMetric) .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionTest/measure_data.json")))); dbSession.commit(); userSession.addProjectPermission(UserRole.USER, project); @@ -160,24 +186,24 @@ public class ProjectStatusActionTest { } @Test - public void return_undefined_status_if_measure_is_not_found() { + public void return_undefined_status_if_specified_analysis_is_not_found() { ComponentDto project = db.components().insertPrivateProject(db.organizations().insert()); SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(project)); dbSession.commit(); userSession.addProjectPermission(UserRole.USER, project); - ProjectStatusResponse result = call(snapshot.getUuid()); + ProjectStatusResponse result = callByAnalysisId(snapshot.getUuid()); assertThat(result.getProjectStatus().getStatus()).isEqualTo(Status.NONE); assertThat(result.getProjectStatus().getConditionsCount()).isEqualTo(0); } @Test - public void return_undefined_status_if_snapshot_is_not_found() { + public void return_undefined_status_if_project_is_not_analyzed() { ComponentDto project = db.components().insertPrivateProject(db.organizations().insert()); userSession.addProjectPermission(UserRole.USER, project); - ProjectStatusResponse result = callByProjectUuid(project.uuid()); + ProjectStatusResponse result = callByProjectId(project.uuid()); assertThat(result.getProjectStatus().getStatus()).isEqualTo(Status.NONE); assertThat(result.getProjectStatus().getConditionsCount()).isEqualTo(0); @@ -190,7 +216,7 @@ public class ProjectStatusActionTest { dbSession.commit(); userSession.addProjectPermission(UserRole.ADMIN, project); - call(snapshot.getUuid()); + callByAnalysisId(snapshot.getUuid()); } @Test @@ -200,7 +226,7 @@ public class ProjectStatusActionTest { dbSession.commit(); userSession.addProjectPermission(UserRole.USER, project); - call(snapshot.getUuid()); + callByAnalysisId(snapshot.getUuid()); } @Test @@ -210,7 +236,7 @@ public class ProjectStatusActionTest { expectedException.expect(NotFoundException.class); expectedException.expectMessage("Analysis with id 'task-uuid' is not found"); - call(ANALYSIS_ID); + callByAnalysisId(ANALYSIS_ID); } @Test @@ -222,7 +248,7 @@ public class ProjectStatusActionTest { expectedException.expect(ForbiddenException.class); - call(snapshot.getUuid()); + callByAnalysisId(snapshot.getUuid()); } @Test @@ -280,13 +306,13 @@ public class ProjectStatusActionTest { .execute(); } - private ProjectStatusResponse call(String taskId) { + private ProjectStatusResponse callByAnalysisId(String taskId) { return ws.newRequest() - .setParam("analysisId", taskId) + .setParam(PARAM_ANALYSIS_ID, taskId) .executeProtobuf(ProjectStatusResponse.class); } - private ProjectStatusResponse callByProjectUuid(String projectUuid) { + private ProjectStatusResponse callByProjectId(String projectUuid) { return ws.newRequest() .setParam(PARAM_PROJECT_ID, projectUuid) .executeProtobuf(ProjectStatusResponse.class); diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatterTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatterTest.java index 62f96974cf8..816bac90633 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatterTest.java @@ -19,9 +19,9 @@ */ package org.sonar.server.qualitygate.ws; -import com.google.common.base.Optional; import java.io.IOException; import java.util.List; +import java.util.Optional; import javax.annotation.Nullable; import org.apache.commons.io.IOUtils; import org.junit.Rule; @@ -150,6 +150,6 @@ public class QualityGateDetailsFormatterTest { } private static QualityGateDetailsFormatter newQualityGateDetailsFormatter(@Nullable String measureData, @Nullable SnapshotDto snapshotDto) { - return new QualityGateDetailsFormatter(Optional.fromNullable(measureData), Optional.fromNullable(snapshotDto)); + return new QualityGateDetailsFormatter(Optional.ofNullable(measureData), Optional.ofNullable(snapshotDto)); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/telemetry/TelemetryDaemonTest.java b/server/sonar-server/src/test/java/org/sonar/server/telemetry/TelemetryDaemonTest.java index b62eef280f1..f82a9649e93 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/telemetry/TelemetryDaemonTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/telemetry/TelemetryDaemonTest.java @@ -41,7 +41,6 @@ import org.sonar.core.platform.PluginRepository; 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.metric.MetricDto; import org.sonar.server.es.EsTester; import org.sonar.server.measure.index.ProjectMeasuresIndex; @@ -126,18 +125,16 @@ public class TelemetryDaemonTest { ComponentDto project1 = db.components().insertMainBranch(db.getDefaultOrganization()); ComponentDto project1Branch = db.components().insertProjectBranch(project1); - SnapshotDto analysis1 = db.components().insertSnapshot(project1); - db.measures().insertMeasure(project1, analysis1, lines, m -> m.setValue(200d)); - db.measures().insertMeasure(project1, analysis1, ncloc, m -> m.setValue(100d)); - db.measures().insertMeasure(project1, analysis1, coverage, m -> m.setValue(80d)); - db.measures().insertMeasure(project1, analysis1, nclocDistrib, m -> m.setData("java=200;js=50")); + db.measures().insertLiveMeasure(project1, lines, m -> m.setValue(200d)); + db.measures().insertLiveMeasure(project1, ncloc, m -> m.setValue(100d)); + db.measures().insertLiveMeasure(project1, coverage, m -> m.setValue(80d)); + db.measures().insertLiveMeasure(project1, nclocDistrib, m -> m.setValue(null).setData("java=200;js=50")); ComponentDto project2 = db.components().insertMainBranch(db.getDefaultOrganization()); - SnapshotDto analysis2 = db.components().insertSnapshot(project2); - db.measures().insertMeasure(project2, analysis2, lines, m -> m.setValue(300d)); - db.measures().insertMeasure(project2, analysis2, ncloc, m -> m.setValue(200d)); - db.measures().insertMeasure(project2, analysis2, coverage, m -> m.setValue(80d)); - db.measures().insertMeasure(project2, analysis2, nclocDistrib, m -> m.setData("java=300;kotlin=2500")); + db.measures().insertLiveMeasure(project2, lines, m -> m.setValue(300d)); + db.measures().insertLiveMeasure(project2, ncloc, m -> m.setValue(200d)); + db.measures().insertLiveMeasure(project2, coverage, m -> m.setValue(80d)); + db.measures().insertLiveMeasure(project2, nclocDistrib, m -> m.setValue(null).setData("java=300;kotlin=2500")); projectMeasuresIndexer.indexOnStartup(emptySet()); underTest.start(); @@ -173,12 +170,9 @@ public class TelemetryDaemonTest { ComponentDto project = db.components().insertMainBranch(db.getDefaultOrganization()); ComponentDto longBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(LONG)); ComponentDto shortBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(SHORT)); - SnapshotDto projectAnalysis = db.components().insertSnapshot(project); - SnapshotDto longBranchAnalysis = db.components().insertSnapshot(longBranch); - SnapshotDto shortBranchAnalysis = db.components().insertSnapshot(shortBranch); - db.measures().insertMeasure(project, projectAnalysis, ncloc, m -> m.setValue(10d)); - db.measures().insertMeasure(longBranch, longBranchAnalysis, ncloc, m -> m.setValue(20d)); - db.measures().insertMeasure(shortBranch, shortBranchAnalysis, ncloc, m -> m.setValue(30d)); + db.measures().insertLiveMeasure(project, ncloc, m -> m.setValue(10d)); + db.measures().insertLiveMeasure(longBranch, ncloc, m -> m.setValue(20d)); + db.measures().insertLiveMeasure(shortBranch, ncloc, m -> m.setValue(30d)); projectMeasuresIndexer.indexOnStartup(emptySet()); underTest.start(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/ui/ws/ComponentActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/ui/ws/ComponentActionTest.java index 536795e12b1..6c376653434 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/ui/ws/ComponentActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/ui/ws/ComponentActionTest.java @@ -76,7 +76,7 @@ import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonar.db.component.ComponentTesting.newModuleDto; import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; import static org.sonar.db.component.SnapshotTesting.newAnalysis; -import static org.sonar.db.measure.MeasureTesting.newMeasureDto; +import static org.sonar.db.measure.MeasureTesting.newLiveMeasure; import static org.sonar.db.metric.MetricTesting.newMetricDto; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_GATES; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES; @@ -262,8 +262,7 @@ public class ComponentActionTest { public void return_quality_profiles() throws Exception { init(); componentDbTester.insertComponent(project); - SnapshotDto analysis = componentDbTester.insertSnapshot(newAnalysis(project)); - addQualityProfiles(project, analysis, + addQualityProfiles(project, createQProfile("qp1", "Sonar Way Java", "java"), createQProfile("qp2", "Sonar Way Xoo", "xoo")); userSession.addProjectPermission(UserRole.USER, project); @@ -329,7 +328,7 @@ public class ComponentActionTest { .build(); init(page); - ComponentDto application = componentDbTester.insertApplication(dbTester.getDefaultOrganization()); + ComponentDto application = componentDbTester.insertPublicApplication(dbTester.getDefaultOrganization()); userSession.registerComponents(application); String result = ws.newRequest() @@ -471,7 +470,7 @@ public class ComponentActionTest { when(resourceTypes.get(project.qualifier())).thenReturn(DefaultResourceTypes.get().getRootType()); UserDto user = dbTester.users().insertUser("obiwan"); propertyDbTester.insertProperty(new PropertyDto().setKey("favourite").setResourceId(project.getId()).setUserId(user.getId())); - addQualityProfiles(project, analysis, + addQualityProfiles(project, createQProfile("qp1", "Sonar Way Java", "java"), createQProfile("qp2", "Sonar Way Xoo", "xoo")); QualityGateDto qualityGateDto = dbTester.qualityGates().insertQualityGate("Sonar way"); @@ -580,11 +579,11 @@ public class ComponentActionTest { verify(execute(componentKey), expectedJson); } - private void addQualityProfiles(ComponentDto project, SnapshotDto analysis, QualityProfile... qps) { - MetricDto metricDto = newMetricDto().setKey(QUALITY_PROFILES_KEY); - dbClient.metricDao().insert(dbTester.getSession(), metricDto); - dbClient.measureDao().insert(dbTester.getSession(), - newMeasureDto(metricDto, project, analysis) + private void addQualityProfiles(ComponentDto project, QualityProfile... qps) { + MetricDto metric = newMetricDto().setKey(QUALITY_PROFILES_KEY); + dbClient.metricDao().insert(dbTester.getSession(), metric); + dbClient.liveMeasureDao().insert(dbTester.getSession(), + newLiveMeasure(project, metric) .setData(qualityProfilesToJson(qps))); dbTester.commit(); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java index 58dec513e16..2d9eceb4190 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java @@ -60,7 +60,6 @@ public class Measure implements Serializable { protected Double variation4; protected Double variation5; protected String url; - protected Integer personId; protected PersistenceMode persistenceMode = PersistenceMode.FULL; public Measure(String metricKey) { @@ -609,17 +608,20 @@ public class Measure implements Serializable { /** * @since 2.14 + * @deprecated in 6.5 with end of support of Developer cockpit plugin. Always return {@code null}. */ @CheckForNull + @Deprecated public Integer getPersonId() { - return personId; + return null; } /** * @since 2.14 + * @deprecated in 6.5 with end of support of Developer cockpit plugin. */ + @Deprecated public Measure setPersonId(@Nullable Integer i) { - this.personId = i; return this; } @@ -663,17 +665,12 @@ public class Measure implements Serializable { } Measure measure = (Measure) o; - if (metricKey != null ? !metricKey.equals(measure.metricKey) : (measure.metricKey != null)) { - return false; - } - return !(personId != null ? !personId.equals(measure.personId) : (measure.personId != null)); + return metricKey != null ? metricKey.equals(measure.metricKey) : (measure.metricKey == null); } @Override public int hashCode() { - int result = metricKey != null ? metricKey.hashCode() : 0; - result = 31 * result + (personId != null ? personId.hashCode() : 0); - return result; + return metricKey != null ? metricKey.hashCode() : 0; } @Override diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java index 7049e46d852..37bc32af4ab 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java @@ -64,8 +64,7 @@ public final class MeasuresFilters { } for (Measure measure : measures) { if (measure.getClass().equals(Measure.class) && - measure.getMetricKey().equals(metricKey) && - measure.getPersonId() == null) { + measure.getMetricKey().equals(metricKey)) { return measure; } } @@ -107,7 +106,7 @@ public final class MeasuresFilters { private boolean apply(Measure measure) { return measure instanceof RuleMeasure && metric.equals(measure.getMetric()) - && measure.getPersonId() == null && ((RuleMeasure) measure).ruleKey() != null; + && ((RuleMeasure) measure).ruleKey() != null; } @Override @@ -157,7 +156,6 @@ public final class MeasuresFilters { private boolean apply(Measure measure) { return measure instanceof RuleMeasure && filterOnMetricKey().equals(measure.getMetricKey()) - && measure.getPersonId() == null && doApply((RuleMeasure) measure); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java index 26abdc18b84..6956b04b490 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java @@ -133,7 +133,6 @@ public class RuleMeasure extends Measure { RuleMeasure other = (RuleMeasure) obj; return new EqualsBuilder() .append(getMetric(), other.getMetric()) - .append(personId, other.personId) .append(ruleKey, other.ruleKey) .isEquals(); } @@ -147,7 +146,6 @@ public class RuleMeasure extends Measure { public int hashCode() { return new HashCodeBuilder(17, 37) .append(getMetric()) - .append(personId) .append(ruleKey) .toHashCode(); } @@ -156,7 +154,6 @@ public class RuleMeasure extends Measure { public String toString() { return new ToStringBuilder(this) .append("metric", metric) - .append("personId", personId) .append("ruleKey", ruleKey) .append("value", value) .append("data", data) diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/measures/MeasureTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/measures/MeasureTest.java index af76fcdc2f5..8e3d97f8658 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/measures/MeasureTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/measures/MeasureTest.java @@ -114,23 +114,6 @@ public class MeasureTest { assertThat(measure2.equals(measure1)).isTrue(); assertThat(measure1.hashCode()).isEqualTo(measure2.hashCode()); - // different committer - measure1.setPersonId(1); - assertThat(measure1.equals(measure2)).isFalse(); - assertThat(measure2.equals(measure1)).isFalse(); - assertThat(measure1.hashCode()).isNotEqualTo(measure2.hashCode()); - - measure2.setPersonId(2); - assertThat(measure1.equals(measure2)).isFalse(); - assertThat(measure2.equals(measure1)).isFalse(); - assertThat(measure1.hashCode()).isNotEqualTo(measure2.hashCode()); - - // same committer - measure2.setPersonId(1); - assertThat(measure1.equals(measure2)).isTrue(); - assertThat(measure2.equals(measure1)).isTrue(); - assertThat(measure1.hashCode()).isEqualTo(measure2.hashCode()); - // value doesn't matter measure1.setValue(1.0); measure2.setValue(2.0); diff --git a/tests/src/test/java/util/ItUtils.java b/tests/src/test/java/util/ItUtils.java index 7a9fbafcd54..9d09abf7dfb 100644 --- a/tests/src/test/java/util/ItUtils.java +++ b/tests/src/test/java/util/ItUtils.java @@ -344,7 +344,7 @@ public class ItUtils { @CheckForNull public static Measure getMeasureWithVariation(Orchestrator orchestrator, String componentKey, String metricKey) { Measures.ComponentWsResponse response = newWsClient(orchestrator).measures().component(new ComponentRequest() - .setComponentKey(componentKey) + .setComponent(componentKey) .setMetricKeys(singletonList(metricKey)) .setAdditionalFields(singletonList("periods"))); List measures = response.getComponent().getMeasuresList(); -- 2.39.5