aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2017-08-10 13:36:54 +0200
committerJanos Gyerik <janos.gyerik@sonarsource.com>2017-09-12 10:59:55 +0200
commit47ef3bd0dc6610d0034f1753e06e8ac467eb906b (patch)
tree5778e6442902616b7a0ea1138795815de17c6eea
parent16a7a9b8996218475966392fcf6c00e9150d70ef (diff)
downloadsonarqube-47ef3bd0dc6610d0034f1753e06e8ac467eb906b.tar.gz
sonarqube-47ef3bd0dc6610d0034f1753e06e8ac467eb906b.zip
SONAR-9616 Support branch in api/issues/search
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java9
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java2
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml12
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java18
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDbTester.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java13
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java144
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java11
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/batch/ProjectDataLoaderTest.java6
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/IssueDocTesting.java31
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryFactoryTest.java109
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryTest.java7
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java96
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java107
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java12
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/issue/SearchWsRequest.java11
19 files changed, 452 insertions, 146 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
index f12e59a954c..b6456c4e12d 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
@@ -35,6 +35,7 @@ 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;
@@ -47,6 +48,7 @@ import static org.sonar.db.DaoDatabaseUtils.buildLikeValue;
import static org.sonar.db.DatabaseUtils.executeLargeInputs;
import static org.sonar.db.DatabaseUtils.executeLargeUpdates;
import static org.sonar.db.WildcardPosition.BEFORE_AND_AFTER;
+import static org.sonar.db.component.ComponentDto.generateBranchKey;
public class ComponentDao implements Dao {
@@ -169,6 +171,11 @@ public class ComponentDao implements Dao {
return executeLargeInputs(keys, mapper(session)::selectByKeys);
}
+ 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);
+ }
+
public List<ComponentDto> selectComponentsHavingSameKeyOrderedById(DbSession session, String key) {
return mapper(session).selectComponentsHavingSameKeyOrderedById(key);
}
@@ -215,7 +222,7 @@ public class ComponentDao implements Dao {
}
public java.util.Optional<ComponentDto> selectByKeyAndBranch(DbSession session, String key, String branch) {
- return java.util.Optional.ofNullable(mapper(session).selectByDbKey(ComponentDto.generateBranchKey(key, branch)));
+ return java.util.Optional.ofNullable(mapper(session).selectByDbKey(generateBranchKey(key, branch)));
}
public List<UuidWithProjectUuidDto> selectAllViewsAndSubViews(DbSession session) {
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java
index 692948c085c..0f3f5d74c33 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java
@@ -52,6 +52,8 @@ public interface ComponentMapper {
List<ComponentDto> selectByKeys(@Param("keys") Collection<String> keys);
+ List<ComponentDto> selectByDbKeys(@Param("keys") Collection<String> keys);
+
List<ComponentDto> selectByIds(@Param("ids") Collection<Long> ids);
List<ComponentDto> selectByUuids(@Param("uuids") Collection<String> uuids);
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
index 0409bbd1f62..d7079ac7a10 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
@@ -89,6 +89,18 @@
</foreach>
</select>
+ <select id="selectByDbKeys" parameterType="String" resultType="Component">
+ select
+ <include refid="componentColumns"/>
+ from projects p
+ where
+ p.enabled=${_true}
+ and p.kee in
+ <foreach collection="keys" open="(" close=")" item="key" separator=",">
+ #{key,jdbcType=VARCHAR}
+ </foreach>
+ </select>
+
<select id="selectByIds" parameterType="long" resultType="Component">
select
<include refid="componentColumns"/>
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java
index e3f4e89f355..21eae5e1253 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java
@@ -250,6 +250,24 @@ public class ComponentDaoTest {
}
@Test
+ public void selectByKeysAndBranch() {
+ ComponentDto project = db.components().insertPrivateProject();
+ 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));
+ ComponentDto anotherBranch = db.components().insertProjectBranch(project, b -> b.setKey("another_branch"));
+ ComponentDto fileOnAnotherBranch = db.components().insertComponent(newFileDto(anotherBranch));
+
+ assertThat(underTest.selectByKeysAndBranch(dbSession, asList(branch.getKey(), file1.getKey(), file2.getKey()), "my_branch")).extracting(ComponentDto::uuid)
+ .containsExactlyInAnyOrder(branch.uuid(), file1.uuid(), file2.uuid());
+ assertThat(underTest.selectByKeysAndBranch(dbSession, asList(file1.getKey(), file2.getKey(), fileOnAnotherBranch.getKey()), "my_branch")).extracting(ComponentDto::uuid)
+ .containsExactlyInAnyOrder(file1.uuid(), file2.uuid());
+ 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();
+ }
+
+ @Test
public void get_by_ids() {
ComponentDto project1 = db.components().insertPrivateProject();
ComponentDto project2 = db.components().insertPrivateProject();
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDbTester.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDbTester.java
index 9723e0d689f..a3a23ec35e1 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDbTester.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDbTester.java
@@ -205,7 +205,7 @@ public class ComponentDbTester {
public final ComponentDto insertProjectBranch(ComponentDto project, Consumer<BranchDto>... dtoPopulators) {
String uuid = Uuids.createFast();
BranchDto branchDto = new BranchDto()
- .setKey(randomAlphanumeric(255))
+ .setKey("branch_" + randomAlphanumeric(248))
.setUuid(uuid)
.setProjectUuid(project.uuid())
.setKeeType(BRANCH)
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java
index 4805a61b58f..35538b9fc77 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java
@@ -81,6 +81,7 @@ public class IssueQuery {
private final Boolean asc;
private final String facetMode;
private final String organizationUuid;
+ private final String branchUuid;
private final boolean checkAuthorization;
private IssueQuery(Builder builder) {
@@ -113,6 +114,7 @@ public class IssueQuery {
this.checkAuthorization = builder.checkAuthorization;
this.facetMode = builder.facetMode;
this.organizationUuid = builder.organizationUuid;
+ this.branchUuid = builder.branchUuid;
}
public Collection<String> issueKeys() {
@@ -236,6 +238,11 @@ public class IssueQuery {
return organizationUuid;
}
+ @CheckForNull
+ public String branchUuid() {
+ return branchUuid;
+ }
+
public String facetMode() {
return facetMode;
}
@@ -279,6 +286,7 @@ public class IssueQuery {
private boolean checkAuthorization = true;
private String facetMode;
private String organizationUuid;
+ private String branchUuid;
private Builder() {
@@ -450,6 +458,11 @@ public class IssueQuery {
this.organizationUuid = s;
return this;
}
+
+ public Builder branchUuid(@Nullable String s) {
+ this.branchUuid = s;
+ return this;
+ }
}
private static <T> Collection<T> defaultCollection(@Nullable Collection<T> c) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java
index e4184a39369..71e53f29e5c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java
@@ -25,6 +25,7 @@ import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
@@ -46,7 +47,6 @@ import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.ServerSide;
import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
-import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
@@ -64,8 +64,10 @@ import static org.sonar.api.utils.DateUtils.longToDate;
import static org.sonar.api.utils.DateUtils.parseDateOrDateTime;
import static org.sonar.api.utils.DateUtils.parseEndingDateOrDateTime;
import static org.sonar.api.utils.DateUtils.parseStartingDateOrDateTime;
+import static org.sonar.core.util.stream.MoreCollectors.toHashSet;
+import static org.sonar.core.util.stream.MoreCollectors.toList;
+import static org.sonar.core.util.stream.MoreCollectors.toSet;
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
-import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
import static org.sonar.server.ws.WsUtils.checkRequest;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENTS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENT_KEYS;
@@ -84,6 +86,9 @@ public class IssueQueryFactory {
public static final String LOGIN_MYSELF = "__me__";
private static final String UNKNOWN = "<UNKNOWN>";
+
+ private static final ComponentDto UNKNOWN_COMPONENT = new ComponentDto().setUuid(UNKNOWN).setProjectUuid(UNKNOWN);
+
private final DbClient dbClient;
private final System2 system;
private final UserSession userSession;
@@ -113,30 +118,17 @@ public class IssueQueryFactory {
.facetMode(request.getFacetMode())
.organizationUuid(convertOrganizationKeyToUuid(dbSession, request.getOrganization()));
- Set<String> allComponentUuids = new HashSet<>();
- boolean effectiveOnComponentOnly = mergeDeprecatedComponentParameters(dbSession,
- request.getOnComponentOnly(),
- request.getComponents(),
- request.getComponentUuids(),
- request.getComponentKeys(),
- request.getComponentRootUuids(),
- request.getComponentRoots(),
- allComponentUuids);
-
- addComponentParameters(builder, dbSession,
- effectiveOnComponentOnly,
- allComponentUuids,
- request);
-
- builder.createdAfter(buildCreatedAfterFromRequest(dbSession, request, allComponentUuids));
+ List<ComponentDto> allComponents = new ArrayList<>();
+ boolean effectiveOnComponentOnly = mergeDeprecatedComponentParameters(dbSession, request, allComponents);
+ addComponentParameters(builder, dbSession, effectiveOnComponentOnly, allComponents, request);
+ builder.createdAfter(buildCreatedAfterFromRequest(dbSession, request, allComponents));
String sort = request.getSort();
if (!Strings.isNullOrEmpty(sort)) {
builder.sort(sort);
builder.asc(request.getAsc());
}
return builder.build();
-
}
}
@@ -161,7 +153,7 @@ public class IssueQueryFactory {
return organization.map(OrganizationDto::getUuid).orElse(UNKNOWN);
}
- private Date buildCreatedAfterFromRequest(DbSession dbSession, SearchWsRequest request, Set<String> componentUuids) {
+ private Date buildCreatedAfterFromRequest(DbSession dbSession, SearchWsRequest request, List<ComponentDto> componentUuids) {
Date createdAfter = parseStartingDateOrDateTime(request.getCreatedAfter());
String createdInLast = request.getCreatedInLast();
@@ -170,16 +162,14 @@ public class IssueQueryFactory {
}
checkRequest(createdAfter == null, "Parameters '%s' and '%s' cannot be set simultaneously", PARAM_CREATED_AFTER, PARAM_SINCE_LEAK_PERIOD);
-
checkArgument(componentUuids.size() == 1, "One and only one component must be provided when searching since leak period");
- String uuid = componentUuids.iterator().next();
- Date createdAfterFromSnapshot = findCreatedAfterFromComponentUuid(dbSession, uuid);
+ ComponentDto component = componentUuids.iterator().next();
+ Date createdAfterFromSnapshot = findCreatedAfterFromComponentUuid(dbSession, component);
return buildCreatedAfterFromDates(createdAfterFromSnapshot, createdInLast);
}
@CheckForNull
- private Date findCreatedAfterFromComponentUuid(DbSession dbSession, String uuid) {
- ComponentDto component = checkFoundWithOptional(dbClient.componentDao().selectByUuid(dbSession, uuid), "Component with id '%s' not found", uuid);
+ private Date findCreatedAfterFromComponentUuid(DbSession dbSession, ComponentDto component) {
Optional<SnapshotDto> snapshot = dbClient.snapshotDao().selectLastAnalysisByComponentUuid(dbSession, component.uuid());
return snapshot.map(s -> longToDate(s.getPeriodDate())).orElse(null);
}
@@ -200,13 +190,15 @@ public class IssueQueryFactory {
return assignees;
}
- private boolean mergeDeprecatedComponentParameters(DbSession session, @Nullable Boolean onComponentOnly,
- @Nullable Collection<String> components,
- @Nullable Collection<String> componentUuids,
- @Nullable Collection<String> componentKeys,
- @Nullable Collection<String> componentRootUuids,
- @Nullable Collection<String> componentRoots,
- Set<String> allComponentUuids) {
+ private boolean mergeDeprecatedComponentParameters(DbSession session, SearchWsRequest request, List<ComponentDto> allComponents) {
+ Boolean onComponentOnly = request.getOnComponentOnly();
+ Collection<String> components = request.getComponents();
+ Collection<String> componentUuids = request.getComponentUuids();
+ Collection<String> componentKeys = request.getComponentKeys();
+ Collection<String> componentRootUuids = request.getComponentRootUuids();
+ Collection<String> componentRoots = request.getComponentRoots();
+ String branch = request.getBranch();
+
boolean effectiveOnComponentOnly = false;
checkArgument(atMostOneNonNullElement(components, componentUuids, componentKeys, componentRootUuids, componentRoots),
@@ -214,19 +206,22 @@ public class IssueQueryFactory {
PARAM_COMPONENT_KEYS, PARAM_COMPONENT_UUIDS, PARAM_COMPONENTS, PARAM_COMPONENT_ROOTS, PARAM_COMPONENT_UUIDS);
if (componentRootUuids != null) {
- allComponentUuids.addAll(componentRootUuids);
+ allComponents.addAll(getComponentsFromUuids(session, componentRootUuids));
+ effectiveOnComponentOnly = false;
} else if (componentRoots != null) {
- allComponentUuids.addAll(convertComponentKeysToUuids(session, componentRoots));
+ allComponents.addAll(getComponentsFromKeys(session, componentRoots, branch));
+ effectiveOnComponentOnly = false;
} else if (components != null) {
- allComponentUuids.addAll(convertComponentKeysToUuids(session, components));
+ allComponents.addAll(getComponentsFromKeys(session, components, branch));
effectiveOnComponentOnly = true;
} else if (componentUuids != null) {
- allComponentUuids.addAll(componentUuids);
+ allComponents.addAll(getComponentsFromUuids(session, componentUuids));
effectiveOnComponentOnly = BooleanUtils.isTrue(onComponentOnly);
} else if (componentKeys != null) {
- allComponentUuids.addAll(convertComponentKeysToUuids(session, componentKeys));
+ allComponents.addAll(getComponentsFromKeys(session, componentKeys, branch));
effectiveOnComponentOnly = BooleanUtils.isTrue(onComponentOnly);
}
+
return effectiveOnComponentOnly;
}
@@ -236,14 +231,14 @@ public class IssueQueryFactory {
.count() <= 1;
}
- private void addComponentParameters(IssueQuery.Builder builder, DbSession session,
- boolean onComponentOnly,
- Collection<String> componentUuids,
- SearchWsRequest request) {
+ 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(componentUuids);
+ builder.componentUuids(components.stream().map(ComponentDto::uuid).collect(toList()));
+ builder.branchUuid(branch == null ? null : components.get(0).projectUuid());
return;
}
@@ -254,64 +249,63 @@ public class IssueQueryFactory {
if (projectUuids != null) {
builder.projectUuids(projectUuids);
} else if (projectKeys != null) {
- builder.projectUuids(convertComponentKeysToUuids(session, projectKeys));
+ 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());
}
builder.moduleUuids(request.getModuleUuids());
builder.directories(request.getDirectories());
builder.fileUuids(request.getFileUuids());
- if (!componentUuids.isEmpty()) {
- addComponentsBasedOnQualifier(builder, session, componentUuids, request);
- }
+ addComponentsBasedOnQualifier(builder, session, components, request);
}
- private void addComponentsBasedOnQualifier(IssueQuery.Builder builder, DbSession dbSession, Collection<String> componentUuids, SearchWsRequest request) {
- if (componentUuids.isEmpty()) {
- builder.componentUuids(componentUuids);
+ private void addComponentsBasedOnQualifier(IssueQuery.Builder builder, DbSession dbSession, List<ComponentDto> components, SearchWsRequest request) {
+ if (components.isEmpty()) {
return;
}
-
- List<ComponentDto> components = dbClient.componentDao().selectByUuids(dbSession, componentUuids);
- if (components.isEmpty()) {
- builder.componentUuids(componentUuids);
+ if (components.stream().map(ComponentDto::uuid).anyMatch(uuid -> uuid.equals(UNKNOWN))) {
+ builder.componentUuids(singleton(UNKNOWN));
return;
}
- Set<String> qualifiers = components.stream().map(ComponentDto::qualifier).collect(MoreCollectors.toHashSet());
+ 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());
String qualifier = qualifiers.iterator().next();
switch (qualifier) {
case Qualifiers.VIEW:
case Qualifiers.SUBVIEW:
- addViewsOrSubViews(builder, componentUuids);
+ addViewsOrSubViews(builder, components);
break;
case Qualifiers.APP:
addApplications(builder, dbSession, components, request);
break;
case Qualifiers.PROJECT:
- builder.projectUuids(componentUuids);
+ builder.projectUuids(components.stream().map(c -> branch == null ? c.projectUuid() : c.getMainBranchProjectUuid()).collect(toList()));
break;
case Qualifiers.MODULE:
- builder.moduleRootUuids(componentUuids);
+ builder.moduleRootUuids(components.stream().map(ComponentDto::uuid).collect(toList()));
break;
case Qualifiers.DIRECTORY:
addDirectories(builder, components);
break;
case Qualifiers.FILE:
case Qualifiers.UNIT_TEST_FILE:
- builder.fileUuids(componentUuids);
+ builder.fileUuids(components.stream().map(ComponentDto::uuid).collect(toList()));
break;
default:
- throw new IllegalArgumentException("Unable to set search root context for components " + Joiner.on(',').join(componentUuids));
+ throw new IllegalArgumentException("Unable to set search root context for components " + Joiner.on(',').join(components));
}
}
- private void addViewsOrSubViews(IssueQuery.Builder builder, Collection<String> viewOrSubViewUuids) {
+ private void addViewsOrSubViews(IssueQuery.Builder builder, Collection<ComponentDto> viewOrSubViewUuids) {
List<String> filteredViewUuids = viewOrSubViewUuids.stream()
- .filter(uuid -> userSession.hasComponentUuidPermission(UserRole.USER, uuid))
+ .filter(uuid -> userSession.hasComponentPermission(UserRole.USER, uuid))
+ .map(ComponentDto::uuid)
.collect(Collectors.toList());
-
if (filteredViewUuids.isEmpty()) {
filteredViewUuids.add(UNKNOWN);
}
@@ -322,7 +316,7 @@ public class IssueQueryFactory {
Set<String> authorizedApplicationUuids = applications.stream()
.filter(app -> userSession.hasComponentPermission(UserRole.USER, app))
.map(ComponentDto::uuid)
- .collect(MoreCollectors.toSet());
+ .collect(toSet());
builder.viewUuids(authorizedApplicationUuids.isEmpty() ? singleton(UNKNOWN) : authorizedApplicationUuids);
addCreatedAfterByProjects(builder, dbSession, request, authorizedApplicationUuids);
@@ -335,7 +329,7 @@ public class IssueQueryFactory {
Set<String> projectUuids = applicationUuids.stream()
.flatMap(app -> dbClient.componentDao().selectProjectsFromView(dbSession, app, app).stream())
- .collect(MoreCollectors.toSet());
+ .collect(toSet());
Map<String, Date> leakByProjects = dbClient.snapshotDao().selectLastAnalysesByRootComponentUuids(dbSession, projectUuids)
.stream()
@@ -355,14 +349,22 @@ public class IssueQueryFactory {
builder.directories(directoryPaths);
}
- private Collection<String> convertComponentKeysToUuids(DbSession dbSession, Collection<String> componentKeys) {
- List<String> componentUuids = dbClient.componentDao().selectByKeys(dbSession, componentKeys).stream().map(ComponentDto::uuid).collect(MoreCollectors.toList());
- // If unknown components are given, but no components are found, then all issues will be returned,
- // so we add this hack in order to return no issue in this case.
- if (!componentKeys.isEmpty() && componentUuids.isEmpty()) {
- return singletonList(UNKNOWN);
+ private List<ComponentDto> getComponentsFromKeys(DbSession dbSession, Collection<String> componentKeys, @Nullable String branch) {
+ List<ComponentDto> componentDtos = branch == null
+ ? dbClient.componentDao().selectByKeys(dbSession, componentKeys)
+ : dbClient.componentDao().selectByKeysAndBranch(dbSession, componentKeys, branch);
+ if (!componentKeys.isEmpty() && componentDtos.isEmpty()) {
+ return singletonList(UNKNOWN_COMPONENT);
+ }
+ return componentDtos;
+ }
+
+ private List<ComponentDto> getComponentsFromUuids(DbSession dbSession, Collection<String> componentUuids) {
+ List<ComponentDto> componentDtos = dbClient.componentDao().selectByUuids(dbSession, componentUuids);
+ if (!componentUuids.isEmpty() && componentDtos.isEmpty()) {
+ return singletonList(UNKNOWN_COMPONENT);
}
- return componentUuids;
+ return componentDtos;
}
@VisibleForTesting
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
index b975c525d9d..5099362e291 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
@@ -76,8 +76,8 @@ import org.sonar.server.permission.index.AuthorizationTypeSupport;
import org.sonar.server.user.UserSession;
import org.sonar.server.view.index.ViewIndexDefinition;
-import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
@@ -205,12 +205,15 @@ public class IssueIndex {
QueryBuilder moduleFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, query.moduleUuids());
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)));
if (BooleanUtils.isTrue(query.onComponentOnly())) {
filters.put(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, componentFilter);
} else {
filters.put("__view", viewFilter);
filters.put(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, projectFilter);
+ filters.put(IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID, branchFilter);
filters.put("__module", moduleRootFilter);
filters.put(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, moduleFilter);
filters.put(IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, directoryFilter);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
index 098ef1720f3..de9e1ae8425 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
@@ -57,6 +57,7 @@ import static java.lang.String.format;
import static java.util.Collections.singletonList;
import static org.sonar.api.utils.Paging.forPageIndex;
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
+import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.ACTION_SEARCH;
@@ -71,6 +72,7 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASC;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASSIGNED;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASSIGNEES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_AUTHORS;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_BRANCH;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENTS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENT_KEYS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENT_ROOTS;
@@ -113,7 +115,7 @@ public class SearchAction implements IssuesWsAction {
private final SearchResponseFormat searchResponseFormat;
public SearchAction(UserSession userSession, IssueIndex issueIndex, IssueQueryFactory issueQueryFactory,
- SearchResponseLoader searchResponseLoader, SearchResponseFormat searchResponseFormat) {
+ SearchResponseLoader searchResponseLoader, SearchResponseFormat searchResponseFormat) {
this.userSession = userSession;
this.issueIndex = issueIndex;
this.issueQueryFactory = issueQueryFactory;
@@ -288,6 +290,12 @@ public class SearchAction implements IssuesWsAction {
.setInternal(true)
.setExampleValue("bdd82933-3070-4903-9188-7d8749e8bb92");
+ action.createParam(PARAM_BRANCH)
+ .setDescription("Branch key")
+ .setExampleValue(KEY_BRANCH_EXAMPLE_001)
+ .setInternal(true)
+ .setSince("6.6");
+
action.createParam(PARAM_ORGANIZATION)
.setDescription("Organization key")
.setRequired(false)
@@ -465,6 +473,7 @@ public class SearchAction implements IssuesWsAction {
.setLanguages(request.paramAsStrings(PARAM_LANGUAGES))
.setModuleUuids(request.paramAsStrings(PARAM_MODULE_UUIDS))
.setOnComponentOnly(request.paramAsBoolean(PARAM_ON_COMPONENT_ONLY))
+ .setBranch(request.param(PARAM_BRANCH))
.setOrganization(request.param(PARAM_ORGANIZATION))
.setPage(request.mandatoryParamAsInt(Param.PAGE))
.setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE))
diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectDataLoaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectDataLoaderTest.java
index a6cb622d013..4866510c08d 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectDataLoaderTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectDataLoaderTest.java
@@ -33,11 +33,13 @@ import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.component.ResourceTypesRule;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.property.PropertyDto;
import org.sonar.db.source.FileSourceDto;
import org.sonar.scanner.protocol.input.FileData;
import org.sonar.scanner.protocol.input.ProjectRepositories;
+import org.sonar.server.component.ComponentFinder;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
@@ -64,8 +66,8 @@ public class ProjectDataLoaderTest {
private DbClient dbClient = db.getDbClient();
private DbSession dbSession = db.getSession();
private int uuidCounter = 0;
-
- private ProjectDataLoader underTest = new ProjectDataLoader(dbClient, userSession);
+ private ResourceTypesRule resourceTypes = new ResourceTypesRule().setRootQualifiers(Qualifiers.PROJECT);
+ private ProjectDataLoader underTest = new ProjectDataLoader(dbClient, userSession, new ComponentFinder(dbClient, resourceTypes));
@Test
public void return_project_settings_with_global_scan_permission() {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueDocTesting.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueDocTesting.java
index 2dcc8de454f..809646fdfe5 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueDocTesting.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueDocTesting.java
@@ -36,6 +36,25 @@ import static org.sonar.api.issue.Issue.STATUS_OPEN;
public class IssueDocTesting {
+ public static IssueDoc newDoc(ComponentDto componentDto) {
+ return newDoc(Uuids.createFast(), componentDto);
+ }
+
+ public static IssueDoc newDoc(String key, ComponentDto componentDto) {
+ String mainBranchProjectUuid = componentDto.getMainBranchProjectUuid();
+ return newDoc()
+ .setKey(key)
+ .setBranchUuid(componentDto.projectUuid())
+ .setComponentUuid(componentDto.uuid())
+ .setModuleUuid(!componentDto.scope().equals(Scopes.PROJECT) ? componentDto.moduleUuid() : componentDto.uuid())
+ .setModuleUuidPath(componentDto.moduleUuidPath())
+ .setProjectUuid(mainBranchProjectUuid == null ? componentDto.projectUuid() : mainBranchProjectUuid)
+ .setOrganizationUuid(componentDto.getOrganizationUuid())
+ // File path make no sens on modules and projects
+ .setFilePath(!componentDto.scope().equals(Scopes.PROJECT) ? componentDto.path() : null)
+ .setIsMainBranch(mainBranchProjectUuid == null);
+ }
+
public static IssueDoc newDoc() {
IssueDoc doc = new IssueDoc(Maps.<String, Object>newHashMap());
doc.setKey(Uuids.createFast());
@@ -60,16 +79,4 @@ public class IssueDocTesting {
doc.setFuncCloseDate(null);
return doc;
}
-
- public static IssueDoc newDoc(String key, ComponentDto componentDto) {
- return newDoc()
- .setKey(key)
- .setComponentUuid(componentDto.uuid())
- .setModuleUuid(!componentDto.scope().equals(Scopes.PROJECT) ? componentDto.moduleUuid() : componentDto.uuid())
- .setModuleUuidPath(componentDto.moduleUuidPath())
- .setProjectUuid(componentDto.projectUuid())
- .setOrganizationUuid(componentDto.getOrganizationUuid())
- // File path make no sens on modules and projects
- .setFilePath(!componentDto.scope().equals(Scopes.PROJECT) ? componentDto.path() : null);
- }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryFactoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryFactoryTest.java
index 7b02648e8b9..01ad51d4888 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryFactoryTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryFactoryTest.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.issue;
+import java.util.ArrayList;
import java.util.Date;
import org.junit.Rule;
import org.junit.Test;
@@ -29,14 +30,12 @@ import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.System2;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentTesting;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.tester.UserSessionRule;
import org.sonarqube.ws.client.issue.SearchWsRequest;
-import static com.google.common.collect.Lists.newArrayList;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
@@ -45,7 +44,11 @@ import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.api.utils.DateUtils.addDays;
+import static org.sonar.db.component.ComponentTesting.newDirectory;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.db.component.ComponentTesting.newModuleDto;
import static org.sonar.db.component.ComponentTesting.newProjectCopy;
+import static org.sonar.db.component.ComponentTesting.newSubView;
public class IssueQueryFactoryTest {
@@ -63,8 +66,8 @@ public class IssueQueryFactoryTest {
public void create_from_parameters() {
OrganizationDto organization = db.organizations().insert();
ComponentDto project = db.components().insertPrivateProject(organization);
- ComponentDto module = db.components().insertComponent(ComponentTesting.newModuleDto(project));
- ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
+ ComponentDto module = db.components().insertComponent(newModuleDto(project));
+ ComponentDto file = db.components().insertComponent(newFileDto(project));
SearchWsRequest request = new SearchWsRequest()
.setIssues(asList("anIssueKey"))
@@ -134,6 +137,23 @@ public class IssueQueryFactoryTest {
}
@Test
+ public void query_without_any_parameter() {
+ SearchWsRequest request = new SearchWsRequest();
+
+ IssueQuery query = underTest.create(request);
+
+ assertThat(query.componentUuids()).isEmpty();
+ assertThat(query.projectUuids()).isEmpty();
+ assertThat(query.moduleUuids()).isEmpty();
+ assertThat(query.moduleRootUuids()).isEmpty();
+ assertThat(query.directories()).isEmpty();
+ assertThat(query.fileUuids()).isEmpty();
+ assertThat(query.viewUuids()).isEmpty();
+ assertThat(query.organizationUuid()).isNull();
+ assertThat(query.branchUuid()).isNull();
+ }
+
+ @Test
public void parse_list_of_rules() {
assertThat(IssueQueryFactory.toRules(null)).isNull();
assertThat(IssueQueryFactory.toRules("")).isEmpty();
@@ -181,7 +201,7 @@ public class IssueQueryFactoryTest {
@Test
public void fail_if_componentRoots_references_components_with_different_qualifier() {
ComponentDto project = db.components().insertPrivateProject();
- ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
+ ComponentDto file = db.components().insertComponent(newFileDto(project));
SearchWsRequest request = new SearchWsRequest()
.setComponentRoots(asList(project.getDbKey(), file.getDbKey()));
@@ -234,18 +254,17 @@ public class IssueQueryFactoryTest {
IssueQuery result = underTest.create(new SearchWsRequest()
.setComponentUuids(singletonList(application.uuid()))
- .setSinceLeakPeriod(true)
- );
+ .setSinceLeakPeriod(true));
assertThat(result.createdAfterByProjectUuids()).containsOnly(
- entry(project1.uuid(), new Date(analysis1.getPeriodDate())));
+ entry(project1.uuid(), new Date(analysis1.getPeriodDate())));
assertThat(result.viewUuids()).containsExactlyInAnyOrder(application.uuid());
}
@Test
public void return_empty_results_if_not_allowed_to_search_for_subview() {
ComponentDto view = db.components().insertView();
- ComponentDto subView = db.components().insertComponent(ComponentTesting.newSubView(view));
+ ComponentDto subView = db.components().insertComponent(newSubView(view));
SearchWsRequest request = new SearchWsRequest()
.setComponentRootUuids(asList(subView.uuid()));
@@ -282,7 +301,7 @@ public class IssueQueryFactoryTest {
@Test
public void should_search_in_tree_with_module_uuid() {
ComponentDto project = db.components().insertPrivateProject();
- ComponentDto module = db.components().insertComponent(ComponentTesting.newModuleDto(project));
+ ComponentDto module = db.components().insertComponent(newModuleDto(project));
SearchWsRequest request = new SearchWsRequest()
.setComponentUuids(asList(module.uuid()));
@@ -294,7 +313,7 @@ public class IssueQueryFactoryTest {
@Test
public void param_componentUuids_enables_search_in_directory_tree() {
ComponentDto project = db.components().insertPrivateProject();
- ComponentDto dir = db.components().insertComponent(ComponentTesting.newDirectory(project, "src/main/java/foo"));
+ ComponentDto dir = db.components().insertComponent(newDirectory(project, "src/main/java/foo"));
SearchWsRequest request = new SearchWsRequest()
.setComponentUuids(asList(dir.uuid()));
@@ -308,7 +327,7 @@ public class IssueQueryFactoryTest {
@Test
public void param_componentUuids_enables_search_by_file() {
ComponentDto project = db.components().insertPrivateProject();
- ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
+ ComponentDto file = db.components().insertComponent(newFileDto(project));
SearchWsRequest request = new SearchWsRequest()
.setComponentUuids(asList(file.uuid()));
@@ -320,7 +339,7 @@ public class IssueQueryFactoryTest {
@Test
public void param_componentUuids_enables_search_by_test_file() {
ComponentDto project = db.components().insertPrivateProject();
- ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project).setQualifier(Qualifiers.UNIT_TEST_FILE));
+ ComponentDto file = db.components().insertComponent(newFileDto(project).setQualifier(Qualifiers.UNIT_TEST_FILE));
SearchWsRequest request = new SearchWsRequest()
.setComponentUuids(asList(file.uuid()));
@@ -330,6 +349,65 @@ public class IssueQueryFactoryTest {
}
@Test
+ public void search_issue_from_branch() {
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto branch = db.components().insertProjectBranch(project);
+
+ 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()));
+
+ 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()));
+ }
+
+ @Test
+ public void search_file_issue_from_branch() {
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto branch = db.components().insertProjectBranch(project);
+ ComponentDto file = db.components().insertComponent(newFileDto(branch));
+
+ 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()));
+
+ 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()));
+
+ 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()));
+ }
+
+ @Test
+ public void search_issue_on_component_only_from_branch() {
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto branch = db.components().insertProjectBranch(project);
+ ComponentDto file = db.components().insertComponent(newFileDto(branch));
+
+ assertThat(underTest.create(new SearchWsRequest()
+ .setComponentKeys(singletonList(file.getKey()))
+ .setBranch(branch.getBranch())
+ .setOnComponentOnly(true)))
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.componentUuids()))
+ .containsOnly(branch.uuid(), singletonList(file.uuid()));
+ }
+
+ @Test
public void fail_if_created_after_and_created_since_are_both_set() {
SearchWsRequest request = new SearchWsRequest()
.setCreatedAfter("2013-07-25T07:35:00+0100")
@@ -372,12 +450,15 @@ public class IssueQueryFactoryTest {
@Test
public void fail_if_several_components_provided_with_since_leak_period() {
+ ComponentDto project1 = db.components().insertPrivateProject();
+ ComponentDto project2 = db.components().insertPrivateProject();
+
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("One and only one component must be provided when searching since leak period");
underTest.create(new SearchWsRequest()
.setSinceLeakPeriod(true)
- .setComponentUuids(newArrayList("foo", "bar")));
+ .setComponentKeys(asList(project1.getKey(), project2.getKey())));
}
@Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryTest.java
index a822662a268..480a72f01c4 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryTest.java
@@ -48,6 +48,8 @@ public class IssueQueryTest {
.languages(newArrayList("xoo"))
.tags(newArrayList("tag1", "tag2"))
.types(newArrayList("RELIABILITY", "SECURITY"))
+ .organizationUuid("orga")
+ .branchUuid("my_branch")
.createdAfterByProjectUuids(ImmutableMap.of("PROJECT", new Date(10_000_000_000L)))
.assigned(true)
.createdAfter(new Date())
@@ -68,6 +70,8 @@ public class IssueQueryTest {
assertThat(query.languages()).containsOnly("xoo");
assertThat(query.tags()).containsOnly("tag1", "tag2");
assertThat(query.types()).containsOnly("RELIABILITY", "SECURITY");
+ assertThat(query.organizationUuid()).isEqualTo("orga");
+ assertThat(query.branchUuid()).isEqualTo("my_branch");
assertThat(query.createdAfterByProjectUuids()).containsOnly(entry("PROJECT", new Date(10_000_000_000L)));
assertThat(query.assigned()).isTrue();
assertThat(query.rules()).containsOnly(RuleKey.of("squid", "AvoidCycle"));
@@ -151,6 +155,9 @@ public class IssueQueryTest {
assertThat(query.severities()).isEmpty();
assertThat(query.languages()).isEmpty();
assertThat(query.tags()).isEmpty();
+ assertThat(query.types()).isEmpty();
+ assertThat(query.organizationUuid()).isNull();
+ assertThat(query.branchUuid()).isNull();
assertThat(query.assigned()).isNull();
assertThat(query.createdAfter()).isNull();
assertThat(query.createdBefore()).isNull();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java
index b407a17cee9..c68a2aecfc5 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java
@@ -74,6 +74,7 @@ import static org.sonar.api.utils.DateUtils.addDays;
import static org.sonar.api.utils.DateUtils.parseDate;
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.db.component.ComponentTesting.newModuleDto;
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
import static org.sonar.db.user.GroupTesting.newGroupDto;
@@ -125,8 +126,8 @@ public class IssueIndexTest {
@Test
public void filter_by_projects() {
ComponentDto project = ComponentTesting.newPrivateProjectDto(newOrganizationDto());
- ComponentDto module = ComponentTesting.newModuleDto(project);
- ComponentDto subModule = ComponentTesting.newModuleDto(module);
+ ComponentDto module = newModuleDto(project);
+ ComponentDto subModule = newModuleDto(module);
indexIssues(
newDoc("I1", project),
@@ -157,8 +158,8 @@ public class IssueIndexTest {
@Test
public void filter_by_modules() {
ComponentDto project = ComponentTesting.newPrivateProjectDto(newOrganizationDto());
- ComponentDto module = ComponentTesting.newModuleDto(project);
- ComponentDto subModule = ComponentTesting.newModuleDto(module);
+ ComponentDto module = newModuleDto(project);
+ ComponentDto subModule = newModuleDto(module);
ComponentDto file = newFileDto(subModule, null);
indexIssues(
@@ -176,8 +177,8 @@ public class IssueIndexTest {
@Test
public void filter_by_components_on_contextualized_search() {
ComponentDto project = ComponentTesting.newPrivateProjectDto(newOrganizationDto());
- ComponentDto module = ComponentTesting.newModuleDto(project);
- ComponentDto subModule = ComponentTesting.newModuleDto(module);
+ ComponentDto module = newModuleDto(project);
+ ComponentDto subModule = newModuleDto(module);
ComponentDto file1 = newFileDto(project, null);
ComponentDto file2 = newFileDto(module, null);
ComponentDto file3 = newFileDto(subModule, null);
@@ -205,9 +206,9 @@ public class IssueIndexTest {
public void filter_by_components_on_non_contextualized_search() {
ComponentDto project = newPrivateProjectDto(newOrganizationDto(), "project");
ComponentDto file1 = newFileDto(project, null, "file1");
- ComponentDto module = ComponentTesting.newModuleDto(project).setUuid("module");
+ ComponentDto module = newModuleDto(project).setUuid("module");
ComponentDto file2 = newFileDto(module, null, "file2");
- ComponentDto subModule = ComponentTesting.newModuleDto(module).setUuid("subModule");
+ ComponentDto subModule = newModuleDto(module).setUuid("subModule");
ComponentDto file3 = newFileDto(subModule, null, "file3");
String view = "ABCD";
indexView(view, asList(project.uuid()));
@@ -318,39 +319,90 @@ public class IssueIndexTest {
Date now = new Date();
OrganizationDto organizationDto = newOrganizationDto();
ComponentDto project1 = newPrivateProjectDto(organizationDto);
- IssueDoc project1Issue1 = newDoc().setProjectUuid(project1.uuid()).setFuncCreationDate(addDays(now, -10));
- IssueDoc project1Issue2 = newDoc().setProjectUuid(project1.uuid()).setFuncCreationDate(addDays(now, -20));
+ IssueDoc project1Issue1 = newDoc(project1).setFuncCreationDate(addDays(now, -10));
+ IssueDoc project1Issue2 = newDoc(project1).setFuncCreationDate(addDays(now, -20));
ComponentDto project2 = newPrivateProjectDto(organizationDto);
- IssueDoc project2Issue1 = newDoc().setProjectUuid(project2.uuid()).setFuncCreationDate(addDays(now, -15));
- IssueDoc project2Issue2 = newDoc().setProjectUuid(project2.uuid()).setFuncCreationDate(addDays(now, -30));
+ IssueDoc project2Issue1 = newDoc(project2).setFuncCreationDate(addDays(now, -15));
+ IssueDoc project2Issue2 = newDoc(project2).setFuncCreationDate(addDays(now, -30));
indexIssues(project1Issue1, project1Issue2, project2Issue1, project2Issue2);
// Search for issues of project 1 having less than 15 days
assertThatSearchReturnsOnly(IssueQuery.builder()
- .createdAfterByProjectUuids(ImmutableMap.of(project1.uuid(), addDays(now, -15))),
+ .createdAfterByProjectUuids(ImmutableMap.of(project1.uuid(), addDays(now, -15))),
project1Issue1.key());
// Search for issues of project 1 having less than 14 days and project 2 having less then 25 days
assertThatSearchReturnsOnly(IssueQuery.builder()
.createdAfterByProjectUuids(ImmutableMap.of(
project1.uuid(), addDays(now, -14),
- project2.uuid(), addDays(now, -25)
- )),
+ project2.uuid(), addDays(now, -25))),
project1Issue1.key(), project2Issue1.key());
// Search for issues of project 1 having less than 30 days
assertThatSearchReturnsOnly(IssueQuery.builder()
- .createdAfterByProjectUuids(ImmutableMap.of(
- project1.uuid(), addDays(now, -30)
- )),
+ .createdAfterByProjectUuids(ImmutableMap.of(
+ project1.uuid(), addDays(now, -30))),
project1Issue1.key(), project1Issue2.key());
// Search for issues of project 1 and project 2 having less than 5 days
assertThatSearchReturnsOnly(IssueQuery.builder()
- .createdAfterByProjectUuids(ImmutableMap.of(
- project1.uuid(), addDays(now, -5),
- project2.uuid(), addDays(now, -5)
- )));
+ .createdAfterByProjectUuids(ImmutableMap.of(
+ project1.uuid(), addDays(now, -5),
+ project2.uuid(), addDays(now, -5))));
+ }
+
+ @Test
+ public void filter_one_issue_by_project_and_branch() {
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto branch = db.components().insertProjectBranch(project);
+ ComponentDto anotherbBranch = db.components().insertProjectBranch(project);
+
+ IssueDoc issueOnProject = newDoc(project);
+ IssueDoc issueOnBranch = newDoc(branch);
+ 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()),
+ issueOnBranch.key());
+ assertThatSearchReturnsEmpty(IssueQuery.builder().branchUuid("unknown"));
+ }
+
+ @Test
+ public void issues_from_branch_component_children() {
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto projectModule = db.components().insertComponent(newModuleDto(project));
+ ComponentDto projectFile = db.components().insertComponent(newFileDto(projectModule));
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
+ ComponentDto branchModule = db.components().insertComponent(newModuleDto(branch));
+ ComponentDto branchFile = db.components().insertComponent(newFileDto(branchModule));
+
+ indexIssues(
+ newDoc("I1", project),
+ newDoc("I2", projectFile),
+ newDoc("I3", projectModule),
+ newDoc("I4", branch),
+ 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"));
+ }
+
+ @Test
+ public void branch_issues_are_ignored_when_no_branch_param() {
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
+
+ IssueDoc projectIssue = newDoc(project);
+ IssueDoc branchIssue = newDoc(branch);
+ indexIssues(projectIssue, branchIssue);
+
+ assertThatSearchReturnsOnly(IssueQuery.builder(), projectIssue.key());
}
@Test
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 5d9e3245848..4379bbb3480 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
@@ -462,7 +462,7 @@ public class IssueIndexerTest {
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.projectUuid()).isEqualTo(branch.getMainBranchProjectUuid());
assertThat(doc.branchUuid()).isEqualTo(branch.uuid());
assertThat(doc.isMainBranch()).isFalse();
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java
index 3f004b84e48..1199319529a 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java
@@ -31,12 +31,14 @@ import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.Durations;
import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.server.es.EsTester;
import org.sonar.server.issue.ActionFinder;
import org.sonar.server.issue.IssueFieldsSetter;
@@ -56,10 +58,13 @@ import org.sonar.server.view.index.ViewIndexer;
import org.sonar.server.ws.WsActionTester;
import org.sonar.server.ws.WsResponseCommonFormat;
import org.sonarqube.ws.Issues.Component;
+import org.sonarqube.ws.Issues;
import org.sonarqube.ws.Issues.Issue;
import org.sonarqube.ws.Issues.SearchWsResponse;
import org.sonarqube.ws.client.issue.IssuesWsParameters;
+import static com.google.common.collect.Lists.newArrayList;
+import static java.util.Collections.emptySet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
import static org.sonar.api.utils.DateUtils.addDays;
@@ -72,6 +77,8 @@ import static org.sonar.db.component.ComponentTesting.newModuleDto;
import static org.sonar.db.component.ComponentTesting.newProjectCopy;
import static org.sonar.db.component.ComponentTesting.newSubView;
import static org.sonar.db.component.ComponentTesting.newView;
+import static org.sonar.db.issue.IssueTesting.newIssue;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BRANCH;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENT_KEYS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECT_KEYS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SINCE_LEAK_PERIOD;
@@ -101,6 +108,21 @@ public class SearchActionComponentsTest {
private WsActionTester ws = new WsActionTester(new SearchAction(userSession, issueIndex, issueQueryFactory, searchResponseLoader, searchResponseFormat));
@Test
+ public void search_all_issues_when_no_parameter() {
+ RuleDefinitionDto rule = db.rules().insert();
+ ComponentDto project = db.components().insertPublicProject();
+ ComponentDto projectFile = db.components().insertComponent(newFileDto(project));
+ IssueDto issue = db.issues().insertIssue(newIssue(rule, project, projectFile));
+ allowAnyoneOnProjects(project);
+ indexIssues();
+
+ SearchWsResponse result = ws.newRequest().executeProtobuf(SearchWsResponse.class);
+
+ assertThat(result.getIssuesList()).extracting(Issues.Issue::getKey)
+ .containsExactlyInAnyOrder(issue.getKey());
+ }
+
+ @Test
public void issues_on_different_projects() throws Exception {
RuleDefinitionDto rule = db.rules().insert(r -> r.setRuleKey(RuleKey.of("xoo", "x1")));
OrganizationDto organization1 = db.organizations().insert();
@@ -525,8 +547,9 @@ public class SearchActionComponentsTest {
ComponentDto application = db.components().insertApplication(db.getDefaultOrganization());
db.components().insertComponents(newProjectCopy("PC1", project1, application));
db.components().insertComponents(newProjectCopy("PC2", project2, application));
- IssueDto issue1 = db.issues().insertIssue(i -> i.setProject(project1));
- IssueDto issue2 = db.issues().insertIssue(i -> i.setProject(project2));
+ RuleDefinitionDto rule = db.rules().insert();
+ IssueDto issue1 = db.issues().insert(rule, project1, project1);
+ IssueDto issue2 = db.issues().insert(rule, project2, project2);
allowAnyoneOnProjects(project1, project2, application);
indexIssuesAndViews();
@@ -543,7 +566,8 @@ public class SearchActionComponentsTest {
ComponentDto project = db.components().insertPublicProject();
ComponentDto application = db.components().insertApplication(db.getDefaultOrganization());
db.components().insertComponents(newProjectCopy("PC1", project, application));
- db.issues().insertIssue(i -> i.setProject(project));
+ RuleDefinitionDto rule = db.rules().insert();
+ db.issues().insert(rule, project, project);
allowAnyoneOnProjects(project);
indexIssuesAndViews();
@@ -558,7 +582,8 @@ public class SearchActionComponentsTest {
public void search_application_without_projects() {
ComponentDto project = db.components().insertPublicProject();
ComponentDto application = db.components().insertApplication(db.getDefaultOrganization());
- db.issues().insertIssue(i -> i.setProject(project));
+ RuleDefinitionDto rule = db.rules().insert();
+ db.issues().insert(rule, project, project);
allowAnyoneOnProjects(project, application);
indexIssuesAndViews();
@@ -572,19 +597,20 @@ public class SearchActionComponentsTest {
@Test
public void search_by_application_and_by_leak() throws Exception {
Date now = new Date();
+ RuleDefinitionDto rule = db.rules().insert();
ComponentDto application = db.components().insertApplication(db.getDefaultOrganization());
// Project 1
ComponentDto project1 = db.components().insertPublicProject();
db.components().insertSnapshot(project1, s -> s.setPeriodDate(addDays(now, -14).getTime()));
db.components().insertComponents(newProjectCopy("PC1", project1, application));
- IssueDto project1Issue1 = db.issues().insertIssue(i -> i.setProject(project1).setIssueCreationDate(addDays(now, -10)));
- IssueDto project1Issue2 = db.issues().insertIssue(i -> i.setProject(project1).setIssueCreationDate(addDays(now, -20)));
+ IssueDto project1Issue1 = db.issues().insert(rule, project1, project1, i -> i.setIssueCreationDate(addDays(now, -10)));
+ IssueDto project1Issue2 = db.issues().insert(rule, project1, project1, i -> i.setIssueCreationDate(addDays(now, -20)));
// Project 2
ComponentDto project2 = db.components().insertPublicProject();
db.components().insertSnapshot(project2, s -> s.setPeriodDate(addDays(now, -25).getTime()));
db.components().insertComponents(newProjectCopy("PC2", project2, application));
- IssueDto project2Issue1 = db.issues().insertIssue(i -> i.setProject(project2).setIssueCreationDate(addDays(now, -15)));
- IssueDto project2Issue2 = db.issues().insertIssue(i -> i.setProject(project2).setIssueCreationDate(addDays(now, -30)));
+ IssueDto project2Issue1 = db.issues().insert(rule, project2, project2, i -> i.setIssueCreationDate(addDays(now, -15)));
+ IssueDto project2Issue2 = db.issues().insert(rule, project2, project2, i -> i.setIssueCreationDate(addDays(now, -30)));
// Permissions and index
allowAnyoneOnProjects(project1, project2, application);
indexIssuesAndViews();
@@ -606,8 +632,9 @@ public class SearchActionComponentsTest {
ComponentDto application = db.components().insertApplication(db.getDefaultOrganization());
db.components().insertComponents(newProjectCopy("PC1", project1, application));
db.components().insertComponents(newProjectCopy("PC2", project2, application));
- IssueDto issue1 = db.issues().insertIssue(i -> i.setProject(project1));
- IssueDto issue2 = db.issues().insertIssue(i -> i.setProject(project2));
+ RuleDefinitionDto rule = db.rules().insert();
+ IssueDto issue1 = db.issues().insert(rule, project1, project1);
+ IssueDto issue2 = db.issues().insert(rule, project2, project2);
allowAnyoneOnProjects(project1, project2, application);
indexIssuesAndViews();
@@ -624,19 +651,20 @@ public class SearchActionComponentsTest {
@Test
public void search_by_application_and_project_and_leak() throws Exception {
Date now = new Date();
+ RuleDefinitionDto rule = db.rules().insert();
ComponentDto application = db.components().insertApplication(db.getDefaultOrganization());
// Project 1
ComponentDto project1 = db.components().insertPublicProject();
db.components().insertSnapshot(project1, s -> s.setPeriodDate(addDays(now, -14).getTime()));
db.components().insertComponents(newProjectCopy("PC1", project1, application));
- IssueDto project1Issue1 = db.issues().insertIssue(i -> i.setProject(project1).setIssueCreationDate(addDays(now, -10)));
- IssueDto project1Issue2 = db.issues().insertIssue(i -> i.setProject(project1).setIssueCreationDate(addDays(now, -20)));
+ IssueDto project1Issue1 = db.issues().insert(rule, project1, project1, i -> i.setIssueCreationDate(addDays(now, -10)));
+ IssueDto project1Issue2 = db.issues().insert(rule, project1, project1, i -> i.setIssueCreationDate(addDays(now, -20)));
// Project 2
ComponentDto project2 = db.components().insertPublicProject();
db.components().insertSnapshot(project2, s -> s.setPeriodDate(addDays(now, -25).getTime()));
db.components().insertComponents(newProjectCopy("PC2", project2, application));
- IssueDto project2Issue1 = db.issues().insertIssue(i -> i.setProject(project2).setIssueCreationDate(addDays(now, -15)));
- IssueDto project2Issue2 = db.issues().insertIssue(i -> i.setProject(project2).setIssueCreationDate(addDays(now, -30)));
+ IssueDto project2Issue1 = db.issues().insert(rule, project2, project2, i -> i.setIssueCreationDate(addDays(now, -15)));
+ IssueDto project2Issue2 = db.issues().insert(rule, project2, project2, i -> i.setIssueCreationDate(addDays(now, -30)));
// Permissions and index
allowAnyoneOnProjects(project1, project2, application);
indexIssuesAndViews();
@@ -655,19 +683,20 @@ public class SearchActionComponentsTest {
@Test
public void search_by_application_and_by_leak_when_one_project_has_no_leak() throws Exception {
Date now = new Date();
+ RuleDefinitionDto rule = db.rules().insert();
ComponentDto application = db.components().insertApplication(db.getDefaultOrganization());
// Project 1
ComponentDto project1 = db.components().insertPublicProject();
db.components().insertSnapshot(project1, s -> s.setPeriodDate(addDays(now, -14).getTime()));
db.components().insertComponents(newProjectCopy("PC1", project1, application));
- IssueDto project1Issue1 = db.issues().insertIssue(i -> i.setProject(project1).setIssueCreationDate(addDays(now, -10)));
- IssueDto project1Issue2 = db.issues().insertIssue(i -> i.setProject(project1).setIssueCreationDate(addDays(now, -20)));
+ IssueDto project1Issue1 = db.issues().insert(rule, project1, project1, i -> i.setIssueCreationDate(addDays(now, -10)));
+ IssueDto project1Issue2 = db.issues().insert(rule, project1, project1, i -> i.setIssueCreationDate(addDays(now, -20)));
// Project 2, without leak => no issue form it should be returned
ComponentDto project2 = db.components().insertPublicProject();
db.components().insertSnapshot(project2, s -> s.setPeriodDate(null));
db.components().insertComponents(newProjectCopy("PC2", project2, application));
- IssueDto project2Issue1 = db.issues().insertIssue(i -> i.setProject(project2).setIssueCreationDate(addDays(now, -15)));
- IssueDto project2Issue2 = db.issues().insertIssue(i -> i.setProject(project2).setIssueCreationDate(addDays(now, -30)));
+ IssueDto project2Issue1 = db.issues().insert(rule, project2, project2, i -> i.setIssueCreationDate(addDays(now, -15)));
+ IssueDto project2Issue2 = db.issues().insert(rule, project2, project2, i -> i.setIssueCreationDate(addDays(now, -30)));
// Permissions and index
allowAnyoneOnProjects(project1, project2, application);
indexIssuesAndViews();
@@ -682,6 +711,48 @@ public class SearchActionComponentsTest {
.doesNotContain(project1Issue2.getKey(), project2Issue1.getKey(), project2Issue2.getKey());
}
+ @Test
+ public void search_by_branch() {
+ RuleDefinitionDto rule = db.rules().insert();
+ ComponentDto project = db.components().insertPrivateProject();
+ userSession.addProjectPermission(UserRole.USER, project);
+ ComponentDto projectFile = db.components().insertComponent(newFileDto(project));
+ IssueDto projectIssue = db.issues().insertIssue(newIssue(rule, project, projectFile));
+ ComponentDto branch = db.components().insertProjectBranch(project);
+ ComponentDto branchFile = db.components().insertComponent(newFileDto(branch));
+ IssueDto branchIssue = db.issues().insertIssue(newIssue(rule, branch, branchFile));
+ allowAnyoneOnProjects(project);
+ indexIssuesAndViews();
+
+ SearchWsResponse result = ws.newRequest()
+ .setParam(PARAM_COMPONENT_KEYS, branch.getKey())
+ .setParam(PARAM_BRANCH, branch.getBranch())
+ .executeProtobuf(SearchWsResponse.class);
+
+ assertThat(result.getIssuesList()).extracting(Issues.Issue::getKey)
+ .containsExactlyInAnyOrder(branchIssue.getKey())
+ .doesNotContain(projectIssue.getKey());
+ }
+
+ @Test
+ public void does_not_return_branch_issues_on_not_contextualized_search() {
+ RuleDefinitionDto rule = db.rules().insert();
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto projectFile = db.components().insertComponent(newFileDto(project));
+ IssueDto projectIssue = db.issues().insertIssue(newIssue(rule, project, projectFile));
+ ComponentDto branch = db.components().insertProjectBranch(project);
+ ComponentDto branchFile = db.components().insertComponent(newFileDto(branch));
+ IssueDto branchIssue = db.issues().insertIssue(newIssue(rule, branch, branchFile));
+ allowAnyoneOnProjects(project);
+ indexIssuesAndViews();
+
+ SearchWsResponse result = ws.newRequest().executeProtobuf(SearchWsResponse.class);
+
+ assertThat(result.getIssuesList()).extracting(Issues.Issue::getKey)
+ .containsExactlyInAnyOrder(projectIssue.getKey())
+ .doesNotContain(branchIssue.getKey());
+ }
+
private void allowAnyoneOnProjects(ComponentDto... projects) {
userSession.registerComponents(projects);
Arrays.stream(projects).forEach(p -> permissionIndexer.allowOnlyAnyone(p));
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
index f264318b94b..850c3099fb3 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
@@ -70,6 +70,7 @@ import org.sonar.server.ws.WsResponseCommonFormat;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BRANCH;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.DEPRECATED_FACET_MODE_DEBT;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.FACET_MODE_EFFORT;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ADDITIONAL_FIELDS;
@@ -128,13 +129,20 @@ public class SearchActionTest {
assertThat(def.responseExampleAsString()).isNotEmpty();
assertThat(def.params()).extracting("key").containsExactlyInAnyOrder(
- "additionalFields", "asc", "assigned", "assignees", "authors", "componentKeys", "componentRootUuids", "componentRoots", "componentUuids", "components",
+ "additionalFields", "asc", "assigned", "assignees", "authors", "componentKeys", "componentRootUuids", "componentRoots", "componentUuids", "components", "branch",
+ "organization",
"createdAfter", "createdAt", "createdBefore", "createdInLast", "directories", "facetMode", "facets", "fileUuids", "issues", "languages", "moduleUuids", "onComponentOnly",
- "organization", "p", "projectUuids", "projects", "ps", "resolutions", "resolved", "rules", "s", "severities", "sinceLeakPeriod",
+ "p", "projectUuids", "projects", "ps", "resolutions", "resolved", "rules", "s", "severities", "sinceLeakPeriod",
"statuses", "tags", "types");
+
assertThat(def.param("organization"))
.matches(WebService.Param::isInternal)
.matches(p -> p.since().equals("6.4"));
+
+ WebService.Param branch = def.param(PARAM_BRANCH);
+ assertThat(branch.isInternal()).isTrue();
+ assertThat(branch.isRequired()).isFalse();
+ assertThat(branch.since()).isEqualTo("6.6");
}
@Test
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java
index 20fe0eb7ff5..7dd45528e06 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java
@@ -69,6 +69,7 @@ public class IssuesWsParameters {
public static final String PARAM_DIRECTORIES = "directories";
public static final String PARAM_FILE_UUIDS = "fileUuids";
public static final String PARAM_ON_COMPONENT_ONLY = "onComponentOnly";
+ public static final String PARAM_BRANCH = "branch";
public static final String PARAM_ORGANIZATION = "organization";
public static final String PARAM_RULES = "rules";
public static final String PARAM_ACTIONS = "actions";
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/SearchWsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/SearchWsRequest.java
index a0011cffe92..427ea7f5845 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/SearchWsRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/SearchWsRequest.java
@@ -47,6 +47,7 @@ public class SearchWsRequest {
private List<String> languages;
private List<String> moduleUuids;
private Boolean onComponentOnly;
+ private String branch;
private String organization;
private Integer page;
private Integer pageSize;
@@ -442,4 +443,14 @@ public class SearchWsRequest {
this.projects = projects;
return this;
}
+
+ @CheckForNull
+ public String getBranch() {
+ return branch;
+ }
+
+ public SearchWsRequest setBranch(@Nullable String branch) {
+ this.branch = branch;
+ return this;
+ }
}