aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-server
diff options
context:
space:
mode:
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>2017-03-09 16:18:31 +0100
committerJulien Lancelot <julien.lancelot@sonarsource.com>2017-03-21 13:05:50 +0100
commit25a9f85391d0391d84402f620e25c19f25769474 (patch)
tree1cd84e9d086faef6440dfbe37b550b8a379efac5 /server/sonar-server
parent1ac32ede34b0ff29bf0af042e4ce359b5999fcd4 (diff)
downloadsonarqube-25a9f85391d0391d84402f620e25c19f25769474.tar.gz
sonarqube-25a9f85391d0391d84402f620e25c19f25769474.zip
SONAR-8892 Create WS api/organizations/add_member
WS adds a member to an organization
Diffstat (limited to 'server/sonar-server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/organization/ws/AddMemberAction.java105
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsModule.java7
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsSupport.java3
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/organization/ws/AddMemberActionTest.java183
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/organization/ws/OrganizationsWsModuleTest.java2
6 files changed, 298 insertions, 4 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/AddMemberAction.java b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/AddMemberAction.java
new file mode 100644
index 00000000000..b226a205cd9
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/AddMemberAction.java
@@ -0,0 +1,105 @@
+/*
+ * 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.sonar.server.organization.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.api.server.ws.WebService.NewController;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.organization.OrganizationMemberDto;
+import org.sonar.db.permission.OrganizationPermission;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.user.UserSession;
+
+import static java.lang.String.format;
+import static org.sonar.server.organization.ws.OrganizationsWsSupport.PARAM_LOGIN;
+import static org.sonar.server.organization.ws.OrganizationsWsSupport.PARAM_ORGANIZATION;
+import static org.sonar.server.ws.KeyExamples.KEY_ORG_EXAMPLE_001;
+import static org.sonar.server.ws.WsUtils.checkFound;
+import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
+
+public class AddMemberAction implements OrganizationsAction {
+ private final DbClient dbClient;
+ private final UuidFactory uuidFactory;
+ private final UserSession userSession;
+
+ public AddMemberAction(DbClient dbClient, UuidFactory uuidFactory, UserSession userSession) {
+ this.dbClient = dbClient;
+ this.uuidFactory = uuidFactory;
+ this.userSession = userSession;
+ }
+
+ @Override
+ public void define(NewController context) {
+ WebService.NewAction action = context.createAction("add_member")
+ .setDescription("Add a user as a member of an organization.<br>" +
+ "Requires 'Administer System' permission on the specified organization.")
+ .setSince("6.4")
+ .setPost(true)
+ .setInternal(true)
+ .setHandler(this);
+
+ action
+ .createParam(PARAM_ORGANIZATION)
+ .setDescription("Organization key")
+ .setRequired(true)
+ .setExampleValue(KEY_ORG_EXAMPLE_001);
+
+ action
+ .createParam("login")
+ .setDescription("User login")
+ .setRequired(true)
+ .setExampleValue("ray.bradbury");
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ String organizationKey = request.mandatoryParam(PARAM_ORGANIZATION);
+ String login = request.mandatoryParam(PARAM_LOGIN);
+
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ OrganizationDto organization = checkFoundWithOptional(dbClient.organizationDao().selectByKey(dbSession, organizationKey), "Organization '%s' is not found",
+ organizationKey);
+ UserDto user = checkFound(dbClient.userDao().selectByLogin(dbSession, login), "User '%s' is not found", login);
+
+ userSession.checkPermission(OrganizationPermission.ADMINISTER, organization);
+
+ OrganizationMemberDto organizationMember = new OrganizationMemberDto()
+ .setOrganizationUuid(organization.getUuid())
+ .setUserId(user.getId());
+
+ dbClient.organizationMemberDao().select(dbSession, organization.getUuid(), user.getId())
+ .ifPresent(o -> {
+ throw BadRequestException.create(format("User '%s' is already a member of organization '%s'", user.getLogin(), organization.getKey()));
+ });
+
+ dbClient.organizationMemberDao().insert(dbSession, organizationMember);
+ dbSession.commit();
+ }
+
+ response.noContent();
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsModule.java
index c05b428bc39..31ea3feb687 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsModule.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsModule.java
@@ -29,12 +29,13 @@ public class OrganizationsWsModule extends Module {
OrganizationsWs.class,
OrganizationsWsSupport.class,
// actions
+ AddMemberAction.class,
CreateAction.class,
+ DeleteAction.class,
EnableSupportAction.class,
SearchAction.class,
- UpdateAction.class,
- DeleteAction.class,
- SearchMyOrganizationsAction.class);
+ SearchMyOrganizationsAction.class,
+ UpdateAction.class);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsSupport.java b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsSupport.java
index 42019093cd6..41131784aa4 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsSupport.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsSupport.java
@@ -32,12 +32,15 @@ import static org.sonar.core.util.Protobuf.setNullable;
* Factorizes code and constants between Organization WS's actions.
*/
public class OrganizationsWsSupport {
+ static final String PARAM_ORGANIZATION = "organization";
static final String PARAM_KEY = "key";
static final String PARAM_NAME = "name";
static final String PARAM_DESCRIPTION = "description";
static final String PARAM_URL = "url";
static final String PARAM_AVATAR_URL = "avatar";
+ static final String PARAM_LOGIN = "login";
+
private final OrganizationValidation organizationValidation;
public OrganizationsWsSupport(OrganizationValidation organizationValidation) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java b/server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java
index 450d42348fb..ee140c23c3d 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java
@@ -27,6 +27,8 @@ public class KeyExamples {
public static final String KEY_PROJECT_EXAMPLE_003 = "third_project";
public static final String KEY_DEVELOPER_EXAMPLE_001 = "DEV:ada@lovelace.com";
+ public static final String KEY_ORG_EXAMPLE_001 = "my-org";
+
private KeyExamples() {
// prevent instantiation
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/AddMemberActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/AddMemberActionTest.java
new file mode 100644
index 00000000000..66d094ad76d
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/AddMemberActionTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.sonar.server.organization.ws;
+
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.core.util.SequenceUuidFactory;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.TestResponse;
+import org.sonar.server.ws.WsActionTester;
+
+import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.core.util.Protobuf.setNullable;
+import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
+import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_GATES;
+import static org.sonar.server.organization.ws.OrganizationsWsSupport.PARAM_ORGANIZATION;
+
+public class AddMemberActionTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone().logIn().setRoot();
+ @Rule
+ public DbTester db = DbTester.create();
+ private DbClient dbClient = db.getDbClient();
+ private DbSession dbSession = db.getSession();
+
+ private WsActionTester ws = new WsActionTester(new AddMemberAction(dbClient, new SequenceUuidFactory(), userSession));
+
+ private OrganizationDto organization;
+ private UserDto user;
+
+ @Before
+ public void setUp() {
+ organization = db.organizations().insert();
+ user = db.users().insertUser();
+ }
+
+ @Test
+ public void definition() {
+ WebService.Action definition = ws.getDef();
+
+ assertThat(definition.key()).isEqualTo("add_member");
+ assertThat(definition.since()).isEqualTo("6.4");
+ assertThat(definition.isPost()).isTrue();
+ assertThat(definition.isInternal()).isTrue();
+ assertThat(definition.params()).extracting(WebService.Param::key).containsOnly("organization", "login");
+
+ WebService.Param organization = definition.param("organization");
+ assertThat(organization.isRequired()).isTrue();
+
+ WebService.Param login = definition.param("login");
+ assertThat(login.isRequired()).isTrue();
+ }
+
+ @Test
+ public void no_content_http_204_returned() {
+ TestResponse result = call(organization.getKey(), user.getLogin());
+
+ assertThat(result.getStatus()).isEqualTo(HTTP_NO_CONTENT);
+ assertThat(result.getInput()).isEmpty();
+ }
+
+ @Test
+ public void add_member_in_db() {
+ call(organization.getKey(), user.getLogin());
+
+ assertMember(organization.getUuid(), user.getId());
+ }
+
+ @Test
+ public void user_can_be_member_of_two_organizations() {
+ OrganizationDto anotherOrg = db.organizations().insert();
+
+ call(organization.getKey(), user.getLogin());
+ call(anotherOrg.getKey(), user.getLogin());
+
+ assertMember(organization.getUuid(), user.getId());
+ assertMember(anotherOrg.getUuid(), user.getId());
+ }
+
+ @Test
+ public void add_member_as_organization_admin() {
+ userSession.logIn().addPermission(ADMINISTER, organization);
+
+ call(organization.getKey(), user.getLogin());
+
+ assertMember(organization.getUuid(), user.getId());
+ }
+
+ @Test
+ public void fail_if_login_does_not_exist() {
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage("User 'login-42' is not found");
+
+ call(organization.getKey(), "login-42");
+ }
+
+ @Test
+ public void fail_if_organization_does_not_exist() {
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage("Organization 'org-42' is not found");
+
+ call("org-42", user.getLogin());
+ }
+
+ @Test
+ public void fail_if_no_login_provided() {
+ expectedException.expect(IllegalArgumentException.class);
+
+ call(organization.getKey(), null);
+ }
+
+ @Test
+ public void fail_if_no_organization_provided() {
+ expectedException.expect(IllegalArgumentException.class);
+
+ call(null, user.getLogin());
+ }
+
+ @Test
+ public void fail_if_user_already_added_in_organization() {
+ call(organization.getKey(), user.getLogin());
+
+ expectedException.expect(BadRequestException.class);
+ expectedException.expectMessage("User '" + user.getLogin() + "' is already a member of organization '" + organization.getKey() + "'");
+
+ call(organization.getKey(), user.getLogin());
+ }
+
+ @Test
+ public void fail_if_insufficient_permissions() {
+ userSession.logIn().addPermission(ADMINISTER_QUALITY_GATES, organization);
+
+ expectedException.expect(ForbiddenException.class);
+
+ call(organization.getKey(), user.getLogin());
+ }
+
+ private TestResponse call(@Nullable String organizationKey, @Nullable String login) {
+ TestRequest request = ws.newRequest();
+ setNullable(organizationKey, o -> request.setParam(PARAM_ORGANIZATION, o));
+ setNullable(login, l -> request.setParam("login", l));
+
+ return request.execute();
+ }
+
+ private void assertMember(String organizationUuid, int userId) {
+ assertThat(dbClient.organizationMemberDao().select(dbSession, organizationUuid, userId)).isPresent();
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/OrganizationsWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/OrganizationsWsModuleTest.java
index eccaae0ecc5..7c833fd7190 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/OrganizationsWsModuleTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/OrganizationsWsModuleTest.java
@@ -33,7 +33,7 @@ public class OrganizationsWsModuleTest {
ComponentContainer container = new ComponentContainer();
underTest.configure(container);
assertThat(container.getPicoContainer().getComponentAdapters())
- .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 8);
+ .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 9);
}
}