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;
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";
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)
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);
}
}
- 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);
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()))
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;
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;
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);
}
@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)
.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();
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();
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();
.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();
issueIndexer.indexOnStartup(issueIndexer.getIndexTypes());
}
+ private void indexViews() {
+ viewIndexer.indexOnStartup(viewIndexer.getIndexTypes());
+ }
+
private RuleDefinitionDto newRule(RuleType ruleType) {
return newRule(ruleType, t -> {
});