return this;
}
- /**
- * Can be null on Views or Devs
- */
- @CheckForNull
public String getProjectUuid() {
return projectUuid;
}
* <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;
}
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;
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;
}
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)
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) {
assertThat(getComponentUuidsOfMeasures())
.containsOnly(view.uuid(), subview.uuid());
- underTest.deleteNonRootComponents(dbSession, asList(subview));
+ underTest.deleteNonRootComponents(dbSession, singletonList(subview));
assertThat(getComponentUuidsOfMeasures())
.containsOnly(view.uuid());
}
.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();
.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();
}
public IssueDoc() {
- super(Maps.newHashMapWithExpectedSize(30));
+ super(Maps.newHashMapWithExpectedSize(32));
}
@Override
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));
}
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;
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";
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();
"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 = ")";
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;
}
}
public boolean isTemplate() {
- return (Boolean) getField(RuleIndexDefinition.FIELD_RULE_IS_TEMPLATE);
+ return getField(RuleIndexDefinition.FIELD_RULE_IS_TEMPLATE);
}
public RuleDoc setIsTemplate(@Nullable Boolean b) {
.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);
.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)
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());
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));
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) {
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;
}