]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6089 Clear views cache when indexing views
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 4 Feb 2015 13:20:36 +0000 (14:20 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 5 Feb 2015 15:46:29 +0000 (16:46 +0100)
server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentDao.java
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
server/sonar-server/src/test/java/org/sonar/server/component/ComponentTesting.java
server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentDaoTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexViewsStepMediumTest.java [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/insert-result.xml
sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java
sonar-core/src/main/java/org/sonar/core/component/db/ComponentMapper.java
sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml
sonar-core/src/test/java/org/sonar/core/component/ComponentDtoTest.java

index 1630b9536fbdcd5cf8cb26214c8c962c8553e33b..85361463f9d541e36b35562498d713089198abf1 100644 (file)
@@ -152,6 +152,6 @@ public class ComponentDao extends BaseDao<ComponentMapper, ComponentDto, String>
   }
 
   public List<String> selectProjectsFromView(DbSession session, String viewUuid, String projectViewUuid) {
-    return mapper(session).selectProjectsFromView("%." + viewUuid + ".%", projectViewUuid, Qualifiers.SUBVIEW);
+    return mapper(session).selectProjectsFromView("%." + viewUuid + ".%", projectViewUuid);
   }
 }
index 406def9df59e4c2cbeaebca01b21cf9e6cbe9bf7..52b4f52fa9218e8efb95d6ce6505cfda75153c6b 100644 (file)
@@ -374,11 +374,16 @@ public class IssueIndex extends BaseIndex<Issue, FakeIssueDto, String> {
         .lookupIndex(ViewIndexDefinition.INDEX)
         .lookupType(ViewIndexDefinition.TYPE_VIEW)
         .lookupId(viewUuid)
-        .lookupPath(ViewIndexDefinition.FIELD_PROJECTS));
+        .lookupPath(ViewIndexDefinition.FIELD_PROJECTS))
+        .cacheKey(cacheKey(viewUuid));
     }
     return viewsFilter;
   }
 
+  public static String cacheKey(String viewUuid) {
+    return IssueIndexDefinition.TYPE_ISSUE + viewUuid + ViewIndexDefinition.TYPE_VIEW;
+  }
+
   private FilterBuilder getAuthorizationFilter(QueryContext options) {
     String user = options.getUserLogin();
     Set<String> groups = options.getUserGroups();
index dad3828497a706e0cf7a514158894a9ca745b77f..b62e768ceb78bc4d8f81af9bddfc2528e99246ae 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.sonar.server.component;
 
+import com.google.common.base.Preconditions;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.resources.Scopes;
 import org.sonar.api.utils.internal.Uuids;
@@ -34,7 +35,7 @@ public class ComponentTesting {
   }
 
   public static ComponentDto newFileDto(ComponentDto module, String fileUuid) {
-    return newComponent(fileUuid, module)
+    return newChildComponent(fileUuid, module)
       .setKey("KEY_" + fileUuid)
       .setName("NAME_" + fileUuid)
       .setLongName("LONG_NAME_" + fileUuid)
@@ -46,7 +47,7 @@ public class ComponentTesting {
 
   public static ComponentDto newDirectory(ComponentDto module, String path) {
     String uuid = Uuids.create();
-    return newComponent(uuid, module)
+    return newChildComponent(uuid, module)
       .setKey(!path.equals("/") ? module.getKey() + ":" + path : module.getKey() + ":/")
       .setName(path)
       .setLongName(path)
@@ -56,7 +57,7 @@ public class ComponentTesting {
   }
 
   public static ComponentDto newModuleDto(String uuid, ComponentDto subProjectOrProject) {
-    return newComponent(uuid, subProjectOrProject)
+    return newChildComponent(uuid, subProjectOrProject)
       .setKey("KEY_" + uuid)
       .setName("NAME_" + uuid)
       .setLongName("LONG_NAME_" + uuid)
@@ -108,7 +109,32 @@ public class ComponentTesting {
       .setEnabled(true);
   }
 
-  private static ComponentDto newComponent(String uuid, ComponentDto module) {
+  public static ComponentDto newView(String uuid) {
+    return newProjectDto(uuid)
+      .setUuid(uuid)
+      .setScope(Scopes.PROJECT)
+      .setQualifier(Qualifiers.VIEW);
+  }
+
+  public static ComponentDto newTechnicalProject(ComponentDto project, ComponentDto view) {
+    return newTechnicalProject(Uuids.create(), project, view);
+  }
+
+  public static ComponentDto newTechnicalProject(String uuid, ComponentDto project, ComponentDto view) {
+    Preconditions.checkNotNull(project.getId(), "The project need to be persisted before creating this technical project.");
+    return newChildComponent(uuid, view)
+      .setUuid(uuid)
+      .setKey(view.key() + project.key())
+      .setName(project.name())
+      .setLongName(project.longName())
+      .setCopyResourceId(project.getId())
+      .setScope(Scopes.FILE)
+      .setQualifier(Qualifiers.PROJECT)
+      .setPath(null)
+      .setLanguage(null);
+  }
+
+  private static ComponentDto newChildComponent(String uuid, ComponentDto module) {
     return new ComponentDto()
       .setUuid(uuid)
       .setProjectUuid(module.projectUuid())
index 041206c38da19ad414cbd9efa336078de1ad3ed7..53b7462d1dfe2f63551100b223a807e5d057f372 100644 (file)
@@ -374,6 +374,7 @@ public class ComponentDaoTest extends AbstractDaoTestCase {
       .setLanguage("java")
       .setPath("src/org/struts/RequestContext.java")
       .setParentProjectId(3L)
+      .setCopyResourceId(5L)
       .setEnabled(true)
       .setAuthorizationUpdatedAt(123456789L);
 
@@ -416,9 +417,9 @@ public class ComponentDaoTest extends AbstractDaoTestCase {
   @Test(expected = IllegalStateException.class)
   public void update() {
     dao.update(session, new ComponentDto()
-        .setId(1L)
-        .setKey("org.struts:struts-core:src/org/struts/RequestContext.java")
-    );
+      .setId(1L)
+      .setKey("org.struts:struts-core:src/org/struts/RequestContext.java")
+      );
   }
 
   @Test
@@ -426,9 +427,9 @@ public class ComponentDaoTest extends AbstractDaoTestCase {
     setupData("shared");
 
     dao.delete(session, new ComponentDto()
-        .setId(1L)
-        .setKey("org.struts:struts-core:src/org/struts/RequestContext.java")
-    );
+      .setId(1L)
+      .setKey("org.struts:struts-core:src/org/struts/RequestContext.java")
+      );
     session.commit();
 
     checkTable("delete", "projects");
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexViewsStepMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexViewsStepMediumTest.java
new file mode 100644 (file)
index 0000000..ddab04e
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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.computation.step;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.security.DefaultGroups;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.issue.db.IssueDto;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.rule.RuleDto;
+import org.sonar.server.component.ComponentTesting;
+import org.sonar.server.component.db.ComponentDao;
+import org.sonar.server.computation.ComputationContext;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.issue.IssueQuery;
+import org.sonar.server.issue.IssueTesting;
+import org.sonar.server.issue.db.IssueDao;
+import org.sonar.server.issue.index.IssueIndex;
+import org.sonar.server.issue.index.IssueIndexer;
+import org.sonar.server.permission.InternalPermissionService;
+import org.sonar.server.permission.PermissionChange;
+import org.sonar.server.rule.RuleTesting;
+import org.sonar.server.rule.db.RuleDao;
+import org.sonar.server.search.QueryContext;
+import org.sonar.server.tester.ServerTester;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.view.index.ViewIndexer;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * It's still not possible to only used EsTester as IssueIndex does not support it yet.
+ */
+public class IndexViewsStepMediumTest {
+
+  ComputationContext context;
+
+  DbSession dbSession;
+
+  ViewIndexer indexer;
+
+  IndexViewsStep step;
+
+  @ClassRule
+  public static ServerTester tester = new ServerTester().addComponents(IndexViewsStep.class);
+
+  IssueIndex index;
+
+  @Before
+  public void setUp() throws Exception {
+    tester.clearIndexes();
+    context = mock(ComputationContext.class);
+    dbSession = tester.get(DbClient.class).openSession(false);
+    index = tester.get(IssueIndex.class);
+    step = tester.get(IndexViewsStep.class);
+  }
+
+  @After
+  public void after() {
+    dbSession.close();
+  }
+
+  @Test
+  public void clear_cache_of_issue_on_view_filter() throws Exception {
+    String viewUuid = "ABCD";
+    when(context.getProject()).thenReturn(ComponentTesting.newProjectDto(viewUuid).setQualifier(Qualifiers.VIEW));
+
+    RuleDto rule = RuleTesting.newXooX1();
+    tester.get(RuleDao.class).insert(dbSession, rule);
+    ComponentDto project1 = addProjectWithIssue(rule);
+
+    ComponentDto view = ComponentTesting.newView("ABCD");
+    ComponentDto techProject1 = ComponentTesting.newTechnicalProject("CDEF", project1, view);
+    tester.get(ComponentDao.class).insert(dbSession, view, techProject1);
+    dbSession.commit();
+    tester.get(ViewIndexer.class).index(viewUuid);
+
+    // Execute issue query on view -> 1 issue on view (and filter on view will be set in cache)
+    assertThat(tester.get(IssueIndex.class).search(IssueQuery.builder().viewUuids(newArrayList(viewUuid)).build(), new QueryContext()).getHits()).hasSize(1);
+
+    // Add a project to the view
+    ComponentDto project2 = addProjectWithIssue(rule);
+    ComponentDto techProject2 = ComponentTesting.newTechnicalProject("EFGH", project2, view);
+    tester.get(ComponentDao.class).insert(dbSession, techProject2);
+    dbSession.commit();
+
+    // Execute issue query on view -> Still 1 issue on view, issue on project2 is not yet visible
+    assertThat(tester.get(IssueIndex.class).search(IssueQuery.builder().viewUuids(newArrayList(viewUuid)).build(), new QueryContext()).getHits()).hasSize(1);
+
+    step.execute(context);
+
+    // Execute issue query on view -> issue of project2 are well taken into account
+    assertThat(tester.get(IssueIndex.class).search(IssueQuery.builder().viewUuids(newArrayList(viewUuid)).build(), new QueryContext()).getHits()).hasSize(2);
+  }
+
+  private ComponentDto addProjectWithIssue(RuleDto rule) {
+    ComponentDto project = ComponentTesting.newProjectDto();
+    ComponentDto file = ComponentTesting.newFileDto(project);
+    tester.get(ComponentDao.class).insert(dbSession, project, file);
+
+    IssueDto issue = IssueTesting.newDto(rule, file, project);
+    tester.get(IssueDao.class).insert(dbSession, issue);
+    dbSession.commit();
+
+    setDefaultProjectPermission(project);
+    tester.get(IssueIndexer.class).indexAll();
+
+    return project;
+  }
+
+  private void setDefaultProjectPermission(ComponentDto project) {
+    // project can be seen by anyone and by code viewer
+    MockUserSession.set().setLogin("admin").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+    tester.get(InternalPermissionService.class).addPermission(new PermissionChange().setComponentKey(project.getKey()).setGroup(DefaultGroups.ANYONE).setPermission(UserRole.USER));
+    MockUserSession.set();
+  }
+}
index 6e5fd06ac0f141f811951a3bd98c3ef9554a81bf..98615e2214e067658f5e44fc2adc4e47b54b1f0d 100644 (file)
@@ -4,7 +4,7 @@
             name="RequestContext.java" long_name="org.struts.RequestContext"
             uuid="GHIJ" project_uuid="ABCD" module_uuid="EFGH" module_uuid_path=".ABCD.EFGH."
       qualifier="FIL" scope="FIL" language="java" path="src/org/struts/RequestContext.java" root_id="3"
-      description="[null]" enabled="[true]" copy_resource_id="[null]" person_id="[null]"
+            description="[null]" enabled="[true]" copy_resource_id="5" person_id="[null]"
       authorization_updated_at="123456789" created_at="2014-06-18"
       />
 
index 87f4799525ee70d8e0022622ebb4a32b02fc0e14..ca3a0b67a30f759b8a2467fbe029c0be1c6a0a4e 100644 (file)
@@ -41,13 +41,14 @@ public class ComponentDto extends Dto<String> implements Component {
   private String projectUuid;
   private String moduleUuid;
   private String moduleUuidPath;
+  private Long parentProjectId;
+  private Long copyResourceId;
 
   private String path;
   private String deprecatedKey;
   private String name;
   private String longName;
   private String language;
-  private Long parentProjectId;
   private boolean enabled = true;
   private Long authorizationUpdatedAt;
 
@@ -201,6 +202,15 @@ public class ComponentDto extends Dto<String> implements Component {
     return this;
   }
 
+  public Long getCopyResourceId() {
+    return copyResourceId;
+  }
+
+  public ComponentDto setCopyResourceId(Long copyResourceId) {
+    this.copyResourceId = copyResourceId;
+    return this;
+  }
+
   /**
    * Only available on projects
    */
index b38344c6921bd2995fc455b13137290b1facf2f8..269ede5a461bbb90fdec655d394333f1df21a6e5 100644 (file)
@@ -94,8 +94,7 @@ public interface ComponentMapper {
   /**
    * Return technical projects from a view or a sub-view
    */
-  List<String> selectProjectsFromView(@Param("viewUuidLikeQuery") String viewUuidLikeQuery, @Param("projectViewUuid") String projectViewUuid,
-                                      @Param("subViewQualifier") String subViewQualifier);
+  List<String> selectProjectsFromView(@Param("viewUuidLikeQuery") String viewUuidLikeQuery, @Param("projectViewUuid") String projectViewUuid);
 
   long countById(long id);
 
index 15fce5b455073ec38b058f4c4b2614d670d5fb20..98426094cd2330f46220e4133017c97590b201fa 100644 (file)
   </select>
 
   <sql id="insertColumns">
-    (kee, deprecated_kee, uuid, project_uuid, module_uuid, module_uuid_path, name, long_name, qualifier, scope, language, root_id, path, enabled, created_at, authorization_updated_at)
+    (kee, deprecated_kee, uuid, project_uuid, module_uuid, module_uuid_path, name, long_name, qualifier, scope, language, root_id, path, copy_resource_id, enabled,
+    created_at, authorization_updated_at)
   </sql>
 
   <insert id="insert" parameterType="Component" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
     insert into projects <include refid="insertColumns"/>
     values (#{kee}, #{deprecatedKey}, #{uuid}, #{projectUuid}, #{moduleUuid}, #{moduleUuidPath}, #{name}, #{longName}, #{qualifier}, #{scope},
-    #{language}, #{parentProjectId}, #{path}, #{enabled}, #{createdAt}, #{authorizationUpdatedAt})
+    #{language}, #{parentProjectId}, #{path}, #{copyResourceId}, #{enabled}, #{createdAt}, #{authorizationUpdatedAt})
   </insert>
 
   <delete id="deleteByKey" parameterType="String">
index e6a1c431a8ddd9847645f20d743ce874d95d183a..52fe57359e7ff5f1a62064b3f31b2a5be4a2eaf3 100644 (file)
@@ -38,6 +38,7 @@ public class ComponentDtoTest {
       .setScope("FIL")
       .setLanguage("java")
       .setPath("src/org/struts/RequestContext.java")
+      .setCopyResourceId(5L)
       .setParentProjectId(3L)
       .setAuthorizationUpdatedAt(123456789L);
 
@@ -51,6 +52,7 @@ public class ComponentDtoTest {
     assertThat(componentDto.path()).isEqualTo("src/org/struts/RequestContext.java");
     assertThat(componentDto.language()).isEqualTo("java");
     assertThat(componentDto.parentProjectId()).isEqualTo(3L);
+    assertThat(componentDto.getCopyResourceId()).isEqualTo(5L);
     assertThat(componentDto.getAuthorizationUpdatedAt()).isEqualTo(123456789L);
   }