From: Teryk Bellahsene Date: Wed, 27 May 2015 14:28:01 +0000 (+0200) Subject: SONAR-6573 WS api/metrics/delete delete metrics and associated custom measures X-Git-Tag: 5.2-RC1~1794 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=refs%2Fpull%2F335%2Fhead;p=sonarqube.git SONAR-6573 WS api/metrics/delete delete metrics and associated custom measures --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/custommeasure/persistence/CustomMeasureDao.java b/server/sonar-server/src/main/java/org/sonar/server/custommeasure/persistence/CustomMeasureDao.java new file mode 100644 index 00000000000..e8bcdcbdaea --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/custommeasure/persistence/CustomMeasureDao.java @@ -0,0 +1,58 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.custommeasure.persistence; + +import com.google.common.base.Function; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import org.sonar.api.server.ServerSide; +import org.sonar.core.custommeasure.db.CustomMeasureDto; +import org.sonar.core.custommeasure.db.CustomMeasureMapper; +import org.sonar.core.persistence.DaoComponent; +import org.sonar.core.persistence.DaoUtils; +import org.sonar.core.persistence.DbSession; + +@ServerSide +public class CustomMeasureDao implements DaoComponent { + public void insert(DbSession session, CustomMeasureDto customMeasureDto) { + mapper(session).insert(customMeasureDto); + } + + public void deleteByMetricIds(final DbSession session, final List metricIds) { + DaoUtils.executeLargeInputsWithoutOutput(metricIds, new Function, Void>() { + @Override + public Void apply(@Nonnull List input) { + mapper(session).deleteByMetricIds(metricIds); + return null; + } + }); + } + + @CheckForNull + public CustomMeasureDto selectNullableById(DbSession session, long id) { + return mapper(session).selectById(id); + } + + private CustomMeasureMapper mapper(DbSession session) { + return session.getMapper(CustomMeasureMapper.class); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java b/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java index 356a5264547..29a59dceec7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java @@ -50,6 +50,7 @@ import org.sonar.server.component.db.ComponentIndexDao; import org.sonar.server.component.db.ComponentLinkDao; import org.sonar.server.component.db.SnapshotDao; import org.sonar.server.computation.db.AnalysisReportDao; +import org.sonar.server.custommeasure.persistence.CustomMeasureDao; import org.sonar.server.dashboard.db.DashboardDao; import org.sonar.server.dashboard.db.WidgetDao; import org.sonar.server.dashboard.db.WidgetPropertyDao; @@ -105,6 +106,7 @@ public class DbClient { private final ComponentLinkDao componentLinkDao; private final EventDao eventDao; private final PurgeDao purgeDao; + private final CustomMeasureDao customMeasureDao; public DbClient(Database db, MyBatis myBatis, DaoComponent... daoComponents) { this.db = db; @@ -125,6 +127,7 @@ public class DbClient { resourceDao = getDao(map, ResourceDao.class); measureDao = getDao(map, MeasureDao.class); metricDao = getDao(map, MetricDao.class); + customMeasureDao = getDao(map, CustomMeasureDao.class); activityDao = getDao(map, ActivityDao.class); authorizationDao = getDao(map, AuthorizationDao.class); userDao = getDao(map, UserDao.class); @@ -213,6 +216,10 @@ public class DbClient { return metricDao; } + public CustomMeasureDao customMeasureDao() { + return customMeasureDao; + } + public ActivityDao activityDao() { return activityDao; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/metric/persistence/MetricDao.java b/server/sonar-server/src/main/java/org/sonar/server/metric/persistence/MetricDao.java index e6a9e352be8..8b8a01d3bec 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/metric/persistence/MetricDao.java +++ b/server/sonar-server/src/main/java/org/sonar/server/metric/persistence/MetricDao.java @@ -20,23 +20,24 @@ package org.sonar.server.metric.persistence; +import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.Maps; +import java.util.List; +import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.apache.ibatis.session.RowBounds; import org.sonar.api.server.ServerSide; import org.sonar.core.metric.db.MetricDto; import org.sonar.core.metric.db.MetricMapper; import org.sonar.core.persistence.DaoComponent; +import org.sonar.core.persistence.DaoUtils; import org.sonar.core.persistence.DbSession; import org.sonar.server.es.SearchOptions; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.List; -import java.util.Map; - import static com.google.common.collect.Lists.newArrayList; @ServerSide @@ -76,4 +77,23 @@ public class MetricDao implements DaoComponent { private MetricMapper mapper(DbSession session) { return session.getMapper(MetricMapper.class); } + + public List selectByKeys(final DbSession session, List keys) { + return DaoUtils.executeLargeInputs(keys, new Function, List>() { + @Override + public List apply(@Nonnull List input) { + return mapper(session).selectByKeys(input); + } + }); + } + + public void disable(final DbSession session, List ids) { + DaoUtils.executeLargeInputsWithoutOutput(ids, new Function, Void>() { + @Override + public Void apply(@Nonnull List input) { + mapper(session).disable(input); + return null; + } + }); + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/metric/ws/DeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/metric/ws/DeleteAction.java new file mode 100644 index 00000000000..0d06f10d576 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/metric/ws/DeleteAction.java @@ -0,0 +1,107 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.metric.ws; + +import com.google.common.base.Function; +import com.google.common.collect.Lists; +import java.util.List; +import javax.annotation.Nonnull; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.core.metric.db.MetricDto; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.server.db.DbClient; +import org.sonar.server.user.UserSession; + +import static com.google.common.base.Preconditions.checkArgument; + +public class DeleteAction implements MetricsWsAction { + public static final String PARAM_IDS = "ids"; + public static final String PARAM_KEYS = "keys"; + + private final DbClient dbClient; + private final UserSession userSession; + + public DeleteAction(DbClient dbClient, UserSession userSession) { + this.dbClient = dbClient; + this.userSession = userSession; + } + + @Override + public void define(WebService.NewController context) { + WebService.NewAction action = context.createAction("delete") + .setHandler(this) + .setSince("5.2") + .setPost(true) + .setDescription("Delete metrics and associated measures. Delete only custom metrics.
Ids or keys must be provided.
Requires 'Administer System' permission."); + + action.createParam(PARAM_IDS) + .setDescription("Metrics ids to delete.") + .setExampleValue("5, 23, 42"); + + action.createParam(PARAM_KEYS) + .setDescription("Metrics keys to delete") + .setExampleValue("team_size, business_value"); + } + + @Override + public void handle(Request request, Response response) throws Exception { + userSession.checkLoggedIn().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN); + DbSession dbSession = dbClient.openSession(false); + try { + List ids = loadIds(dbSession, request); + dbClient.metricDao().disable(dbSession, ids); + dbClient.customMeasureDao().deleteByMetricIds(dbSession, ids); + dbSession.commit(); + } finally { + MyBatis.closeQuietly(dbSession); + } + + response.noContent(); + } + + private List loadIds(DbSession dbSession, Request request) { + List idsAsStrings = request.paramAsStrings(PARAM_IDS); + List keys = request.paramAsStrings(PARAM_KEYS); + checkArgument(idsAsStrings != null || keys != null, "Ids or keys must be provided."); + List ids = null; + if (idsAsStrings != null) { + ids = Lists.transform(idsAsStrings, new Function() { + @Override + public Integer apply(String id) { + return Integer.valueOf(id); + } + }); + } else if (keys != null) { + ids = Lists.transform(dbClient.metricDao().selectByKeys(dbSession, keys), new Function() { + @Override + public Integer apply(@Nonnull MetricDto input) { + return input.getId(); + } + }); + } + + return ids; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java index 8216f74dfa1..cb582d899aa 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java @@ -39,6 +39,7 @@ import org.sonar.server.component.db.ComponentIndexDao; import org.sonar.server.component.db.ComponentLinkDao; import org.sonar.server.component.db.SnapshotDao; import org.sonar.server.computation.db.AnalysisReportDao; +import org.sonar.server.custommeasure.persistence.CustomMeasureDao; import org.sonar.server.dashboard.db.DashboardDao; import org.sonar.server.dashboard.db.WidgetDao; import org.sonar.server.dashboard.db.WidgetPropertyDao; @@ -146,6 +147,7 @@ public class PlatformLevel1 extends PlatformLevel { MeasureDao.class, MetricDao.class, MeasureFilterDao.class, + CustomMeasureDao.class, // components ComponentDao.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index fc5d6d50cab..eca68f9b1f4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -501,6 +501,7 @@ public class PlatformLevel4 extends PlatformLevel { org.sonar.server.metric.ws.ListAction.class, org.sonar.server.metric.ws.TypesAction.class, org.sonar.server.metric.ws.DomainsAction.class, + org.sonar.server.metric.ws.DeleteAction.class, // quality gates QualityGateDao.class, diff --git a/server/sonar-server/src/test/java/org/sonar/server/custommeasure/persistence/CustomMeasureDaoTest.java b/server/sonar-server/src/test/java/org/sonar/server/custommeasure/persistence/CustomMeasureDaoTest.java new file mode 100644 index 00000000000..e47fa32e5c8 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/custommeasure/persistence/CustomMeasureDaoTest.java @@ -0,0 +1,87 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.custommeasure.persistence; + +import java.util.Arrays; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.sonar.core.custommeasure.db.CustomMeasureDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.DbTester; +import org.sonar.server.db.DbClient; +import org.sonar.test.DbTests; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.offset; + +@Category(DbTests.class) +public class CustomMeasureDaoTest { + @ClassRule + public static DbTester db = new DbTester(); + + CustomMeasureDao sut; + DbSession session; + + @Before + public void setUp() { + DbClient dbClient = new DbClient(db.database(), db.myBatis(), new CustomMeasureDao()); + session = dbClient.openSession(false); + sut = dbClient.customMeasureDao(); + db.truncateTables(); + } + + @After + public void tearDown() { + session.close(); + } + + @Test + public void insert() { + CustomMeasureDto measure = CustomMeasureTesting.newDto(); + + sut.insert(session, measure); + + CustomMeasureDto result = sut.selectNullableById(session, measure.getId()); + assertThat(result.getId()).isEqualTo(measure.getId()); + assertThat(result.getMetricId()).isEqualTo(measure.getMetricId()); + assertThat(result.getResourceId()).isEqualTo(measure.getResourceId()); + assertThat(result.getDescription()).isEqualTo(measure.getDescription()); + assertThat(result.getUserLogin()).isEqualTo(measure.getUserLogin()); + assertThat(result.getTextValue()).isEqualTo(measure.getTextValue()); + assertThat(result.getValue()).isCloseTo(measure.getValue(), offset(0.001d)); + assertThat(result.getCreatedAt()).isEqualTo(measure.getCreatedAt()); + assertThat(result.getUpdatedAt()).isEqualTo(measure.getUpdatedAt()); + } + + @Test + public void delete() { + CustomMeasureDto measure = CustomMeasureTesting.newDto(); + sut.insert(session, measure); + assertThat(sut.selectNullableById(session, measure.getId())).isNotNull(); + + sut.deleteByMetricIds(session, Arrays.asList(measure.getMetricId())); + + assertThat(sut.selectNullableById(session, measure.getId())).isNull(); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/custommeasure/persistence/CustomMeasureTesting.java b/server/sonar-server/src/test/java/org/sonar/server/custommeasure/persistence/CustomMeasureTesting.java new file mode 100644 index 00000000000..3b21581df74 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/custommeasure/persistence/CustomMeasureTesting.java @@ -0,0 +1,45 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.custommeasure.persistence; + +import org.apache.commons.lang.RandomStringUtils; +import org.apache.commons.lang.math.RandomUtils; +import org.sonar.api.utils.System2; +import org.sonar.core.custommeasure.db.CustomMeasureDto; + +public class CustomMeasureTesting { + private CustomMeasureTesting() { + // static stuff only + } + + public static CustomMeasureDto newDto() { + return new CustomMeasureDto() + .setDescription(RandomStringUtils.random(255)) + .setTextValue(RandomStringUtils.random(255)) + .setUserLogin(RandomStringUtils.random(255)) + .setValue(RandomUtils.nextDouble()) + .setMetricId(RandomUtils.nextInt()) + .setResourceId(RandomUtils.nextInt()) + .setCreatedAt(System2.INSTANCE.now()) + .setUpdatedAt(System2.INSTANCE.now()) + ; + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/metric/ws/DeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/metric/ws/DeleteActionTest.java new file mode 100644 index 00000000000..a663ac83922 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/metric/ws/DeleteActionTest.java @@ -0,0 +1,163 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.metric.ws; + +import java.util.Arrays; +import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.ExpectedException; +import org.sonar.core.custommeasure.db.CustomMeasureDto; +import org.sonar.core.metric.db.MetricDto; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.DbTester; +import org.sonar.server.custommeasure.persistence.CustomMeasureDao; +import org.sonar.server.custommeasure.persistence.CustomMeasureTesting; +import org.sonar.server.db.DbClient; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.metric.persistence.MetricDao; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.ws.WsTester; +import org.sonar.test.DbTests; + +import static org.assertj.core.api.Assertions.assertThat; + +@Category(DbTests.class) +public class DeleteActionTest { + + @ClassRule + public static DbTester db = new DbTester(); + @Rule + public UserSessionRule userSessionRule = UserSessionRule.standalone(); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + DbClient dbClient; + DbSession dbSession; + WsTester ws; + MetricDao metricDao; + + @Before + public void setUp() throws Exception { + dbClient = new DbClient(db.database(), db.myBatis(), new MetricDao(), new CustomMeasureDao()); + dbSession = dbClient.openSession(false); + db.truncateTables(); + userSessionRule.login("login").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN); + ws = new WsTester(new MetricsWs(new DeleteAction(dbClient, userSessionRule))); + metricDao = dbClient.metricDao(); + } + + @After + public void tearDown() throws Exception { + dbSession.close(); + } + + @Test + public void delete_by_keys() throws Exception { + insertCustomEnabledMetrics(1, 2, 3); + dbSession.commit(); + + newRequest().setParam("keys", "key-1, key-3").execute(); + dbSession.commit(); + + List disabledMetrics = metricDao.selectByKeys(dbSession, Arrays.asList("key-1", "key-3")); + assertThat(disabledMetrics).extracting("enabled").containsOnly(false); + assertThat(metricDao.selectByKey(dbSession, "key-2").isEnabled()).isTrue(); + } + + @Test + public void delete_by_id() throws Exception { + MetricDto metric = newCustomEnabledMetric(1); + metricDao.insert(dbSession, metric); + dbSession.commit(); + + WsTester.Result result = newRequest().setParam("ids", String.valueOf(metric.getId())).execute(); + dbSession.commit(); + + assertThat(metricDao.selectEnabled(dbSession)).isEmpty(); + result.assertNoContent(); + } + + @Test + public void do_not_delete_non_custom_metric() throws Exception { + metricDao.insert(dbSession, newCustomEnabledMetric(1).setUserManaged(false)); + dbSession.commit(); + + newRequest().setParam("keys", "key-1").execute(); + dbSession.commit(); + + MetricDto metric = metricDao.selectByKey(dbSession, "key-1"); + assertThat(metric.isEnabled()).isTrue(); + } + + @Test + public void delete_associated_measures() throws Exception { + MetricDto metric = newCustomEnabledMetric(1); + metricDao.insert(dbSession, metric); + CustomMeasureDto customMeasure = CustomMeasureTesting.newDto().setMetricId(metric.getId()); + CustomMeasureDto undeletedCustomMeasure = CustomMeasureTesting.newDto().setMetricId(metric.getId() + 1); + dbClient.customMeasureDao().insert(dbSession, customMeasure); + dbClient.customMeasureDao().insert(dbSession, undeletedCustomMeasure); + dbSession.commit(); + + newRequest().setParam("keys", "key-1").execute(); + + assertThat(dbClient.customMeasureDao().selectNullableById(dbSession, customMeasure.getId())).isNull(); + assertThat(dbClient.customMeasureDao().selectNullableById(dbSession, undeletedCustomMeasure.getId())).isNotNull(); + } + + @Test + public void fail_when_no_argument() throws Exception { + expectedException.expect(IllegalArgumentException.class); + + newRequest().execute(); + } + + @Test + public void fail_when_insufficient_privileges() throws Exception { + expectedException.expect(ForbiddenException.class); + + userSessionRule.setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); + insertCustomEnabledMetrics(1); + + newRequest().setParam("keys", "key-1").execute(); + } + + private MetricDto newCustomEnabledMetric(int id) { + return MetricTesting.newDto().setEnabled(true).setUserManaged(true).setKey("key-" + id); + } + + private void insertCustomEnabledMetrics(int... ids) { + for (int id : ids) { + metricDao.insert(dbSession, newCustomEnabledMetric(id)); + } + + dbSession.commit(); + } + + private WsTester.TestRequest newRequest() { + return ws.newPostRequest(MetricsWs.ENDPOINT, "delete"); + } +} diff --git a/sonar-core/src/main/java/org/sonar/core/custommeasure/db/CustomMeasureDto.java b/sonar-core/src/main/java/org/sonar/core/custommeasure/db/CustomMeasureDto.java new file mode 100644 index 00000000000..96c395d7487 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/custommeasure/db/CustomMeasureDto.java @@ -0,0 +1,114 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.core.custommeasure.db; + +public class CustomMeasureDto { + private long id; + private int metricId; + private int resourceId; + private double value; + private String textValue; + private String userLogin; + private String description; + private long createdAt; + private long updatedAt; + + public String getDescription() { + return description; + } + + public CustomMeasureDto setDescription(String description) { + this.description = description; + return this; + } + + public String getUserLogin() { + return userLogin; + } + + public CustomMeasureDto setUserLogin(String userLogin) { + this.userLogin = userLogin; + return this; + } + + public String getTextValue() { + return textValue; + } + + public CustomMeasureDto setTextValue(String textValue) { + this.textValue = textValue; + return this; + } + + public double getValue() { + return value; + } + + public CustomMeasureDto setValue(double value) { + this.value = value; + return this; + } + + public int getResourceId() { + return resourceId; + } + + public CustomMeasureDto setResourceId(int resourceId) { + this.resourceId = resourceId; + return this; + } + + public int getMetricId() { + return metricId; + } + + public CustomMeasureDto setMetricId(int metricId) { + this.metricId = metricId; + return this; + } + + public long getId() { + return id; + } + + public CustomMeasureDto setId(long id) { + this.id = id; + return this; + } + + public long getUpdatedAt() { + return updatedAt; + } + + public CustomMeasureDto setUpdatedAt(long updatedAt) { + this.updatedAt = updatedAt; + return this; + } + + public long getCreatedAt() { + return createdAt; + } + + public CustomMeasureDto setCreatedAt(long createdAt) { + this.createdAt = createdAt; + return this; + } +} diff --git a/sonar-core/src/main/java/org/sonar/core/custommeasure/db/CustomMeasureMapper.java b/sonar-core/src/main/java/org/sonar/core/custommeasure/db/CustomMeasureMapper.java new file mode 100644 index 00000000000..95bb4fd3b23 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/custommeasure/db/CustomMeasureMapper.java @@ -0,0 +1,32 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.core.custommeasure.db; + +import java.util.List; +import org.apache.ibatis.annotations.Param; + +public interface CustomMeasureMapper { + void insert(CustomMeasureDto customMeasureDto); + + void deleteByMetricIds(@Param("metricIds") List metricIds); + + CustomMeasureDto selectById(long id); +} diff --git a/sonar-core/src/main/java/org/sonar/core/custommeasure/db/package-info.java b/sonar-core/src/main/java/org/sonar/core/custommeasure/db/package-info.java new file mode 100644 index 00000000000..8a19bbe9399 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/custommeasure/db/package-info.java @@ -0,0 +1,25 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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. + */ + +@ParametersAreNonnullByDefault +package org.sonar.core.custommeasure.db; + +import javax.annotation.ParametersAreNonnullByDefault; + diff --git a/sonar-core/src/main/java/org/sonar/core/metric/db/MetricMapper.java b/sonar-core/src/main/java/org/sonar/core/metric/db/MetricMapper.java index 3b1a44375e2..46eaf787e21 100644 --- a/sonar-core/src/main/java/org/sonar/core/metric/db/MetricMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/metric/db/MetricMapper.java @@ -20,11 +20,10 @@ package org.sonar.core.metric.db; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.session.RowBounds; - import java.util.List; import java.util.Map; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.session.RowBounds; public interface MetricMapper { @@ -37,4 +36,8 @@ public interface MetricMapper { void insert(MetricDto dto); List selectDomains(); + + List selectByKeys(@Param("keys") List keys); + + void disable(@Param("ids") List ids); } diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java index 935bc122b29..03372a70717 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java @@ -116,6 +116,24 @@ public final class DaoUtils { return results; } + /** + * Partition by 1000 elements a list of input and execute a function on each part. + * The function has not output (ex: delete operation) + * + * The goal is to prevent issue with ORACLE when there's more than 1000 elements in a 'in ('X', 'Y', ...)' + * and with MsSQL when there's more than 2000 parameters in a query + */ + public static void executeLargeInputsWithoutOutput(Collection input, Function, Void> function) { + if (input.isEmpty()) { + return; + } + + List> partitions = Lists.partition(newArrayList(input), PARTITION_SIZE_FOR_ORACLE); + for (List partition : partitions) { + function.apply(partition); + } + } + public static String repeatCondition(String sql, int count, String separator) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < count; i++) { diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java b/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java index abf18b7ef4c..4e79ef0e850 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java @@ -53,6 +53,8 @@ import org.sonar.core.component.db.SnapshotMapper; import org.sonar.core.computation.db.AnalysisReportDto; import org.sonar.core.computation.db.AnalysisReportMapper; import org.sonar.core.config.Logback; +import org.sonar.core.custommeasure.db.CustomMeasureDto; +import org.sonar.core.custommeasure.db.CustomMeasureMapper; import org.sonar.core.dashboard.ActiveDashboardDto; import org.sonar.core.dashboard.ActiveDashboardMapper; import org.sonar.core.dashboard.DashboardDto; @@ -238,6 +240,7 @@ public class MyBatis { loadAlias(conf, "FilePathWithHash", FilePathWithHashDto.class); loadAlias(conf, "UuidWithProjectUuid", UuidWithProjectUuidDto.class); loadAlias(conf, "Event", EventDto.class); + loadAlias(conf, "CustomMeasure", CustomMeasureDto.class); // AuthorizationMapper has to be loaded before IssueMapper because this last one used it loadMapper(conf, "org.sonar.core.user.AuthorizationMapper"); @@ -256,7 +259,7 @@ public class MyBatis { ActionPlanStatsMapper.class, NotificationQueueMapper.class, CharacteristicMapper.class, GroupMembershipMapper.class, QualityProfileMapper.class, ActiveRuleMapper.class, - MeasureMapper.class, MetricMapper.class, QualityGateMapper.class, QualityGateConditionMapper.class, ComponentMapper.class, SnapshotMapper.class, + MeasureMapper.class, MetricMapper.class, CustomMeasureMapper.class, QualityGateMapper.class, QualityGateConditionMapper.class, ComponentMapper.class, SnapshotMapper.class, ProjectQgateAssociationMapper.class, EventMapper.class, AnalysisReportMapper.class, ComponentIndexMapper.class, ComponentLinkMapper.class, Migration45Mapper.class, Migration50Mapper.class diff --git a/sonar-core/src/main/resources/org/sonar/core/custommeasure/db/CustomMeasureMapper.xml b/sonar-core/src/main/resources/org/sonar/core/custommeasure/db/CustomMeasureMapper.xml new file mode 100644 index 00000000000..4aefce1188d --- /dev/null +++ b/sonar-core/src/main/resources/org/sonar/core/custommeasure/db/CustomMeasureMapper.xml @@ -0,0 +1,42 @@ + + + + + + m.id, + m.metric_id as metricId, + m.resource_id as resourceId, + m.value, + m.text_value as textValue, + m.user_login as userLogin, + m.description, + m.created_at as createdAt, + m.updated_at as updatedAt + + + + + + INSERT INTO manual_measures ( + metric_id, resource_id, value, text_value, user_login, description, created_at, updated_at + ) + VALUES ( + #{metricId, jdbcType=INTEGER}, #{resourceId, jdbcType=INTEGER}, #{value, jdbcType=DOUBLE}, + #{textValue, jdbcType=VARCHAR}, #{userLogin, jdbcType=VARCHAR},#{description, jdbcType=VARCHAR}, + #{createdAt, jdbcType=BIGINT}, #{updatedAt, jdbcType=BIGINT} + ) + + + + delete from manual_measures + where metric_id in + + #{metricId} + + + diff --git a/sonar-core/src/main/resources/org/sonar/core/metric/db/MetricMapper.xml b/sonar-core/src/main/resources/org/sonar/core/metric/db/MetricMapper.xml index 1ec976a4bea..4c38d99c12a 100644 --- a/sonar-core/src/main/resources/org/sonar/core/metric/db/MetricMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/metric/db/MetricMapper.xml @@ -49,9 +49,11 @@ ORDER BY UPPER(m.short_name) - + INSERT INTO metrics ( - name, description, direction, domain, short_name, qualitative, val_type, user_managed, enabled, origin, worst_value, best_value, optimized_best_value, hidden, delete_historical_data) + name, description, direction, domain, short_name, qualitative, val_type, user_managed, enabled, origin, worst_value, + best_value, optimized_best_value, hidden, delete_historical_data) VALUES ( #{name, jdbcType=VARCHAR}, #{description, jdbcType=VARCHAR}, #{direction, jdbcType=INTEGER}, #{domain, jdbcType=VARCHAR}, #{shortName, jdbcType=VARCHAR}, #{qualitative, jdbcType=BOOLEAN}, @@ -67,4 +69,28 @@ where m.domain is not null + + update metrics + set enabled=${_false} + + AND user_managed=${_true} + AND id in + + #{id} + + + + + +