diff options
7 files changed, 133 insertions, 5 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationDao.java index a6513bd16b1..4ddc575bd86 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationDao.java @@ -117,6 +117,10 @@ public class OrganizationDao implements Dao { return getMapper(dbSession).selectNewProjectPrivateByUuid(organization.getUuid()); } + public void setNewProjectPrivate(DbSession dbSession, OrganizationDto organization, boolean newProjectPrivate) { + getMapper(dbSession).updateNewProjectPrivate(organization.getUuid(), newProjectPrivate, system2.now()); + } + public int update(DbSession dbSession, OrganizationDto organization) { checkDto(organization); organization.setUpdatedAt(system2.now()); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationMapper.java index fb024e3c934..5f891da130a 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationMapper.java @@ -70,5 +70,7 @@ public interface OrganizationMapper { void updateDefaultGroupId(@Param("organizationUuid") String organizationUuid, @Param("defaultGroupId") int defaultGroupId, @Param("now") long now); + void updateNewProjectPrivate(@Param("organizationUuid") String organizationUuid, @Param("newProjectPrivate") boolean newProjectPrivate, @Param("now") long now); + int deleteByUuid(@Param("uuid") String uuid); } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/organization/OrganizationMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/organization/OrganizationMapper.xml index c9394628103..ee804cfdf04 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/organization/OrganizationMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/organization/OrganizationMapper.xml @@ -256,6 +256,15 @@ uuid = #{organizationUuid, jdbcType=VARCHAR} </update> + <update id="updateNewProjectPrivate"> + update organizations + set + new_project_private = #{newProjectPrivate, jdbcType=INTEGER}, + updated_at = #{now, jdbcType=BIGINT} + where + uuid = #{organizationUuid, jdbcType=VARCHAR} + </update> + <delete id="deleteByUuid"> delete from organizations where 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 afd1579bad0..45d0c69a3cc 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 @@ -48,6 +48,7 @@ import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT public class CreateAction implements ProjectsWsAction { private static final String DEPRECATED_PARAM_KEY = "key"; + static final String PARAM_VISIBILITY = "visibility"; private final ProjectsWsSupport support; private final DbClient dbClient; @@ -76,8 +77,7 @@ public class CreateAction implements ProjectsWsAction { action.setChangelog( new Change("6.3", "The response format has been updated and does not contain the database ID anymore"), - new Change("6.3", "The 'key' parameter has been renamed 'project'") - ); + new Change("6.3", "The 'key' parameter has been renamed 'project'")); action.createParam(PARAM_PROJECT) .setDescription("Key of the project") @@ -94,6 +94,14 @@ public class CreateAction implements ProjectsWsAction { .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"); + action.createParam(PARAM_VISIBILITY) + .setDescription("Whether the created project should be visible to everyone, or only specific user/groups.<br/>" + + "If no visibility is specified, the default project visibility of the organization will be used.") + .setRequired(false) + .setInternal(true) + .setSince("6.4") + .setPossibleValues("private", "public"); + support.addOrganizationParam(action); } @@ -114,6 +122,7 @@ public class CreateAction implements ProjectsWsAction { .setKey(request.getKey()) .setName(request.getName()) .setBranch(request.getBranch()) + .setPrivate(request.getVisibility().map("private"::equals).orElseGet(() -> dbClient.organizationDao().getNewProjectPrivate(dbSession, organization))) .setQualifier(PROJECT) .build(), userSession.isLoggedIn() ? userSession.getUserId() : null); @@ -127,6 +136,7 @@ public class CreateAction implements ProjectsWsAction { .setKey(request.mandatoryParam(PARAM_PROJECT)) .setName(request.mandatoryParam(PARAM_NAME)) .setBranch(request.param(PARAM_BRANCH)) + .setVisibility(request.param(PARAM_VISIBILITY)) .build(); } @@ -135,7 +145,8 @@ public class CreateAction implements ProjectsWsAction { .setProject(CreateWsResponse.Project.newBuilder() .setKey(componentDto.key()) .setName(componentDto.name()) - .setQualifier(componentDto.qualifier())) + .setQualifier(componentDto.qualifier()) + .setVisibility(componentDto.isPrivate() ? "private" : "public")) .build(); } 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 6287a347a27..cc6e2ba5f52 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 @@ -51,10 +51,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.sonar.core.util.Protobuf.setNullable; import static org.sonar.db.permission.OrganizationPermission.PROVISION_PROJECTS; +import static org.sonar.server.project.ws.CreateAction.PARAM_VISIBILITY; 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_BRANCH; import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NAME; +import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT; public class CreateActionTest { @@ -179,6 +182,72 @@ public class CreateActionTest { } @Test + public void apply_project_visibility_public() { + OrganizationDto organization = db.organizations().insert(); + userSession.addPermission(PROVISION_PROJECTS, organization); + expectSuccessfulCallToComponentUpdater(); + + CreateWsResponse result = ws.newRequest() + .setParam("key", DEFAULT_PROJECT_KEY) + .setParam("name", DEFAULT_PROJECT_NAME) + .setParam("organization", organization.getKey()) + .setParam("visibility", "public") + .executeProtobuf(CreateWsResponse.class); + + assertThat(result.getProject().getVisibility()).isEqualTo("public"); + } + + @Test + public void apply_project_visibility_private() { + OrganizationDto organization = db.organizations().insert(); + userSession.addPermission(PROVISION_PROJECTS, organization); + expectSuccessfulCallToComponentUpdater(); + + CreateWsResponse result = ws.newRequest() + .setParam("key", DEFAULT_PROJECT_KEY) + .setParam("name", DEFAULT_PROJECT_NAME) + .setParam("organization", organization.getKey()) + .setParam("visibility", "private") + .executeProtobuf(CreateWsResponse.class); + + assertThat(result.getProject().getVisibility()).isEqualTo("private"); + } + + @Test + public void apply_default_project_visibility_public() { + OrganizationDto organization = db.organizations().insert(); + db.getDbClient().organizationDao().setNewProjectPrivate(db.getSession(), organization, false); + db.commit(); + userSession.addPermission(PROVISION_PROJECTS, organization); + expectSuccessfulCallToComponentUpdater(); + + CreateWsResponse result = ws.newRequest() + .setParam("key", DEFAULT_PROJECT_KEY) + .setParam("name", DEFAULT_PROJECT_NAME) + .setParam("organization", organization.getKey()) + .executeProtobuf(CreateWsResponse.class); + + assertThat(result.getProject().getVisibility()).isEqualTo("public"); + } + + @Test + public void apply_default_project_visibility_private() { + OrganizationDto organization = db.organizations().insert(); + db.getDbClient().organizationDao().setNewProjectPrivate(db.getSession(), organization, true); + db.commit(); + userSession.addPermission(PROVISION_PROJECTS, organization); + expectSuccessfulCallToComponentUpdater(); + + CreateWsResponse result = ws.newRequest() + .setParam("key", DEFAULT_PROJECT_KEY) + .setParam("name", DEFAULT_PROJECT_NAME) + .setParam("organization", organization.getKey()) + .executeProtobuf(CreateWsResponse.class); + + assertThat(result.getProject().getVisibility()).isEqualTo("private"); + } + + @Test public void definition() { WebService.Action definition = ws.getDef(); @@ -187,13 +256,26 @@ public class CreateActionTest { Assertions.assertThat(definition.isInternal()).isFalse(); Assertions.assertThat(definition.responseExampleAsString()).isNotEmpty(); - Assertions.assertThat(definition.params()).hasSize(4); + Assertions.assertThat(definition.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder( + PARAM_VISIBILITY, + PARAM_ORGANIZATION, + PARAM_NAME, + PARAM_PROJECT, + PARAM_BRANCH + ); 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"); + + WebService.Param isPrivate = definition.param(PARAM_VISIBILITY); + Assertions.assertThat(isPrivate.description()).isNotEmpty(); + Assertions.assertThat(isPrivate.isInternal()).isTrue(); + Assertions.assertThat(isPrivate.isRequired()).isFalse(); + Assertions.assertThat(isPrivate.since()).isEqualTo("6.4"); + Assertions.assertThat(isPrivate.possibleValues()).containsExactlyInAnyOrder("private", "public"); } private CreateWsResponse call(CreateRequest request) { @@ -215,7 +297,7 @@ public class CreateActionTest { private void expectSuccessfulCallToComponentUpdater() { when(componentUpdater.create(any(DbSession.class), any(NewComponent.class), anyInt())).thenAnswer(invocation -> { NewComponent newC = invocation.getArgumentAt(1, NewComponent.class); - return new ComponentDto().setKey(newC.key()).setQualifier(newC.qualifier()).setName(newC.name()); + return new ComponentDto().setKey(newC.key()).setQualifier(newC.qualifier()).setName(newC.name()).setPrivate(newC.isPrivate()); }); } } 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 962e04f2150..8a03fff7c13 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 @@ -19,10 +19,14 @@ */ package org.sonarqube.ws.client.project; +import java.util.Optional; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Arrays.asList; + @Immutable public class CreateRequest { @@ -30,12 +34,15 @@ public class CreateRequest { private final String key; private final String name; private final String branch; + @CheckForNull + private final String visibility; private CreateRequest(Builder builder) { this.organization = builder.organization; this.key = builder.key; this.name = builder.name; this.branch = builder.branch; + this.visibility = builder.visibility; } @CheckForNull @@ -56,6 +63,10 @@ public class CreateRequest { return branch; } + public Optional<String> getVisibility() { + return Optional.ofNullable(visibility); + } + public static Builder builder() { return new Builder(); } @@ -65,6 +76,8 @@ public class CreateRequest { private String key; private String name; private String branch; + @CheckForNull + private String visibility; private Builder() { } @@ -89,6 +102,12 @@ public class CreateRequest { return this; } + public Builder setVisibility(@Nullable String visibility) { + checkArgument(visibility == null || asList("private", "public").contains(visibility), "Unexpected visibility '" + visibility + "'"); + this.visibility = visibility; + return this; + } + public CreateRequest build() { return new CreateRequest(this); } diff --git a/sonar-ws/src/main/protobuf/ws-projects.proto b/sonar-ws/src/main/protobuf/ws-projects.proto index 86ed442a8d6..2836db8c48c 100644 --- a/sonar-ws/src/main/protobuf/ws-projects.proto +++ b/sonar-ws/src/main/protobuf/ws-projects.proto @@ -54,6 +54,7 @@ message CreateWsResponse { optional string key = 1; optional string name = 2; optional string qualifier = 3; + optional string visibility = 4; } } |