import java.util.List;
import java.util.Locale;
import java.util.Set;
+import java.util.stream.Stream;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Scopes;
-import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
import org.sonar.db.RowNotFoundException;
import static java.util.Collections.emptyList;
import static java.util.Objects.requireNonNull;
import static org.apache.commons.lang.StringUtils.isBlank;
+import static org.sonar.core.util.stream.MoreCollectors.toList;
import static org.sonar.db.DaoDatabaseUtils.buildLikeValue;
import static org.sonar.db.DatabaseUtils.executeLargeInputs;
import static org.sonar.db.DatabaseUtils.executeLargeUpdates;
}
public List<ComponentDto> selectByKeysAndBranch(DbSession session, Collection<String> keys, String branch) {
- List<String> dbKeys = keys.stream().map(k -> generateBranchKey(k, branch)).collect(MoreCollectors.toList());
- return executeLargeInputs(dbKeys, mapper(session)::selectByDbKeys);
+ List<String> dbKeys = keys.stream().map(k -> generateBranchKey(k, branch)).collect(toList());
+ List<String> allKeys = Stream.of(keys, dbKeys) .flatMap(x -> x.stream()) .collect(toList());
+ return executeLargeInputs(allKeys, subKeys -> mapper(session).selectByKeysAndBranch(subKeys, branch));
}
public List<ComponentDto> selectComponentsHavingSameKeyOrderedById(DbSession session, String key) {
}
public java.util.Optional<ComponentDto> selectByKeyAndBranch(DbSession session, String key, String branch) {
- return java.util.Optional.ofNullable(mapper(session).selectByDbKey(generateBranchKey(key, branch)));
+ return java.util.Optional.ofNullable(mapper(session).selectByKeyAndBranch(key, generateBranchKey(key, branch), branch));
}
public List<UuidWithProjectUuidDto> selectAllViewsAndSubViews(DbSession session) {
@CheckForNull
ComponentDto selectByKey(String key);
- /**
- * This method should be used to get a component by its key without filtering out branches
- */
@CheckForNull
- ComponentDto selectByDbKey(String dbKey);
+ ComponentDto selectByKeyAndBranch(@Param("key") String key, @Param("dbKey") String dbKey, @Param("branch") String branch);
@CheckForNull
ComponentDto selectById(long id);
List<ComponentDto> selectByKeys(@Param("keys") Collection<String> keys);
- List<ComponentDto> selectByDbKeys(@Param("keys") Collection<String> keys);
+ List<ComponentDto> selectByKeysAndBranch(@Param("keys") Collection<String> keys, @Param("branch") String branch);
List<ComponentDto> selectByIds(@Param("ids") Collection<Long> ids);
p.kee=#{key,jdbcType=VARCHAR}
</select>
- <select id="selectByDbKey" parameterType="String" resultType="Component">
+ <select id="selectByKeyAndBranch" parameterType="String" resultType="Component">
SELECT
<include refid="componentColumns"/>
FROM projects p
- where
- p.kee=#{key,jdbcType=VARCHAR}
+ INNER JOIN project_branches pb on pb.uuid = p.project_uuid
+ <where>
+ (p.kee=#{dbKey,jdbcType=VARCHAR} OR p.kee=#{key,jdbcType=VARCHAR})
+ AND pb.kee=#{branch,jdbcType=VARCHAR}
+ </where>
</select>
<select id="selectComponentsHavingSameKeyOrderedById" parameterType="String" resultType="Component">
</foreach>
</select>
- <select id="selectByDbKeys" parameterType="String" resultType="Component">
- select
+ <select id="selectByKeysAndBranch" parameterType="String" resultType="Component">
+ SELECT
<include refid="componentColumns"/>
- from projects p
- where
+ FROM projects p
+ INNER JOIN project_branches pb on pb.uuid = p.project_uuid
+ <where>
p.enabled=${_true}
- and p.kee in
+ AND p.kee IN
<foreach collection="keys" open="(" close=")" item="key" separator=",">
#{key,jdbcType=VARCHAR}
</foreach>
+ AND pb.kee=#{branch,jdbcType=VARCHAR}
+ </where>
</select>
<select id="selectByIds" parameterType="long" resultType="Component">
@Test
public void selectByKeyAndBranch() {
- ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto project = db.components().insertMainBranch();
ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
ComponentDto file = db.components().insertComponent(newFileDto(branch));
- assertThat(underTest.selectByKeyAndBranch(dbSession, project.getKey(), "my_branch").get().uuid()).isEqualTo(branch.uuid());
+ assertThat(underTest.selectByKeyAndBranch(dbSession, project.getKey(), "master").get().uuid()).isEqualTo(project.uuid());
+ assertThat(underTest.selectByKeyAndBranch(dbSession, branch.getKey(), "my_branch").get().uuid()).isEqualTo(branch.uuid());
assertThat(underTest.selectByKeyAndBranch(dbSession, file.getKey(), "my_branch").get().uuid()).isEqualTo(file.uuid());
assertThat(underTest.selectByKeyAndBranch(dbSession, "unknown", "my_branch")).isNotPresent();
assertThat(underTest.selectByKeyAndBranch(dbSession, file.getKey(), "unknown")).isNotPresent();
@Test
public void selectByKeysAndBranch() {
- ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto project = db.components().insertMainBranch();
ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
ComponentDto file1 = db.components().insertComponent(newFileDto(branch));
ComponentDto file2 = db.components().insertComponent(newFileDto(branch));
assertThat(underTest.selectByKeysAndBranch(dbSession, singletonList(fileOnAnotherBranch.getKey()), "my_branch")).isEmpty();
assertThat(underTest.selectByKeysAndBranch(dbSession, singletonList(file1.getKey()), "unknown")).isEmpty();
assertThat(underTest.selectByKeysAndBranch(dbSession, singletonList("unknown"), "my_branch")).isEmpty();
+ assertThat(underTest.selectByKeysAndBranch(dbSession, singletonList(branch.getKey()), "master")).extracting(ComponentDto::uuid).containsExactlyInAnyOrder(project.uuid());
}
@Test
private final String facetMode;
private final String organizationUuid;
private final String branchUuid;
+ private final boolean mainBranch;
private final boolean checkAuthorization;
private IssueQuery(Builder builder) {
this.facetMode = builder.facetMode;
this.organizationUuid = builder.organizationUuid;
this.branchUuid = builder.branchUuid;
+ this.mainBranch = builder.mainBranch;
}
public Collection<String> issueKeys() {
return branchUuid;
}
+ public boolean isMainBranch() {
+ return mainBranch;
+ }
+
public String facetMode() {
return facetMode;
}
private String facetMode;
private String organizationUuid;
private String branchUuid;
+ private boolean mainBranch = true;
private Builder() {
this.branchUuid = s;
return this;
}
+
+ public Builder mainBranch(boolean mainBranch) {
+ this.mainBranch = mainBranch;
+ return this;
+ }
}
private static <T> Collection<T> defaultCollection(@Nullable Collection<T> c) {
private void addComponentParameters(IssueQuery.Builder builder, DbSession session, boolean onComponentOnly,
List<ComponentDto> components, SearchWsRequest request) {
- String branch = request.getBranch();
builder.onComponentOnly(onComponentOnly);
if (onComponentOnly) {
builder.componentUuids(components.stream().map(ComponentDto::uuid).collect(toList()));
- builder.branchUuid(branch == null ? null : components.get(0).projectUuid());
+ setBranch(builder, components.get(0), request.getBranch());
return;
}
if (projectUuids != null) {
builder.projectUuids(projectUuids);
} else if (projectKeys != null) {
- List<ComponentDto> projects = getComponentsFromKeys(session, projectKeys, branch);
- builder.projectUuids(projects.stream().map(p -> branch == null ? p.projectUuid() : p.getMainBranchProjectUuid()).collect(toList()));
- builder.branchUuid(branch == null ? null : projects.get(0).projectUuid());
+ List<ComponentDto> projects = getComponentsFromKeys(session, projectKeys, request.getBranch());
+ builder.projectUuids(projects.stream().map(IssueQueryFactory::toProjectUuid).collect(toList()));
+ setBranch(builder, projects.get(0), request.getBranch());
}
builder.moduleUuids(request.getModuleUuids());
builder.directories(request.getDirectories());
Set<String> qualifiers = components.stream().map(ComponentDto::qualifier).collect(toHashSet());
checkArgument(qualifiers.size() == 1, "All components must have the same qualifier, found %s", String.join(",", qualifiers));
- String branch = request.getBranch();
- builder.branchUuid(branch == null ? null : components.get(0).projectUuid());
+ setBranch(builder, components.get(0), request.getBranch());
String qualifier = qualifiers.iterator().next();
switch (qualifier) {
case Qualifiers.VIEW:
addApplications(builder, dbSession, components, request);
break;
case Qualifiers.PROJECT:
- builder.projectUuids(components.stream().map(c -> branch == null ? c.projectUuid() : c.getMainBranchProjectUuid()).collect(toList()));
+ builder.projectUuids(components.stream().map(IssueQueryFactory::toProjectUuid).collect(toList()));
break;
case Qualifiers.MODULE:
builder.moduleRootUuids(components.stream().map(ComponentDto::uuid).collect(toList()));
}
return null;
}
+
+ private static String toProjectUuid(ComponentDto componentDto) {
+ String mainBranchProjectUuid = componentDto.getMainBranchProjectUuid();
+ return mainBranchProjectUuid == null ? componentDto.projectUuid() : mainBranchProjectUuid;
+ }
+
+ private static void setBranch(IssueQuery.Builder builder, ComponentDto component, @Nullable String branch){
+ builder.branchUuid(branch == null ? null : component.projectUuid());
+ builder.mainBranch(branch == null || !branch.equals(component.getBranch()));
+ }
}
QueryBuilder directoryFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, query.directories());
QueryBuilder fileFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.fileUuids());
QueryBuilder branchFilter = createTermFilter(IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID, query.branchUuid());
- filters.put("__is_main_branch", createTermFilter(IssueIndexDefinition.FIELD_ISSUE_IS_MAIN_BRANCH, Boolean.toString(query.branchUuid() == null)));
+ filters.put("__is_main_branch", createTermFilter(IssueIndexDefinition.FIELD_ISSUE_IS_MAIN_BRANCH, Boolean.toString(query.isMainBranch())));
if (BooleanUtils.isTrue(query.onComponentOnly())) {
filters.put(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, componentFilter);
@Test
public void get_by_key_and_branch() {
- ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto project = db.components().insertMainBranch();
ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
ComponentDto module = db.components().insertComponent(newModuleDto(branch));
ComponentDto directory = db.components().insertComponent(newDirectory(module, "scr"));
assertThat(underTest.getByKeyAndBranch(dbSession, directory.getKey(), "my_branch").uuid()).isEqualTo(directory.uuid());
}
+ @Test
+ public void get_by_key_and_branch_accept_main_branch() {
+ ComponentDto project = db.components().insertMainBranch();
+
+ assertThat(underTest.getByKeyAndBranch(dbSession, project.getKey(), "master").uuid()).isEqualTo(project.uuid());
+ }
+
@Test
public void fail_to_get_by_key_and_branch_when_branch_does_not_exist() {
ComponentDto project = db.components().insertPrivateProject();
assertThat(underTest.create(new SearchWsRequest()
.setProjectKeys(singletonList(branch.getKey()))
.setBranch(branch.getBranch())))
- .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()))
- .containsOnly(branch.uuid(), singletonList(project.uuid()));
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
+ .containsOnly(branch.uuid(), singletonList(project.uuid()), false);
assertThat(underTest.create(new SearchWsRequest()
.setComponentKeys(singletonList(branch.getKey()))
.setBranch(branch.getBranch())))
- .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()))
- .containsOnly(branch.uuid(), singletonList(project.uuid()));
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
+ .containsOnly(branch.uuid(), singletonList(project.uuid()), false);
}
@Test
assertThat(underTest.create(new SearchWsRequest()
.setComponentKeys(singletonList(file.getKey()))
.setBranch(branch.getBranch())))
- .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.fileUuids()))
- .containsOnly(branch.uuid(), singletonList(file.uuid()));
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.fileUuids()), IssueQuery::isMainBranch)
+ .containsOnly(branch.uuid(), singletonList(file.uuid()), false);
assertThat(underTest.create(new SearchWsRequest()
.setComponentKeys(singletonList(branch.getKey()))
.setFileUuids(singletonList(file.uuid()))
.setBranch(branch.getBranch())))
- .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.fileUuids()))
- .containsOnly(branch.uuid(), singletonList(file.uuid()));
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.fileUuids()), IssueQuery::isMainBranch)
+ .containsOnly(branch.uuid(), singletonList(file.uuid()), false);
assertThat(underTest.create(new SearchWsRequest()
.setProjectKeys(singletonList(branch.getKey()))
.setFileUuids(singletonList(file.uuid()))
.setBranch(branch.getBranch())))
- .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.fileUuids()))
- .containsOnly(branch.uuid(), singletonList(file.uuid()));
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.fileUuids()), IssueQuery::isMainBranch)
+ .containsOnly(branch.uuid(), singletonList(file.uuid()), false);
}
@Test
.setComponentKeys(singletonList(file.getKey()))
.setBranch(branch.getBranch())
.setOnComponentOnly(true)))
- .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.componentUuids()))
- .containsOnly(branch.uuid(), singletonList(file.uuid()));
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.componentUuids()), IssueQuery::isMainBranch)
+ .containsOnly(branch.uuid(), singletonList(file.uuid()), false);
+ }
+
+ @Test
+ public void search_issues_from_main_branch() {
+ ComponentDto project = db.components().insertMainBranch();
+ ComponentDto branch = db.components().insertProjectBranch(project);
+
+ assertThat(underTest.create(new SearchWsRequest()
+ .setProjectKeys(singletonList(project.getKey()))
+ .setBranch("master")))
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
+ .containsOnly(project.uuid(), singletonList(project.uuid()), true);
+ assertThat(underTest.create(new SearchWsRequest()
+ .setComponentKeys(singletonList(project.getKey()))
+ .setBranch("master")))
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
+ .containsOnly(project.uuid(), singletonList(project.uuid()), true);
}
@Test
IssueDoc issueOnAnotherBranch = newDoc(anotherbBranch);
indexIssues(issueOnProject, issueOnBranch, issueOnAnotherBranch);
- assertThatSearchReturnsOnly(IssueQuery.builder().branchUuid(branch.uuid()), issueOnBranch.key());
- assertThatSearchReturnsOnly(IssueQuery.builder().componentUuids(singletonList(branch.uuid())).branchUuid(branch.uuid()), issueOnBranch.key());
- assertThatSearchReturnsOnly(IssueQuery.builder().projectUuids(singletonList(project.uuid())).branchUuid(branch.uuid()), issueOnBranch.key());
- assertThatSearchReturnsOnly(IssueQuery.builder().componentUuids(singletonList(branch.uuid())).projectUuids(singletonList(project.uuid())).branchUuid(branch.uuid()),
+ assertThatSearchReturnsOnly(IssueQuery.builder().branchUuid(branch.uuid()).mainBranch(false), issueOnBranch.key());
+ assertThatSearchReturnsOnly(IssueQuery.builder().componentUuids(singletonList(branch.uuid())).branchUuid(branch.uuid()).mainBranch(false), issueOnBranch.key());
+ assertThatSearchReturnsOnly(IssueQuery.builder().projectUuids(singletonList(project.uuid())).branchUuid(branch.uuid()).mainBranch(false), issueOnBranch.key());
+ assertThatSearchReturnsOnly(IssueQuery.builder().componentUuids(singletonList(branch.uuid())).projectUuids(singletonList(project.uuid())).branchUuid(branch.uuid()).mainBranch(false),
issueOnBranch.key());
assertThatSearchReturnsEmpty(IssueQuery.builder().branchUuid("unknown"));
}
newDoc("I5", branchModule),
newDoc("I6", branchFile));
- assertThatSearchReturnsOnly(IssueQuery.builder().branchUuid(branch.uuid()), "I4", "I5", "I6");
- assertThatSearchReturnsOnly(IssueQuery.builder().moduleUuids(singletonList(branchModule.uuid())).branchUuid(branch.uuid()), "I5", "I6");
- assertThatSearchReturnsOnly(IssueQuery.builder().fileUuids(singletonList(branchFile.uuid())).branchUuid(branch.uuid()), "I6");
- assertThatSearchReturnsEmpty(IssueQuery.builder().fileUuids(singletonList(branchFile.uuid())).branchUuid("unknown"));
+ assertThatSearchReturnsOnly(IssueQuery.builder().branchUuid(branch.uuid()).mainBranch(false), "I4", "I5", "I6");
+ assertThatSearchReturnsOnly(IssueQuery.builder().moduleUuids(singletonList(branchModule.uuid())).branchUuid(branch.uuid()).mainBranch(false), "I5", "I6");
+ assertThatSearchReturnsOnly(IssueQuery.builder().fileUuids(singletonList(branchFile.uuid())).branchUuid(branch.uuid()).mainBranch(false), "I6");
+ assertThatSearchReturnsEmpty(IssueQuery.builder().fileUuids(singletonList(branchFile.uuid())).mainBranch(false).branchUuid("unknown"));
+ }
+
+ @Test
+ public void issues_from_main_branch() {
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto branch = db.components().insertProjectBranch(project);
+
+ IssueDoc issueOnProject = newDoc(project);
+ IssueDoc issueOnBranch = newDoc(branch);
+ indexIssues(issueOnProject, issueOnBranch);
+
+ assertThatSearchReturnsOnly(IssueQuery.builder().branchUuid(project.uuid()).mainBranch(true), issueOnProject.key());
+ assertThatSearchReturnsOnly(IssueQuery.builder().componentUuids(singletonList(project.uuid())).branchUuid(project.uuid()).mainBranch(true), issueOnProject.key());
+ assertThatSearchReturnsOnly(IssueQuery.builder().projectUuids(singletonList(project.uuid())).branchUuid(project.uuid()).mainBranch(true), issueOnProject.key());
+ assertThatSearchReturnsOnly(IssueQuery.builder().componentUuids(singletonList(project.uuid())).projectUuids(singletonList(project.uuid())).branchUuid(project.uuid()).mainBranch(true),
+ issueOnProject.key());
}
@Test
assertThat(underTest.searchBranchStatistics(project.uuid(), singletonList("unknown"))).isEmpty();
}
- private void addIssues(ComponentDto branch, int bugs, int vulnerabilities, int codeSmelles) {
+ private void addIssues(ComponentDto component, int bugs, int vulnerabilities, int codeSmelles) {
List<IssueDoc> issues = new ArrayList<>();
- IntStream.range(0, bugs).forEach(b -> issues.add(newDoc(branch).setType(BUG)));
- IntStream.range(0, vulnerabilities).forEach(v -> issues.add(newDoc(branch).setType(VULNERABILITY)));
- IntStream.range(0, codeSmelles).forEach(c -> issues.add(newDoc(branch).setType(CODE_SMELL)));
+ IntStream.range(0, bugs).forEach(b -> issues.add(newDoc(component).setType(BUG)));
+ IntStream.range(0, vulnerabilities).forEach(v -> issues.add(newDoc(component).setType(VULNERABILITY)));
+ IntStream.range(0, codeSmelles).forEach(c -> issues.add(newDoc(component).setType(CODE_SMELL)));
indexIssues(issues.toArray(new IssueDoc[issues.size()]));
}
tuple(branch.getKey(), branch.getBranch()));
}
+ @Test
+ public void search_using_main_branch_name() {
+ RuleDefinitionDto rule = db.rules().insert();
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.addProjectPermission(UserRole.USER, project);
+ ComponentDto projectFile = db.components().insertComponent(newFileDto(project));
+ IssueDto projectIssue = db.issues().insertIssue(newIssue(rule, project, projectFile));
+ allowAnyoneOnProjects(project);
+ indexIssuesAndViews();
+
+ SearchWsResponse result = ws.newRequest()
+ .setParam(PARAM_COMPONENT_KEYS, project.getKey())
+ .setParam(PARAM_BRANCH, "master")
+ .executeProtobuf(SearchWsResponse.class);
+
+ assertThat(result.getIssuesList())
+ .extracting(Issue::getKey, Issue::getComponent, Issue::hasBranch)
+ .containsExactlyInAnyOrder(tuple(projectIssue.getKey(), projectFile.getKey(), false));
+ assertThat(result.getComponentsList())
+ .extracting(Issues.Component::getKey, Issues.Component::hasBranch)
+ .containsExactlyInAnyOrder(
+ tuple(projectFile.getKey(), false),
+ tuple(project.getKey(), false));
+ }
+
@Test
public void does_not_return_branch_issues_on_not_contextualized_search() {
RuleDefinitionDto rule = db.rules().insert();