@@ -19,7 +19,9 @@ | |||
*/ | |||
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; | |||
@@ -40,6 +42,8 @@ import static org.apache.commons.lang.math.RandomUtils.nextInt; | |||
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 | |||
@@ -52,7 +56,7 @@ public class IssueTesting { | |||
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) |
@@ -19,6 +19,7 @@ | |||
*/ | |||
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; | |||
@@ -28,6 +29,7 @@ import org.sonar.server.user.UserSession; | |||
import static java.lang.String.format; | |||
import static java.util.Objects.requireNonNull; | |||
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT; | |||
public class IssueFinder { | |||
@@ -41,6 +43,12 @@ 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; | |||
} |
@@ -19,7 +19,6 @@ | |||
*/ | |||
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; | |||
@@ -27,14 +26,11 @@ import java.util.Arrays; | |||
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; | |||
@@ -47,7 +43,6 @@ import org.sonar.api.utils.System2; | |||
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; | |||
@@ -81,7 +76,6 @@ import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW; | |||
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; | |||
@@ -135,9 +129,10 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_STATUSES; | |||
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, | |||
@@ -163,26 +158,22 @@ public class SearchAction implements IssuesWsAction, Startable { | |||
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; | |||
} | |||
@@ -199,8 +190,10 @@ public class SearchAction implements IssuesWsAction, Startable { | |||
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)), | |||
@@ -261,7 +254,7 @@ public class SearchAction implements IssuesWsAction, Startable { | |||
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.") | |||
@@ -391,14 +384,14 @@ public class SearchAction implements IssuesWsAction, Startable { | |||
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); | |||
@@ -407,7 +400,7 @@ public class SearchAction implements IssuesWsAction, Startable { | |||
.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); | |||
@@ -436,7 +429,7 @@ public class SearchAction implements IssuesWsAction, Startable { | |||
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()); | |||
@@ -446,29 +439,13 @@ public class SearchAction implements IssuesWsAction, Startable { | |||
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()); | |||
@@ -486,13 +463,23 @@ public class SearchAction implements IssuesWsAction, Startable { | |||
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) { | |||
@@ -562,13 +549,20 @@ public class SearchAction implements IssuesWsAction, Startable { | |||
.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<>(); | |||
@@ -592,14 +586,4 @@ public class SearchAction implements IssuesWsAction, Startable { | |||
} | |||
return assigneeUuid; | |||
} | |||
@Override | |||
public void start() { | |||
this.isOnSonarCloud = config.getBoolean(SONARCLOUD_ENABLED.getKey()).orElse(false); | |||
} | |||
@Override | |||
public void stop() { | |||
// Nothing to do | |||
} | |||
} |
@@ -19,12 +19,15 @@ | |||
*/ | |||
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; | |||
@@ -57,6 +60,8 @@ import static org.sonar.api.web.UserRole.USER; | |||
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(); | |||
@@ -81,7 +86,7 @@ public class EditCommentActionTest { | |||
@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); | |||
@@ -98,7 +103,7 @@ public class EditCommentActionTest { | |||
@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); | |||
@@ -115,7 +120,7 @@ public class EditCommentActionTest { | |||
@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(); | |||
@@ -128,7 +133,7 @@ public class EditCommentActionTest { | |||
@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); | |||
@@ -164,7 +169,7 @@ public class EditCommentActionTest { | |||
@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); | |||
@@ -181,7 +186,7 @@ public class EditCommentActionTest { | |||
@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); | |||
@@ -190,6 +195,18 @@ public class EditCommentActionTest { | |||
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(); | |||
@@ -207,6 +224,10 @@ public class EditCommentActionTest { | |||
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(), |
@@ -25,7 +25,6 @@ import java.util.Date; | |||
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; | |||
@@ -110,7 +109,7 @@ public class SearchActionComponentsTest { | |||
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() { | |||
@@ -169,12 +168,12 @@ public class SearchActionComponentsTest { | |||
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 | |||
@@ -482,11 +481,11 @@ public class SearchActionComponentsTest { | |||
.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() | |||
@@ -494,10 +493,10 @@ public class SearchActionComponentsTest { | |||
.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 | |||
@@ -668,24 +667,24 @@ public class SearchActionComponentsTest { | |||
.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 |
@@ -21,12 +21,14 @@ package org.sonar.server.issue.ws; | |||
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; | |||
@@ -73,6 +75,10 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECTS; | |||
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 | |||
@@ -92,8 +98,7 @@ public class SearchActionFacetsTest { | |||
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() { | |||
@@ -117,7 +122,7 @@ public class SearchActionFacetsTest { | |||
.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))) | |||
@@ -126,7 +131,7 @@ public class SearchActionFacetsTest { | |||
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)), | |||
@@ -155,7 +160,7 @@ public class SearchActionFacetsTest { | |||
.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))) | |||
@@ -164,7 +169,7 @@ public class SearchActionFacetsTest { | |||
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)), | |||
@@ -420,17 +425,36 @@ public class SearchActionFacetsTest { | |||
} | |||
@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(); | |||
@@ -450,10 +474,10 @@ public class SearchActionFacetsTest { | |||
// 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 | |||
@@ -509,7 +533,7 @@ public class SearchActionFacetsTest { | |||
.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))) | |||
@@ -518,7 +542,7 @@ public class SearchActionFacetsTest { | |||
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)), |
@@ -28,7 +28,6 @@ import org.junit.Before; | |||
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; | |||
@@ -51,12 +50,12 @@ import org.sonar.db.rule.RuleDefinitionDto; | |||
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; | |||
@@ -92,7 +91,6 @@ import static org.sonar.db.component.ComponentTesting.newFileDto; | |||
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; | |||
@@ -123,8 +121,8 @@ public class SearchActionTest { | |||
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 | |||
@@ -682,7 +680,7 @@ public class SearchActionTest { | |||
} | |||
@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(); | |||
@@ -697,11 +695,11 @@ public class SearchActionTest { | |||
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(); | |||
@@ -712,17 +710,12 @@ public class SearchActionTest { | |||
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 | |||
@@ -750,27 +743,6 @@ public class SearchActionTest { | |||
.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(); |
@@ -1,170 +0,0 @@ | |||
/* | |||
* 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)); | |||
} | |||
} |
@@ -48,6 +48,7 @@ import org.sonar.db.rule.RuleDefinitionDto; | |||
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; | |||
@@ -189,13 +190,14 @@ public class SetTypeActionTest { | |||
@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()); | |||
} | |||