aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-webserver-es
diff options
context:
space:
mode:
authorlukasz-jarocki-sonarsource <lukasz.jarocki@sonarsource.com>2023-12-20 16:02:32 +0100
committersonartech <sonartech@sonarsource.com>2024-01-17 20:02:44 +0000
commit90af6b13ef8e0f62ba6492fd4cd730643f9c96ec (patch)
tree561728b9e3a3096c3c49e66ed42d1e7e0638f86c /server/sonar-webserver-es
parenta8d2df8b6546115dd219a28c913f035f63cdcfd0 (diff)
downloadsonarqube-90af6b13ef8e0f62ba6492fd4cd730643f9c96ec.tar.gz
sonarqube-90af6b13ef8e0f62ba6492fd4cd730643f9c96ec.zip
SONAR-21259 added new param to api/issues/search
Diffstat (limited to 'server/sonar-webserver-es')
-rw-r--r--server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java15
-rw-r--r--server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQuery.java6
-rw-r--r--server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java76
-rw-r--r--server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryFactoryTest.java58
-rw-r--r--server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryTest.java6
5 files changed, 146 insertions, 15 deletions
diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
index 677830a4ba0..df616a13ebc 100644
--- a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
+++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
@@ -465,7 +465,7 @@ public class IssueIndex {
}
// Field Filters
- filters.addFilter(FIELD_ISSUE_KEY, new SimpleFieldFilterScope(FIELD_ISSUE_KEY), createTermsFilter(FIELD_ISSUE_KEY, query.issueKeys()));
+ filters.addFilter(FIELD_ISSUE_KEY, new SimpleFieldFilterScope(FIELD_ISSUE_KEY), createTermsFilterForNullableCollection(FIELD_ISSUE_KEY, query.issueKeys()));
filters.addFilter(FIELD_ISSUE_ASSIGNEE_UUID, ASSIGNEES.getFilterScope(), createTermsFilter(FIELD_ISSUE_ASSIGNEE_UUID, query.assignees()));
filters.addFilter(FIELD_ISSUE_SCOPE, SCOPES.getFilterScope(), createTermsFilter(FIELD_ISSUE_SCOPE, query.scopes()));
filters.addFilter(FIELD_ISSUE_LANGUAGE, LANGUAGES.getFilterScope(), createTermsFilter(FIELD_ISSUE_LANGUAGE, query.languages()));
@@ -741,11 +741,24 @@ public class IssueIndex {
return FACET_MODE_EFFORT.equals(query.facetMode());
}
+ /**
+ * This method is for creating a filter that passes null to the elasticsearch query whenever empty or null collection is passed.
+ * This means that filter will not filter anything, all the documents (issues) will be returned in this case.
+ */
@CheckForNull
private static QueryBuilder createTermsFilter(String field, Collection<?> values) {
return values.isEmpty() ? null : termsQuery(field, values);
}
+ /**
+ * This method is for creating a filter that passes null to the elasticsearch query only when null collection is passed.
+ * This ensures that whenever we pass empty collection to the filter, it will filter out all the documents (issues).
+ */
+ @CheckForNull
+ private static QueryBuilder createTermsFilterForNullableCollection(String field, @Nullable Collection<?> values) {
+ return values != null ? termsQuery(field, values) : null;
+ }
+
@CheckForNull
private static QueryBuilder createTermFilter(String field, @Nullable String value) {
return value == null ? null : termQuery(field, value);
diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQuery.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQuery.java
index 0612edb8b1e..5da45db5877 100644
--- a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQuery.java
+++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQuery.java
@@ -105,7 +105,7 @@ public class IssueQuery {
private final Collection<String> cleanCodeAttributesCategories;
private IssueQuery(Builder builder) {
- this.issueKeys = defaultCollection(builder.issueKeys);
+ this.issueKeys = nullableDefaultCollection(builder.issueKeys);
this.severities = defaultCollection(builder.severities);
this.impactSeverities = defaultCollection(builder.impactSeverities);
this.impactSoftwareQualities = defaultCollection(builder.impactSoftwareQualities);
@@ -679,6 +679,10 @@ public class IssueQuery {
return c == null ? Collections.emptyList() : Collections.unmodifiableCollection(c);
}
+ private static <T> Collection<T> nullableDefaultCollection(@Nullable Collection<T> c) {
+ return c == null ? null : Collections.unmodifiableCollection(c);
+ }
+
private static <K, V> Map<K, V> defaultMap(@Nullable Map<K, V> map) {
return map == null ? Collections.emptyMap() : Collections.unmodifiableMap(map);
}
diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java
index 8f602db25f4..1222629845f 100644
--- a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java
+++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java
@@ -53,7 +53,9 @@ import org.sonar.db.DbSession;
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.issue.IssueFixedDto;
import org.sonar.db.permission.GlobalPermission;
+import org.sonar.db.project.ProjectDto;
import org.sonar.db.rule.RuleDto;
import org.sonar.server.issue.SearchRequest;
import org.sonar.server.issue.index.IssueQuery.PeriodStart;
@@ -79,6 +81,7 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENTS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENT_UUIDS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_AFTER;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_IN_LAST;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_FIXED_IN_PULL_REQUEST;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_IN_NEW_CODE_PERIOD;
/**
@@ -99,7 +102,8 @@ public class IssueQueryFactory {
.map(Enum::name)
.collect(Collectors.toSet());
private static final ComponentDto UNKNOWN_COMPONENT = new ComponentDto().setUuid(UNKNOWN).setBranchUuid(UNKNOWN);
- private static final Set<String> QUALIFIERS_WITHOUT_LEAK_PERIOD = new HashSet<>(Arrays.asList(Qualifiers.APP, Qualifiers.VIEW, Qualifiers.SUBVIEW));
+ private static final Set<String> QUALIFIERS_WITHOUT_LEAK_PERIOD = new HashSet<>(Arrays.asList(Qualifiers.APP, Qualifiers.VIEW,
+ Qualifiers.SUBVIEW));
private final DbClient dbClient;
private final Clock clock;
private final UserSession userSession;
@@ -116,13 +120,14 @@ public class IssueQueryFactory {
Collection<RuleDto> ruleDtos = ruleKeysToRuleId(dbSession, request.getRules());
Collection<String> ruleUuids = ruleDtos.stream().map(RuleDto::getUuid).collect(Collectors.toSet());
+ Collection<String> issueKeys = collectIssueKeys(dbSession, request);
if (request.getRules() != null && request.getRules().stream().collect(Collectors.toSet()).size() != ruleDtos.size()) {
ruleUuids.add("non-existing-uuid");
}
IssueQuery.Builder builder = IssueQuery.builder()
- .issueKeys(request.getIssues())
+ .issueKeys(issueKeys)
.severities(request.getSeverities())
.cleanCodeAttributesCategories(request.getCleanCodeAttributesCategories())
.impactSoftwareQualities(request.getImpactSoftwareQualities())
@@ -169,6 +174,49 @@ public class IssueQueryFactory {
}
}
+ private Collection<String> collectIssueKeys(DbSession dbSession, SearchRequest request) {
+ Collection<String> issueKeys = null;
+ if (request.getFixedInPullRequest() != null) {
+ issueKeys = getIssuesFixedByPullRequest(dbSession, request);
+ }
+ if (request.getIssues() != null && !request.getIssues().isEmpty()) {
+ if (issueKeys == null) {
+ issueKeys = new ArrayList<>();
+ }
+ issueKeys.addAll(request.getIssues());
+ }
+
+ return issueKeys;
+ }
+
+ private Collection<String> getIssuesFixedByPullRequest(DbSession dbSession, SearchRequest request) {
+ String fixedInPullRequest = request.getFixedInPullRequest();
+ List<String> componentKeys = request.getComponentKeys();
+ if (componentKeys == null || componentKeys.size() != 1) {
+ throw new IllegalArgumentException("Exactly one project needs to be provided in the " +
+ "'" + PARAM_COMPONENTS + "' param when used together with '" + PARAM_FIXED_IN_PULL_REQUEST + "' param");
+ }
+ String projectKey = componentKeys.get(0);
+ ProjectDto projectDto = dbClient.projectDao().selectProjectByKey(dbSession, projectKey)
+ .orElseThrow(() -> new IllegalArgumentException("Project with key '" + projectKey + "' does not exist"));
+ BranchDto pullRequest = dbClient.branchDao().selectByPullRequestKey(dbSession, projectDto.getUuid(), fixedInPullRequest)
+ .orElseThrow(() -> new IllegalArgumentException("Pull request with key '" + fixedInPullRequest + "' does not exist for a project " +
+ projectKey));
+
+ if (request.getBranch() != null) {
+ BranchDto targetBranch = dbClient.branchDao().selectByBranchKey(dbSession, projectDto.getUuid(), request.getBranch())
+ .orElseThrow(() -> new IllegalArgumentException("Branch with key '" + request.getBranch() + "' does not exist"));
+ if (!Objects.equals(targetBranch.getUuid(), pullRequest.getMergeBranchUuid())) {
+ throw new IllegalArgumentException("Pull request with key '" + fixedInPullRequest + "' does not target branch '" + request.getBranch() + "'");
+ }
+ }
+ return dbClient.issueFixedDao().selectByPullRequest(dbSession, pullRequest.getUuid())
+ .stream()
+ .map(IssueFixedDto::issueKey)
+ .collect(Collectors.toSet());
+ }
+
+
private static Optional<ZoneId> parseTimeZone(@Nullable String timeZone) {
if (timeZone == null) {
return Optional.empty();
@@ -181,7 +229,8 @@ public class IssueQueryFactory {
}
}
- private void setCreatedAfterFromDates(IssueQuery.Builder builder, @Nullable Date createdAfter, @Nullable String createdInLast, boolean createdAfterInclusive) {
+ private void setCreatedAfterFromDates(IssueQuery.Builder builder, @Nullable Date createdAfter, @Nullable String createdInLast,
+ boolean createdAfterInclusive) {
Date actualCreatedAfter = createdAfter;
if (createdInLast != null) {
actualCreatedAfter = Date.from(
@@ -192,16 +241,19 @@ public class IssueQueryFactory {
builder.createdAfter(actualCreatedAfter, createdAfterInclusive);
}
- private void setCreatedAfterFromRequest(DbSession dbSession, IssueQuery.Builder builder, SearchRequest request, List<ComponentDto> componentUuids, ZoneId timeZone) {
+ private void setCreatedAfterFromRequest(DbSession dbSession, IssueQuery.Builder builder, SearchRequest request,
+ List<ComponentDto> componentUuids, ZoneId timeZone) {
Date createdAfter = parseStartingDateOrDateTime(request.getCreatedAfter(), timeZone);
String createdInLast = request.getCreatedInLast();
if (notInNewCodePeriod(request)) {
- checkArgument(createdAfter == null || createdInLast == null, format("Parameters %s and %s cannot be set simultaneously", PARAM_CREATED_AFTER, PARAM_CREATED_IN_LAST));
+ checkArgument(createdAfter == null || createdInLast == null, format("Parameters %s and %s cannot be set simultaneously",
+ PARAM_CREATED_AFTER, PARAM_CREATED_IN_LAST));
setCreatedAfterFromDates(builder, createdAfter, createdInLast, true);
} else {
// If the filter is on leak period
- checkArgument(createdAfter == null, "Parameters '%s' and '%s' cannot be set simultaneously", PARAM_CREATED_AFTER, PARAM_IN_NEW_CODE_PERIOD);
+ checkArgument(createdAfter == null, "Parameters '%s' and '%s' cannot be set simultaneously", PARAM_CREATED_AFTER,
+ PARAM_IN_NEW_CODE_PERIOD);
checkArgument(createdInLast == null,
format("Parameters '%s' and '%s' cannot be set simultaneously", PARAM_CREATED_IN_LAST, PARAM_IN_NEW_CODE_PERIOD));
@@ -306,7 +358,8 @@ public class IssueQueryFactory {
.collect(Collectors.toSet());
}
- private void addComponentsBasedOnQualifier(IssueQuery.Builder builder, DbSession dbSession, List<ComponentDto> components, SearchRequest request) {
+ private void addComponentsBasedOnQualifier(IssueQuery.Builder builder, DbSession dbSession, List<ComponentDto> components,
+ SearchRequest request) {
if (components.isEmpty()) {
return;
}
@@ -369,7 +422,8 @@ public class IssueQueryFactory {
builder.viewUuids(filteredViewUuids);
}
- private void addApplications(IssueQuery.Builder builder, DbSession dbSession, List<ComponentDto> appBranchComponents, SearchRequest request) {
+ private void addApplications(IssueQuery.Builder builder, DbSession dbSession, List<ComponentDto> appBranchComponents,
+ SearchRequest request) {
Set<String> authorizedAppBranchUuids = appBranchComponents.stream()
.filter(app -> userSession.hasComponentPermission(USER, app) && userSession.hasChildProjectsPermission(USER, app))
.map(ComponentDto::uuid)
@@ -379,7 +433,8 @@ public class IssueQueryFactory {
addCreatedAfterByProjects(builder, dbSession, request, authorizedAppBranchUuids);
}
- private void addCreatedAfterByProjects(IssueQuery.Builder builder, DbSession dbSession, SearchRequest request, Set<String> appBranchUuids) {
+ private void addCreatedAfterByProjects(IssueQuery.Builder builder, DbSession dbSession, SearchRequest request,
+ Set<String> appBranchUuids) {
if (notInNewCodePeriod(request) || request.getPullRequest() != null) {
return;
}
@@ -415,7 +470,8 @@ public class IssueQueryFactory {
builder.directories(paths);
}
- private List<ComponentDto> getComponentsFromKeys(DbSession dbSession, Collection<String> componentKeys, @Nullable String branch, @Nullable String pullRequest) {
+ private List<ComponentDto> getComponentsFromKeys(DbSession dbSession, Collection<String> componentKeys, @Nullable String branch,
+ @Nullable String pullRequest) {
List<ComponentDto> componentDtos = dbClient.componentDao().selectByKeys(dbSession, componentKeys, branch, pullRequest);
if (!componentKeys.isEmpty() && componentDtos.isEmpty()) {
return singletonList(UNKNOWN_COMPONENT);
diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryFactoryTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryFactoryTest.java
index f84916093e5..2d98b68b1fb 100644
--- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryFactoryTest.java
+++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryFactoryTest.java
@@ -136,6 +136,64 @@ public class IssueQueryFactoryTest {
}
@Test
+ public void getIssuesFixedByPullRequest_returnIssuesFixedByThePullRequest() {
+ String ruleAdHocName = "New Name";
+ UserDto user = db.users().insertUser(u -> u.setLogin("joanna"));
+ ProjectData projectData = db.components().insertPrivateProject();
+ ComponentDto project = projectData.getMainBranchComponent();
+ ComponentDto file = db.components().insertComponent(newFileDto(project));
+
+ RuleDto rule1 = ruleDbTester.insert(r -> r.setAdHocName(ruleAdHocName));
+ RuleDto rule2 = ruleDbTester.insert(r -> r.setAdHocName(ruleAdHocName));
+ newRule(RuleKey.of("findbugs", "NullReference"));
+ SearchRequest request = new SearchRequest()
+ .setIssues(asList("anIssueKey"))
+ .setSeverities(asList("MAJOR", "MINOR"))
+ .setStatuses(asList("CLOSED"))
+ .setResolutions(asList("FALSE-POSITIVE"))
+ .setResolved(true)
+ .setProjectKeys(asList(project.getKey()))
+ .setDirectories(asList("aDirPath"))
+ .setFiles(asList(file.uuid()))
+ .setAssigneesUuid(asList(user.getUuid()))
+ .setScopes(asList("MAIN", "TEST"))
+ .setLanguages(asList("xoo"))
+ .setTags(asList("tag1", "tag2"))
+ .setAssigned(true)
+ .setCreatedAfter("2013-04-16T09:08:24+0200")
+ .setCreatedBefore("2013-04-17T09:08:24+0200")
+ .setRules(asList(rule1.getKey().toString(), rule2.getKey().toString()))
+ .setSort("CREATION_DATE")
+ .setAsc(true)
+ .setCodeVariants(asList("variant1", "variant2"));
+
+ IssueQuery query = underTest.create(request);
+
+ assertThat(query.issueKeys()).containsOnly("anIssueKey");
+ assertThat(query.severities()).containsOnly("MAJOR", "MINOR");
+ assertThat(query.statuses()).containsOnly("CLOSED");
+ assertThat(query.resolutions()).containsOnly("FALSE-POSITIVE");
+ assertThat(query.resolved()).isTrue();
+ assertThat(query.projectUuids()).containsOnly(projectData.projectUuid());
+ assertThat(query.files()).containsOnly(file.uuid());
+ assertThat(query.assignees()).containsOnly(user.getUuid());
+ assertThat(query.scopes()).containsOnly("TEST", "MAIN");
+ assertThat(query.languages()).containsOnly("xoo");
+ assertThat(query.tags()).containsOnly("tag1", "tag2");
+ assertThat(query.onComponentOnly()).isFalse();
+ assertThat(query.assigned()).isTrue();
+ assertThat(query.rules()).hasSize(2);
+ assertThat(query.ruleUuids()).hasSize(2);
+ assertThat(query.directories()).containsOnly("aDirPath");
+ assertThat(query.createdAfter().date()).isEqualTo(parseDateTime("2013-04-16T09:08:24+0200"));
+ assertThat(query.createdAfter().inclusive()).isTrue();
+ assertThat(query.createdBefore()).isEqualTo(parseDateTime("2013-04-17T09:08:24+0200"));
+ assertThat(query.sort()).isEqualTo(IssueQuery.SORT_BY_CREATION_DATE);
+ assertThat(query.asc()).isTrue();
+ assertThat(query.codeVariants()).containsOnly("variant1", "variant2");
+ }
+
+ @Test
public void create_with_rule_key_that_does_not_exist_in_the_db() {
db.users().insertUser(u -> u.setLogin("joanna"));
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryTest.java
index fd44cf728a3..d50061f65f8 100644
--- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryTest.java
+++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryTest.java
@@ -153,7 +153,7 @@ public class IssueQueryTest {
}
@Test
- public void collection_params_should_not_be_null_but_empty() {
+ public void collection_params_should_not_be_null_but_empty_except_issue_keys() {
IssueQuery query = IssueQuery.builder()
.issueKeys(null)
.projectUuids(null)
@@ -171,7 +171,7 @@ public class IssueQueryTest {
.cwe(null)
.createdAfterByProjectUuids(null)
.build();
- assertThat(query.issueKeys()).isEmpty();
+ assertThat(query.issueKeys()).isNull();
assertThat(query.projectUuids()).isEmpty();
assertThat(query.componentUuids()).isEmpty();
assertThat(query.statuses()).isEmpty();
@@ -191,7 +191,6 @@ public class IssueQueryTest {
@Test
public void test_default_query() {
IssueQuery query = IssueQuery.builder().build();
- assertThat(query.issueKeys()).isEmpty();
assertThat(query.projectUuids()).isEmpty();
assertThat(query.componentUuids()).isEmpty();
assertThat(query.statuses()).isEmpty();
@@ -202,6 +201,7 @@ public class IssueQueryTest {
assertThat(query.languages()).isEmpty();
assertThat(query.tags()).isEmpty();
assertThat(query.types()).isEmpty();
+ assertThat(query.issueKeys()).isNull();
assertThat(query.branchUuid()).isNull();
assertThat(query.assigned()).isNull();
assertThat(query.createdAfter()).isNull();