*/
package org.sonar.db.issue;
+import java.util.Arrays;
import java.util.Date;
+import java.util.Random;
import org.apache.commons.lang.math.RandomUtils;
import org.sonar.api.issue.Issue;
import org.sonar.api.resources.Qualifiers;
import static org.apache.commons.lang.math.RandomUtils.nextLong;
public class IssueTesting {
+ private static final RuleType[] RULE_TYPES_EXCEPT_HOTSPOTS = Arrays.stream(RuleType.values())
+ .filter(ruleType -> RuleType.SECURITY_HOTSPOT != ruleType).toArray(RuleType[]::new);
private IssueTesting() {
// only statics
return new IssueDto()
.setKee("uuid_" + randomAlphabetic(5))
.setRule(rule)
- .setType(RuleType.values()[nextInt(RuleType.values().length)])
+ .setType(RULE_TYPES_EXCEPT_HOTSPOTS[new Random().nextInt(RULE_TYPES_EXCEPT_HOTSPOTS.length)])
.setProject(project)
.setComponent(file)
.setStatus(Issue.STATUS_OPEN)
*/
package org.sonar.server.issue;
+import org.sonar.api.rules.RuleType;
import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
+import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
public class IssueFinder {
public IssueDto getByKey(DbSession session, String issueKey) {
IssueDto issue = dbClient.issueDao().selectByKey(session, issueKey).orElseThrow(() -> new NotFoundException(format("Issue with key '%s' does not exist", issueKey)));
+
+ RuleType ruleType = RuleType.valueOfNullable(issue.getType());
+ if (SECURITY_HOTSPOT.equals(ruleType)) {
+ throw new NotFoundException(format("Issue with key '%s' does not exist", issueKey));
+ }
+
userSession.checkComponentUuidPermission(UserRole.USER, requireNonNull(issue.getProjectUuid()));
return issue;
}
*/
package org.sonar.server.issue.ws;
-import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchHit;
-import org.sonar.api.Startable;
-import org.sonar.api.config.Configuration;
import org.sonar.api.rule.Severity;
import org.sonar.api.rules.RuleType;
import org.sonar.api.server.ws.Change;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
-import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.es.Facets;
import org.sonar.server.es.SearchOptions;
import static org.sonar.api.server.ws.WebService.Param.FACETS;
import static org.sonar.api.utils.Paging.forPageIndex;
import static org.sonar.core.util.stream.MoreCollectors.toSet;
-import static org.sonar.process.ProcessProperties.Property.SONARCLOUD_ENABLED;
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
import static org.sonar.server.issue.index.IssueIndex.FACET_ASSIGNED_TO_ME;
import static org.sonar.server.issue.index.IssueIndex.FACET_PROJECTS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TAGS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TYPES;
-public class SearchAction implements IssuesWsAction, Startable {
+public class SearchAction implements IssuesWsAction {
private static final String LOGIN_MYSELF = "__me__";
+ 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(
FACET_PROJECTS,
private static final String INTERNAL_PARAMETER_DISCLAIMER = "This parameter is mostly used by the Issues page, please prefer usage of the componentKeys parameter. ";
private static final Set<String> FACETS_REQUIRING_PROJECT_OR_ORGANIZATION = newHashSet(PARAM_MODULE_UUIDS, PARAM_FILE_UUIDS, PARAM_DIRECTORIES);
- private static final Joiner COMA_JOINER = Joiner.on(",");
private final UserSession userSession;
private final IssueIndex issueIndex;
private final IssueQueryFactory issueQueryFactory;
private final SearchResponseLoader searchResponseLoader;
private final SearchResponseFormat searchResponseFormat;
- private final Configuration config;
private final System2 system2;
private final DbClient dbClient;
- private boolean isOnSonarCloud;
public SearchAction(UserSession userSession, IssueIndex issueIndex, IssueQueryFactory issueQueryFactory, SearchResponseLoader searchResponseLoader,
- SearchResponseFormat searchResponseFormat, Configuration config, System2 system2, DbClient dbClient) {
+ SearchResponseFormat searchResponseFormat, System2 system2, DbClient dbClient) {
this.userSession = userSession;
this.issueIndex = issueIndex;
this.issueQueryFactory = issueQueryFactory;
this.searchResponseLoader = searchResponseLoader;
this.searchResponseFormat = searchResponseFormat;
- this.config = config;
this.system2 = system2;
this.dbClient = dbClient;
}
PARAM_COMPONENT_KEYS, PARAM_COMPONENT_UUIDS)
.setSince("3.6")
.setChangelog(
- new Change("8.1", "response field 'fromHotspot' has been deprecated and is no more populated"),
- new Change("8.1", format("Status %s for Security Hotspots has been deprecated", STATUS_IN_REVIEW)),
+ new Change("8.2", "'REVIEWED', 'TO_REVIEW' status param values are no longer supported"),
+ new Change("8.2", "Security hotspots are no longer returned"),
+ new Change("8.2", "response field 'fromHotspot' has been deprecated and is no more populated"),
+ new Change("8.2", format("Status %s for Security Hotspots has been deprecated", STATUS_IN_REVIEW)),
new Change("7.8", format("added new Security Hotspots statuses : %s, %s and %s", STATUS_TO_REVIEW, STATUS_IN_REVIEW, STATUS_REVIEWED)),
new Change("7.8", "Security hotspots are returned by default"),
new Change("7.7", format("Value '%s' in parameter '%s' is deprecated, please use '%s' instead", DEPRECATED_PARAM_AUTHORS, FACETS, PARAM_AUTHOR)),
action.createParam(PARAM_TYPES)
.setDescription("Comma-separated list of types.")
.setSince("5.5")
- .setPossibleValues(RuleType.values())
+ .setPossibleValues(ALL_RULE_TYPES_EXCEPT_SECURITY_HOTSPOTS)
.setExampleValue(format("%s,%s", RuleType.CODE_SMELL, RuleType.BUG));
action.createParam(PARAM_OWASP_TOP_10)
.setDescription("Comma-separated list of OWASP Top 10 lowercase categories.")
public final void handle(Request request, Response response) {
try (DbSession dbSession = dbClient.openSession(false)) {
SearchRequest searchRequest = toSearchWsRequest(dbSession, request);
- SearchWsResponse searchWsResponse = doHandle(dbSession, searchRequest);
+ SearchWsResponse searchWsResponse = doHandle(searchRequest);
writeProtobuf(searchWsResponse, request, response);
}
}
- private SearchWsResponse doHandle(DbSession dbSession, SearchRequest request) {
+ private SearchWsResponse doHandle(SearchRequest request) {
// prepare the Elasticsearch request
- SearchOptions options = createSearchOptionsFromRequest(dbSession, request);
+ SearchOptions options = createSearchOptionsFromRequest(request);
EnumSet<SearchAdditionalField> additionalFields = SearchAdditionalField.getFromRequest(request);
IssueQuery query = issueQueryFactory.create(request);
.collect(toSet());
checkArgument(facetsRequiringProjectOrOrganizationParameter.isEmpty() ||
(!query.projectUuids().isEmpty()) || query.organizationUuid() != null, "Facet(s) '%s' require to also filter by project or organization",
- COMA_JOINER.join(facetsRequiringProjectOrOrganizationParameter));
+ String.join(",", facetsRequiringProjectOrOrganizationParameter));
// execute request
SearchResponse result = issueIndex.search(query, options);
return searchResponseFormat.formatSearch(additionalFields, data, paging, facets);
}
- private SearchOptions createSearchOptionsFromRequest(DbSession dbSession, SearchRequest request) {
+ private SearchOptions createSearchOptionsFromRequest(SearchRequest request) {
SearchOptions options = new SearchOptions();
options.setPage(request.getPage(), request.getPageSize());
return options;
}
- List<String> requestedFacets = new ArrayList<>(facets);
- if (isOnSonarCloud) {
- Optional<OrganizationDto> organizationDto = Optional.empty();
- String organizationKey = request.getOrganization();
- if (organizationKey != null) {
- organizationDto = dbClient.organizationDao().selectByKey(dbSession, organizationKey);
- }
-
- if (!organizationDto.isPresent() || !userSession.hasMembership(organizationDto.get())) {
- // In order to display the authors facet, the organization parameter must be set and the user
- // must be member of this organization
- requestedFacets.remove(PARAM_AUTHOR);
- requestedFacets.remove(DEPRECATED_PARAM_AUTHORS);
- }
- }
-
- options.addFacets(requestedFacets);
+ options.addFacets(facets);
return options;
}
private void completeFacets(Facets facets, SearchRequest request, IssueQuery query) {
addMandatoryValuesToFacet(facets, PARAM_SEVERITIES, Severity.ALL);
- addMandatoryValuesToFacet(facets, PARAM_STATUSES, STATUSES);
+ addMandatoryValuesToFacet(facets, PARAM_STATUSES, STATUSES.stream().filter(s -> !STATUS_TO_REVIEW.equals(s)).filter(s -> !STATUS_REVIEWED.equals(s)).collect(toList()));
addMandatoryValuesToFacet(facets, PARAM_RESOLUTIONS, concat(singletonList(""), RESOLUTIONS));
addMandatoryValuesToFacet(facets, FACET_PROJECTS, query.projectUuids());
addMandatoryValuesToFacet(facets, PARAM_MODULE_UUIDS, query.moduleUuids());
addMandatoryValuesToFacet(facets, PARAM_RULES, query.rules().stream().map(IssueDoc::formatRuleId).collect(toList()));
addMandatoryValuesToFacet(facets, PARAM_LANGUAGES, request.getLanguages());
addMandatoryValuesToFacet(facets, PARAM_TAGS, request.getTags());
- addMandatoryValuesToFacet(facets, PARAM_TYPES, RuleType.names());
+
+ setTypesFacet(facets);
+
addMandatoryValuesToFacet(facets, PARAM_OWASP_TOP_10, request.getOwaspTop10());
addMandatoryValuesToFacet(facets, PARAM_SANS_TOP_25, request.getSansTop25());
addMandatoryValuesToFacet(facets, PARAM_CWE, request.getCwe());
addMandatoryValuesToFacet(facets, PARAM_SONARSOURCE_SECURITY, request.getSonarsourceSecurity());
}
+ private void setTypesFacet(Facets facets) {
+ Map<String, Long> typeFacet = facets.get(PARAM_TYPES);
+ if (typeFacet != null) {
+ typeFacet.remove(RuleType.SECURITY_HOTSPOT.name());
+ }
+ addMandatoryValuesToFacet(facets, PARAM_TYPES, ALL_RULE_TYPES_EXCEPT_SECURITY_HOTSPOTS.stream().map(Enum::name).collect(Collectors.toList()));
+ }
+
private static void addMandatoryValuesToFacet(Facets facets, String facetName, @Nullable Iterable<String> mandatoryValues) {
Map<String, Long> buckets = facets.get(facetName);
if (buckets != null && mandatoryValues != null) {
.setSeverities(request.paramAsStrings(PARAM_SEVERITIES))
.setStatuses(request.paramAsStrings(PARAM_STATUSES))
.setTags(request.paramAsStrings(PARAM_TAGS))
- .setTypes(request.paramAsStrings(PARAM_TYPES))
+ .setTypes(allRuleTypesExceptHotspotsIfEmpty(request.paramAsStrings(PARAM_TYPES)))
.setOwaspTop10(request.paramAsStrings(PARAM_OWASP_TOP_10))
.setSansTop25(request.paramAsStrings(PARAM_SANS_TOP_25))
.setCwe(request.paramAsStrings(PARAM_CWE))
.setSonarsourceSecurity(request.paramAsStrings(PARAM_SONARSOURCE_SECURITY));
}
+ private List<String> allRuleTypesExceptHotspotsIfEmpty(@Nullable List<String> types) {
+ if (types == null || types.isEmpty()) {
+ return ALL_RULE_TYPES_EXCEPT_SECURITY_HOTSPOTS.stream().map(Enum::name).collect(toList());
+ }
+ return types;
+ }
+
private List<String> getLogins(DbSession dbSession, @Nullable List<String> assigneeLogins) {
List<String> userLogins = new ArrayList<>();
}
return assigneeUuid;
}
-
- @Override
- public void start() {
- this.isOnSonarCloud = config.getBoolean(SONARCLOUD_ENABLED.getKey()).orElse(false);
- }
-
- @Override
- public void stop() {
- // Nothing to do
- }
}
*/
package org.sonar.server.issue.ws;
+import java.util.Arrays;
+import java.util.Random;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
+import org.sonar.api.rules.RuleType;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
public class EditCommentActionTest {
private static final long NOW = 10_000_000_000L;
+ private static final RuleType[] RULE_TYPES_EXCEPT_HOTSPOTS = Arrays.stream(RuleType.values())
+ .filter(ruleType -> RuleType.SECURITY_HOTSPOT != ruleType).toArray(RuleType[]::new);
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void edit_comment() {
- IssueDto issueDto = issueDbTester.insertIssue();
+ IssueDto issueDto = newIssue();
UserDto user = dbTester.users().insertUser();
IssueChangeDto commentDto = issueDbTester.insertComment(issueDto, user, "please fix it");
loginWithBrowsePermission(user, USER, issueDto);
@Test
public void edit_comment_using_deprecated_key_parameter() {
- IssueDto issueDto = issueDbTester.insertIssue();
+ IssueDto issueDto = newIssue();
UserDto user = dbTester.users().insertUser();
IssueChangeDto commentDto = issueDbTester.insertComment(issueDto, user, "please fix it");
loginWithBrowsePermission(user, USER, issueDto);
@Test
public void fail_when_comment_does_not_belong_to_current_user() {
- IssueDto issueDto = issueDbTester.insertIssue();
+ IssueDto issueDto = newIssue();
UserDto user = dbTester.users().insertUser();
IssueChangeDto commentDto = issueDbTester.insertComment(issueDto, user, "please fix it");
UserDto another = dbTester.users().insertUser();
@Test
public void fail_when_comment_has_not_user() {
- IssueDto issueDto = issueDbTester.insertIssue();
+ IssueDto issueDto = newIssue();
UserDto user = dbTester.users().insertUser();
IssueChangeDto commentDto = issueDbTester.insertComment(issueDto, null, "please fix it");
loginWithBrowsePermission(user, USER, issueDto);
@Test
public void fail_when_empty_comment_text() {
- IssueDto issueDto = issueDbTester.insertIssue();
+ IssueDto issueDto = newIssue();
UserDto user = dbTester.users().insertUser();
IssueChangeDto commentDto = issueDbTester.insertComment(issueDto, user, "please fix it");
loginWithBrowsePermission(user, USER, issueDto);
@Test
public void fail_when_not_enough_permission() {
- IssueDto issueDto = issueDbTester.insertIssue();
+ IssueDto issueDto = newIssue();
UserDto user = dbTester.users().insertUser();
IssueChangeDto commentDto = issueDbTester.insertComment(issueDto, user, "please fix it");
loginWithBrowsePermission(user, CODEVIEWER, issueDto);
call(commentDto.getKey(), "please have a look");
}
+ @Test
+ public void fail_NFE_if_security_hotspots() {
+ IssueDto issueDto = issueDbTester.insertIssue(i -> i.setType(RuleType.SECURITY_HOTSPOT));
+ UserDto user = dbTester.users().insertUser();
+ IssueChangeDto commentDto = issueDbTester.insertComment(issueDto, user, "please fix it");
+ loginWithBrowsePermission(user, CODEVIEWER, issueDto);
+
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage(String.format("Issue with key '%s' does not exist", issueDto.getKey()));
+ call(commentDto.getKey(), "please have a look");
+ }
+
@Test
public void test_definition() {
WebService.Action action = tester.getDef();
return request.execute();
}
+ private IssueDto newIssue() {
+ return issueDbTester.insertIssue(i -> i.setType(RULE_TYPES_EXCEPT_HOTSPOTS[new Random().nextInt(RULE_TYPES_EXCEPT_HOTSPOTS.length)]));
+ }
+
private void loginWithBrowsePermission(UserDto user, String permission, IssueDto issueDto) {
userSession.logIn(user).addProjectPermission(permission,
dbClient.componentDao().selectByUuid(dbTester.getSession(), issueDto.getProjectUuid()).get(),
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
-import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.resources.Languages;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.rule.RuleKey;
private PermissionIndexerTester permissionIndexer = new PermissionIndexerTester(es, issueIndexer);
private WsActionTester ws = new WsActionTester(new SearchAction(userSession, issueIndex, issueQueryFactory, searchResponseLoader, searchResponseFormat,
- new MapSettings().asConfig(), System2.INSTANCE, dbClient));
+ System2.INSTANCE, dbClient));
@Test
public void search_all_issues_when_no_parameter() {
assertThat(ws.newRequest()
.setParam(PARAM_COMPONENT_KEYS, module1.getKey())
.executeProtobuf(SearchWsResponse.class).getIssuesList()).extracting(Issue::getKey)
- .containsExactlyInAnyOrder(issue1.getKey());
+ .containsExactlyInAnyOrder(issue1.getKey());
assertThat(ws.newRequest()
.setParam(PARAM_MODULE_UUIDS, module1.uuid())
.executeProtobuf(SearchWsResponse.class).getIssuesList()).extracting(Issue::getKey)
- .containsExactlyInAnyOrder(issue1.getKey());
+ .containsExactlyInAnyOrder(issue1.getKey());
}
@Test
.setParam(PARAM_COMPONENT_KEYS, applicationBranch1.getKey())
.setParam(PARAM_BRANCH, applicationBranch1.getBranch())
.executeProtobuf(SearchWsResponse.class).getIssuesList())
- .extracting(Issue::getKey, Issue::getComponent, Issue::getProject, Issue::getBranch, Issue::hasBranch)
- .containsExactlyInAnyOrder(
- tuple(issueOnProject1Branch1.getKey(), project1Branch1.getKey(), project1Branch1.getKey(), project1Branch1.getBranch(), true),
- tuple(issueOnFileOnProject1Branch1.getKey(), fileOnProject1Branch1.getKey(), project1Branch1.getKey(), project1Branch1.getBranch(), true),
- tuple(issueOnProject2.getKey(), project2.getKey(), project2.getKey(), "", false));
+ .extracting(Issue::getKey, Issue::getComponent, Issue::getProject, Issue::getBranch, Issue::hasBranch)
+ .containsExactlyInAnyOrder(
+ tuple(issueOnProject1Branch1.getKey(), project1Branch1.getKey(), project1Branch1.getKey(), project1Branch1.getBranch(), true),
+ tuple(issueOnFileOnProject1Branch1.getKey(), fileOnProject1Branch1.getKey(), project1Branch1.getKey(), project1Branch1.getBranch(), true),
+ tuple(issueOnProject2.getKey(), project2.getKey(), project2.getKey(), "", false));
// Issues on project1Branch1
assertThat(ws.newRequest()
.setParam(PARAM_PROJECTS, project1.getKey())
.setParam(PARAM_BRANCH, applicationBranch1.getBranch())
.executeProtobuf(SearchWsResponse.class).getIssuesList())
- .extracting(Issue::getKey, Issue::getComponent, Issue::getBranch)
- .containsExactlyInAnyOrder(
- tuple(issueOnProject1Branch1.getKey(), project1Branch1.getKey(), project1Branch1.getBranch()),
- tuple(issueOnFileOnProject1Branch1.getKey(), fileOnProject1Branch1.getKey(), project1Branch1.getBranch()));
+ .extracting(Issue::getKey, Issue::getComponent, Issue::getBranch)
+ .containsExactlyInAnyOrder(
+ tuple(issueOnProject1Branch1.getKey(), project1Branch1.getKey(), project1Branch1.getBranch()),
+ tuple(issueOnFileOnProject1Branch1.getKey(), fileOnProject1Branch1.getKey(), project1Branch1.getBranch()));
}
@Test
.setParam(PARAM_COMPONENT_KEYS, project.getKey())
.setParam(PARAM_BRANCH, branch.getBranch())
.executeProtobuf(SearchWsResponse.class).getIssuesList())
- .extracting(Issue::getKey, Issue::getComponent, Issue::getBranch)
- .containsExactlyInAnyOrder(tuple(branchIssue.getKey(), branchFile.getKey(), branchFile.getBranch()));
+ .extracting(Issue::getKey, Issue::getComponent, Issue::getBranch)
+ .containsExactlyInAnyOrder(tuple(branchIssue.getKey(), branchFile.getKey(), branchFile.getBranch()));
// On project key + branch
assertThat(ws.newRequest()
.setParam(PARAM_PROJECT_KEYS, project.getKey())
.setParam(PARAM_BRANCH, branch.getBranch())
.executeProtobuf(SearchWsResponse.class).getIssuesList())
- .extracting(Issue::getKey, Issue::getComponent, Issue::getBranch)
- .containsExactlyInAnyOrder(tuple(branchIssue.getKey(), branchFile.getKey(), branchFile.getBranch()));
+ .extracting(Issue::getKey, Issue::getComponent, Issue::getBranch)
+ .containsExactlyInAnyOrder(tuple(branchIssue.getKey(), branchFile.getKey(), branchFile.getBranch()));
// On file key + branch
assertThat(ws.newRequest()
.setParam(PARAM_COMPONENT_KEYS, branchFile.getKey())
.setParam(PARAM_BRANCH, branch.getBranch())
.executeProtobuf(SearchWsResponse.class).getIssuesList())
- .extracting(Issue::getKey, Issue::getComponent, Issue::getBranch)
- .containsExactlyInAnyOrder(tuple(branchIssue.getKey(), branchFile.getKey(), branchFile.getBranch()));
+ .extracting(Issue::getKey, Issue::getComponent, Issue::getBranch)
+ .containsExactlyInAnyOrder(tuple(branchIssue.getKey(), branchFile.getKey(), branchFile.getBranch()));
}
@Test
import com.google.common.collect.ImmutableMap;
import java.time.Clock;
+import java.util.Arrays;
import java.util.Map;
+import java.util.Random;
import java.util.stream.IntStream;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
-import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.issue.Issue;
import org.sonar.api.resources.Languages;
import org.sonar.api.rules.RuleType;
import org.sonar.api.server.ws.WebService;
public class SearchActionFacetsTest {
+ private static final RuleType[] RULE_TYPES_EXCEPT_HOTSPOT = Arrays.stream(RuleType.values()).filter(ruleType -> RuleType.SECURITY_HOTSPOT != ruleType).toArray(RuleType[]::new);
+ private static final String[] ISSUE_STATUSES = Issue.STATUSES.stream().filter(s -> !Issue.STATUS_TO_REVIEW.equals(s)).filter(s -> !Issue.STATUS_REVIEWED.equals(s))
+ .toArray(String[]::new);
+
@Rule
public UserSessionRule userSession = standalone();
@Rule
private SearchResponseFormat searchResponseFormat = new SearchResponseFormat(new Durations(), languages, new TextRangeResponseFormatter(), userFormatter);
private WsActionTester ws = new WsActionTester(
- new SearchAction(userSession, issueIndex, issueQueryFactory, searchResponseLoader, searchResponseFormat,
- new MapSettings().asConfig(), System2.INSTANCE, db.getDbClient()));
+ new SearchAction(userSession, issueIndex, issueQueryFactory, searchResponseLoader, searchResponseFormat, System2.INSTANCE, db.getDbClient()));
@Test
public void display_all_facets() {
.executeProtobuf(SearchWsResponse.class);
Map<String, Number> expectedStatuses = ImmutableMap.<String, Number>builder().put("OPEN", 1L).put("CONFIRMED", 0L)
- .put("REOPENED", 0L).put("RESOLVED", 0L).put("CLOSED", 0L).put("TO_REVIEW", 0L).put("REVIEWED", 0L).build();
+ .put("REOPENED", 0L).put("RESOLVED", 0L).put("CLOSED", 0L).build();
assertThat(response.getFacets().getFacetsList())
.extracting(Common.Facet::getProperty, facet -> facet.getValuesList().stream().collect(toMap(FacetValue::getVal, FacetValue::getCount)))
tuple("statuses", expectedStatuses),
tuple("resolutions", of("", 1L, "FALSE-POSITIVE", 0L, "FIXED", 0L, "REMOVED", 0L, "WONTFIX", 0L)),
tuple("rules", of(rule.getKey().toString(), 1L)),
- tuple("types", of("CODE_SMELL", 1L, "BUG", 0L, "VULNERABILITY", 0L, "SECURITY_HOTSPOT", 0L)),
+ tuple("types", of("CODE_SMELL", 1L, "BUG", 0L, "VULNERABILITY", 0L)),
tuple("languages", of(rule.getLanguage(), 1L)),
tuple("projects", of(project.getKey(), 1L)),
tuple("moduleUuids", of(module.uuid(), 1L)),
.executeProtobuf(SearchWsResponse.class);
Map<String, Number> expectedStatuses = ImmutableMap.<String, Number>builder().put("OPEN", 10L).put("CONFIRMED", 0L)
- .put("REOPENED", 0L).put("RESOLVED", 0L).put("CLOSED", 0L).put("TO_REVIEW", 0L).put("REVIEWED", 0L).build();
+ .put("REOPENED", 0L).put("RESOLVED", 0L).put("CLOSED", 0L).build();
assertThat(response.getFacets().getFacetsList())
.extracting(Common.Facet::getProperty, facet -> facet.getValuesList().stream().collect(toMap(FacetValue::getVal, FacetValue::getCount)))
tuple("statuses", expectedStatuses),
tuple("resolutions", of("", 10L, "FALSE-POSITIVE", 0L, "FIXED", 0L, "REMOVED", 0L, "WONTFIX", 0L)),
tuple("rules", of(rule.getKey().toString(), 10L)),
- tuple("types", of("CODE_SMELL", 10L, "BUG", 0L, "VULNERABILITY", 0L, "SECURITY_HOTSPOT", 0L)),
+ tuple("types", of("CODE_SMELL", 10L, "BUG", 0L, "VULNERABILITY", 0L)),
tuple("languages", of(rule.getLanguage(), 10L)),
tuple("projects", of(project.getKey(), 10L)),
tuple("fileUuids", of(file.uuid(), 10L)),
}
@Test
- public void check_facets_max_size() {
+ public void check_facets_max_size_for_issues() {
ComponentDto project = db.components().insertPublicProject();
+ Random random = new Random();
IntStream.rangeClosed(1, 110)
.forEach(index -> {
- RuleDefinitionDto rule = db.rules().insert();
UserDto user = db.users().insertUser();
ComponentDto module = db.components().insertComponent(newModuleDto(project));
ComponentDto directory = db.components().insertComponent(newDirectory(module, "dir" + index));
ComponentDto file = db.components().insertComponent(newFileDto(directory));
- db.issues().insert(rule, project, file, i -> i.setAssigneeUuid(user.getUuid()));
+
+ RuleDefinitionDto rule = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto.setType(RULE_TYPES_EXCEPT_HOTSPOT[random.nextInt(RULE_TYPES_EXCEPT_HOTSPOT.length)]));
+ db.issues().insert(rule, project, file, i -> i.setAssigneeUuid(user.getUuid())
+ .setStatus(ISSUE_STATUSES[random.nextInt(ISSUE_STATUSES.length)])
+ .setType(rule.getType()));
});
+
+ // insert some hotspots which should be filtered by default
+ IntStream.rangeClosed(1, 30)
+ .forEach(index -> {
+ UserDto user = db.users().insertUser();
+ ComponentDto module = db.components().insertComponent(newModuleDto(project));
+ ComponentDto directory = db.components().insertComponent(newDirectory(module, "dir" + index));
+ ComponentDto file = db.components().insertComponent(newFileDto(directory));
+
+ RuleDefinitionDto rule = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto.setType(RuleType.SECURITY_HOTSPOT));
+ db.issues().insert(rule, project, file, i -> i.setAssigneeUuid(user.getUuid())
+ .setStatus(random.nextBoolean() ? Issue.STATUS_TO_REVIEW : Issue.STATUS_REVIEWED)
+ .setType(rule.getType()));
+ });
+
indexPermissions();
indexIssues();
// Assignees contains one additional element : it's the empty string that will return number of unassigned issues
tuple("assignees", 101),
// Following facets returned fixed number of elements
- tuple("statuses", 7),
+ tuple("statuses", 5),
tuple("resolutions", 5),
tuple("severities", 5),
- tuple("types", 4));
+ tuple("types", 3));
}
@Test
.executeProtobuf(SearchWsResponse.class);
Map<String, Number> expectedStatuses = ImmutableMap.<String, Number>builder().put("OPEN", 1L).put("CONFIRMED", 0L)
- .put("REOPENED", 0L).put("RESOLVED", 0L).put("CLOSED", 0L).put("TO_REVIEW", 0L).put("REVIEWED", 0L).build();
+ .put("REOPENED", 0L).put("RESOLVED", 0L).put("CLOSED", 0L).build();
assertThat(response.getFacets().getFacetsList())
.extracting(Common.Facet::getProperty, facet -> facet.getValuesList().stream().collect(toMap(FacetValue::getVal, FacetValue::getCount)))
tuple("statuses", expectedStatuses),
tuple("resolutions", of("", 1L, "FALSE-POSITIVE", 0L, "FIXED", 0L, "REMOVED", 0L, "WONTFIX", 0L)),
tuple("rules", of(rule1.getKey().toString(), 1L, rule2.getKey().toString(), 0L)),
- tuple("types", of("CODE_SMELL", 1L, "BUG", 0L, "VULNERABILITY", 0L, "SECURITY_HOTSPOT", 0L)),
+ tuple("types", of("CODE_SMELL", 1L, "BUG", 0L, "VULNERABILITY", 0L)),
tuple("languages", of(rule1.getLanguage(), 1L, rule2.getLanguage(), 0L)),
tuple("projects", of(project1.getKey(), 1L, project2.getKey(), 0L)),
tuple("moduleUuids", of(module1.uuid(), 1L, module2.uuid(), 0L)),
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
-import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.resources.Languages;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rules.RuleType;
import org.sonar.db.rule.RuleDto;
import org.sonar.db.rule.RuleTesting;
import org.sonar.db.user.UserDto;
-import org.sonar.server.issue.TextRangeResponseFormatter;
import org.sonar.server.es.EsTester;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.es.StartupIndexer;
import org.sonar.server.issue.AvatarResolverImpl;
import org.sonar.server.issue.IssueFieldsSetter;
+import org.sonar.server.issue.TextRangeResponseFormatter;
import org.sonar.server.issue.TransitionService;
import org.sonar.server.issue.index.IssueIndex;
import org.sonar.server.issue.index.IssueIndexer;
import static org.sonar.db.issue.IssueTesting.newDto;
import static org.sonar.server.tester.UserSessionRule.standalone;
import static org.sonarqube.ws.Common.RuleType.BUG;
-import static org.sonarqube.ws.Common.RuleType.SECURITY_HOTSPOT;
import static org.sonarqube.ws.Common.RuleType.VULNERABILITY;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BRANCH;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ADDITIONAL_FIELDS;
private Languages languages = new Languages();
private UserResponseFormatter userFormatter = new UserResponseFormatter(new AvatarResolverImpl());
private SearchResponseFormat searchResponseFormat = new SearchResponseFormat(new Durations(), languages, new TextRangeResponseFormatter(), userFormatter);
- private WsActionTester ws = new WsActionTester(new SearchAction(userSession, issueIndex, issueQueryFactory, searchResponseLoader, searchResponseFormat,
- new MapSettings().asConfig(), System2.INSTANCE, dbClient));
+ private WsActionTester ws = new WsActionTester(
+ new SearchAction(userSession, issueIndex, issueQueryFactory, searchResponseLoader, searchResponseFormat, System2.INSTANCE, dbClient));
private StartupIndexer permissionIndexer = new PermissionIndexer(dbClient, es.client(), issueIndexer);
@Before
}
@Test
- public void security_hotspots_are_returned_by_default() {
+ public void security_hotspots_are_not_returned_by_default() {
ComponentDto project = db.components().insertPublicProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
RuleDefinitionDto rule = db.rules().insert();
assertThat(result.getIssuesList())
.extracting(Issue::getType)
- .containsExactlyInAnyOrder(BUG, VULNERABILITY, Common.RuleType.CODE_SMELL, SECURITY_HOTSPOT);
+ .containsExactlyInAnyOrder(BUG, VULNERABILITY, Common.RuleType.CODE_SMELL);
}
@Test
- public void security_hotspot_type_included_when_explicitly_selected() {
+ public void fail_if_trying_to_filter_issues_by_hotspots() {
ComponentDto project = db.components().insertPublicProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
RuleDefinitionDto rule = newRule().getDefinition();
indexPermissions();
indexIssues();
- assertThat(ws.newRequest()
- .setParam("types", RuleType.SECURITY_HOTSPOT.toString())
- .executeProtobuf(SearchWsResponse.class).getIssuesList())
- .extracting(Issue::getType)
- .containsExactlyInAnyOrder(SECURITY_HOTSPOT);
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Value of parameter 'types' (SECURITY_HOTSPOT) must be one of: [CODE_SMELL, BUG, VULNERABILITY]");
- assertThat(ws.newRequest()
- .setParam("types", String.format("%s,%s", RuleType.BUG, RuleType.SECURITY_HOTSPOT))
- .executeProtobuf(SearchWsResponse.class).getIssuesList())
- .extracting(Issue::getType)
- .containsExactlyInAnyOrder(BUG, SECURITY_HOTSPOT);
+ ws.newRequest()
+ .setParam("types", RuleType.SECURITY_HOTSPOT.toString())
+ .execute();
}
@Test
.containsExactlyInAnyOrder(tuple("MAJOR", 3L), tuple("INFO", 0L), tuple("MINOR", 0L), tuple("CRITICAL", 0L), tuple("BLOCKER", 0L));
}
- @Test
- public void do_not_return_severity_on_security_hotspots() {
- ComponentDto project = db.components().insertPublicProject();
- ComponentDto file = db.components().insertComponent(newFileDto(project));
- RuleDefinitionDto rule = db.rules().insert();
- db.issues().insert(rule, project, file, i -> i.setType(RuleType.BUG).setSeverity(Severity.MAJOR.name()));
- db.issues().insert(rule, project, file, i -> i.setType(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.MAJOR.name()));
- indexPermissions();
- indexIssues();
-
- SearchWsResponse result = ws.newRequest()
- .setParam("types", String.format("%s,%s", RuleType.BUG, RuleType.SECURITY_HOTSPOT))
- .executeProtobuf(SearchWsResponse.class);
-
- assertThat(result.getIssuesList())
- .extracting(Issue::getType, Issue::hasSeverity)
- .containsExactlyInAnyOrder(
- tuple(BUG, true),
- tuple(SECURITY_HOTSPOT, false));
- }
-
@Test
public void return_total_effort() {
UserDto john = db.users().insertUser();
+++ /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.ws;
-
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParser;
-import java.time.Clock;
-import java.util.Arrays;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.api.resources.Languages;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.utils.Durations;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.organization.OrganizationDto;
-import org.sonar.db.rule.RuleDefinitionDto;
-import org.sonar.db.user.UserDto;
-import org.sonar.server.issue.TextRangeResponseFormatter;
-import org.sonar.server.es.EsTester;
-import org.sonar.server.issue.AvatarResolverImpl;
-import org.sonar.server.issue.IssueFieldsSetter;
-import org.sonar.server.issue.TransitionService;
-import org.sonar.server.issue.index.IssueIndex;
-import org.sonar.server.issue.index.IssueIndexer;
-import org.sonar.server.issue.index.IssueIteratorFactory;
-import org.sonar.server.issue.index.IssueQueryFactory;
-import org.sonar.server.issue.workflow.FunctionExecutor;
-import org.sonar.server.issue.workflow.IssueWorkflow;
-import org.sonar.server.permission.index.PermissionIndexerTester;
-import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
-import org.sonar.server.tester.UserSessionRule;
-import org.sonar.server.ws.WsActionTester;
-import org.sonar.test.JsonAssert;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.api.server.ws.WebService.Param.FACETS;
-import static org.sonar.db.component.ComponentTesting.newFileDto;
-import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENT_KEYS;
-import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ORGANIZATION;
-
-public class SearchActionTestOnSonarCloud {
- @Rule
- public UserSessionRule userSession = UserSessionRule.standalone();
- @Rule
- public DbTester db = DbTester.create();
- @Rule
- public EsTester es = EsTester.create();
-
- private MapSettings mapSettings = new MapSettings().setProperty("sonar.sonarcloud.enabled", true);
-
- private DbClient dbClient = db.getDbClient();
- private IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSession, new WebAuthorizationTypeSupport(userSession));
- private IssueIndexer issueIndexer = new IssueIndexer(es.client(), dbClient, new IssueIteratorFactory(dbClient));
- private IssueQueryFactory issueQueryFactory = new IssueQueryFactory(dbClient, Clock.systemUTC(), userSession);
- private IssueFieldsSetter issueFieldsSetter = new IssueFieldsSetter();
- private IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter);
- private SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSession, dbClient, new TransitionService(userSession, issueWorkflow));
- private Languages languages = new Languages();
- private UserResponseFormatter userFormatter = new UserResponseFormatter(new AvatarResolverImpl());
- private SearchResponseFormat searchResponseFormat = new SearchResponseFormat(new Durations(), languages, new TextRangeResponseFormatter(), userFormatter);
- private PermissionIndexerTester permissionIndexer = new PermissionIndexerTester(es, issueIndexer);
-
- private SearchAction underTest = new SearchAction(userSession, issueIndex, issueQueryFactory, searchResponseLoader, searchResponseFormat,
- mapSettings.asConfig(), System2.INSTANCE, dbClient);
- private WsActionTester ws = new WsActionTester(underTest);
-
- private OrganizationDto organization;
- private UserDto user;
- private ComponentDto project;
-
- @Before
- public void setup() {
- underTest.start();
- organization = db.organizations().insert(o -> o.setKey("org-1"));
- user = db.users().insertUser();
-
- project = db.components().insertPublicProject(organization, p -> p.setDbKey("PK1"));
- ComponentDto file = db.components().insertComponent(newFileDto(project, null, "F1").setDbKey("FK1"));
- RuleDefinitionDto rule = db.rules().insert(r -> r.setRuleKey(RuleKey.of("xoo", "x1")));
- db.issues().insert(rule, project, file, i -> i.setAuthorLogin("leia").setKee("2bd4eac2-b650-4037-80bc-7b112bd4eac2"));
- db.issues().insert(rule, project, file, i -> i.setAuthorLogin("luke@skywalker.name").setKee("82fd47d4-b650-4037-80bc-7b1182fd47d4"));
- db.commit();
- allowAnyoneOnProjects(project);
- indexIssues();
- }
-
- @Test
- public void authors_facet_is_hidden_if_organization_is_not_set() {
- db.organizations().addMember(organization, user);
- userSession
- .logIn(user)
- .addMembership(organization);
-
- String input = ws.newRequest()
- .setParam(PARAM_COMPONENT_KEYS, project.getKey())
- .setParam(FACETS, "authors")
- .execute()
- .getInput();
-
- JsonAssert.assertJson(input).isSimilarTo(this.getClass().getResource(this.getClass().getSimpleName() + "/no_authors_facet.json"));
-
- JsonElement gson = new JsonParser().parse(input);
- assertThat(gson.getAsJsonObject().get("facets").getAsJsonArray()).isEmpty();
- }
-
- @Test
- public void authors_facet_is_hidden_if_user_is_not_a_member_of_the_organization() {
- userSession
- .logIn(user);
-
- String input = ws.newRequest()
- .setParam(PARAM_COMPONENT_KEYS, project.getKey())
- .setParam(FACETS, "authors")
- .execute()
- .getInput();
-
- JsonAssert.assertJson(input).isSimilarTo(this.getClass().getResource(this.getClass().getSimpleName() + "/no_author_and_no_authors_facet.json"));
-
- JsonElement gson = new JsonParser().parse(input);
- assertThat(gson.getAsJsonObject().get("facets").getAsJsonArray()).isEmpty();
-
- }
-
- @Test
- public void authors_facet_is_shown_if_organization_is_set_and_user_is_member_of_the_organization() {
- db.organizations().addMember(organization, user);
-
- userSession
- .logIn(user)
- .addMembership(organization);
-
- ws.newRequest()
- .setParam(PARAM_COMPONENT_KEYS, project.getKey())
- .setParam(FACETS, "authors")
- .setParam(PARAM_ORGANIZATION, organization.getKey())
- .execute()
- .assertJson(this.getClass(), "with_authors_facet.json");
- }
-
- private void indexIssues() {
- issueIndexer.indexOnStartup(null);
- }
-
- private void allowAnyoneOnProjects(ComponentDto... projects) {
- userSession.registerComponents(projects);
- Arrays.stream(projects).forEach(p -> permissionIndexer.allowOnlyAnyone(p));
- }
-}
import org.sonar.db.rule.RuleDto;
import org.sonar.server.es.EsTester;
import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.issue.IssueFieldsSetter;
import org.sonar.server.issue.IssueFinder;
@Test
@UseDataProvider("allTypesExceptSecurityHotspot")
- public void fail_if_trying_to_change_type_of_a_hotspot(RuleType type) {
+ public void fail_NFE_if_trying_to_change_type_of_a_hotspot(RuleType type) {
long now = 1_999_777_234L;
when(system2.now()).thenReturn(now);
IssueDto issueDto = issueDbTester.insertHotspot();
setUserWithBrowseAndAdministerIssuePermission(issueDto);
- expectedException.expect(IllegalArgumentException.class);
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage(String.format("Issue with key '%s' does not exist", issueDto.getKey()));
call(issueDto.getKey(), type.name());
}