]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9616 index the issues part of a non-main branch
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Tue, 1 Aug 2017 13:33:47 +0000 (15:33 +0200)
committerJanos Gyerik <janos.gyerik@sonarsource.com>
Tue, 12 Sep 2017 08:55:09 +0000 (10:55 +0200)
12 files changed:
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueTesting.java
server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDbTester.java
server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueDoc.java
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexDefinition.java
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDoc.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/UpdateConflictResolverTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetTagsActionTest.java
sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java

index 2220fa710149caec9b2ab5f3b54009d1fd93e41e..2dbc1c731aa4a38f4371bf8ddb05092dae1e90ab 100644 (file)
@@ -559,10 +559,6 @@ public final class IssueDto implements Serializable {
     return this;
   }
 
-  /**
-   * Can be null on Views or Devs
-   */
-  @CheckForNull
   public String getProjectUuid() {
     return projectUuid;
   }
@@ -572,8 +568,8 @@ public final class IssueDto implements Serializable {
    * <p/>
    * Please use {@link #setProject(ComponentDto)} instead
    */
-  public IssueDto setProjectUuid(@Nullable String s) {
-    checkArgument(s == null || s.length() <= 50, "Value is too long for column ISSUES.PROJECT_UUID: %s", s);
+  public IssueDto setProjectUuid(String s) {
+    checkArgument(s.length() <= 50, "Value is too long for column ISSUES.PROJECT_UUID: %s", s);
     this.projectUuid = s;
     return this;
   }
index 98ca360e510ddbb035f05033813607aa9bbd9f62..32d524b67e0f05090aa53e0b95c091284c3f56d1 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.db.issue;
 import java.util.Date;
 import org.apache.commons.lang.math.RandomUtils;
 import org.sonar.api.issue.Issue;
+import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.utils.DateUtils;
@@ -30,6 +31,7 @@ import org.sonar.db.component.ComponentDto;
 import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.rule.RuleDto;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.collect.Sets.newHashSet;
 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
@@ -42,6 +44,9 @@ public class IssueTesting {
   }
 
   public static IssueDto newIssue(RuleDefinitionDto rule, ComponentDto project, ComponentDto file) {
+    checkArgument(project.qualifier().equals(Qualifiers.PROJECT));
+    checkArgument(file.projectUuid().equals(project.uuid()));
+
     return new IssueDto()
       .setKee("uuid_" + randomAlphabetic(5))
       .setRule(rule)
index 0c090b4787196a4315f4f462f86f5fa0498780f4..bd7b3e0f9d7ca3e739aa236f25fd0a48cd30288f 100644 (file)
@@ -75,9 +75,9 @@ public class IssueDbTester {
     RuleDefinitionDto rule = db.rules().insert();
     ComponentDto project = db.components().insertPrivateProject(organizationDto);
     ComponentDto file = db.components().insertComponent(newFileDto(project));
-    IssueDto issueDto = newIssue(rule, file, project);
-    populateIssueDto.accept(issueDto);
-    return insertIssue(issueDto);
+    IssueDto issue = newIssue(rule, project, file);
+    populateIssueDto.accept(issue);
+    return insertIssue(issue);
   }
 
   public IssueChangeDto insertChange(IssueChangeDto issueChangeDto) {
index 2e196ac87a9875d86c4e02bdc51c21fcf02988a8..45d4daf52f39b428820a0b5cb95d6dddd6966c03 100644 (file)
@@ -456,7 +456,7 @@ public class PurgeDaoTest {
     assertThat(getComponentUuidsOfMeasures())
       .containsOnly(view.uuid(), subview.uuid());
 
-    underTest.deleteNonRootComponents(dbSession, asList(subview));
+    underTest.deleteNonRootComponents(dbSession, singletonList(subview));
     assertThat(getComponentUuidsOfMeasures())
       .containsOnly(view.uuid());
   }
@@ -479,23 +479,6 @@ public class PurgeDaoTest {
       .containsOnly(project.uuid());
   }
 
-  @Test
-  public void deleteNonRootComponents_deletes_issues_and_changes_of_any_non_root_component_of_a_view() {
-    ComponentDto view = dbTester.components().insertView();
-    ComponentDto subview = dbTester.components().insertComponent(ComponentTesting.newSubView(view));
-    ComponentDto pc = dbTester.components().insertComponent(newProjectCopy("a", dbTester.components().insertPrivateProject(), view));
-    insertIssueAndChangesFor(view, subview, pc);
-    assertThat(getComponentUuidsOfIssueChanges()).containsOnly(view.uuid(), subview.uuid(), pc.uuid());
-
-    underTest.deleteNonRootComponents(dbSession, singletonList(pc));
-    assertThat(getComponentUuidsOfIssueChanges())
-      .containsOnly(view.uuid(), subview.uuid());
-
-    underTest.deleteNonRootComponents(dbSession, asList(subview));
-    assertThat(getComponentUuidsOfIssueChanges())
-      .containsOnly(view.uuid());
-  }
-
   @Test
   public void deleteNonRootComponents_deletes_file_sources_of_file_component_of_a_project_only() {
     ComponentDto project = new Random().nextBoolean() ? dbTester.components().insertPublicProject() : dbTester.components().insertPrivateProject();
@@ -669,9 +652,9 @@ public class PurgeDaoTest {
       .map(row -> (String) row.get("COMPONENT_UUID"));
   }
 
-  private void insertIssueAndChangesFor(ComponentDto... components) {
-    Arrays.stream(components).forEach(componentDto -> {
-      IssueDto issue = dbTester.issues().insert(RuleTesting.newRule(), componentDto, componentDto);
+  private void insertIssueAndChangesFor(ComponentDto project, ComponentDto... otherComponents) {
+    Stream.concat(Stream.of(project), Arrays.stream(otherComponents)).forEach(componentDto -> {
+      IssueDto issue = dbTester.issues().insert(RuleTesting.newRule(), project, componentDto);
       dbTester.issues().insertComment(issue, "foo", "bar");
     });
     dbSession.commit();
index eef37179e5b1492e86fd79f7e804a4c9a68c5d30..8fc5c0d7d15d355c8f4f6bfe19a6aa8a78c6a8ce 100644 (file)
@@ -38,7 +38,7 @@ public class IssueDoc extends BaseDoc {
   }
 
   public IssueDoc() {
-    super(Maps.newHashMapWithExpectedSize(30));
+    super(Maps.newHashMapWithExpectedSize(32));
   }
 
   @Override
@@ -77,6 +77,14 @@ public class IssueDoc extends BaseDoc {
     return getField(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID);
   }
 
+  public String branchUuid() {
+    return getField(IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID);
+  }
+
+  public boolean isMainBranch() {
+    return getField(IssueIndexDefinition.FIELD_ISSUE_IS_MAIN_BRANCH);
+  }
+
   public RuleKey ruleKey() {
     return RuleKey.parse(getField(IssueIndexDefinition.FIELD_ISSUE_RULE_KEY));
   }
@@ -172,11 +180,21 @@ public class IssueDoc extends BaseDoc {
     return this;
   }
 
-  public IssueDoc setProjectUuid(@Nullable String s) {
+  public IssueDoc setProjectUuid(String s) {
     setField(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, s);
     return this;
   }
 
+  public IssueDoc setBranchUuid(String s) {
+    setField(IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID, s);
+    return this;
+  }
+
+  public IssueDoc setIsMainBranch(boolean b) {
+    setField(IssueIndexDefinition.FIELD_ISSUE_IS_MAIN_BRANCH, b);
+    return this;
+  }
+
   public IssueDoc setRuleKey(@Nullable String s) {
     setField(IssueIndexDefinition.FIELD_ISSUE_RULE_KEY, s);
     return this;
index 0eabde4223ceb5e51e66b1a309506b522859cab9..e127d99c2f08e844b6eec40855a403683caef23b 100644 (file)
@@ -58,7 +58,32 @@ public class IssueIndexDefinition implements IndexDefinition {
   public static final String FIELD_ISSUE_MODULE_UUID = "module";
   public static final String FIELD_ISSUE_MODULE_PATH = "modulePath";
   public static final String FIELD_ISSUE_ORGANIZATION_UUID = "organization";
+
+  /**
+   * The (real) project, equivalent of projects.main_branch_project_uuid | projects.project_uuid, so
+   * it's never empty.
+   * On main branches, it is the UUID of the project.
+   * On non-main branches, it is the UUID of the main branch (which represents the project).
+   * This field maps the parent association with issues/authorization.
+   */
   public static final String FIELD_ISSUE_PROJECT_UUID = "project";
+
+  /**
+   * The branch, as represented by the component with TRK qualifier. It's never
+   * empty. It maps the DB column projects.project_uuid:
+   * - on main branches, it is the UUID of the project. It equals {@link #FIELD_ISSUE_PROJECT_UUID}.
+   * - on non-main branches, it is the UUID of the project representing the branch and it
+   * is different than {@link #FIELD_ISSUE_PROJECT_UUID}.
+   */
+  public static final String FIELD_ISSUE_BRANCH_UUID = "branch";
+
+  /**
+   * Whether component is in a main branch or not.
+   * If true, then {@link #FIELD_ISSUE_BRANCH_UUID} equals {@link #FIELD_ISSUE_PROJECT_UUID}.
+   * If false, then {@link #FIELD_ISSUE_BRANCH_UUID} is different than {@link #FIELD_ISSUE_PROJECT_UUID}.
+   */
+  public static final String FIELD_ISSUE_IS_MAIN_BRANCH = "isMainBranch";
+
   public static final String FIELD_ISSUE_DIRECTORY_PATH = "dirPath";
   public static final String FIELD_ISSUE_RESOLUTION = "resolution";
   public static final String FIELD_ISSUE_RULE_KEY = "ruleKey";
@@ -117,6 +142,8 @@ public class IssueIndexDefinition implements IndexDefinition {
     type.createUuidPathField(FIELD_ISSUE_MODULE_PATH);
     type.keywordFieldBuilder(FIELD_ISSUE_ORGANIZATION_UUID).disableNorms().build();
     type.keywordFieldBuilder(FIELD_ISSUE_PROJECT_UUID).disableNorms().addSubFields(SORTABLE_ANALYZER).build();
+    type.keywordFieldBuilder(FIELD_ISSUE_BRANCH_UUID).disableNorms().build();
+    type.createBooleanField(FIELD_ISSUE_IS_MAIN_BRANCH);
     type.keywordFieldBuilder(FIELD_ISSUE_DIRECTORY_PATH).disableNorms().build();
     type.keywordFieldBuilder(FIELD_ISSUE_RESOLUTION).disableNorms().build();
     type.keywordFieldBuilder(FIELD_ISSUE_RULE_KEY).disableNorms().build();
index a939bfac5a1841e67210550ada9df8195c1e3f1f..6a72a75c264932dbe218294e32606c8fcae9aade 100644 (file)
@@ -69,23 +69,24 @@ class IssueIteratorForSingleChunk implements IssueIterator {
     "r.plugin_name",
     "r.plugin_rule_key",
     "r.language",
-    "p.uuid",
-    "p.module_uuid_path",
-    "p.path",
-    "p.scope",
-    "p.organization_uuid",
-    "p.project_uuid",
+    "c.uuid",
+    "c.module_uuid_path",
+    "c.path",
+    "c.scope",
+    "c.organization_uuid",
+    "c.project_uuid",
 
     // column 21
+    "c.main_branch_project_uuid",
     "i.tags",
     "i.issue_type"
   };
 
   private static final String SQL_ALL = "select " + StringUtils.join(FIELDS, ",") + " from issues i " +
     "inner join rules r on r.id = i.rule_id " +
-    "inner join projects p on p.uuid = i.component_uuid ";
+    "inner join projects c on c.uuid = i.component_uuid ";
 
-  private static final String PROJECT_FILTER = " and p.project_uuid=?";
+  private static final String PROJECT_FILTER = " and c.project_uuid = ?";
   private static final String ISSUE_KEY_FILTER_PREFIX = " and i.kee in (";
   private static final String ISSUE_KEY_FILTER_SUFFIX = ")";
 
@@ -212,11 +213,19 @@ class IssueIteratorForSingleChunk implements IssueIterator {
       doc.setFilePath(filePath);
       doc.setDirectoryPath(extractDirPath(doc.filePath(), scope));
       doc.setOrganizationUuid(rs.getString(19));
-      String projectUuid = rs.getString(20);
-      doc.setProjectUuid(projectUuid);
-      String tags = rs.getString(21);
+      String branchUuid = rs.getString(20);
+      String mainBranchProjectUuid = DatabaseUtils.getString(rs, 21);
+      doc.setBranchUuid(branchUuid);
+      if (mainBranchProjectUuid == null) {
+        doc.setProjectUuid(branchUuid);
+        doc.setIsMainBranch(true);
+      } else {
+        doc.setProjectUuid(mainBranchProjectUuid);
+        doc.setIsMainBranch(false);
+      }
+      String tags = rs.getString(22);
       doc.setTags(ImmutableList.copyOf(IssueIteratorForSingleChunk.TAGS_SPLITTER.split(tags == null ? "" : tags)));
-      doc.setType(RuleType.valueOf(rs.getInt(22)));
+      doc.setType(RuleType.valueOf(rs.getInt(23)));
       return doc;
     }
 
index 95f4ce1105dd13737ec39fd914b475ece86b2bf0..e28ad1cc35a5a5e6ab68cecac56da3a23735a311 100644 (file)
@@ -164,7 +164,7 @@ public class RuleDoc extends BaseDoc {
   }
 
   public boolean isTemplate() {
-    return (Boolean) getField(RuleIndexDefinition.FIELD_RULE_IS_TEMPLATE);
+    return getField(RuleIndexDefinition.FIELD_RULE_IS_TEMPLATE);
   }
 
   public RuleDoc setIsTemplate(@Nullable Boolean b) {
index a67e1da58b5abb373482dd35665f82428a76e6ef..662b3f84e8ccade285d70f1954ba1aa3ea9557f4 100644 (file)
@@ -43,7 +43,8 @@ public class UpdateConflictResolverTest {
       .setKey("ABCDE")
       .setType(RuleType.CODE_SMELL)
       .setRuleKey(RuleKey.of("squid", "AvoidCycles"))
-      .setComponentKey("struts:org.apache.struts.Action")
+      .setProjectUuid("U1")
+      .setComponentUuid("U2")
       .setNew(false)
       .setStatus(Issue.STATUS_OPEN);
 
@@ -55,7 +56,8 @@ public class UpdateConflictResolverTest {
         .setType(RuleType.CODE_SMELL)
         .setRuleId(10)
         .setRuleKey("squid", "AvoidCycles")
-        .setComponentKey("struts:org.apache.struts.Action")
+        .setProjectUuid("U1")
+        .setComponentUuid("U2")
         .setLine(10)
         .setStatus(Issue.STATUS_OPEN)
 
index 0954b7dba439fd5a1a75d68b80d387c58eda898d..dd42cc7e85e32092c45cd6597b2df224672a3cf3 100644 (file)
@@ -120,7 +120,10 @@ public class IssueIndexerTest {
     assertThat(doc.organizationUuid()).isEqualTo(organization.getUuid());
     assertThat(doc.assignee()).isEqualTo(issue.getAssignee());
     assertThat(doc.authorLogin()).isEqualTo(issue.getAuthorLogin());
-    assertThat(doc.componentUuid()).isEqualTo(issue.getComponentUuid());
+    assertThat(doc.componentUuid()).isEqualTo(file.uuid());
+    assertThat(doc.projectUuid()).isEqualTo(project.uuid());
+    assertThat(doc.branchUuid()).isEqualTo(project.uuid());
+    assertThat(doc.isMainBranch()).isTrue();
     assertThat(doc.closeDate()).isEqualTo(issue.getIssueCloseDate());
     assertThat(doc.creationDate()).isEqualToIgnoringMillis(issue.getIssueCreationDate());
     assertThat(doc.directoryPath()).isEqualTo(dir.path());
@@ -445,6 +448,26 @@ public class IssueIndexerTest {
     assertThat(es.countDocuments(INDEX_TYPE_ISSUE)).isEqualTo(1L);
   }
 
+  @Test
+  public void index_issue_in_non_main_branch() {
+    RuleDefinitionDto rule = db.rules().insert();
+    ComponentDto project = db.components().insertPrivateProject(organization);
+    ComponentDto branch = db.components().insertProjectBranch(project, "feature/foo");
+    ComponentDto dir = db.components().insertComponent(ComponentTesting.newDirectory(branch, "src/main/java/foo"));
+    ComponentDto file = db.components().insertComponent(newFileDto(branch, dir, "F1"));
+    IssueDto issue = db.issues().insertIssue(IssueTesting.newIssue(rule, branch, file));
+
+    underTest.indexOnStartup(emptySet());
+
+    IssueDoc doc = es.getDocuments(INDEX_TYPE_ISSUE, IssueDoc.class).get(0);
+    assertThat(doc.getId()).isEqualTo(issue.getKey());
+    assertThat(doc.organizationUuid()).isEqualTo(organization.getUuid());
+    assertThat(doc.componentUuid()).isEqualTo(file.uuid());
+    assertThat(doc.projectUuid()).isEqualTo(project.uuid());
+    assertThat(doc.branchUuid()).isEqualTo(branch.uuid());
+    assertThat(doc.isMainBranch()).isFalse();
+  }
+
   private void addIssueToIndex(String projectUuid, String issueKey) {
     es.putDocuments(INDEX_TYPE_ISSUE,
       newDoc().setKey(issueKey).setProjectUuid(projectUuid));
index 614b3e90f0d893e0a7df7b72e3991b438b2e66b3..f1fb31543c2af0ce25cc2575c36bb6830e8a7e00 100644 (file)
@@ -236,7 +236,7 @@ public class SetTagsActionTest {
     RuleDefinitionDto rule = db.rules().insert();
     ComponentDto project = db.components().insertPublicProject();
     ComponentDto file = db.components().insertComponent(newFileDto(project));
-    return IssueTesting.newIssue(rule, file, project);
+    return IssueTesting.newIssue(rule, project, file);
   }
 
   private void logIn(IssueDto issueDto) {
index e397f7dfe7d3131187a4dfd44dfbbfd3a245e661..a92b911d44f21ae4989bf6d6defa1fe6e5754a21 100644 (file)
@@ -178,17 +178,13 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure.
     return this;
   }
 
-  /**
-   * Can be null on Views
-   */
   @Override
-  @CheckForNull
   public String projectUuid() {
     return projectUuid;
   }
 
-  public DefaultIssue setProjectUuid(@Nullable String projectUuid) {
-    this.projectUuid = projectUuid;
+  public DefaultIssue setProjectUuid(String s) {
+    this.projectUuid = s;
     return this;
   }