private List<String> facets;
private List<String> fileUuids;
private List<String> issues;
+ private Set<String> scopes;
private List<String> languages;
private List<String> moduleUuids;
private Boolean onComponentOnly;
private List<String> sonarsourceSecurity;
private List<String> cwe;
+ public SearchRequest() {
+ // nothing to do here
+ }
+
@CheckForNull
public List<String> getActionPlans() {
return actionPlans;
return this;
}
+ @CheckForNull
+ public Set<String> getScopes() {
+ return scopes;
+ }
+
+ public SearchRequest setScopes(@Nullable Collection<String> scopes) {
+ this.scopes = scopes == null ? null : ImmutableSet.copyOf(scopes);
+ return this;
+ }
+
@CheckForNull
public List<String> getLanguages() {
return languages;
return getField(IssueIndexDefinition.FIELD_ISSUE_RULE_UUID);
}
+ public IssueScope scope() {
+ return IssueScope.valueOf(getField(IssueIndexDefinition.FIELD_ISSUE_SCOPE));
+ }
+
public String language() {
return getField(IssueIndexDefinition.FIELD_ISSUE_LANGUAGE);
}
return this;
}
+ public IssueDoc setScope(IssueScope s) {
+ setField(IssueIndexDefinition.FIELD_ISSUE_SCOPE, s.toString());
+ return this;
+ }
+
public IssueDoc setLanguage(@Nullable String s) {
setField(IssueIndexDefinition.FIELD_ISSUE_LANGUAGE, s);
return this;
*/
public static final String FIELD_ISSUE_FUNC_CLOSED_AT = "issueClosedAt";
public static final String FIELD_ISSUE_KEY = "key";
+ public static final String FIELD_ISSUE_SCOPE = "scope";
public static final String FIELD_ISSUE_LANGUAGE = "language";
public static final String FIELD_ISSUE_LINE = "line";
public static final String FIELD_ISSUE_MODULE_UUID = "module";
mapping.createDateTimeField(FIELD_ISSUE_FUNC_UPDATED_AT);
mapping.createDateTimeField(FIELD_ISSUE_FUNC_CLOSED_AT);
mapping.keywordFieldBuilder(FIELD_ISSUE_KEY).disableNorms().addSubFields(SORTABLE_ANALYZER).build();
+ mapping.keywordFieldBuilder(FIELD_ISSUE_SCOPE).disableNorms().build();
mapping.keywordFieldBuilder(FIELD_ISSUE_LANGUAGE).disableNorms().build();
mapping.createIntegerField(FIELD_ISSUE_LINE);
mapping.keywordFieldBuilder(FIELD_ISSUE_MODULE_UUID).disableNorms().build();
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
+import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Scopes;
import org.sonar.api.rules.RuleType;
import org.sonar.db.DatabaseUtils;
// column 21
"i.tags",
"i.issue_type",
- "r.security_standards"
+ "r.security_standards",
+ "c.qualifier"
};
private static final String SQL_ALL = "select " + StringUtils.join(FIELDS, ",") + " from issues i " +
doc.setSansTop25(securityStandards.getSansTop25());
doc.setSonarSourceSecurityCategory(sqCategory);
doc.setVulnerabilityProbability(sqCategory.getVulnerability());
+
+ doc.setScope(Qualifiers.UNIT_TEST_FILE.equals(rs.getString(24)) ? IssueScope.TEST : IssueScope.MAIN);
return doc;
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.issue.index;
+
+/**
+ * @since 8.5
+ */
+public enum IssueScope {
+ MAIN, TEST
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.issue;
+
+import org.junit.Test;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class SearchRequestTest {
+
+ @Test
+ public void settersAndGetters() {
+ SearchRequest underTest = new SearchRequest()
+ .setIssues(singletonList("anIssueKey"))
+ .setSeverities(asList("MAJOR", "MINOR"))
+ .setStatuses(singletonList("CLOSED"))
+ .setResolutions(singletonList("FALSE-POSITIVE"))
+ .setResolved(true)
+ .setProjects(singletonList("project-a"))
+ .setModuleUuids(singletonList("module-a"))
+ .setDirectories(singletonList("aDirPath"))
+ .setFileUuids(asList("file-a", "file-b"))
+ .setAssigneesUuid(asList("user-a", "user-b"))
+ .setScopes(asList("MAIN", "TEST"))
+ .setLanguages(singletonList("xoo"))
+ .setTags(asList("tag1", "tag2"))
+ .setOrganization("org-a")
+ .setAssigned(true)
+ .setCreatedAfter("2013-04-16T09:08:24+0200")
+ .setCreatedBefore("2013-04-17T09:08:24+0200")
+ .setRules(asList("key-a", "key-b"))
+ .setSort("CREATION_DATE")
+ .setAsc(true);
+
+ assertThat(underTest.getIssues()).containsOnlyOnce("anIssueKey");
+ assertThat(underTest.getSeverities()).containsExactly("MAJOR", "MINOR");
+ assertThat(underTest.getStatuses()).containsExactly("CLOSED");
+ assertThat(underTest.getResolutions()).containsExactly("FALSE-POSITIVE");
+ assertThat(underTest.getResolved()).isTrue();
+ assertThat(underTest.getProjects()).containsExactly("project-a");
+ assertThat(underTest.getModuleUuids()).containsExactly("module-a");
+ assertThat(underTest.getDirectories()).containsExactly("aDirPath");
+ assertThat(underTest.getFileUuids()).containsExactly("file-a", "file-b");
+ assertThat(underTest.getAssigneeUuids()).containsExactly("user-a", "user-b");
+ assertThat(underTest.getScopes()).containsExactly("MAIN", "TEST");
+ assertThat(underTest.getLanguages()).containsExactly("xoo");
+ assertThat(underTest.getTags()).containsExactly("tag1", "tag2");
+ assertThat(underTest.getOrganization()).isEqualTo("org-a");
+ assertThat(underTest.getAssigned()).isTrue();
+ assertThat(underTest.getCreatedAfter()).isEqualTo("2013-04-16T09:08:24+0200");
+ assertThat(underTest.getCreatedBefore()).isEqualTo("2013-04-17T09:08:24+0200");
+ assertThat(underTest.getRules()).containsExactly("key-a", "key-b");
+ assertThat(underTest.getSort()).isEqualTo("CREATION_DATE");
+ assertThat(underTest.getAsc()).isTrue();
+ }
+
+ @Test
+ public void setScopesAcceptsNull() {
+ SearchRequest underTest = new SearchRequest().setScopes(null);
+
+ assertThat(underTest.getScopes()).isNull();
+ }
+}
assertThat(doc.creationDate()).isEqualToIgnoringMillis(issue.getIssueCreationDate());
assertThat(doc.directoryPath()).isEqualTo(dir.path());
assertThat(doc.filePath()).isEqualTo(file.path());
+ assertThat(doc.scope()).isEqualTo(IssueScope.MAIN);
assertThat(doc.language()).isEqualTo(issue.getLanguage());
assertThat(doc.line()).isEqualTo(issue.getLine());
// functional date
assertThat(doc.projectUuid()).isEqualTo(branch.getMainBranchProjectUuid());
assertThat(doc.branchUuid()).isEqualTo(branch.uuid());
assertThat(doc.isMainBranch()).isFalse();
+ assertThat(doc.scope()).isEqualTo(IssueScope.MAIN);
}
@Test
- public void getType(){
+ public void issue_on_test_file_has_test_scope() {
+ RuleDefinitionDto rule = db.rules().insert();
+ ComponentDto project = db.components().insertPrivateProject(organization);
+ ComponentDto dir = db.components().insertComponent(ComponentTesting.newDirectory(project, "src/main/java/foo"));
+ ComponentDto file = db.components().insertComponent(newFileDto(project, dir, "F1").setQualifier("UTS"));
+ IssueDto issue = db.issues().insert(rule, project, file);
+
+ underTest.indexAllIssues();
+
+ IssueDoc doc = es.getDocuments(TYPE_ISSUE, IssueDoc.class).get(0);
+ assertThat(doc.getId()).isEqualTo(issue.getKey());
+ assertThat(doc.organizationUuid()).isEqualTo(organization.getUuid());
+ assertThat(doc.componentUuid()).isEqualTo(file.uuid());
+ assertThat(doc.scope()).isEqualTo(IssueScope.TEST);
+ }
+
+ @Test
+ public void issue_on_directory_has_main_code_scope() {
+ RuleDefinitionDto rule = db.rules().insert();
+ ComponentDto project = db.components().insertPrivateProject(organization);
+ ComponentDto dir = db.components().insertComponent(ComponentTesting.newDirectory(project, "src/main/java/foo"));
+ IssueDto issue = db.issues().insert(rule, project, dir);
+
+ underTest.indexAllIssues();
+
+ IssueDoc doc = es.getDocuments(TYPE_ISSUE, IssueDoc.class).get(0);
+ assertThat(doc.getId()).isEqualTo(issue.getKey());
+ assertThat(doc.organizationUuid()).isEqualTo(organization.getUuid());
+ assertThat(doc.componentUuid()).isEqualTo(dir.uuid());
+ assertThat(doc.scope()).isEqualTo(IssueScope.MAIN);
+ }
+
+ @Test
+ public void issue_on_project_has_main_code_scope() {
+ RuleDefinitionDto rule = db.rules().insert();
+ ComponentDto project = db.components().insertPrivateProject(organization);
+ IssueDto issue = db.issues().insert(rule, project, project);
+
+ underTest.indexAllIssues();
+
+ IssueDoc doc = es.getDocuments(TYPE_ISSUE, IssueDoc.class).get(0);
+ assertThat(doc.getId()).isEqualTo(issue.getKey());
+ assertThat(doc.organizationUuid()).isEqualTo(organization.getUuid());
+ assertThat(doc.componentUuid()).isEqualTo(project.uuid());
+ assertThat(doc.scope()).isEqualTo(IssueScope.MAIN);
+ }
+
+ @Test
+ public void getType() {
Assertions.assertThat(underTest.getType()).isEqualTo(StartupIndexer.Type.ASYNCHRONOUS);
}
import org.sonar.core.util.Uuids;
import org.sonar.db.component.ComponentDto;
import org.sonar.server.issue.index.IssueDoc;
+import org.sonar.server.issue.index.IssueScope;
import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
import static org.apache.commons.lang.math.RandomUtils.nextInt;
doc.setType(RuleType.CODE_SMELL);
doc.setAssigneeUuid("assignee_uuid_" + randomAlphabetic(26));
doc.setAuthorLogin("author_" + randomAlphabetic(5));
+ doc.setScope(IssueScope.MAIN);
doc.setLanguage("language_" + randomAlphabetic(5));
doc.setComponentUuid(Uuids.createFast());
doc.setFilePath("filePath_" + randomAlphabetic(5));
import static org.sonar.server.issue.index.IssueIndex.Facet.RESOLUTIONS;
import static org.sonar.server.issue.index.IssueIndex.Facet.RULES;
import static org.sonar.server.issue.index.IssueIndex.Facet.SANS_TOP_25;
+import static org.sonar.server.issue.index.IssueIndex.Facet.SCOPES;
import static org.sonar.server.issue.index.IssueIndex.Facet.SEVERITIES;
import static org.sonar.server.issue.index.IssueIndex.Facet.SONARSOURCE_SECURITY;
import static org.sonar.server.issue.index.IssueIndex.Facet.STATUSES;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_RESOLUTION;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_RULE_UUID;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SANS_TOP_25;
+import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SCOPE;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SEVERITY;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SEVERITY_VALUE;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SQ_SECURITY_CATEGORY;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RESOLUTIONS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RULES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SANS_TOP_25;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SCOPES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SEVERITIES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SONARSOURCE_SECURITY;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_STATUSES;
// Resolutions facet returns one more element than the number of resolutions to take into account unresolved issues
RESOLUTIONS(PARAM_RESOLUTIONS, FIELD_ISSUE_RESOLUTION, STICKY, Issue.RESOLUTIONS.size() + 1),
TYPES(PARAM_TYPES, FIELD_ISSUE_TYPE, STICKY, RuleType.values().length),
+ SCOPES(PARAM_SCOPES, FIELD_ISSUE_SCOPE, STICKY, MAX_FACET_SIZE),
LANGUAGES(PARAM_LANGUAGES, FIELD_ISSUE_LANGUAGE, STICKY, MAX_FACET_SIZE),
RULES(PARAM_RULES, FIELD_ISSUE_RULE_UUID, STICKY, MAX_FACET_SIZE),
TAGS(PARAM_TAGS, FIELD_ISSUE_TAGS, STICKY, MAX_FACET_SIZE),
// Field Filters
filters.addFilter(FIELD_ISSUE_KEY, new SimpleFieldFilterScope(FIELD_ISSUE_KEY), createTermsFilter(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()));
filters.addFilter(FIELD_ISSUE_TAGS, TAGS.getFilterScope(), createTermsFilter(FIELD_ISSUE_TAGS, query.tags()));
filters.addFilter(FIELD_ISSUE_TYPE, TYPES.getFilterScope(), createTermsFilter(FIELD_ISSUE_TYPE, query.types()));
addFacetIfNeeded(options, aggregationHelper, esRequest, MODULE_UUIDS, query.moduleUuids().toArray());
addFacetIfNeeded(options, aggregationHelper, esRequest, DIRECTORIES, query.directories().toArray());
addFacetIfNeeded(options, aggregationHelper, esRequest, FILE_UUIDS, query.fileUuids().toArray());
+ addFacetIfNeeded(options, aggregationHelper, esRequest, SCOPES, query.scopes().toArray());
addFacetIfNeeded(options, aggregationHelper, esRequest, LANGUAGES, query.languages().toArray());
addFacetIfNeeded(options, aggregationHelper, esRequest, RULES, query.rules().stream().map(RuleDefinitionDto::getUuid).toArray());
addFacetIfNeeded(options, aggregationHelper, esRequest, AUTHORS, query.authors().toArray());
private final Collection<RuleDefinitionDto> rules;
private final Collection<String> assignees;
private final Collection<String> authors;
+ private final Collection<String> scopes;
private final Collection<String> languages;
private final Collection<String> tags;
private final Collection<String> types;
this.rules = defaultCollection(builder.rules);
this.assignees = defaultCollection(builder.assigneeUuids);
this.authors = defaultCollection(builder.authors);
+ this.scopes = defaultCollection(builder.scopes);
this.languages = defaultCollection(builder.languages);
this.tags = defaultCollection(builder.tags);
this.types = defaultCollection(builder.types);
return authors;
}
+ public Collection<String> scopes() {
+ return scopes;
+ }
+
public Collection<String> languages() {
return languages;
}
private Collection<RuleDefinitionDto> rules;
private Collection<String> assigneeUuids;
private Collection<String> authors;
+ private Collection<String> scopes;
private Collection<String> languages;
private Collection<String> tags;
private Collection<String> types;
return this;
}
+ public Builder scopes(@Nullable Collection<String> s) {
+ this.scopes = s;
+ return this;
+ }
+
public Builder languages(@Nullable Collection<String> l) {
this.languages = l;
return this;
.rules(ruleKeysToRuleId(dbSession, request.getRules()))
.assigneeUuids(request.getAssigneeUuids())
.authors(request.getAuthors())
+ .scopes(request.getScopes())
.languages(request.getLanguages())
.tags(request.getTags())
.types(request.getTypes())
@Rule
public DbTester db = DbTester.create();
- private RuleDbTester ruleDbTester = new RuleDbTester(db);
-
- private Clock clock = mock(Clock.class);
- private IssueQueryFactory underTest = new IssueQueryFactory(db.getDbClient(), clock, userSession);
+ private final RuleDbTester ruleDbTester = new RuleDbTester(db);
+ private final Clock clock = mock(Clock.class);
+ private final IssueQueryFactory underTest = new IssueQueryFactory(db.getDbClient(), clock, userSession);
@Test
public void create_from_parameters() {
.setDirectories(asList("aDirPath"))
.setFileUuids(asList(file.uuid()))
.setAssigneesUuid(asList(user.getUuid()))
+ .setScopes(asList("MAIN", "TEST"))
.setLanguages(asList("xoo"))
.setTags(asList("tag1", "tag2"))
.setOrganization(organization.getKey())
assertThat(query.moduleUuids()).containsOnly(module.uuid());
assertThat(query.fileUuids()).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.organizationUuid()).isEqualTo(organization.getUuid());
import org.sonar.server.issue.index.IssueIndexSyncProgressChecker;
import org.sonar.server.issue.index.IssueQuery;
import org.sonar.server.issue.index.IssueQueryFactory;
+import org.sonar.server.issue.index.IssueScope;
import org.sonar.server.security.SecurityStandards.SQCategory;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Issues.SearchWsResponse;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RESOLVED;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RULES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SANS_TOP_25;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SCOPES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SEVERITIES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SINCE_LEAK_PERIOD;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SONARSOURCE_SECURITY;
public class SearchAction implements IssuesWsAction {
private static final String LOGIN_MYSELF = "__me__";
+ private static final Set<String> ISSUE_SCOPES = Arrays.stream(IssueScope.values()).map(Enum::name).collect(Collectors.toSet());
private static final EnumSet<RuleType> ALL_RULE_TYPES_EXCEPT_SECURITY_HOTSPOTS = EnumSet.complementOf(EnumSet.of(RuleType.SECURITY_HOTSPOT));
static final List<String> SUPPORTED_FACETS = ImmutableList.of(
DEPRECATED_PARAM_AUTHORS,
PARAM_AUTHOR,
PARAM_DIRECTORIES,
+ PARAM_SCOPES,
PARAM_LANGUAGES,
PARAM_TAGS,
PARAM_TYPES,
.createAction(ACTION_SEARCH)
.setHandler(this)
.setDescription("Search for issues.<br>Requires the 'Browse' permission on the specified project(s)."
- + "<br/>When issue indexation is in progress returns 503 service unavailable HTTP code.")
+ + "<br/>When issue indexation is in progress returns 503 service unavailable HTTP code.")
.setSince("3.6")
.setChangelog(
new Change("8.4", "parameters 'componentUuids', 'projectKeys' has been dropped."),
action.createParam(PARAM_ASSIGNED)
.setDescription("To retrieve assigned or unassigned issues")
.setBooleanPossibleValues();
+ action.createParam(PARAM_SCOPES)
+ .setDescription("Comma-separated list of scopes. Available since 8.5")
+ .setPossibleValues(IssueScope.MAIN.name(), IssueScope.TEST.name())
+ .setExampleValue(format("%s,%s", IssueScope.MAIN.name(), IssueScope.TEST.name()));
action.createParam(PARAM_LANGUAGES)
.setDescription("Comma-separated list of languages. Available since 4.4")
.setExampleValue("java,js");
addMandatoryValuesToFacet(facets, PARAM_ASSIGNEES, assignees);
addMandatoryValuesToFacet(facets, FACET_ASSIGNED_TO_ME, singletonList(userSession.getUuid()));
addMandatoryValuesToFacet(facets, PARAM_RULES, query.rules().stream().map(RuleDefinitionDto::getUuid).collect(toList()));
+ addMandatoryValuesToFacet(facets, PARAM_SCOPES, ISSUE_SCOPES);
addMandatoryValuesToFacet(facets, PARAM_LANGUAGES, request.getLanguages());
addMandatoryValuesToFacet(facets, PARAM_TAGS, request.getTags());
.setFacets(request.paramAsStrings(FACETS))
.setFileUuids(request.paramAsStrings(PARAM_FILE_UUIDS))
.setIssues(request.paramAsStrings(PARAM_ISSUES))
+ .setScopes(request.paramAsStrings(PARAM_SCOPES))
.setLanguages(request.paramAsStrings(PARAM_LANGUAGES))
.setModuleUuids(request.paramAsStrings(PARAM_MODULE_UUIDS))
.setOnComponentOnly(request.paramAsBoolean(PARAM_ON_COMPONENT_ONLY))
import java.util.Set;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Languages;
+import org.sonar.api.resources.Qualifiers;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.DateUtils;
import org.sonar.markdown.Markdown;
import org.sonar.server.es.Facets;
import org.sonar.server.issue.TextRangeResponseFormatter;
+import org.sonar.server.issue.index.IssueScope;
import org.sonar.server.issue.workflow.Transition;
import org.sonarqube.ws.Common;
import org.sonarqube.ws.Common.Comment;
ofNullable(dto.getIssueCreationDate()).map(DateUtils::formatDateTime).ifPresent(issueBuilder::setCreationDate);
ofNullable(dto.getIssueUpdateDate()).map(DateUtils::formatDateTime).ifPresent(issueBuilder::setUpdateDate);
ofNullable(dto.getIssueCloseDate()).map(DateUtils::formatDateTime).ifPresent(issueBuilder::setCloseDate);
+ issueBuilder.setScope(Qualifiers.UNIT_TEST_FILE.equals(component.qualifier()) ? IssueScope.TEST.name() : IssueScope.MAIN.name());
}
private static String engineNameFrom(RuleKey ruleKey) {
if (issueKeysToLoad.isEmpty()) {
return issueKeys.stream()
- .map(new KeyToIssueFunction(preloadedIssues)::apply).filter(Objects::nonNull)
+ .map(new KeyToIssueFunction(preloadedIssues)).filter(Objects::nonNull)
.collect(Collectors.toList());
}
.collect(toList(preloadedIssues.size() + loadedIssues.size()));
return issueKeys.stream()
- .map(new KeyToIssueFunction(unorderedIssues)::apply).filter(Objects::nonNull)
+ .map(new KeyToIssueFunction(unorderedIssues)).filter(Objects::nonNull)
.collect(Collectors.toList());
}
import static org.junit.rules.ExpectedException.none;
import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
+import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE;
import static org.sonar.api.rules.RuleType.CODE_SMELL;
import static org.sonar.api.server.ws.WebService.Param.FACETS;
import static org.sonar.api.utils.DateUtils.formatDateTime;
assertThat(ws.newRequest()
.setMultiParam("author", singletonList("unknown"))
.executeProtobuf(SearchWsResponse.class).getIssuesList())
- .isEmpty();
+ .isEmpty();
+ }
+
+ @Test
+ public void filter_by_test_scope() {
+ OrganizationDto organization = db.organizations().insert(p -> p.setUuid("org-1").setKey("org-1"));
+ ComponentDto project = db.components().insertComponent(ComponentTesting.newPublicProjectDto(organization, "PROJECT_ID").setDbKey("PROJECT_KEY"));
+ indexPermissions();
+ ComponentDto mainCodeFile = db.components().insertComponent(
+ newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
+ ComponentDto testCodeFile = db.components().insertComponent(
+ newFileDto(project, null, "ANOTHER_FILE_ID").setDbKey("ANOTHER_FILE_KEY").setQualifier(UNIT_TEST_FILE));
+ RuleDto rule = newIssueRule();
+ IssueDto issue1 = newDto(rule, mainCodeFile, project)
+ .setIssueCreationDate(parseDate("2014-09-04"))
+ .setIssueUpdateDate(parseDate("2017-12-04"))
+ .setEffort(10L)
+ .setStatus("OPEN")
+ .setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
+ .setSeverity("MAJOR");
+ IssueDto issue2 = newDto(rule, mainCodeFile, project)
+ .setIssueCreationDate(parseDate("2014-09-04"))
+ .setIssueUpdateDate(parseDate("2017-12-04"))
+ .setEffort(10L)
+ .setStatus("OPEN")
+ .setKee("7b112bd4-b650-4037-80bc-82fd47d4eac2")
+ .setSeverity("MAJOR");
+ IssueDto issue3 = newDto(rule, testCodeFile, project)
+ .setIssueCreationDate(parseDate("2014-09-04"))
+ .setIssueUpdateDate(parseDate("2017-12-04"))
+ .setEffort(10L)
+ .setStatus("OPEN")
+ .setKee("82fd47d4-4037-b650-80bc-7b112bd4eac2")
+ .setSeverity("MAJOR");
+ dbClient.issueDao().insert(session, issue1, issue2, issue3);
+ session.commit();
+ indexIssues();
+
+ ws.newRequest()
+ .setParam("scopes", "TEST")
+ .setParam(FACETS, "scopes")
+ .execute()
+ .assertJson(this.getClass(), "filter_by_test_scope.json");
+ }
+
+ @Test
+ public void filter_by_main_scope() {
+ OrganizationDto organization = db.organizations().insert(p -> p.setUuid("org-1").setKey("org-1"));
+ ComponentDto project = db.components().insertComponent(ComponentTesting.newPublicProjectDto(organization, "PROJECT_ID").setDbKey("PROJECT_KEY"));
+ indexPermissions();
+ ComponentDto mainCodeFile = db.components().insertComponent(
+ newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
+ ComponentDto testCodeFile = db.components().insertComponent(
+ newFileDto(project, null, "ANOTHER_FILE_ID").setDbKey("ANOTHER_FILE_KEY").setQualifier(UNIT_TEST_FILE));
+ RuleDto rule = newIssueRule();
+ IssueDto issue1 = newDto(rule, mainCodeFile, project)
+ .setType(CODE_SMELL)
+ .setIssueCreationDate(parseDate("2014-09-04"))
+ .setIssueUpdateDate(parseDate("2017-12-04"))
+ .setEffort(10L)
+ .setStatus("OPEN")
+ .setKee("83ec1d05-9397-4137-9978-85368bcc3b90")
+ .setSeverity("MAJOR");
+ IssueDto issue2 = newDto(rule, mainCodeFile, project)
+ .setType(CODE_SMELL)
+ .setIssueCreationDate(parseDate("2014-09-04"))
+ .setIssueUpdateDate(parseDate("2017-12-04"))
+ .setEffort(10L)
+ .setStatus("OPEN")
+ .setKee("7b112bd4-b650-4037-80bc-82fd47d4eac2")
+ .setSeverity("MAJOR");
+ IssueDto issue3 = newDto(rule, testCodeFile, project)
+ .setType(CODE_SMELL)
+ .setIssueCreationDate(parseDate("2014-09-04"))
+ .setIssueUpdateDate(parseDate("2017-12-04"))
+ .setEffort(10L)
+ .setStatus("OPEN")
+ .setKee("82fd47d4-4037-b650-80bc-7b112bd4eac2")
+ .setSeverity("MAJOR");
+ dbClient.issueDao().insert(session, issue1, issue2, issue3);
+ session.commit();
+ indexIssues();
+
+ ws.newRequest()
+ .setParam("scopes", "MAIN")
+ .setParam(FACETS, "scopes")
+ .execute()
+ .assertJson(this.getClass(), "filter_by_main_scope.json");
+ }
+
+ @Test
+ public void filter_by_scope_always_returns_all_scope_facet_values() {
+ OrganizationDto organization = db.organizations().insert(p -> p.setUuid("org-1").setKey("org-1"));
+ ComponentDto project = db.components().insertComponent(ComponentTesting.newPublicProjectDto(organization, "PROJECT_ID").setDbKey("PROJECT_KEY"));
+ indexPermissions();
+ ComponentDto mainCodeFile = db.components().insertComponent(
+ newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
+ RuleDto rule = newIssueRule();
+ IssueDto issue1 = newDto(rule, mainCodeFile, project)
+ .setType(CODE_SMELL)
+ .setIssueCreationDate(parseDate("2014-09-04"))
+ .setIssueUpdateDate(parseDate("2017-12-04"))
+ .setEffort(10L)
+ .setStatus("OPEN")
+ .setKee("83ec1d05-9397-4137-9978-85368bcc3b90")
+ .setSeverity("MAJOR");
+ IssueDto issue2 = newDto(rule, mainCodeFile, project)
+ .setType(CODE_SMELL)
+ .setIssueCreationDate(parseDate("2014-09-04"))
+ .setIssueUpdateDate(parseDate("2017-12-04"))
+ .setEffort(10L)
+ .setStatus("OPEN")
+ .setKee("7b112bd4-b650-4037-80bc-82fd47d4eac2")
+ .setSeverity("MAJOR");
+ dbClient.issueDao().insert(session, issue1, issue2);
+ session.commit();
+ indexIssues();
+
+ ws.newRequest()
+ .setParam("scopes", "MAIN")
+ .setParam(FACETS, "scopes")
+ .execute()
+ .assertJson(this.getClass(), "filter_by_main_scope_2.json");
}
@Test
// This parameter will be ignored
.setParam("authors", "leia")
.executeProtobuf(SearchWsResponse.class).getIssuesList())
- .extracting(Issue::getKey)
- .containsExactlyInAnyOrder(issue2.getKey());
+ .extracting(Issue::getKey)
+ .containsExactlyInAnyOrder(issue2.getKey());
}
@Test
assertThatThrownBy(() -> ws.newRequest()
.setParam("types", RuleType.SECURITY_HOTSPOT.toString())
.execute())
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessage("Value of parameter 'types' (SECURITY_HOTSPOT) must be one of: [CODE_SMELL, BUG, VULNERABILITY]");
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Value of parameter 'types' (SECURITY_HOTSPOT) must be one of: [CODE_SMELL, BUG, VULNERABILITY]");
}
@Test
assertThat(def.params()).extracting("key").containsExactlyInAnyOrder(
"additionalFields", "asc", "assigned", "assignees", "authors", "author", "componentKeys", "branch",
"pullRequest", "organization",
- "createdAfter", "createdAt", "createdBefore", "createdInLast", "directories", "facetMode", "facets", "fileUuids", "issues", "languages", "moduleUuids", "onComponentOnly",
+ "createdAfter", "createdAt", "createdBefore", "createdInLast", "directories", "facetMode", "facets", "fileUuids", "issues", "scopes", "languages", "moduleUuids",
+ "onComponentOnly",
"p", "projects", "ps", "resolutions", "resolved", "rules", "s", "severities", "sinceLeakPeriod",
"statuses", "tags", "types", "owaspTop10", "sansTop25", "cwe", "sonarsourceSecurity");
--- /dev/null
+{
+ "total": 2,
+ "p": 1,
+ "ps": 100,
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 2
+ },
+ "effortTotal": 20,
+ "debtTotal": 20,
+ "issues": [
+ {
+ "key": "7b112bd4-b650-4037-80bc-82fd47d4eac2",
+ "rule": "xoo:x1",
+ "severity": "MAJOR",
+ "component": "FILE_KEY",
+ "project": "PROJECT_KEY",
+ "flows": [],
+ "status": "OPEN",
+ "effort": "10min",
+ "debt": "10min",
+ "organization": "org-1",
+ "scope": "MAIN"
+ },
+ {
+ "key": "83ec1d05-9397-4137-9978-85368bcc3b90",
+ "rule": "xoo:x1",
+ "severity": "MAJOR",
+ "component": "FILE_KEY",
+ "project": "PROJECT_KEY",
+ "flows": [],
+ "status": "OPEN",
+ "effort": "10min",
+ "debt": "10min",
+ "organization": "org-1",
+ "scope": "MAIN"
+ }
+ ],
+ "components": [
+ {
+ "organization": "org-1",
+ "key": "FILE_KEY",
+ "uuid": "FILE_ID",
+ "enabled": true,
+ "qualifier": "FIL",
+ "name": "NAME_FILE_ID",
+ "longName": "null/NAME_FILE_ID",
+ "path": "null/NAME_FILE_ID"
+ },
+ {
+ "organization": "org-1",
+ "key": "PROJECT_KEY",
+ "uuid": "PROJECT_ID",
+ "enabled": true,
+ "qualifier": "TRK",
+ "name": "NAME_PROJECT_ID",
+ "longName": "LONG_NAME_PROJECT_ID"
+ }
+ ],
+ "facets": [
+ {
+ "property": "scopes",
+ "values": [
+ {
+ "val": "MAIN",
+ "count": 2
+ },
+ {
+ "val": "TEST",
+ "count": 1
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "total": 2,
+ "p": 1,
+ "ps": 100,
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 2
+ },
+ "effortTotal": 20,
+ "debtTotal": 20,
+ "issues": [
+ {
+ "key": "7b112bd4-b650-4037-80bc-82fd47d4eac2",
+ "rule": "xoo:x1",
+ "severity": "MAJOR",
+ "component": "FILE_KEY",
+ "project": "PROJECT_KEY",
+ "flows": [],
+ "status": "OPEN",
+ "effort": "10min",
+ "debt": "10min",
+ "organization": "org-1",
+ "scope": "MAIN"
+ },
+ {
+ "key": "83ec1d05-9397-4137-9978-85368bcc3b90",
+ "rule": "xoo:x1",
+ "severity": "MAJOR",
+ "component": "FILE_KEY",
+ "project": "PROJECT_KEY",
+ "flows": [],
+ "status": "OPEN",
+ "effort": "10min",
+ "debt": "10min",
+ "organization": "org-1",
+ "scope": "MAIN"
+ }
+ ],
+ "components": [
+ {
+ "organization": "org-1",
+ "key": "FILE_KEY",
+ "uuid": "FILE_ID",
+ "enabled": true,
+ "qualifier": "FIL",
+ "name": "NAME_FILE_ID",
+ "longName": "null/NAME_FILE_ID",
+ "path": "null/NAME_FILE_ID"
+ },
+ {
+ "organization": "org-1",
+ "key": "PROJECT_KEY",
+ "uuid": "PROJECT_ID",
+ "enabled": true,
+ "qualifier": "TRK",
+ "name": "NAME_PROJECT_ID",
+ "longName": "LONG_NAME_PROJECT_ID"
+ }
+ ],
+ "facets": [
+ {
+ "property": "scopes",
+ "values": [
+ {
+ "val": "MAIN",
+ "count": 2
+ },
+ {
+ "val": "TEST",
+ "count": 0
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "total": 1,
+ "p": 1,
+ "ps": 100,
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 1
+ },
+ "effortTotal": 10,
+ "debtTotal": 10,
+ "issues": [
+ {
+ "key": "82fd47d4-4037-b650-80bc-7b112bd4eac2",
+ "rule": "xoo:x1",
+ "severity": "MAJOR",
+ "component": "ANOTHER_FILE_KEY",
+ "project": "PROJECT_KEY",
+ "flows": [],
+ "status": "OPEN",
+ "effort": "10min",
+ "debt": "10min",
+ "organization": "org-1",
+ "scope": "TEST"
+ }
+ ],
+ "components": [
+ {
+ "organization": "org-1",
+ "key": "ANOTHER_FILE_KEY",
+ "uuid": "ANOTHER_FILE_ID",
+ "enabled": true,
+ "qualifier": "UTS",
+ "name": "NAME_ANOTHER_FILE_ID",
+ "longName": "null/NAME_ANOTHER_FILE_ID",
+ "path": "null/NAME_ANOTHER_FILE_ID"
+ },
+ {
+ "organization": "org-1",
+ "key": "PROJECT_KEY",
+ "uuid": "PROJECT_ID",
+ "enabled": true,
+ "qualifier": "TRK",
+ "name": "NAME_PROJECT_ID",
+ "longName": "LONG_NAME_PROJECT_ID"
+ }
+ ],
+ "facets": [
+ {
+ "property": "scopes",
+ "values": [
+ {
+ "val": "TEST",
+ "count": 1
+ },
+ {
+ "val": "MAIN",
+ "count": 2
+ }
+ ]
+ }
+ ]
+}
public static final String DEPRECATED_PARAM_AUTHORS = "authors";
public static final String PARAM_AUTHOR = "author";
+ public static final String PARAM_SCOPES = "scopes";
public static final String PARAM_LANGUAGES = "languages";
public static final String PARAM_TAGS = "tags";
public static final String PARAM_TYPES = "types";
optional string externalRuleEngine = 33;
optional bool fromHotspot = 34;
+ optional string scope = 35;
}
message Transitions {