From: Simon Brandhof Date: Tue, 1 Aug 2017 13:33:47 +0000 (+0200) Subject: SONAR-9616 index the issues part of a non-main branch X-Git-Tag: 6.6-RC1~380^2~133 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=d3a6ef3150009d5828f1be984e372db8bf6ae80a;p=sonarqube.git SONAR-9616 index the issues part of a non-main branch --- diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java index 2220fa71014..2dbc1c731aa 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java @@ -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 { *

* 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; } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueTesting.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueTesting.java index 98ca360e510..32d524b67e0 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueTesting.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueTesting.java @@ -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) diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDbTester.java b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDbTester.java index 0c090b47871..bd7b3e0f9d7 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDbTester.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDbTester.java @@ -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) { 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 2e196ac87a9..45d4daf52f3 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 @@ -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(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueDoc.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueDoc.java index eef37179e5b..8fc5c0d7d15 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueDoc.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueDoc.java @@ -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; diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexDefinition.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexDefinition.java index 0eabde4223c..e127d99c2f0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexDefinition.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexDefinition.java @@ -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(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java index a939bfac5a1..6a72a75c264 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java @@ -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; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDoc.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDoc.java index 95f4ce1105d..e28ad1cc35a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDoc.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDoc.java @@ -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) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/UpdateConflictResolverTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/UpdateConflictResolverTest.java index a67e1da58b5..662b3f84e8c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/UpdateConflictResolverTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/UpdateConflictResolverTest.java @@ -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) diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java index 0954b7dba43..dd42cc7e85e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java @@ -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)); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetTagsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetTagsActionTest.java index 614b3e90f0d..f1fb31543c2 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetTagsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetTagsActionTest.java @@ -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) { diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java index e397f7dfe7d..a92b911d44f 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java @@ -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; }