Browse Source

SONAR-7299 Replace Ruby WS api/projects/create by java

tags/6.3-RC1
Julien Lancelot 7 years ago
parent
commit
528c231251
18 changed files with 595 additions and 85 deletions
  1. 14
    13
      it/it-tests/src/test/java/it/authorisation/ProvisioningPermissionTest.java
  2. 12
    6
      it/it-tests/src/test/java/it/qualityGate/QualityGateTest.java
  3. 0
    2
      server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java
  4. 2
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/queue/ReportSubmitter.java
  5. 141
    0
      server/sonar-server/src/main/java/org/sonar/server/project/ws/CreateAction.java
  6. 2
    31
      server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWs.java
  7. 1
    0
      server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWsModule.java
  8. 7
    0
      server/sonar-server/src/main/resources/org/sonar/server/project/ws/create-example.json
  9. 0
    8
      server/sonar-server/src/main/resources/org/sonar/server/project/ws/projects-example-create.json
  10. 19
    1
      server/sonar-server/src/test/java/org/sonar/server/i18n/I18nRule.java
  11. 322
    0
      server/sonar-server/src/test/java/org/sonar/server/project/ws/CreateActionTest.java
  12. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsModuleTest.java
  13. 2
    11
      server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsTest.java
  14. 8
    2
      sonar-db/src/test/java/org/sonar/db/favorite/FavoriteDbTester.java
  15. 14
    7
      sonar-ws/src/main/java/org/sonarqube/ws/client/project/ProjectsService.java
  16. 35
    0
      sonar-ws/src/main/java/org/sonarqube/ws/client/project/ProjectsWsParameters.java
  17. 11
    0
      sonar-ws/src/main/protobuf/ws-projects.proto
  18. 4
    2
      sonar-ws/src/test/java/org/sonarqube/ws/client/project/ProjectsServiceTest.java

+ 14
- 13
it/it-tests/src/test/java/it/authorisation/ProvisioningPermissionTest.java View File

@@ -27,19 +27,19 @@ import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.wsclient.SonarClient;
import org.sonar.wsclient.base.HttpException;
import org.sonar.wsclient.project.NewProject;
import org.sonar.wsclient.project.Project;
import org.sonarqube.ws.WsProjects.CreateWsResponse.Project;
import org.sonarqube.ws.client.HttpException;
import org.sonarqube.ws.client.permission.AddGroupWsRequest;
import org.sonarqube.ws.client.permission.AddUserWsRequest;
import org.sonarqube.ws.client.permission.PermissionsService;
import org.sonarqube.ws.client.permission.RemoveGroupWsRequest;
import org.sonarqube.ws.client.permission.RemoveUserWsRequest;
import org.sonarqube.ws.client.project.CreateRequest;
import util.user.UserRule;

import static org.assertj.core.api.Assertions.assertThat;
import static util.ItUtils.newAdminWsClient;
import static util.ItUtils.newUserWsClient;
import static util.selenium.Selenese.runSelenese;

public class ProvisioningPermissionTest {
@@ -117,13 +117,13 @@ public class ProvisioningPermissionTest {
final String newKey = "new-project";
final String newName = "New Project";

SonarClient client = orchestrator.getServer().wsClient(USER_WITH_PROVISIONING, PASSWORD);
Project created = client.projectClient().create(NewProject.create().key(newKey).name(newName));
Project created = newUserWsClient(orchestrator, USER_WITH_PROVISIONING, PASSWORD).projects()
.create(CreateRequest.builder().setKey(newKey).setName(newName).build())
.getProject();

assertThat(created).isNotNull();
assertThat(created.key()).isEqualTo(newKey);
assertThat(created.name()).isEqualTo(newName);
assertThat(created.getKey()).isEqualTo(newKey);
assertThat(created.getName()).isEqualTo(newName);
}

/**
@@ -132,11 +132,12 @@ public class ProvisioningPermissionTest {
*/
@Test
public void should_not_be_allowed_on_ws_without_permission() {
SonarClient client = orchestrator.getServer().wsClient(USER_WITHOUT_PROVISIONING, PASSWORD);

thrown.expect(HttpException.class);
thrown.expectMessage("401");
client.projectClient().create(NewProject.create().key("new-project").name("New Project"));
thrown.expectMessage("403");

newUserWsClient(orchestrator, USER_WITHOUT_PROVISIONING, PASSWORD).projects()
.create(CreateRequest.builder().setKey("new-project").setName("New Project").build())
.getProject();
}

private static void addUserPermission(String login, String permission) {

+ 12
- 6
it/it-tests/src/test/java/it/qualityGate/QualityGateTest.java View File

@@ -40,7 +40,6 @@ import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.sonar.wsclient.project.NewProject;
import org.sonar.wsclient.qualitygate.NewCondition;
import org.sonar.wsclient.qualitygate.QualityGate;
import org.sonar.wsclient.qualitygate.QualityGateClient;
@@ -49,6 +48,7 @@ import org.sonarqube.ws.WsCe;
import org.sonarqube.ws.WsMeasures.Measure;
import org.sonarqube.ws.WsQualityGates.ProjectStatusWsResponse;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.PostRequest;
import org.sonarqube.ws.client.WsClient;
import org.sonarqube.ws.client.WsResponse;
import org.sonarqube.ws.client.qualitygate.ProjectStatusWsRequest;
@@ -71,8 +71,6 @@ public class QualityGateTest {

private static long DEFAULT_QUALITY_GATE;

private long provisionedProjectId = -1L;

private static final String PROJECT_KEY = "sample";

@ClassRule
@@ -91,9 +89,9 @@ public class QualityGateTest {
}

@Before
public void cleanUp() {
public void before() {
orchestrator.resetData();
provisionedProjectId = Long.parseLong(orchestrator.getServer().adminWsClient().projectClient().create(NewProject.create().key(PROJECT_KEY).name("Sample")).id());
orchestrator.getServer().provisionProject(PROJECT_KEY, "Sample");
}

@Test
@@ -190,7 +188,7 @@ public class QualityGateTest {
qgClient().createCondition(NewCondition.create(error.id()).metricKey("ncloc").operator("GT").errorThreshold("10"));

qgClient().setDefault(alert.id());
qgClient().selectProject(error.id(), provisionedProjectId);
associateQualityGateToProject(error.id(), PROJECT_KEY);

try {
SonarScanner build = SonarScanner.create(projectDir("qualitygate/xoo-sample"));
@@ -303,6 +301,14 @@ public class QualityGateTest {
return orchestrator.getServer().adminWsClient().qualityGateClient();
}

private static void associateQualityGateToProject(long qGateId, String projectKey) {
newAdminWsClient(orchestrator).wsConnector()
.call(new PostRequest("api/qualitygates/select")
.setParam("gateId", qGateId)
.setParam("projectKey", projectKey))
.failIfNotSuccessful();
}

private static List<String> extractPosttaskPluginLogs(String taskUuid, Iterable<String> ceLogs) {
return StreamSupport.stream(ceLogs.spliterator(), false)
.filter(s -> s.contains("POSTASKPLUGIN: finished()"))

+ 0
- 2
server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java View File

@@ -35,7 +35,6 @@ import org.sonar.api.server.ServerSide;
import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
import org.sonar.core.component.ComponentKeys;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.util.Uuids;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
@@ -89,7 +88,6 @@ public class ComponentService {

// Used by SQ and Governance
public ComponentDto create(DbSession session, NewComponent newComponent) {
userSession.checkPermission(GlobalPermissions.PROVISIONING);
checkKeyFormat(newComponent.qualifier(), newComponent.key());
ComponentDto rootComponent = createRootComponent(session, newComponent);
removeDuplicatedProjects(session, rootComponent.getKey());

+ 2
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/queue/ReportSubmitter.java View File

@@ -43,6 +43,7 @@ import org.sonar.server.user.UserSession;

import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
import static org.sonar.core.permission.GlobalPermissions.PROVISIONING;
import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
import static org.sonar.server.component.NewComponent.newComponentBuilder;
import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
@@ -97,6 +98,7 @@ public class ReportSubmitter {
}

private ComponentDto createProject(DbSession dbSession, String organizationUuid, String projectKey, @Nullable String projectBranch, @Nullable String projectName) {
userSession.checkPermission(PROVISIONING);
Integer userId = userSession.getUserId();
Long projectCreatorUserId = userId == null ? null : userId.longValue();

@@ -113,7 +115,6 @@ public class ReportSubmitter {
.setBranch(projectBranch)
.setQualifier(Qualifiers.PROJECT)
.build();
// "provisioning" permission is check in ComponentService
ComponentDto project = componentService.create(dbSession, newProject);
if (permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(dbSession, organizationUuid, project)) {
favoriteUpdater.add(dbSession, project);

+ 141
- 0
server/sonar-server/src/main/java/org/sonar/server/project/ws/CreateAction.java View File

@@ -0,0 +1,141 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact 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.project.ws;

import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.server.component.ComponentService;
import org.sonar.server.favorite.FavoriteUpdater;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.permission.PermissionTemplateService;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.WsProjects.CreateWsResponse;
import org.sonarqube.ws.client.project.CreateRequest;

import static org.sonar.api.resources.Qualifiers.PROJECT;
import static org.sonar.core.permission.GlobalPermissions.PROVISIONING;
import static org.sonar.server.component.NewComponent.newComponentBuilder;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_CREATE;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_BRANCH;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NAME;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT;

public class CreateAction implements ProjectsWsAction {

public static final String DEPRECATED_PARAM_KEY = "key";

private final DbClient dbClient;
private final UserSession userSession;
private final ComponentService componentService;
private final DefaultOrganizationProvider defaultOrganizationProvider;
private final PermissionTemplateService permissionTemplateService;
private final FavoriteUpdater favoriteUpdater;

public CreateAction(DbClient dbClient, UserSession userSession, ComponentService componentService, PermissionTemplateService permissionTemplateService,
FavoriteUpdater favoriteUpdater, DefaultOrganizationProvider defaultOrganizationProvider) {
this.dbClient = dbClient;
this.userSession = userSession;
this.componentService = componentService;
this.permissionTemplateService = permissionTemplateService;
this.favoriteUpdater = favoriteUpdater;
this.defaultOrganizationProvider = defaultOrganizationProvider;
}

@Override
public void define(WebService.NewController context) {
WebService.NewAction action = context.createAction(ACTION_CREATE)
.setDescription("Create a project.<br/>" +
"Requires 'Create Projects' permission<br/>" +
"Since 6.3, the response has been updated and does not contain the database ID anymore")
.setSince("4.0")
.setPost(true)
.setResponseExample(getClass().getResource("create-example.json"))
.setHandler(this);

action.createParam(PARAM_PROJECT)
.setDescription("Key of the project")
.setDeprecatedKey(DEPRECATED_PARAM_KEY)
.setRequired(true)
.setExampleValue(KEY_PROJECT_EXAMPLE_001);

action.createParam(PARAM_NAME)
.setDescription("Name of the project")
.setRequired(true)
.setExampleValue("SonarQube");

action.createParam(PARAM_BRANCH)
.setDescription("SCM Branch of the project. The key of the project will become key:branch, for instance 'SonarQube:branch-5.0'")
.setExampleValue("branch-5.0");
}

@Override
public void handle(Request request, Response response) throws Exception {
userSession.checkPermission(PROVISIONING);
CreateRequest createRequest = toCreateRequest(request);
writeProtobuf(doHandle(createRequest), request, response);
}

private CreateWsResponse doHandle(CreateRequest request) {
String organizationUuid = defaultOrganizationProvider.get().getUuid();
try (DbSession dbSession = dbClient.openSession(false)) {
ComponentDto componentDto = componentService.create(dbSession, newComponentBuilder()
.setOrganizationUuid(organizationUuid)
.setKey(request.getKey())
.setName(request.getName())
.setBranch(request.getBranch())
.setQualifier(PROJECT)
.build());
handlePermissionTemplate(dbSession, componentDto, organizationUuid);
return toCreateResponse(componentDto);
}
}

private void handlePermissionTemplate(DbSession dbSession, ComponentDto componentDto, String organizationUuid) {
permissionTemplateService.applyDefault(dbSession, organizationUuid, componentDto, userSession.isLoggedIn() ? userSession.getUserId().longValue() : null);
if (permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(dbSession, organizationUuid, componentDto)) {
favoriteUpdater.add(dbSession, componentDto);
dbSession.commit();
}
}

private static CreateRequest toCreateRequest(Request request) {
return CreateRequest.builder()
.setKey(request.mandatoryParam(PARAM_PROJECT))
.setName(request.mandatoryParam(PARAM_NAME))
.setBranch(request.param(PARAM_BRANCH))
.build();
}

private static CreateWsResponse toCreateResponse(ComponentDto componentDto) {
return CreateWsResponse.newBuilder()
.setProject(CreateWsResponse.Project.newBuilder()
.setKey(componentDto.key())
.setName(componentDto.name())
.setQualifier(componentDto.qualifier()))
.build();
}

}

+ 2
- 31
server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWs.java View File

@@ -20,6 +20,7 @@
package org.sonar.server.project.ws;

import com.google.common.io.Resources;
import java.util.Arrays;
import org.sonar.api.server.ws.RailsHandler;
import org.sonar.api.server.ws.WebService;

@@ -43,12 +44,7 @@ public class ProjectsWs implements WebService {
.setDescription("Manage project existence.");

defineIndexAction(controller);
defineCreateAction(controller);

for (ProjectsWsAction action : actions) {
action.define(controller);
}

Arrays.stream(actions).forEach(action -> action.define(controller));
controller.done();
}

@@ -95,29 +91,4 @@ public class ProjectsWs implements WebService {
RailsHandler.addFormatParam(action);
}

private void defineCreateAction(NewController controller) {
WebService.NewAction action = controller.createAction("create")
.setDescription("Create a project. Requires Create Projects permission")
.setSince("4.0")
.setPost(true)
.setHandler(RailsHandler.INSTANCE)
.setResponseExample(Resources.getResource(this.getClass(), "projects-example-create.json"));

action.createParam("key")
.setDescription("Key of the project")
.setRequired(true)
.setExampleValue(KEY_PROJECT_EXAMPLE_001);

action.createParam("name")
.setDescription("Name of the project")
.setRequired(true)
.setExampleValue("SonarQube");

action.createParam("branch")
.setDescription("SCM Branch of the project. The key of the project will become key:branch, for instance 'SonarQube:branch-5.0'")
.setRequired(false)
.setExampleValue("branch-5.0");

RailsHandler.addFormatParam(action);
}
}

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

@@ -26,6 +26,7 @@ public class ProjectsWsModule extends Module {
protected void configureModule() {
add(
ProjectsWs.class,
CreateAction.class,
BulkDeleteAction.class,
DeleteAction.class,
GhostsAction.class,

+ 7
- 0
server/sonar-server/src/main/resources/org/sonar/server/project/ws/create-example.json View File

@@ -0,0 +1,7 @@
{
"project": {
"key": "project-key",
"name": "project-name",
"qualifier": "TRK"
}
}

+ 0
- 8
server/sonar-server/src/main/resources/org/sonar/server/project/ws/projects-example-create.json View File

@@ -1,8 +0,0 @@
{
"id": "30057",
"k": "org.jenkins-ci.plugins:sonar",
"nm": "Jenkins Sonar Plugin",
"sc": "PRJ",
"qu": "TRK"
}


+ 19
- 1
server/sonar-server/src/test/java/org/sonar/server/i18n/I18nRule.java View File

@@ -26,9 +26,12 @@ import java.util.Locale;
import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.sonar.api.i18n.I18n;

public class I18nRule implements I18n {
public class I18nRule implements TestRule, I18n {
private final Map<String, String> messages = new HashMap<>();

public I18nRule put(String key, String value) {
@@ -36,6 +39,20 @@ public class I18nRule implements I18n {
return this;
}

@Override
public Statement apply(final Statement statement, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
try {
statement.evaluate();
} finally {
messages.clear();
}
}
};
}

public void setProjectPermissions() {
put("projects_role.admin", "Administer");
put("projects_role.admin.desc", "Ability to access project settings and perform administration tasks. " +
@@ -101,4 +118,5 @@ public class I18nRule implements I18n {
public String formatInteger(Locale locale, Integer value) {
return String.valueOf(value);
}

}

+ 322
- 0
server/sonar-server/src/test/java/org/sonar/server/project/ws/CreateActionTest.java View File

@@ -0,0 +1,322 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact 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.project.ws;

import com.google.common.base.Throwables;
import java.io.IOException;
import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.config.MapSettings;
import org.sonar.api.config.Settings;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.permission.template.PermissionTemplateDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.component.ComponentService;
import org.sonar.server.component.index.ComponentIndexDefinition;
import org.sonar.server.component.index.ComponentIndexer;
import org.sonar.server.es.EsTester;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.favorite.FavoriteUpdater;
import org.sonar.server.i18n.I18nRule;
import org.sonar.server.measure.index.ProjectMeasuresIndexDefinition;
import org.sonar.server.measure.index.ProjectMeasuresIndexer;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import org.sonar.server.permission.PermissionTemplateService;
import org.sonar.server.permission.index.PermissionIndexer;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.MediaTypes;
import org.sonarqube.ws.WsProjects.CreateWsResponse;
import org.sonarqube.ws.client.project.CreateRequest;

import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
import static org.sonar.api.web.UserRole.USER;
import static org.sonar.core.permission.GlobalPermissions.PROVISIONING;
import static org.sonar.core.permission.GlobalPermissions.QUALITY_GATE_ADMIN;
import static org.sonar.core.util.Protobuf.setNullable;
import static org.sonar.server.component.index.ComponentIndexDefinition.INDEX_COMPONENTS;
import static org.sonar.server.component.index.ComponentIndexDefinition.TYPE_COMPONENT;
import static org.sonar.server.measure.index.ProjectMeasuresIndexDefinition.INDEX_PROJECT_MEASURES;
import static org.sonar.server.measure.index.ProjectMeasuresIndexDefinition.TYPE_PROJECT_MEASURE;
import static org.sonar.test.JsonAssert.assertJson;
import static org.sonarqube.ws.client.WsRequest.Method.POST;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NAME;

public class CreateActionTest {

private static final String DEFAULT_PROJECT_KEY = "project-key";
private static final String DEFAULT_PROJECT_NAME = "project-name";

private System2 system2 = System2.INSTANCE;

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

@Rule
public DbTester db = DbTester.create(system2);

@Rule
public EsTester es = new EsTester(new ComponentIndexDefinition(new MapSettings()), new ProjectMeasuresIndexDefinition(new MapSettings()));

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

@Rule
public I18nRule i18n = new I18nRule().put("qualifier.TRK", "Project");

private Settings settings = new MapSettings();

private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);

private PermissionTemplateDto permissionTemplateDto;

private WsActionTester ws = new WsActionTester(
new CreateAction(
db.getDbClient(), userSession,
new ComponentService(db.getDbClient(), i18n, userSession, system2,
new ProjectMeasuresIndexer(system2, db.getDbClient(), es.client()),
new ComponentIndexer(db.getDbClient(), es.client())),
new PermissionTemplateService(db.getDbClient(), settings, new PermissionIndexer(db.getDbClient(), es.client()), userSession),
new FavoriteUpdater(db.getDbClient(), userSession),
defaultOrganizationProvider));

@Before
public void setUp() throws Exception {
permissionTemplateDto = db.permissionTemplates().insertTemplate();
setTemplateAsDefault(permissionTemplateDto);
}

@Test
public void create_project() throws Exception {
userSession.setGlobalPermissions(PROVISIONING);

CreateWsResponse response = call(CreateRequest.builder()
.setKey(DEFAULT_PROJECT_KEY)
.setName(DEFAULT_PROJECT_NAME)
.build());

assertThat(response.getProject().getKey()).isEqualTo(DEFAULT_PROJECT_KEY);
assertThat(response.getProject().getName()).isEqualTo(DEFAULT_PROJECT_NAME);
assertThat(response.getProject().getQualifier()).isEqualTo("TRK");
ComponentDto project = db.getDbClient().componentDao().selectOrFailByKey(db.getSession(), DEFAULT_PROJECT_KEY);
assertThat(project.getKey()).isEqualTo(DEFAULT_PROJECT_KEY);
assertThat(project.name()).isEqualTo(DEFAULT_PROJECT_NAME);
assertThat(project.qualifier()).isEqualTo("TRK");
}

@Test
public void create_project_with_branch() throws Exception {
userSession.setGlobalPermissions(PROVISIONING);

CreateWsResponse response = call(CreateRequest.builder()
.setKey(DEFAULT_PROJECT_KEY)
.setName(DEFAULT_PROJECT_NAME)
.setBranch("origin/master")
.build());

assertThat(response.getProject().getKey()).isEqualTo("project-key:origin/master");
}

@Test
public void verify_permission_template_is_applied() throws Exception {
UserDto userDto = db.users().insertUser();
userSession.login(userDto).setGlobalPermissions(PROVISIONING);
db.permissionTemplates().addUserToTemplate(permissionTemplateDto.getId(), userDto.getId(), USER);

call(CreateRequest.builder()
.setKey(DEFAULT_PROJECT_KEY)
.setName(DEFAULT_PROJECT_NAME)
.build());

ComponentDto project = db.getDbClient().componentDao().selectOrFailByKey(db.getSession(), DEFAULT_PROJECT_KEY);
assertThat(db.users().selectProjectPermissionsOfUser(userDto, project)).containsOnly(USER);
}

@Test
public void add_project_to_favorite_when_logged() throws Exception {
UserDto userDto = db.users().insertUser();
userSession.login(userDto).setGlobalPermissions(PROVISIONING);
db.permissionTemplates().addProjectCreatorToTemplate(permissionTemplateDto.getId(), USER);

call(CreateRequest.builder()
.setKey(DEFAULT_PROJECT_KEY)
.setName(DEFAULT_PROJECT_NAME)
.build());

ComponentDto project = db.getDbClient().componentDao().selectOrFailByKey(db.getSession(), DEFAULT_PROJECT_KEY);
assertThat(db.favorites().hasFavorite(project, userDto.getId())).isTrue();
}

@Test
public void does_not_add_project_to_favorite_when_not_logged() throws Exception {
userSession.setGlobalPermissions(PROVISIONING);
db.permissionTemplates().addProjectCreatorToTemplate(permissionTemplateDto.getId(), USER);

call(CreateRequest.builder()
.setKey(DEFAULT_PROJECT_KEY)
.setName(DEFAULT_PROJECT_NAME)
.build());

ComponentDto project = db.getDbClient().componentDao().selectOrFailByKey(db.getSession(), DEFAULT_PROJECT_KEY);
assertThat(db.favorites().hasNoFavorite(project)).isTrue();
}

@Test
public void does_not_add_project_to_favorite_when_project_create_has_no_permission_on_template() throws Exception {
UserDto userDto = db.users().insertUser();
userSession.login(userDto).setGlobalPermissions(PROVISIONING);

call(CreateRequest.builder()
.setKey(DEFAULT_PROJECT_KEY)
.setName(DEFAULT_PROJECT_NAME)
.build());

ComponentDto project = db.getDbClient().componentDao().selectOrFailByKey(db.getSession(), DEFAULT_PROJECT_KEY);
assertThat(db.favorites().hasNoFavorite(project)).isTrue();
}

@Test
public void verify_project_exists_in_es_indexes() throws Exception {
userSession.setGlobalPermissions(PROVISIONING);

call(CreateRequest.builder()
.setKey(DEFAULT_PROJECT_KEY)
.setName(DEFAULT_PROJECT_NAME)
.build());

ComponentDto project = db.getDbClient().componentDao().selectOrFailByKey(db.getSession(), DEFAULT_PROJECT_KEY);
assertThat(es.getIds(INDEX_COMPONENTS, TYPE_COMPONENT)).containsOnly(project.uuid());
assertThat(es.getIds(INDEX_PROJECT_MEASURES, TYPE_PROJECT_MEASURE)).containsOnly(project.uuid());
}

@Test
public void create_project_with_deprecated_parameter() throws Exception {
userSession.setGlobalPermissions(PROVISIONING);

ws.newRequest()
.setMethod(POST.name())
.setParam("key", DEFAULT_PROJECT_KEY)
.setParam(PARAM_NAME, DEFAULT_PROJECT_NAME)
.execute();

assertThat(db.getDbClient().componentDao().selectByKey(db.getSession(), DEFAULT_PROJECT_KEY).isPresent()).isTrue();
}

@Test
public void fail_when_project_already_exists() throws Exception {
userSession.setGlobalPermissions(PROVISIONING);
db.components().insertComponent(ComponentTesting.newProjectDto(db.getDefaultOrganization()).setKey(DEFAULT_PROJECT_KEY));
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Could not create Project, key already exists: project-key");

call(CreateRequest.builder()
.setKey(DEFAULT_PROJECT_KEY)
.setName(DEFAULT_PROJECT_NAME)
.build());
}

@Test
public void fail_when_missing_project_parameter() throws Exception {
userSession.setGlobalPermissions(PROVISIONING);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("The 'project' parameter is missing");

call(CreateRequest.builder().setName(DEFAULT_PROJECT_NAME).build());
}

@Test
public void fail_when_missing_name_parameter() throws Exception {
userSession.setGlobalPermissions(PROVISIONING);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("The 'name' parameter is missing");

call(CreateRequest.builder().setKey(DEFAULT_PROJECT_KEY).build());
}

@Test
public void fail_when_key_has_bad_format() throws Exception {
userSession.setGlobalPermissions(PROVISIONING);
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Malformed key for Project: 1234");

call(CreateRequest.builder().setKey("1234").setName(DEFAULT_PROJECT_NAME).build());
}

@Test
public void fail_when_missing_create_project_permission() throws Exception {
userSession.setGlobalPermissions(QUALITY_GATE_ADMIN);
expectedException.expect(ForbiddenException.class);

call(CreateRequest.builder().setKey(DEFAULT_PROJECT_KEY).setName(DEFAULT_PROJECT_NAME).build());
}

@Test
public void test_example() {
userSession.setGlobalPermissions(PROVISIONING);

String result = ws.newRequest()
.setParam("key", DEFAULT_PROJECT_KEY)
.setParam("name", DEFAULT_PROJECT_NAME)
.execute().getInput();

assertJson(result).isSimilarTo(getClass().getResource("create-example.json"));
}

@Test
public void definition() {
WebService.Action definition = ws.getDef();

Assertions.assertThat(definition.key()).isEqualTo("create");
Assertions.assertThat(definition.since()).isEqualTo("4.0");
Assertions.assertThat(definition.isInternal()).isFalse();
Assertions.assertThat(definition.responseExampleAsString()).isNotEmpty();
Assertions.assertThat(definition.params()).hasSize(3);
}

private CreateWsResponse call(CreateRequest request) {
TestRequest httpRequest = ws.newRequest()
.setMethod(POST.name())
.setMediaType(MediaTypes.PROTOBUF);
setNullable(request.getKey(), e -> httpRequest.setParam("project", e));
setNullable(request.getName(), e -> httpRequest.setParam("name", e));
setNullable(request.getBranch(), e -> httpRequest.setParam("branch", e));
try {
return CreateWsResponse.parseFrom(httpRequest.execute().getInputStream());
} catch (IOException e) {
throw Throwables.propagate(e);
}
}

private void setTemplateAsDefault(PermissionTemplateDto permissionTemplateDto) {
settings.appendProperty("sonar.permission.template.default", permissionTemplateDto.getUuid());
}

}

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsModuleTest.java View File

@@ -29,6 +29,6 @@ public class ProjectsWsModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new ProjectsWsModule().configure(container);
assertThat(container.size()).isEqualTo(2 + 7);
assertThat(container.size()).isEqualTo(2 + 8);
}
}

+ 2
- 11
server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsTest.java View File

@@ -41,8 +41,7 @@ public class ProjectsWsTest {
ws = new WsTester(new ProjectsWs(
new BulkDeleteAction(mock(ComponentCleanerService.class), mock(DbClient.class), mock(UserSession.class)),
new GhostsAction(mock(DbClient.class), mock(UserSession.class)),
new ProvisionedAction(mock(DbClient.class), mock(UserSession.class))
));
new ProvisionedAction(mock(DbClient.class), mock(UserSession.class))));
controller = ws.controller("api/projects");
}

@@ -51,7 +50,7 @@ public class ProjectsWsTest {
assertThat(controller).isNotNull();
assertThat(controller.description()).isNotEmpty();
assertThat(controller.since()).isEqualTo("2.10");
assertThat(controller.actions()).hasSize(5);
assertThat(controller.actions()).hasSize(4);
}

@Test
@@ -63,12 +62,4 @@ public class ProjectsWsTest {
assertThat(action.params()).hasSize(8);
}

@Test
public void define_create_action() {
WebService.Action action = controller.action("create");
assertThat(action).isNotNull();
assertThat(action.handler()).isInstanceOf(RailsHandler.class);
assertThat(action.responseExampleAsString()).isNotEmpty();
assertThat(action.params()).hasSize(4);
}
}

+ 8
- 2
sonar-db/src/test/java/org/sonar/db/favorite/FavoriteDbTester.java View File

@@ -31,12 +31,10 @@ import org.sonar.db.property.PropertyQuery;
public class FavoriteDbTester {
private static final String PROP_FAVORITE_KEY = "favourite";

private final DbTester db;
private final DbClient dbClient;
private final DbSession dbSession;

public FavoriteDbTester(DbTester db) {
this.db = db;
this.dbClient = db.getDbClient();
this.dbSession = db.getSession();
}
@@ -58,4 +56,12 @@ public class FavoriteDbTester {

return !result.isEmpty();
}

public boolean hasNoFavorite(ComponentDto componentDto) {
List<PropertyDto> result = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder()
.setKey(PROP_FAVORITE_KEY)
.setComponentId(componentDto.getId())
.build(), dbSession);
return result.isEmpty();
}
}

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

@@ -20,10 +20,17 @@

package org.sonarqube.ws.client.project;

import org.sonarqube.ws.WsProjects.CreateWsResponse;
import org.sonarqube.ws.client.BaseService;
import org.sonarqube.ws.client.PostRequest;
import org.sonarqube.ws.client.WsConnector;

import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_CREATE;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.CONTROLLER;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_BRANCH;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NAME;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT;

/**
* Maps web service {@code api/projects}.
* @since 5.5
@@ -31,7 +38,7 @@ import org.sonarqube.ws.client.WsConnector;
public class ProjectsService extends BaseService {

public ProjectsService(WsConnector wsConnector) {
super(wsConnector, "api/projects");
super(wsConnector, CONTROLLER);
}

/**
@@ -39,12 +46,12 @@ public class ProjectsService extends BaseService {
*
* @throws org.sonarqube.ws.client.HttpException if HTTP status code is not 2xx.
*/
public void create(CreateRequest project) {
PostRequest request = new PostRequest(path("create"))
.setParam("key", project.getKey())
.setParam("name", project.getName())
.setParam("branch", project.getBranch());
call(request);
public CreateWsResponse create(CreateRequest project) {
PostRequest request = new PostRequest(path(ACTION_CREATE))
.setParam(PARAM_PROJECT, project.getKey())
.setParam(PARAM_NAME, project.getName())
.setParam(PARAM_BRANCH, project.getBranch());
return call(request, CreateWsResponse.parser());
}

/**

+ 35
- 0
sonar-ws/src/main/java/org/sonarqube/ws/client/project/ProjectsWsParameters.java View File

@@ -0,0 +1,35 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact 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.ws.client.project;

public class ProjectsWsParameters {

public static final String CONTROLLER = "api/projects";

public static final String ACTION_CREATE = "create";

public static final String PARAM_PROJECT = "project";
public static final String PARAM_NAME = "name";
public static final String PARAM_BRANCH = "branch";

private ProjectsWsParameters() {
// static utils only
}
}

+ 11
- 0
sonar-ws/src/main/protobuf/ws-projects.proto View File

@@ -46,3 +46,14 @@ message SearchMyProjectsWsResponse {
optional sonarqube.ws.commons.Paging paging = 1;
repeated Project projects = 2;
}

message CreateWsResponse {
optional Project project = 1;

message Project {
optional string key = 1;
optional string name = 2;
optional string qualifier = 3;
}
}


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

@@ -21,6 +21,7 @@ package org.sonarqube.ws.client.project;

import org.junit.Rule;
import org.junit.Test;
import org.sonarqube.ws.WsProjects;
import org.sonarqube.ws.client.ServiceTester;
import org.sonarqube.ws.client.WsConnector;

@@ -42,9 +43,10 @@ public class ProjectsServiceTest {
.setName("Project Name")
.build());

assertThat(serviceTester.getPostParser()).isSameAs(WsProjects.CreateWsResponse.parser());
assertThat(serviceTester.getPostRequest().getPath()).isEqualTo("api/projects/create");
assertThat(serviceTester.getPostRequest().getParams()).containsOnly(
entry("key", "project_key"),
entry("project", "project_key"),
entry("name", "Project Name"));
}

@@ -58,7 +60,7 @@ public class ProjectsServiceTest {

assertThat(serviceTester.getPostRequest().getPath()).isEqualTo("api/projects/create");
assertThat(serviceTester.getPostRequest().getParams()).containsOnly(
entry("key", "project_key"),
entry("project", "project_key"),
entry("name", "Project Name"),
entry("branch", "the_branch"));
}

Loading…
Cancel
Save