Browse Source

SONAR-12726 api/hotspots/search supports Applications

tags/8.2.0.32929
Sébastien Lesaint 4 years ago
parent
commit
7d5898c9ad

+ 15
- 0
server/sonar-db-dao/src/testFixtures/java/org/sonar/db/component/ComponentDbTester.java View File

@@ -166,16 +166,31 @@ public class ComponentDbTester {
return insertComponentImpl(ComponentTesting.newView(organization).setPrivate(false), false, dtoPopulators);
}

@SafeVarargs
public final ComponentDto insertPrivatePortfolio(Consumer<ComponentDto>... dtoPopulators) {
return insertComponentImpl(ComponentTesting.newView(db.getDefaultOrganization()).setPrivate(true), true, dtoPopulators);
}

@SafeVarargs
public final ComponentDto insertPrivatePortfolio(OrganizationDto organization, Consumer<ComponentDto>... dtoPopulators) {
return insertComponentImpl(ComponentTesting.newView(organization).setPrivate(true), true, dtoPopulators);
}

@SafeVarargs
public final ComponentDto insertPublicApplication(Consumer<ComponentDto>... dtoPopulators) {
return insertComponentImpl(ComponentTesting.newApplication(db.getDefaultOrganization()).setPrivate(false), false, dtoPopulators);
}

@SafeVarargs
public final ComponentDto insertPublicApplication(OrganizationDto organization, Consumer<ComponentDto>... dtoPopulators) {
return insertComponentImpl(ComponentTesting.newApplication(organization).setPrivate(false), false, dtoPopulators);
}

@SafeVarargs
public final ComponentDto insertPrivateApplication(Consumer<ComponentDto>... dtoPopulators) {
return insertComponentImpl(ComponentTesting.newApplication(db.getDefaultOrganization()).setPrivate(true), true, dtoPopulators);
}

@SafeVarargs
public final ComponentDto insertPrivateApplication(OrganizationDto organization, Consumer<ComponentDto>... dtoPopulators) {
return insertComponentImpl(ComponentTesting.newApplication(organization).setPrivate(true), true, dtoPopulators);

+ 15
- 6
server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java View File

@@ -60,6 +60,7 @@ import org.sonarqube.ws.Common;
import org.sonarqube.ws.Hotspots;
import org.sonarqube.ws.Hotspots.SearchWsResponse;

import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
import static java.util.Collections.singleton;
@@ -84,6 +85,7 @@ import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.WsUtils.nullToEmpty;

public class SearchAction implements HotspotsWsAction {
private static final Set<String> SUPPORTED_QUALIFIERS = ImmutableSet.of(Qualifiers.PROJECT, Qualifiers.APP);
private static final String PARAM_PROJECT_KEY = "projectKey";
private static final String PARAM_STATUS = "status";
private static final String PARAM_RESOLUTION = "resolution";
@@ -118,7 +120,7 @@ public class SearchAction implements HotspotsWsAction {
action.addPagingParams(100);
action.createParam(PARAM_PROJECT_KEY)
.setDescription(format(
"Key of the project. This parameter is required unless %s is provided.",
"Key of the project or application. This parameter is required unless %s is provided.",
PARAM_HOTSPOTS))
.setExampleValue(KEY_PROJECT_EXAMPLE_001);
action.createParam(PARAM_BRANCH)
@@ -162,7 +164,7 @@ public class SearchAction implements HotspotsWsAction {
WsRequest wsRequest = toWsRequest(request);
validateParameters(wsRequest);
try (DbSession dbSession = dbClient.openSession(false)) {
Optional<ComponentDto> project = getAndValidateProject(dbSession, wsRequest);
Optional<ComponentDto> project = getAndValidateProjectOrApplication(dbSession, wsRequest);

SearchResponseData searchResponseData = searchHotspots(wsRequest, dbSession, project, wsRequest.getHotspotKeys());
loadComponents(dbSession, searchResponseData);
@@ -222,10 +224,10 @@ public class SearchAction implements HotspotsWsAction {
}
}

private Optional<ComponentDto> getAndValidateProject(DbSession dbSession, WsRequest wsRequest) {
private Optional<ComponentDto> getAndValidateProjectOrApplication(DbSession dbSession, WsRequest wsRequest) {
return wsRequest.getProjectKey().map(projectKey -> {
ComponentDto project = getProject(dbSession, projectKey, wsRequest.getBranch(), wsRequest.getPullRequest())
.filter(t -> Scopes.PROJECT.equals(t.scope()) && Qualifiers.PROJECT.equals(t.qualifier()))
.filter(t -> Scopes.PROJECT.equals(t.scope()) && SUPPORTED_QUALIFIERS.contains(t.qualifier()))
.filter(ComponentDto::isEnabled)
.orElseThrow(() -> new NotFoundException(format("Project '%s' not found", projectKey)));
userSession.checkComponentPermission(UserRole.USER, project);
@@ -274,13 +276,20 @@ public class SearchAction implements HotspotsWsAction {
project.ifPresent(p -> {
builder.organizationUuid(p.getOrganizationUuid());

String projectUuid = firstNonNull(p.getMainBranchProjectUuid(), p.uuid());
if (Qualifiers.APP.equals(p.qualifier())) {
builder.viewUuids(singletonList(projectUuid));
} else {
builder.projectUuids(singletonList(projectUuid));
}

if (p.getMainBranchProjectUuid() == null) {
builder.mainBranch(true);
builder.projectUuids(singletonList(p.uuid()));
} else {
builder.branchUuid(p.projectUuid());
builder.branchUuid(p.uuid());
builder.mainBranch(false);
}

if (wsRequest.isSinceLeakPeriod()) {
dbClient.snapshotDao().selectLastAnalysisByComponentUuid(dbSession, p.uuid())
.map(s -> longToDate(s.getPeriodDate()))

+ 133
- 6
server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/SearchActionTest.java View File

@@ -52,7 +52,6 @@ import org.sonar.db.issue.IssueDto;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.rule.RuleTesting;
import org.sonar.server.es.EsTester;
import org.sonar.server.es.StartupIndexer;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.issue.index.IssueIndex;
@@ -64,6 +63,7 @@ import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
import org.sonar.server.security.SecurityStandards;
import org.sonar.server.security.SecurityStandards.SQCategory;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.view.index.ViewIndexer;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.Hotspots;
@@ -105,7 +105,8 @@ public class SearchActionTest {

private IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSessionRule, new WebAuthorizationTypeSupport(userSessionRule));
private IssueIndexer issueIndexer = new IssueIndexer(es.client(), dbClient, new IssueIteratorFactory(dbClient));
private StartupIndexer permissionIndexer = new PermissionIndexer(dbClient, es.client(), issueIndexer);
private ViewIndexer viewIndexer = new ViewIndexer(dbClient, es.client());
private PermissionIndexer permissionIndexer = new PermissionIndexer(dbClient, es.client(), issueIndexer);
private HotspotWsResponseFormatter responseFormatter = new HotspotWsResponseFormatter(defaultOrganizationProvider);

private SearchAction underTest = new SearchAction(dbClient, userSessionRule, issueIndex, responseFormatter);
@@ -268,15 +269,14 @@ public class SearchActionTest {
}

@Test
public void fails_with_NotFoundException_if_project_is_not_a_project() {
public void fails_with_NotFoundException_if_project_is_neither_a_project_nor_an_application() {
ComponentDto project = dbTester.components().insertPrivateProject();
ComponentDto directory = dbTester.components().insertComponent(ComponentTesting.newDirectory(project, "foo"));
ComponentDto file = dbTester.components().insertComponent(ComponentTesting.newFileDto(project));
ComponentDto portfolio = dbTester.components().insertPrivatePortfolio(dbTester.getDefaultOrganization());
ComponentDto application = dbTester.components().insertPrivateApplication(dbTester.getDefaultOrganization());
ComponentDto portfolio = dbTester.components().insertPrivatePortfolio();
TestRequest request = actionTester.newRequest();

for (ComponentDto component : Arrays.asList(directory, file, portfolio, application)) {
for (ComponentDto component : Arrays.asList(directory, file, portfolio)) {
request.setParam("projectKey", component.getKey());

assertThatThrownBy(request::execute)
@@ -296,6 +296,17 @@ public class SearchActionTest {
.hasMessage("Insufficient privileges");
}

@Test
public void fails_with_ForbiddenException_if_application_is_private_and_not_allowed() {
ComponentDto application = dbTester.components().insertPrivateApplication();
userSessionRule.registerComponents(application);
TestRequest request = newRequest(application);

assertThatThrownBy(request::execute)
.isInstanceOf(ForbiddenException.class)
.hasMessage("Insufficient privileges");
}

@Test
public void succeeds_on_public_project() {
ComponentDto project = dbTester.components().insertPublicProject();
@@ -308,6 +319,18 @@ public class SearchActionTest {
assertThat(response.getComponentsList()).isEmpty();
}

@Test
public void succeeds_on_public_application() {
ComponentDto application = dbTester.components().insertPublicApplication();
userSessionRule.registerComponents(application);

SearchWsResponse response = newRequest(application)
.executeProtobuf(SearchWsResponse.class);

assertThat(response.getHotspotsList()).isEmpty();
assertThat(response.getComponentsList()).isEmpty();
}

@Test
public void succeeds_on_private_project_with_permission() {
ComponentDto project = dbTester.components().insertPrivateProject();
@@ -321,6 +344,19 @@ public class SearchActionTest {
assertThat(response.getComponentsList()).isEmpty();
}

@Test
public void succeeds_on_private_application_with_permission() {
ComponentDto application = dbTester.components().insertPrivateApplication();
userSessionRule.registerComponents(application);
userSessionRule.logIn().addProjectPermission(UserRole.USER, application);

SearchWsResponse response = newRequest(application)
.executeProtobuf(SearchWsResponse.class);

assertThat(response.getHotspotsList()).isEmpty();
assertThat(response.getComponentsList()).isEmpty();
}

@Test
public void does_not_fail_if_rule_of_hotspot_does_not_exist_in_DB() {
ComponentDto project = dbTester.components().insertPublicProject();
@@ -462,6 +498,93 @@ public class SearchActionTest {
.containsOnly(project2.getKey(), file2.getKey());
}

@Test
public void returns_hotspots_of_specified_application() {
ComponentDto application1 = dbTester.components().insertPublicApplication();
ComponentDto application2 = dbTester.components().insertPublicApplication();
ComponentDto project1 = dbTester.components().insertPublicProject();
ComponentDto project2 = dbTester.components().insertPublicProject();
dbTester.components().insertComponent(ComponentTesting.newProjectCopy(project1, application1));
dbTester.components().insertComponent(ComponentTesting.newProjectCopy(project2, application2));
indexViews();
userSessionRule.registerComponents(application1, application2, project1, project2);
indexPermissions();
ComponentDto file1 = dbTester.components().insertComponent(newFileDto(project1));
ComponentDto file2 = dbTester.components().insertComponent(newFileDto(project2));
IssueDto[] hotspots2 = IntStream.range(0, 1 + RANDOM.nextInt(10))
.mapToObj(i -> {
RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
insertHotspot(project1, file1, rule);
return insertHotspot(project2, file2, rule);
})
.toArray(IssueDto[]::new);
indexIssues();

SearchWsResponse responseApplication1 = newRequest(application1)
.executeProtobuf(SearchWsResponse.class);

assertThat(responseApplication1.getHotspotsList())
.extracting(SearchWsResponse.Hotspot::getKey)
.doesNotContainAnyElementsOf(Arrays.stream(hotspots2).map(IssueDto::getKey).collect(toList()));
assertThat(responseApplication1.getComponentsList())
.extracting(Component::getKey)
.containsOnly(project1.getKey(), file1.getKey());

SearchWsResponse responseApplication2 = newRequest(application2)
.executeProtobuf(SearchWsResponse.class);

assertThat(responseApplication2.getHotspotsList())
.extracting(SearchWsResponse.Hotspot::getKey)
.containsOnly(Arrays.stream(hotspots2).map(IssueDto::getKey).toArray(String[]::new));
assertThat(responseApplication2.getComponentsList())
.extracting(Component::getKey)
.containsOnly(project2.getKey(), file2.getKey());
}

@Test
public void returns_hotspots_of_specified_application_branch() {
ComponentDto application = dbTester.components().insertPublicApplication();
ComponentDto applicationBranch = dbTester.components().insertProjectBranch(application);
ComponentDto project1 = dbTester.components().insertPublicProject();
ComponentDto project2 = dbTester.components().insertPublicProject();
dbTester.components().insertComponent(ComponentTesting.newProjectCopy(project1, application));
dbTester.components().insertComponent(ComponentTesting.newProjectCopy(project2, applicationBranch));
indexViews();
userSessionRule.registerComponents(application, applicationBranch, project1, project2);
indexPermissions();
ComponentDto file1 = dbTester.components().insertComponent(newFileDto(project1));
ComponentDto file2 = dbTester.components().insertComponent(newFileDto(project2));
IssueDto[] hotspots2 = IntStream.range(0, 1 + RANDOM.nextInt(10))
.mapToObj(i -> {
RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
insertHotspot(project1, file1, rule);
return insertHotspot(project2, file2, rule);
})
.toArray(IssueDto[]::new);
indexIssues();

SearchWsResponse responseApplication = newRequest(application)
.executeProtobuf(SearchWsResponse.class);

assertThat(responseApplication.getHotspotsList())
.extracting(SearchWsResponse.Hotspot::getKey)
.doesNotContainAnyElementsOf(Arrays.stream(hotspots2).map(IssueDto::getKey).collect(toList()));
assertThat(responseApplication.getComponentsList())
.extracting(Component::getKey)
.containsOnly(project1.getKey(), file1.getKey());

SearchWsResponse responseApplicationBranch = newRequest(applicationBranch)
.executeProtobuf(SearchWsResponse.class);

assertThat(responseApplicationBranch.getHotspotsList())
.extracting(SearchWsResponse.Hotspot::getKey)
.containsOnly(Arrays.stream(hotspots2).map(IssueDto::getKey).toArray(String[]::new));
assertThat(responseApplicationBranch.getComponentsList())
.extracting(Component::getKey)
.containsOnly(project2.getKey(), file2.getKey());

}

@Test
public void returns_hotspot_of_branch_or_pullRequest() {
ComponentDto project = dbTester.components().insertPublicProject();
@@ -1261,6 +1384,10 @@ public class SearchActionTest {
issueIndexer.indexOnStartup(issueIndexer.getIndexTypes());
}

private void indexViews() {
viewIndexer.indexOnStartup(viewIndexer.getIndexTypes());
}

private RuleDefinitionDto newRule(RuleType ruleType) {
return newRule(ruleType, t -> {
});

Loading…
Cancel
Save