diff options
7 files changed, 108 insertions, 34 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/CreateAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/CreateAction.java index 4267b349a9a..2ffa96b7c03 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/CreateAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/CreateAction.java @@ -25,15 +25,18 @@ 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.db.organization.OrganizationDto; import org.sonar.server.component.ComponentUpdater; import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.user.UserSession; import org.sonarqube.ws.WsProjects.CreateWsResponse; import org.sonarqube.ws.client.project.CreateRequest; +import static java.util.Optional.ofNullable; 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.project.ws.ProjectsWsSupport.PARAM_ORGANIZATION; 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; @@ -43,14 +46,17 @@ import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT public class CreateAction implements ProjectsWsAction { - public static final String DEPRECATED_PARAM_KEY = "key"; + private static final String DEPRECATED_PARAM_KEY = "key"; + private final ProjectsWsSupport support; private final DbClient dbClient; private final UserSession userSession; private final ComponentUpdater componentUpdater; private final DefaultOrganizationProvider defaultOrganizationProvider; - public CreateAction(DbClient dbClient, UserSession userSession, ComponentUpdater componentUpdater, DefaultOrganizationProvider defaultOrganizationProvider) { + public CreateAction(ProjectsWsSupport support, DbClient dbClient, UserSession userSession, ComponentUpdater componentUpdater, + DefaultOrganizationProvider defaultOrganizationProvider) { + this.support = support; this.dbClient = dbClient; this.userSession = userSession; this.componentUpdater = componentUpdater; @@ -83,19 +89,24 @@ public class CreateAction implements ProjectsWsAction { 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"); + + support.addOrganizationParam(action); } @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) { try (DbSession dbSession = dbClient.openSession(false)) { + OrganizationDto organization = support.getOrganization(dbSession, ofNullable(request.getOrganization()) + .orElseGet(defaultOrganizationProvider.get()::getKey)); + userSession.checkOrganizationPermission(organization.getUuid(), PROVISIONING); + ComponentDto componentDto = componentUpdater.create(dbSession, newComponentBuilder() - .setOrganizationUuid(defaultOrganizationProvider.get().getUuid()) + .setOrganizationUuid(organization.getUuid()) .setKey(request.getKey()) .setName(request.getName()) .setBranch(request.getBranch()) @@ -108,6 +119,7 @@ public class CreateAction implements ProjectsWsAction { private static CreateRequest toCreateRequest(Request request) { return CreateRequest.builder() + .setOrganization(request.param(PARAM_ORGANIZATION)) .setKey(request.mandatoryParam(PARAM_PROJECT)) .setName(request.mandatoryParam(PARAM_NAME)) .setBranch(request.param(PARAM_BRANCH)) diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWsSupport.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWsSupport.java index e587cbfff33..813cb6d101d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWsSupport.java +++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWsSupport.java @@ -27,7 +27,7 @@ import org.sonar.db.organization.OrganizationDto; import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; public class ProjectsWsSupport { - static final String PARAM_ORGANIZATION = "organization"; + public static final String PARAM_ORGANIZATION = "organization"; private final DbClient dbClient; diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentUpdaterTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentUpdaterTest.java index 5442ad996d3..541aebe1e7e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentUpdaterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentUpdaterTest.java @@ -180,6 +180,22 @@ public class ComponentUpdaterTest { } @Test + public void fail_when_project_key_already_exists_on_other_organization() throws Exception { + ComponentDto existing = db.components().insertProject(db.organizations().insert()); + + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("Could not create Project, key already exists: " + existing.key()); + + underTest.create(db.getSession(), + NewComponent.newComponentBuilder() + .setKey(existing.key()) + .setName(DEFAULT_PROJECT_NAME) + .setOrganizationUuid(existing.getOrganizationUuid()) + .build(), + null); + } + + @Test public void fail_when_key_has_bad_format() throws Exception { expectedException.expect(BadRequestException.class); expectedException.expectMessage("Malformed key for Project: 1234"); diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/CreateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/CreateActionTest.java index 7a7411062e2..3e0ee7ad05b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/CreateActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/CreateActionTest.java @@ -32,6 +32,7 @@ import org.sonar.api.utils.System2; import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; +import org.sonar.db.organization.OrganizationDto; import org.sonar.server.component.ComponentUpdater; import org.sonar.server.component.NewComponent; import org.sonar.server.exceptions.BadRequestException; @@ -54,6 +55,7 @@ import static org.mockito.Mockito.when; 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.project.ws.ProjectsWsSupport.PARAM_ORGANIZATION; 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; @@ -71,24 +73,26 @@ public class CreateActionTest { public DbTester db = DbTester.create(system2); @Rule public UserSessionRule userSession = UserSessionRule.standalone(); + private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db); private ComponentUpdater componentUpdater = mock(ComponentUpdater.class, Mockito.RETURNS_MOCKS); private WsActionTester ws = new WsActionTester( - new CreateAction( - db.getDbClient(), userSession, - componentUpdater, - defaultOrganizationProvider)); + new CreateAction( + new ProjectsWsSupport(db.getDbClient()), + db.getDbClient(), userSession, + componentUpdater, + defaultOrganizationProvider)); @Test public void create_project() throws Exception { - userSession.setGlobalPermissions(PROVISIONING); + userSession.addOrganizationPermission(db.getDefaultOrganization(), PROVISIONING); expectSuccessfulCallToComponentUpdater(); CreateWsResponse response = call(CreateRequest.builder() - .setKey(DEFAULT_PROJECT_KEY) - .setName(DEFAULT_PROJECT_NAME) - .build()); + .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); @@ -97,13 +101,13 @@ public class CreateActionTest { @Test public void create_project_with_branch() throws Exception { - userSession.setGlobalPermissions(PROVISIONING); + userSession.addOrganizationPermission(db.getDefaultOrganization(), PROVISIONING); call(CreateRequest.builder() - .setKey(DEFAULT_PROJECT_KEY) - .setName(DEFAULT_PROJECT_NAME) - .setBranch("origin/master") - .build()); + .setKey(DEFAULT_PROJECT_KEY) + .setName(DEFAULT_PROJECT_NAME) + .setBranch("origin/master") + .build()); NewComponent called = verifyCallToComponentUpdater(); assertThat(called.key()).isEqualTo(DEFAULT_PROJECT_KEY); @@ -112,13 +116,15 @@ public class CreateActionTest { @Test public void create_project_with_deprecated_parameter() throws Exception { - userSession.setGlobalPermissions(PROVISIONING); + OrganizationDto organization = db.organizations().insert(); + userSession.addOrganizationPermission(organization, PROVISIONING); ws.newRequest() - .setMethod(POST.name()) - .setParam("key", DEFAULT_PROJECT_KEY) - .setParam(PARAM_NAME, DEFAULT_PROJECT_NAME) - .execute(); + .setMethod(POST.name()) + .setParam("organization", organization.getKey()) + .setParam("key", DEFAULT_PROJECT_KEY) + .setParam(PARAM_NAME, DEFAULT_PROJECT_NAME) + .execute(); NewComponent called = verifyCallToComponentUpdater(); assertThat(called.key()).isEqualTo(DEFAULT_PROJECT_KEY); @@ -127,15 +133,17 @@ public class CreateActionTest { @Test public void fail_when_project_already_exists() throws Exception { - userSession.setGlobalPermissions(PROVISIONING); + OrganizationDto organization = db.organizations().insert(); when(componentUpdater.create(any(DbSession.class), any(NewComponent.class), anyLong())).thenThrow(new BadRequestException("already exists")); + userSession.addOrganizationPermission(organization, PROVISIONING); expectedException.expect(BadRequestException.class); call(CreateRequest.builder() - .setKey(DEFAULT_PROJECT_KEY) - .setName(DEFAULT_PROJECT_NAME) - .build()); + .setOrganization(organization.getKey()) + .setKey(DEFAULT_PROJECT_KEY) + .setName(DEFAULT_PROJECT_NAME) + .build()); } @Test @@ -166,13 +174,13 @@ public class CreateActionTest { @Test public void test_example() { - userSession.setGlobalPermissions(PROVISIONING); + userSession.addOrganizationPermission(db.getDefaultOrganization(), PROVISIONING); expectSuccessfulCallToComponentUpdater(); String result = ws.newRequest() - .setParam("key", DEFAULT_PROJECT_KEY) - .setParam("name", DEFAULT_PROJECT_NAME) - .execute().getInput(); + .setParam("key", DEFAULT_PROJECT_KEY) + .setParam("name", DEFAULT_PROJECT_NAME) + .execute().getInput(); assertJson(result).isSimilarTo(getClass().getResource("create-example.json")); } @@ -185,13 +193,21 @@ public class CreateActionTest { Assertions.assertThat(definition.since()).isEqualTo("4.0"); Assertions.assertThat(definition.isInternal()).isFalse(); Assertions.assertThat(definition.responseExampleAsString()).isNotEmpty(); - Assertions.assertThat(definition.params()).hasSize(3); + + Assertions.assertThat(definition.params()).hasSize(4); + + WebService.Param organization = definition.param(PARAM_ORGANIZATION); + Assertions.assertThat(organization.description()).isEqualTo("The key of the organization"); + Assertions.assertThat(organization.isInternal()).isTrue(); + Assertions.assertThat(organization.isRequired()).isFalse(); + Assertions.assertThat(organization.since()).isEqualTo("6.3"); } private CreateWsResponse call(CreateRequest request) { TestRequest httpRequest = ws.newRequest() - .setMethod(POST.name()) - .setMediaType(MediaTypes.PROTOBUF); + .setMethod(POST.name()) + .setMediaType(MediaTypes.PROTOBUF); + setNullable(request.getOrganization(), e -> httpRequest.setParam("organization", e)); setNullable(request.getKey(), e -> httpRequest.setParam("project", e)); setNullable(request.getName(), e -> httpRequest.setParam("name", e)); setNullable(request.getBranch(), e -> httpRequest.setParam("branch", e)); diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/project/CreateRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/project/CreateRequest.java index b68e7746999..06ab43c2270 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/project/CreateRequest.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/project/CreateRequest.java @@ -26,16 +26,23 @@ import javax.annotation.concurrent.Immutable; @Immutable public class CreateRequest { + private final String organization; private final String key; private final String name; private final String branch; private CreateRequest(Builder builder) { + this.organization = builder.organization; this.key = builder.key; this.name = builder.name; this.branch = builder.branch; } + @CheckForNull + public String getOrganization() { + return organization; + } + public String getKey() { return key; } @@ -54,6 +61,7 @@ public class CreateRequest { } public static class Builder { + private String organization; private String key; private String name; private String branch; @@ -61,6 +69,11 @@ public class CreateRequest { private Builder() { } + public Builder setOrganization(String organization) { + this.organization = organization; + return this; + } + public Builder setKey(String key) { this.key = key; return this; diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/project/ProjectsService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/project/ProjectsService.java index 37f0db639a7..328045fbd86 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/project/ProjectsService.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/project/ProjectsService.java @@ -48,6 +48,7 @@ public class ProjectsService extends BaseService { */ public CreateWsResponse create(CreateRequest project) { PostRequest request = new PostRequest(path(ACTION_CREATE)) + .setParam("organization", project.getOrganization()) .setParam(PARAM_PROJECT, project.getKey()) .setParam(PARAM_NAME, project.getName()) .setParam(PARAM_BRANCH, project.getBranch()); diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/project/ProjectsServiceTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/project/ProjectsServiceTest.java index 94fc4c2b91e..1d3e3c51881 100644 --- a/sonar-ws/src/test/java/org/sonarqube/ws/client/project/ProjectsServiceTest.java +++ b/sonar-ws/src/test/java/org/sonarqube/ws/client/project/ProjectsServiceTest.java @@ -51,6 +51,22 @@ public class ProjectsServiceTest { } @Test + public void creates_project_on_organization() { + underTest.create(CreateRequest.builder() + .setOrganization("org_key") + .setKey("project_key") + .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("organization", "org_key"), + entry("project", "project_key"), + entry("name", "Project Name")); + } + + @Test public void creates_project_on_branch() { underTest.create(CreateRequest.builder() .setKey("project_key") |