Browse Source

SONAR-9181 WS api/projects/bulk_delete accepts the sames parameters as api/projects/search

tags/6.6-RC1
Teryk Bellahsene 6 years ago
parent
commit
0926670e79

+ 76
- 44
server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java View File

@@ -19,23 +19,35 @@
*/
package org.sonar.server.project.ws;

import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.WebService.Param;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentQuery;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.permission.OrganizationPermission;
import org.sonar.server.component.ComponentCleanerService;
import org.sonar.server.project.Visibility;
import org.sonar.server.user.UserSession;

import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
import org.sonarqube.ws.client.project.SearchWsRequest;

import static org.sonar.api.resources.Qualifiers.APP;
import static org.sonar.api.resources.Qualifiers.PROJECT;
import static org.sonar.api.resources.Qualifiers.VIEW;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02;
import static org.sonar.server.project.ws.SearchAction.buildDbQuery;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_002;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ANALYZED_BEFORE;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ON_PROVISIONED_ONLY;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ORGANIZATION;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECTS;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT_IDS;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_QUALIFIERS;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_VISIBILITY;

public class BulkDeleteAction implements ProjectsWsAction {

@@ -64,59 +76,79 @@ public class BulkDeleteAction implements ProjectsWsAction {
.setSince("5.2")
.setHandler(this);

action
.createParam(PARAM_PROJECT_IDS)
.setDescription("List of project IDs to delete")
.setDeprecatedKey("ids", "6.4")
.setDeprecatedSince("6.4")
.setExampleValue("ce4c03d6-430f-40a9-b777-ad877c00aa4d,c526ef20-131b-4486-9357-063fa64b5079");
support.addOrganizationParam(action);

action
.createParam(PARAM_PROJECTS)
.setDescription("List of project keys to delete")
.setDescription("Comma-separated list of project keys")
.setDeprecatedKey("keys", "6.4")
.setExampleValue(KEY_PROJECT_EXAMPLE_001);
.setExampleValue(String.join(",", KEY_PROJECT_EXAMPLE_001, KEY_PROJECT_EXAMPLE_002));

support.addOrganizationParam(action);
action
.createParam(PARAM_PROJECT_IDS)
.setDescription("Comma-separated list of project ids")
.setDeprecatedKey("ids", "6.4")
.setDeprecatedSince("6.4")
.setExampleValue(String.join(",", UUID_EXAMPLE_01, UUID_EXAMPLE_02));

action.createParam(Param.TEXT_QUERY)
.setDescription("Limit to: <ul>" +
"<li>component names that contain the supplied string</li>" +
"<li>component keys that contain the supplied string</li>" +
"</ul>")
.setExampleValue("sonar");

action.createParam(PARAM_QUALIFIERS)
.setDescription("Comma-separated list of component qualifiers. Filter the results with the specified qualifiers")
.setPossibleValues(PROJECT, VIEW, APP)
.setDefaultValue(PROJECT);

action.createParam(PARAM_VISIBILITY)
.setDescription("Filter the projects that should be visible to everyone (%s), or only specific user/groups (%s).<br/>" +
"If no visibility is specified, the default project visibility of the organization will be used.",
Visibility.PUBLIC.getLabel(), Visibility.PRIVATE.getLabel())
.setRequired(false)
.setInternal(true)
.setSince("6.4")
.setPossibleValues(Visibility.getLabels());

action.createParam(PARAM_ANALYZED_BEFORE)
.setDescription("Filter the projects for which last analysis is older than the given date (exclusive).<br> " +
"Format: date or datetime ISO formats.")
.setSince("6.6");

action.createParam(PARAM_ON_PROVISIONED_ONLY)
.setDescription("Filter the projects that are provisioned")
.setBooleanPossibleValues()
.setDefaultValue("false")
.setSince("6.6");
}

@Override
public void handle(Request request, Response response) throws Exception {
SearchWsRequest searchRequest = toSearchWsRequest(request);
userSession.checkLoggedIn();

List<String> uuids = request.paramAsStrings(PARAM_PROJECT_IDS);
List<String> keys = request.paramAsStrings(PARAM_PROJECTS);
String orgKey = request.param(ProjectsWsSupport.PARAM_ORGANIZATION);

try (DbSession dbSession = dbClient.openSession(false)) {
Optional<OrganizationDto> org = loadOrganizationByKey(dbSession, orgKey);
List<ComponentDto> projects = searchProjects(dbSession, uuids, keys);
projects.stream()
.filter(p -> !org.isPresent() || org.get().getUuid().equals(p.getOrganizationUuid()))
OrganizationDto organization = support.getOrganization(dbSession, searchRequest.getOrganization());
userSession.checkPermission(OrganizationPermission.ADMINISTER, organization);

ComponentQuery query = buildDbQuery(searchRequest);
dbClient.componentDao().selectByQuery(dbSession, organization.getUuid(), query, 0, Integer.MAX_VALUE)
.forEach(p -> componentCleanerService.delete(dbSession, p));
}

response.noContent();
}

private Optional<OrganizationDto> loadOrganizationByKey(DbSession dbSession, @Nullable String orgKey) {
if (orgKey == null) {
userSession.checkIsSystemAdministrator();
return Optional.empty();
}
OrganizationDto org = support.getOrganization(dbSession, orgKey);
userSession.checkPermission(ADMINISTER, org);
return Optional.of(org);
}

private List<ComponentDto> searchProjects(DbSession dbSession, @Nullable List<String> uuids, @Nullable List<String> keys) {
if (uuids != null) {
return dbClient.componentDao().selectByUuids(dbSession, uuids);
}
if (keys != null) {
return dbClient.componentDao().selectByKeys(dbSession, keys);
}

throw new IllegalArgumentException("ids or keys must be provided");
private static SearchWsRequest toSearchWsRequest(Request request) {
return SearchWsRequest.builder()
.setOrganization(request.param(PARAM_ORGANIZATION))
.setQualifiers(request.mandatoryParamAsStrings(PARAM_QUALIFIERS))
.setQuery(request.param(Param.TEXT_QUERY))
.setVisibility(request.param(PARAM_VISIBILITY))
.setAnalyzedBefore(request.param(PARAM_ANALYZED_BEFORE))
.setOnProvisionedOnly(request.mandatoryParamAsBoolean(PARAM_ON_PROVISIONED_ONLY))
.setProjects(request.paramAsStrings(PARAM_PROJECTS))
.setProjectIds(request.paramAsStrings(PARAM_PROJECT_IDS))
.build();
}
}

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java View File

@@ -176,7 +176,7 @@ public class SearchAction implements ProjectsWsAction {
}
}

private static ComponentQuery buildDbQuery(SearchWsRequest request) {
static ComponentQuery buildDbQuery(SearchWsRequest request) {
List<String> qualifiers = request.getQualifiers();
ComponentQuery.Builder query = ComponentQuery.builder()
.setQualifiers(qualifiers.toArray(new String[qualifiers.size()]));

+ 109
- 34
server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java View File

@@ -19,11 +19,17 @@
*/
package org.sonar.server.project.ws;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Date;
import java.util.stream.IntStream;
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.resources.Qualifiers;
import org.sonar.api.server.ws.WebService.Param;
import org.sonar.api.utils.System2;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
@@ -36,7 +42,8 @@ import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.organization.BillingValidationsProxy;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.WsTester;
import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
@@ -44,80 +51,149 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.sonar.api.utils.DateUtils.formatDate;
import static org.sonar.db.component.SnapshotTesting.newAnalysis;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ANALYZED_BEFORE;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ON_PROVISIONED_ONLY;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ORGANIZATION;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECTS;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_QUALIFIERS;

public class BulkDeleteActionTest {

private static final String ACTION = "bulk_delete";

@Rule
public DbTester db = DbTester.create(System2.INSTANCE);

@Rule
public UserSessionRule userSession = UserSessionRule.standalone();

@Rule
public ExpectedException expectedException = ExpectedException.none();

private ComponentCleanerService componentCleanerService = mock(ComponentCleanerService.class);
private WsTester ws;
private DbClient dbClient = db.getDbClient();
private ProjectsWsSupport support = new ProjectsWsSupport(dbClient, TestDefaultOrganizationProvider.from(db), mock(BillingValidationsProxy.class));

private BulkDeleteAction underTest = new BulkDeleteAction(componentCleanerService, dbClient, userSession, support);
private WsActionTester ws = new WsActionTester(underTest);

private OrganizationDto org1;
private OrganizationDto org2;

@Before
public void setUp() {
ws = new WsTester(new ProjectsWs(underTest));
org1 = db.organizations().insert();
org2 = db.organizations().insert();
}

@Test
public void system_administrator_deletes_projects_by_uuids_in_all_organizations() throws Exception {
userSession.logIn().setSystemAdministrator();
public void delete_projects_in_default_organization_if_no_org_provided() throws Exception {
userSession.logIn().setRoot();
OrganizationDto defaultOrganization = db.getDefaultOrganization();
ComponentDto toDeleteInOrg1 = db.components().insertPrivateProject(org1);
ComponentDto toDeleteInOrg2 = db.components().insertPrivateProject(org2);
ComponentDto toKeep = db.components().insertPrivateProject(org2);
ComponentDto toDeleteInOrg2 = db.components().insertPrivateProject(defaultOrganization);
ComponentDto toKeep = db.components().insertPrivateProject(defaultOrganization);

WsTester.Result result = ws.newPostRequest("api/projects", ACTION)
.setParam("ids", toDeleteInOrg1.uuid() + "," + toDeleteInOrg2.uuid())
TestResponse result = ws.newRequest()
.setParam("projectIds", toDeleteInOrg1.uuid() + "," + toDeleteInOrg2.uuid())
.execute();
result.assertNoContent();

verifyDeleted(toDeleteInOrg1, toDeleteInOrg2);
assertThat(result.getStatus()).isEqualTo(HttpURLConnection.HTTP_NO_CONTENT);
assertThat(result.getInput()).isEmpty();
verifyDeleted(toDeleteInOrg2);
}

@Test
public void system_administrator_deletes_projects_by_keys_in_all_organizations() throws Exception {
userSession.logIn().setSystemAdministrator();
public void delete_projects_by_keys() throws Exception {
userSession.logIn().setRoot();
ComponentDto toDeleteInOrg1 = db.components().insertPrivateProject(org1);
ComponentDto toDeleteInOrg2 = db.components().insertPrivateProject(org2);
ComponentDto toKeep = db.components().insertPrivateProject(org2);
ComponentDto toDeleteInOrg2 = db.components().insertPrivateProject(org1);
ComponentDto toKeep = db.components().insertPrivateProject(org1);

WsTester.Result result = ws.newPostRequest("api/projects", ACTION)
.setParam("keys", toDeleteInOrg1.getDbKey() + "," + toDeleteInOrg2.getDbKey())
ws.newRequest()
.setParam(PARAM_ORGANIZATION, org1.getKey())
.setParam(PARAM_PROJECTS, toDeleteInOrg1.getDbKey() + "," + toDeleteInOrg2.getDbKey())
.execute();
result.assertNoContent();

verifyDeleted(toDeleteInOrg1, toDeleteInOrg2);
}

@Test
public void projects_that_dont_exist_are_ignored_and_dont_break_bulk_deletion() throws Exception {
userSession.logIn().setSystemAdministrator();
userSession.logIn().setRoot();
ComponentDto toDelete1 = db.components().insertPrivateProject(org1);
ComponentDto toDelete2 = db.components().insertPrivateProject(org1);

WsTester.Result result = ws.newPostRequest("api/projects", ACTION)
.setParam("keys", toDelete1.getDbKey() + ",missing," + toDelete2.getDbKey() + ",doesNotExist")
ws.newRequest()
.setParam("organization", org1.getKey())
.setParam("projects", toDelete1.getDbKey() + ",missing," + toDelete2.getDbKey() + ",doesNotExist")
.execute();
result.assertNoContent();

verifyDeleted(toDelete1, toDelete2);
}

@Test
public void old_projects() {
userSession.logIn().addPermission(ADMINISTER, db.getDefaultOrganization());
long aLongTimeAgo = 1_000_000_000L;
long recentTime = 3_000_000_000L;
ComponentDto oldProject = db.components().insertPublicProject();
db.getDbClient().snapshotDao().insert(db.getSession(), newAnalysis(oldProject).setCreatedAt(aLongTimeAgo));
ComponentDto recentProject = db.components().insertPublicProject();
db.getDbClient().snapshotDao().insert(db.getSession(), newAnalysis(recentProject).setCreatedAt(recentTime));
db.commit();

ws.newRequest()
.setParam(PARAM_ANALYZED_BEFORE, formatDate(new Date(recentTime)))
.execute();

verifyDeleted(oldProject);
}

@Test
public void provisioned_projects() {
userSession.logIn().addPermission(ADMINISTER, db.getDefaultOrganization());
ComponentDto provisionedProject = db.components().insertPrivateProject();
ComponentDto analyzedProject = db.components().insertPrivateProject();
db.components().insertSnapshot(newAnalysis(analyzedProject));

ws.newRequest().setParam(PARAM_ON_PROVISIONED_ONLY, "true").execute();

verifyDeleted(provisionedProject);
}

@Test
public void delete_more_than_50_projects() {
userSession.logIn().addPermission(ADMINISTER, db.getDefaultOrganization());
ComponentDto[] projects = IntStream.range(0, 55).mapToObj(i -> db.components().insertPrivateProject()).toArray(ComponentDto[]::new);

ws.newRequest().execute();

verifyDeleted(projects);
}

@Test
public void projects_and_views() throws IOException {
userSession.logIn().addPermission(ADMINISTER, db.getDefaultOrganization());
ComponentDto project = db.components().insertPrivateProject();
ComponentDto view = db.components().insertView();

ws.newRequest().setParam(PARAM_QUALIFIERS, String.join(",", Qualifiers.PROJECT, Qualifiers.VIEW)).execute();

verifyDeleted(project, view);
}

@Test
public void delete_by_key_query_with_partial_match_case_insensitive() throws IOException {
userSession.logIn().addPermission(ADMINISTER, db.getDefaultOrganization());
ComponentDto matchKeyProject = db.components().insertPrivateProject(p -> p.setDbKey("project-_%-key"));
ComponentDto matchUppercaseKeyProject = db.components().insertPrivateProject(p -> p.setDbKey("PROJECT-_%-KEY"));
ComponentDto noMatchProject = db.components().insertPrivateProject(p -> p.setDbKey("project-key-without-escaped-characters"));

ws.newRequest().setParam(Param.TEXT_QUERY, "JeCt-_%-k").execute();

verifyDeleted(matchKeyProject, matchUppercaseKeyProject);
}

@Test
public void throw_ForbiddenException_if_organization_administrator_does_not_set_organization_parameter() throws Exception {
userSession.logIn().addPermission(ADMINISTER, org1);
@@ -126,8 +202,8 @@ public class BulkDeleteActionTest {
expectedException.expect(ForbiddenException.class);
expectedException.expectMessage("Insufficient privileges");

ws.newPostRequest("api/projects", ACTION)
.setParam("keys", project.getDbKey())
ws.newRequest()
.setParam("projects", project.getDbKey())
.execute();

verifyNoDeletions();
@@ -139,11 +215,10 @@ public class BulkDeleteActionTest {
ComponentDto toDelete = db.components().insertPrivateProject(org1);
ComponentDto cantBeDeleted = db.components().insertPrivateProject(org2);

WsTester.Result result = ws.newPostRequest("api/projects", ACTION)
ws.newRequest()
.setParam("organization", org1.getKey())
.setParam("keys", toDelete.getDbKey() + "," + cantBeDeleted.getDbKey())
.setParam("projects", toDelete.getDbKey() + "," + cantBeDeleted.getDbKey())
.execute();
result.assertNoContent();

verifyDeleted(toDelete);
}
@@ -153,7 +228,7 @@ public class BulkDeleteActionTest {
expectedException.expect(UnauthorizedException.class);
expectedException.expectMessage("Authentication is required");

ws.newPostRequest("api/projects", ACTION)
ws.newRequest()
.setParam("ids", "whatever-the-uuid").execute();

verifyNoDeletions();
@@ -166,7 +241,7 @@ public class BulkDeleteActionTest {
expectedException.expect(ForbiddenException.class);
expectedException.expectMessage("Insufficient privileges");

ws.newPostRequest("api/projects", ACTION)
ws.newRequest()
.setParam("ids", "whatever-the-uuid").execute();

verifyNoDeletions();
@@ -179,7 +254,7 @@ public class BulkDeleteActionTest {
expectedException.expect(ForbiddenException.class);
expectedException.expectMessage("Insufficient privileges");

ws.newPostRequest("api/projects", ACTION)
ws.newRequest()
.setParam("organization", org1.getKey())
.setParam("ids", "whatever-the-uuid")
.execute();

+ 1
- 4
sonar-ws/src/main/java/org/sonarqube/ws/client/BaseService.java View File

@@ -19,7 +19,6 @@
*/
package org.sonarqube.ws.client;

import com.google.common.base.Joiner;
import com.google.protobuf.Message;
import com.google.protobuf.Parser;
import java.io.InputStream;
@@ -34,8 +33,6 @@ import static com.google.common.base.Strings.isNullOrEmpty;

public abstract class BaseService {

private static final Joiner MULTI_VALUES_JOINER = Joiner.on(",");

private final WsConnector wsConnector;
protected final String controller;

@@ -71,6 +68,6 @@ public abstract class BaseService {

@CheckForNull
protected static String inlineMultipleParamValue(@Nullable List<String> values) {
return values == null ? null : MULTI_VALUES_JOINER.join(values);
return values == null ? null : String.join(",", values);
}
}

+ 10
- 5
sonar-ws/src/main/java/org/sonarqube/ws/client/project/ProjectsService.java View File

@@ -85,10 +85,15 @@ public class ProjectsService extends BaseService {
.setParam("project", request.getKey()));
}

public void bulkDelete(BulkDeleteRequest request) {
public void bulkDelete(SearchWsRequest request) {
PostRequest post = new PostRequest(path("bulk_delete"))
.setParam("organization", request.getOrganization())
.setParam("projects", String.join(",", request.getProjectKeys()));
.setParam(PARAM_ORGANIZATION, request.getOrganization())
.setParam(PARAM_QUALIFIERS, inlineMultipleParamValue(request.getQualifiers()))
.setParam(PARAM_ANALYZED_BEFORE, request.getAnalyzedBefore())
.setParam(TEXT_QUERY, request.getQuery())
.setParam(PARAM_ON_PROVISIONED_ONLY, request.isOnProvisionedOnly())
.setParam(PARAM_PROJECTS, inlineMultipleParamValue(request.getProjects()))
.setParam(PARAM_PROJECT_IDS, inlineMultipleParamValue(request.getProjectIds()));

call(post);
}
@@ -121,8 +126,8 @@ public class ProjectsService extends BaseService {
.setParam(PAGE, request.getPage())
.setParam(PAGE_SIZE, request.getPageSize())
.setParam(PARAM_ON_PROVISIONED_ONLY, request.isOnProvisionedOnly())
.setParam(PARAM_PROJECTS, request.getProjects())
.setParam(PARAM_PROJECT_IDS, request.getProjectIds());
.setParam(PARAM_PROJECTS, inlineMultipleParamValue(request.getProjects()))
.setParam(PARAM_PROJECT_IDS, inlineMultipleParamValue(request.getProjectIds()));
return call(get, SearchWsResponse.parser());
}


+ 3
- 2
sonar-ws/src/main/java/org/sonarqube/ws/client/project/SearchWsRequest.java View File

@@ -19,12 +19,13 @@
*/
package org.sonarqube.ws.client.project;

import java.util.ArrayList;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.resources.Qualifiers;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Collections.singletonList;
import static java.util.Objects.requireNonNull;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.MAX_PAGE_SIZE;

@@ -108,7 +109,7 @@ public class SearchWsRequest {

public static class Builder {
private String organization;
private List<String> qualifiers = new ArrayList<>();
private List<String> qualifiers = singletonList(Qualifiers.PROJECT);
private Integer page;
private Integer pageSize;
private String query;

+ 17
- 4
sonar-ws/src/test/java/org/sonarqube/ws/client/project/ProjectsServiceTest.java View File

@@ -125,11 +125,24 @@ public class ProjectsServiceTest {

@Test
public void bulk_delete() {
BulkDeleteRequest request = BulkDeleteRequest.builder().setProjectKeys(Arrays.asList("p1", "p2")).setOrganization("my-org").build();
underTest.bulkDelete(request);
underTest.bulkDelete(SearchWsRequest.builder()
.setOrganization("default")
.setQuery("project")
.setQualifiers(asList("TRK", "VW"))
.setAnalyzedBefore("2017-09-01")
.setProjects(Arrays.asList("P1", "P2"))
.setOnProvisionedOnly(true)
.build());

assertThat(serviceTester.getPostRequest().getPath()).isEqualTo("api/projects/bulk_delete");
assertThat(serviceTester.getPostRequest().getParams()).containsOnly(entry("organization", "my-org"), entry("projects", "p1,p2"));
serviceTester.assertThat(serviceTester.getPostRequest())
.hasPath("bulk_delete")
.hasParam("organization", "default")
.hasParam("q", "project")
.hasParam("analyzedBefore", "2017-09-01")
.hasParam("qualifiers", "TRK,VW")
.hasParam("onProvisionedOnly", "true")
.hasParam("projects", "P1,P2")
.andNoOtherParam();
}

@Test

+ 1
- 1
sonar-ws/src/test/java/org/sonarqube/ws/client/project/SearchWsRequestTest.java View File

@@ -50,7 +50,7 @@ public class SearchWsRequestTest {
}

@Test
public void fail_when_page_size_is_greather_then_500() throws Exception {
public void fail_when_page_size_is_greater_then_500() throws Exception {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Page size must not be greater than 500");


+ 2
- 2
tests/src/test/java/org/sonarqube/tests/Category6Suite.java View File

@@ -35,7 +35,7 @@ import org.sonarqube.tests.organization.OrganizationMembershipUiTest;
import org.sonarqube.tests.organization.OrganizationTest;
import org.sonarqube.tests.organization.PersonalOrganizationTest;
import org.sonarqube.tests.organization.RootUserOnOrganizationTest;
import org.sonarqube.tests.projectAdministration.ProjectDeletionTest;
import org.sonarqube.tests.projectAdministration.ProjectDeleteTest;
import org.sonarqube.tests.projectAdministration.ProjectKeyUpdateTest;
import org.sonarqube.tests.projectAdministration.ProjectProvisioningTest;
import org.sonarqube.tests.projectAdministration.ProjectSearchTest;
@@ -77,7 +77,7 @@ import static util.ItUtils.xooPlugin;
LeakProjectsPageTest.class,
SearchProjectsTest.class,
RulesWsTest.class,
ProjectDeletionTest.class,
ProjectDeleteTest.class,
ProjectProvisioningTest.class,
ProjectKeyUpdateTest.class,
ProjectSearchTest.class,

+ 7
- 4
tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectAdministrationTest.java View File

@@ -47,8 +47,10 @@ import org.sonarqube.ws.WsPermissions;
import org.sonarqube.ws.client.permission.AddUserToTemplateWsRequest;
import org.sonarqube.ws.client.permission.CreateTemplateWsRequest;
import org.sonarqube.ws.client.permission.UsersWsRequest;
import org.sonarqube.ws.client.project.SearchWsRequest;

import static com.codeborne.selenide.Selenide.$;
import static java.util.Collections.singletonList;
import static org.apache.commons.lang.time.DateUtils.addDays;
import static org.assertj.core.api.Assertions.assertThat;
import static util.ItUtils.getComponent;
@@ -97,14 +99,15 @@ public class ProjectAdministrationTest {

@Test
public void fail_when_trying_to_delete_a_file() {
expectedException.expect(HttpException.class);
scanSampleWithDate(ANALYSIS_DATE);

assertThat(getComponent(orchestrator, PROJECT_KEY)).isNotNull();
assertThat(getComponent(orchestrator, FILE_KEY)).isNotNull();

// it's forbidden to delete only some files
orchestrator.getServer().adminWsClient().post(DELETE_WS_ENDPOINT, "keys", FILE_KEY);
expectedException.expect(org.sonarqube.ws.client.HttpException.class);

tester.wsClient().projects().bulkDelete(SearchWsRequest.builder()
.setQualifiers(singletonList("FIL"))
.setProjects(singletonList(FILE_KEY)).build());
}

@Test

+ 86
- 0
tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectBulkDeleteTest.java View File

@@ -0,0 +1,86 @@
/*
* SonarQube
* Copyright (C) 2009-2017 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.sonarqube.tests.projectAdministration;

import com.sonar.orchestrator.Orchestrator;
import java.util.List;
import java.util.stream.IntStream;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.sonarqube.tests.Category6Suite;
import org.sonarqube.tests.Tester;
import org.sonarqube.ws.Organizations;
import org.sonarqube.ws.WsProjects.CreateWsResponse;
import org.sonarqube.ws.WsProjects.SearchWsResponse.Component;
import org.sonarqube.ws.client.project.SearchWsRequest;

import static org.assertj.core.api.Assertions.assertThat;
import static util.ItUtils.runProjectAnalysis;

public class ProjectBulkDeleteTest {

@ClassRule
public static Orchestrator orchestrator = Category6Suite.ORCHESTRATOR;
@Rule
public Tester tester = new Tester(orchestrator);

@Test
public void delete_projects() {
Organizations.Organization organization = tester.organizations().generate();
CreateWsResponse.Project firstProvisionedProject = tester.projects().generate(organization, p -> p.setKey("first-provisioned-project"));
CreateWsResponse.Project secondProvisionedProject = tester.projects().generate(organization, p -> p.setKey("second-provisioned-project"));
CreateWsResponse.Project analyzedProject = tester.projects().generate(organization);

analyzeProject(analyzedProject.getKey(), organization.getKey());

tester.wsClient().projects().bulkDelete(SearchWsRequest.builder()
.setOrganization(organization.getKey())
.setQuery("FIRST-PROVISIONED")
.setOnProvisionedOnly(true).build());

List<Component> projects = tester.wsClient().projects().search(SearchWsRequest.builder().setOrganization(organization.getKey()).build()).getComponentsList();
assertThat(projects).extracting(Component::getKey)
.containsExactlyInAnyOrder(analyzedProject.getKey(), secondProvisionedProject.getKey())
.doesNotContain(firstProvisionedProject.getKey());
}

@Test
public void delete_more_than_50_projects_at_the_same_time() {
Organizations.Organization organization = tester.organizations().generate();
IntStream.range(0, 60).forEach(i -> tester.projects().generate(organization));
SearchWsRequest request = SearchWsRequest.builder().setOrganization(organization.getKey()).build();
assertThat(tester.wsClient().projects().search(request).getPaging().getTotal()).isEqualTo(60);

tester.wsClient().projects().bulkDelete(request);

assertThat(tester.wsClient().projects().search(request).getComponentsList()).isEmpty();
assertThat(tester.wsClient().projects().search(request).getPaging().getTotal()).isEqualTo(0);
}

private void analyzeProject(String projectKey, String organizationKey) {
runProjectAnalysis(orchestrator, "shared/xoo-sample",
"sonar.organization", organizationKey,
"sonar.projectKey", projectKey,
"sonar.login", "admin",
"sonar.password", "admin");
}
}

tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectDeletionTest.java → tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectDeleteTest.java View File

@@ -39,7 +39,6 @@ import org.sonarqube.ws.WsProjects.CreateWsResponse.Project;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.WsResponse;
import org.sonarqube.ws.client.component.SearchProjectsRequest;
import org.sonarqube.ws.client.project.BulkDeleteRequest;
import org.sonarqube.ws.client.project.CreateRequest;
import org.sonarqube.ws.client.project.DeleteRequest;
import org.sonarqube.ws.client.project.SearchWsRequest;
@@ -48,7 +47,7 @@ import util.ItUtils;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;

public class ProjectDeletionTest {
public class ProjectDeleteTest {

@ClassRule
public static final Orchestrator orchestrator = Category6Suite.ORCHESTRATOR;
@@ -137,9 +136,9 @@ public class ProjectDeletionTest {
}

private void bulkDeleteProjects(Organizations.Organization organization, Project... projects) {
BulkDeleteRequest request = BulkDeleteRequest.builder()
SearchWsRequest request = SearchWsRequest.builder()
.setOrganization(organization.getKey())
.setProjectKeys(Arrays.stream(projects).map(Project::getKey).collect(Collectors.toList()))
.setProjects(Arrays.stream(projects).map(Project::getKey).collect(Collectors.toList()))
.build();
tester.wsClient().projects().bulkDelete(request);
}

Loading…
Cancel
Save