]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9328 delete settings & manual measures by project/module
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Tue, 30 May 2017 15:40:00 +0000 (17:40 +0200)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Thu, 1 Jun 2017 13:19:45 +0000 (15:19 +0200)
and by views/subviews

server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java
server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeMapperTest.java [new file with mode: 0644]

index d3ed34b2cdc954c3726dd193e9c4074ceb186788..1c673d97846129fde19b9d87a0437f7d4b0a0541 100644 (file)
@@ -163,26 +163,24 @@ class PurgeCommands {
     profiler.stop();
   }
 
-  void deleteComponents(List<IdUuidPair> componentIdUuids) {
-    List<List<Long>> componentIdPartitions = Lists.partition(IdUuidPairs.ids(componentIdUuids), MAX_RESOURCES_PER_QUERY);
-    List<List<String>> componentUuidsPartitions = Lists.partition(IdUuidPairs.uuids(componentIdUuids), MAX_RESOURCES_PER_QUERY);
-    // Note : do not merge the delete statements into a single loop of resource ids. It's
-    // voluntarily grouped by tables in order to benefit from JDBC batch mode.
-    // Batch requests can only relate to the same PreparedStatement.
-
-    // possible missing optimization: filter requests according to resource scope
+  void deleteByRootAndModulesOrSubviews(List<IdUuidPair> rootAndModulesOrSubviewsIds) {
+    List<List<Long>> idPartitions = Lists.partition(IdUuidPairs.ids(rootAndModulesOrSubviewsIds), MAX_RESOURCES_PER_QUERY);
+    List<List<String>> uuidsPartitions = Lists.partition(IdUuidPairs.uuids(rootAndModulesOrSubviewsIds), MAX_RESOURCES_PER_QUERY);
 
-    profiler.start("deleteResourceProperties (properties)");
-    componentIdPartitions.forEach(purgeMapper::deleteComponentProperties);
+    profiler.start("deleteByRootAndModulesOrSubviews (properties)");
+    idPartitions.forEach(purgeMapper::deleteComponentProperties);
     session.commit();
     profiler.stop();
 
-    profiler.start("deleteResourceManualMeasures (manual_measures)");
-    componentUuidsPartitions.forEach(purgeMapper::deleteComponentManualMeasures);
+    profiler.start("deleteByRootAndModulesOrSubviews (manual_measures)");
+    uuidsPartitions.forEach(purgeMapper::deleteComponentManualMeasures);
     session.commit();
     profiler.stop();
+  }
 
-    profiler.start("deleteResource (projects)");
+  void deleteComponents(List<IdUuidPair> componentIdUuids) {
+    List<List<String>> componentUuidsPartitions = Lists.partition(IdUuidPairs.uuids(componentIdUuids), MAX_RESOURCES_PER_QUERY);
+    profiler.start("deleteComponents (projects)");
     componentUuidsPartitions.forEach(purgeMapper::deleteComponents);
     session.commit();
     profiler.stop();
index c7ee2fc5e9e59c82b108503cdf425f5e489bcd29..fdeafba33c2da8baafdcf32e3e6c2823a9ce17da 100644 (file)
@@ -153,16 +153,18 @@ public class PurgeDao implements Dao {
   }
 
   private static void deleteProject(String rootUuid, PurgeMapper mapper, PurgeCommands commands) {
-    List<IdUuidPair> childrenIds = mapper.selectComponentsByProjectUuid(rootUuid);
-    long rootId = childrenIds.stream()
+    List<IdUuidPair> componentsIds = mapper.selectComponentsByProjectUuid(rootUuid);
+    List<IdUuidPair> rootAndModulesOrSubviews = mapper.selectRootAndModulesOrSubviewsByProjectUuid(rootUuid);
+    long rootId = rootAndModulesOrSubviews.stream()
       .filter(pair -> pair.getUuid().equals(rootUuid))
       .map(IdUuidPair::getId)
       .findFirst()
-      .orElseThrow(() -> new IllegalStateException("Couldn't find component for root uuid " + rootUuid));
+      .orElseThrow(() -> new IllegalArgumentException("Couldn't find root component with uuid " + rootUuid));
     commands.deletePermissions(rootId);
     commands.deleteLinks(rootUuid);
     commands.deleteAnalyses(rootUuid);
-    commands.deleteComponents(childrenIds);
+    commands.deleteByRootAndModulesOrSubviews(rootAndModulesOrSubviews);
+    commands.deleteComponents(componentsIds);
     commands.deleteIssues(rootUuid);
     commands.deleteFileSources(rootUuid);
     commands.deleteCeActivity(rootUuid);
index 71f4d1afc6760c44bad03a3185946fc3dfeb65ad..e8e65b82cea318897183aa5730f65adc58846318 100644 (file)
@@ -32,6 +32,11 @@ public interface PurgeMapper {
    */
   List<IdUuidPair> selectComponentsByProjectUuid(String projectUuid);
 
+  /**
+   * Returns the list of modules/subviews and the view/project for the specified project_uuid.
+   */
+  List<IdUuidPair> selectRootAndModulesOrSubviewsByProjectUuid(@Param("rootUuid") String rootUuid);
+
   void deleteAnalyses(@Param("analysisUuids") List<String> analysisUuids);
 
   void deleteAnalysisDuplications(@Param("analysisUuids") List<String> analysisUuids);
index a2d461853e097167458df9138138dd44078d520d..c1926a98446235522e98ee88667c252a951d2624 100644 (file)
     select id, uuid from projects where project_uuid=#{uuid,jdbcType=VARCHAR} or uuid=#{uuid,jdbcType=VARCHAR}
   </select>
 
+  <select id="selectRootAndModulesOrSubviewsByProjectUuid" resultType="org.sonar.db.purge.IdUuidPair" parameterType="String">
+    select
+      p.id, p.uuid
+    from
+      projects p
+    where
+      (
+        p.project_uuid=#{rootUuid,jdbcType=VARCHAR}
+        and p.scope = 'PRJ' and p.qualifier in ('SVW','BRC')
+      )
+      or (
+        uuid=#{rootUuid,jdbcType=VARCHAR}
+        and p.scope = 'PRJ' and p.qualifier in ('VW','TRK')
+      )
+  </select>
+
   <delete id="deleteAnalysisMeasures" parameterType="map">
     delete from project_measures
     where
index 02ff99161e282d2900888b2b99b50946b53cc757..4f6052a2ed6fd5f55dcd262de14a8e6ae42c4839 100644 (file)
@@ -27,6 +27,7 @@ import java.util.List;
 import org.apache.commons.lang.math.RandomUtils;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 import org.mockito.ArgumentCaptor;
 import org.sonar.api.resources.Scopes;
 import org.sonar.api.utils.System2;
@@ -57,6 +58,8 @@ public class PurgeDaoTest {
 
   @Rule
   public DbTester dbTester = DbTester.create(system2);
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
 
   private DbClient dbClient = dbTester.getDbClient();
   private DbSession dbSession = dbTester.getSession();
@@ -186,18 +189,52 @@ public class PurgeDaoTest {
   }
 
   @Test
-  public void delete_view_sub_view_and_tech_project() {
-    dbTester.prepareDbUnit(getClass(), "view_sub_view_and_tech_project.xml");
+  public void deleteProject_fails_with_IAE_if_specified_component_is_module() {
+    ComponentDto privateProject = dbTester.components().insertPrivateProject();
+    ComponentDto module = dbTester.components().insertComponent(ComponentTesting.newModuleDto(privateProject));
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Couldn't find root component with uuid " + module.uuid());
+    
+    underTest.deleteProject(dbSession, module.uuid());
+  }
 
-    // technical project
-    underTest.deleteProject(dbSession, "D");
-    dbSession.commit();
-    assertThat(dbTester.countSql("select count(1) from projects where uuid='D'")).isZero();
+  @Test
+  public void deleteProject_fails_with_IAE_if_specified_component_is_directory() {
+    ComponentDto privateProject = dbTester.components().insertPrivateProject();
+    ComponentDto directory = dbTester.components().insertComponent(ComponentTesting.newDirectory(privateProject, "A/B"));
 
-    // sub view
-    underTest.deleteProject(dbSession, "B");
-    dbSession.commit();
-    assertThat(dbTester.countSql("select count(1) from projects where uuid='B'")).isZero();
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Couldn't find root component with uuid " + directory.uuid());
+
+    underTest.deleteProject(dbSession, directory.uuid());
+  }
+
+  @Test
+  public void deleteProject_fails_with_IAE_if_specified_component_is_file() {
+    ComponentDto privateProject = dbTester.components().insertPrivateProject();
+    ComponentDto file = dbTester.components().insertComponent(ComponentTesting.newFileDto(privateProject));
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Couldn't find root component with uuid " + file.uuid());
+
+    underTest.deleteProject(dbSession, file.uuid());
+  }
+
+  @Test
+  public void deleteProject_fails_with_IAE_if_specified_component_is_subview() {
+    ComponentDto view = dbTester.components().insertView();
+    ComponentDto subview = dbTester.components().insertComponent(ComponentTesting.newSubView(view));
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Couldn't find root component with uuid " + subview.uuid());
+
+    underTest.deleteProject(dbSession, subview.uuid());
+  }
+
+  @Test
+  public void delete_view_sub_view_and_tech_project() {
+    dbTester.prepareDbUnit(getClass(), "view_sub_view_and_tech_project.xml");
 
     // view
     underTest.deleteProject(dbSession, "A");
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeMapperTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeMapperTest.java
new file mode 100644 (file)
index 0000000..f9da4da
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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.purge;
+
+import java.util.Random;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PurgeMapperTest {
+
+  @Rule
+  public DbTester db = DbTester.create(System2.INSTANCE);
+
+  private DbSession dbSession;
+  private PurgeMapper purgeMapper;
+
+  @Before
+  public void setUp() throws Exception {
+    dbSession = db.getDbClient().openSession(false);
+    purgeMapper = dbSession.getMapper(PurgeMapper.class);
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    if (dbSession != null) {
+      dbSession.close();
+    }
+  }
+
+  @Test
+  public void selectRootAndModulesOrSubviewsByProjectUuid_returns_empty_when_table_is_empty() {
+    assertThat(purgeMapper.selectRootAndModulesOrSubviewsByProjectUuid("foo")).isEmpty();
+  }
+
+  @Test
+  public void selectRootAndModulesOrSubviewsByProjectUuid_returns_project_with_specified_uuid() {
+    ComponentDto project = randomPublicOrPrivateProject();
+
+    assertThat(purgeMapper.selectRootAndModulesOrSubviewsByProjectUuid(project.uuid()))
+      .extracting(IdUuidPair::getUuid)
+      .containsOnly(project.uuid());
+  }
+
+  @Test
+  public void selectRootAndModulesOrSubviewsByProjectUuid_returns_modules_with_specified_project_uuid_and_project() {
+    ComponentDto project = randomPublicOrPrivateProject();
+    ComponentDto module1 = db.components().insertComponent(ComponentTesting.newModuleDto(project));
+    ComponentDto module2 = db.components().insertComponent(ComponentTesting.newModuleDto(project));
+    ComponentDto module3 = db.components().insertComponent(ComponentTesting.newModuleDto(project));
+
+    assertThat(purgeMapper.selectRootAndModulesOrSubviewsByProjectUuid(project.uuid()))
+      .extracting(IdUuidPair::getUuid)
+      .containsOnly(project.uuid(), module1.uuid(), module2.uuid(), module3.uuid());
+  }
+
+  private ComponentDto randomPublicOrPrivateProject() {
+    return new Random().nextBoolean() ? db.components().insertPrivateProject() : db.components().insertPublicProject();
+  }
+
+  @Test
+  public void selectRootAndModulesOrSubviewsByProjectUuid_returns_view_with_specified_uuid() {
+    ComponentDto view = db.components().insertView();
+
+    assertThat(purgeMapper.selectRootAndModulesOrSubviewsByProjectUuid(view.uuid()))
+      .extracting(IdUuidPair::getUuid)
+      .containsOnly(view.uuid());
+  }
+
+  @Test
+  public void selectRootAndModulesOrSubviewsByProjectUuid_returns_subviews_with_specified_project_uuid_and_view() {
+    ComponentDto view = db.components().insertView();
+    ComponentDto subview1 = db.components().insertComponent(ComponentTesting.newSubView(view));
+    ComponentDto subview2 = db.components().insertComponent(ComponentTesting.newSubView(view));
+    ComponentDto subview3 = db.components().insertComponent(ComponentTesting.newSubView(view));
+
+    assertThat(purgeMapper.selectRootAndModulesOrSubviewsByProjectUuid(view.uuid()))
+      .extracting(IdUuidPair::getUuid)
+      .containsOnly(view.uuid(), subview1.uuid(), subview2.uuid(), subview3.uuid());
+  }
+
+  @Test
+  public void selectRootAndModulesOrSubviewsByProjectUuid_does_not_return_project_copy_with_specified_project_uuid() {
+    ComponentDto privateProject = db.components().insertPrivateProject();
+    ComponentDto view = db.components().insertView();
+    db.components().insertComponent(ComponentTesting.newProjectCopy("a", view, privateProject));
+
+    assertThat(purgeMapper.selectRootAndModulesOrSubviewsByProjectUuid(view.uuid()))
+      .extracting(IdUuidPair::getUuid)
+      .containsOnly(view.uuid());
+  }
+
+  @Test
+  public void selectRootAndModulesOrSubviewsByProjectUuid_does_not_return_module_with_specified_uuid() {
+    ComponentDto privateProject = db.components().insertPrivateProject();
+    ComponentDto module = db.components().insertComponent(ComponentTesting.newModuleDto(privateProject));
+
+    assertThat(purgeMapper.selectRootAndModulesOrSubviewsByProjectUuid(module.uuid()))
+      .isEmpty();
+  }
+
+  @Test
+  public void selectRootAndModulesOrSubviewsByProjectUuid_does_not_return_directory_with_specified_uuid() {
+    ComponentDto privateProject = db.components().insertPrivateProject();
+    ComponentDto directory = db.components().insertComponent(ComponentTesting.newDirectory(privateProject, "A/B"));
+
+    assertThat(purgeMapper.selectRootAndModulesOrSubviewsByProjectUuid(directory.uuid()))
+      .isEmpty();
+  }
+
+  @Test
+  public void selectRootAndModulesOrSubviewsByProjectUuid_does_not_return_file_with_specified_uuid() {
+    ComponentDto privateProject = db.components().insertPrivateProject();
+    ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(privateProject));
+
+    assertThat(purgeMapper.selectRootAndModulesOrSubviewsByProjectUuid(file.uuid()))
+      .isEmpty();
+  }
+
+  @Test
+  public void selectRootAndModulesOrSubviewsByProjectUuid_does_not_return_subview_with_specified_uuid() {
+    ComponentDto view = db.components().insertView();
+    ComponentDto subview = db.components().insertComponent(ComponentTesting.newSubView(view));
+
+    assertThat(purgeMapper.selectRootAndModulesOrSubviewsByProjectUuid(subview.uuid()))
+      .isEmpty();
+  }
+
+  @Test
+  public void selectRootAndModulesOrSubviewsByProjectUuid_does_not_return_technicalCopy_with_specified_uuid() {
+    ComponentDto privateProject = db.components().insertPrivateProject();
+    ComponentDto view = db.components().insertView();
+    ComponentDto technicalCopy = db.components().insertComponent(ComponentTesting.newProjectCopy("a", view, privateProject));
+
+    assertThat(purgeMapper.selectRootAndModulesOrSubviewsByProjectUuid(technicalCopy.uuid()))
+      .isEmpty();
+  }
+}