From: Jean-Baptiste Lievremont Date: Tue, 26 May 2015 14:37:31 +0000 (+0200) Subject: SONAR-6473 New WS to update a user group X-Git-Tag: 5.2-RC1~1790 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=38de95e612ad36ba9fdab9ecc73738a7ef691b77;p=sonarqube.git SONAR-6473 New WS to update a user group --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index eca68f9b1f4..85c19f94224 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -311,7 +311,7 @@ import org.sonar.server.user.ws.CurrentAction; import org.sonar.server.user.ws.FavoritesWs; import org.sonar.server.user.ws.UserPropertiesWs; import org.sonar.server.user.ws.UsersWs; -import org.sonar.server.usergroups.ws.UserGroupsWs; +import org.sonar.server.usergroups.ws.UserGroupsModule; import org.sonar.server.util.BooleanTypeValidation; import org.sonar.server.util.FloatTypeValidation; import org.sonar.server.util.IntegerTypeValidation; @@ -562,10 +562,7 @@ public class PlatformLevel4 extends PlatformLevel { // groups GroupMembershipService.class, GroupMembershipFinder.class, - UserGroupsWs.class, - org.sonar.server.usergroups.ws.SearchAction.class, - org.sonar.server.usergroups.ws.CreateAction.class, - org.sonar.server.usergroups.ws.DeleteAction.class, + UserGroupsModule.class, // permissions PermissionFacade.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/db/GroupDao.java b/server/sonar-server/src/main/java/org/sonar/server/user/db/GroupDao.java index 88229ea14f3..55c3a25916b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/db/GroupDao.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/db/GroupDao.java @@ -58,8 +58,16 @@ public class GroupDao implements DaoComponent { return mapper(session).selectByKey(key); } - @CheckForNull public GroupDto selectById(DbSession dbSession, long groupId) { + GroupDto group = selectNullableById(dbSession, groupId); + if (group == null) { + throw new NotFoundException(String.format("Could not find a group with id '%d'", groupId)); + } + return group; + } + + @CheckForNull + public GroupDto selectNullableById(DbSession dbSession, long groupId) { return mapper(dbSession).selectById(groupId); } @@ -83,6 +91,12 @@ public class GroupDao implements DaoComponent { return item; } + public GroupDto update(DbSession session, GroupDto item) { + item.setUpdatedAt(new Date(system.now())); + mapper(session).update(item); + return item; + } + public List findByUserLogin(DbSession session, String login){ return mapper(session).selectByUserLogin(login); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/CreateAction.java b/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/CreateAction.java index cdc9ad5fcf0..56e1f7d76b3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/CreateAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/CreateAction.java @@ -19,39 +19,32 @@ */ package org.sonar.server.usergroups.ws; -import com.google.common.base.Preconditions; -import java.net.HttpURLConnection; -import javax.annotation.Nullable; -import org.sonar.api.security.DefaultGroups; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService.NewAction; import org.sonar.api.server.ws.WebService.NewController; +import org.sonar.api.utils.text.JsonWriter; import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.persistence.DbSession; import org.sonar.core.user.GroupDto; import org.sonar.server.db.DbClient; -import org.sonar.server.exceptions.ServerException; import org.sonar.server.user.UserSession; import static org.sonar.core.persistence.MyBatis.closeQuietly; +import static org.sonar.server.usergroups.ws.GroupUpdater.DESCRIPTION_MAX_LENGTH; +import static org.sonar.server.usergroups.ws.GroupUpdater.NAME_MAX_LENGTH; +import static org.sonar.server.usergroups.ws.GroupUpdater.PARAM_DESCRIPTION; +import static org.sonar.server.usergroups.ws.GroupUpdater.PARAM_NAME; public class CreateAction implements UserGroupsWsAction { - private static final String PARAM_DESCRIPTION = "description"; - private static final String PARAM_NAME = "name"; - - // Database column size should be 500 (since migration #353), - // but on some instances, column size is still 255, - // hence the validation is done with 255 - private static final int NAME_MAX_LENGTH = 255; - private static final int DESCRIPTION_MAX_LENGTH = 200; - private final DbClient dbClient; private final UserSession userSession; + private final GroupUpdater groupUpdater; - public CreateAction(DbClient dbClient, UserSession userSession) { + public CreateAction(DbClient dbClient, UserSession userSession, GroupUpdater groupUpdater) { this.dbClient = dbClient; + this.groupUpdater = groupUpdater; this.userSession = userSession; } @@ -65,13 +58,13 @@ public class CreateAction implements UserGroupsWsAction { .setSince("5.2"); action.createParam(PARAM_NAME) - .setDescription("Name for the new group. A group name cannot be larger than 255 characters and must be unique. " + - "The value 'anyone' (whatever the case) is reserved and cannot be used.") + .setDescription(String.format("Name for the new group. A group name cannot be larger than %d characters and must be unique. " + + "The value 'anyone' (whatever the case) is reserved and cannot be used.", NAME_MAX_LENGTH)) .setExampleValue("sonar-users") .setRequired(true); action.createParam(PARAM_DESCRIPTION) - .setDescription("Description for the new group. A group description cannot be larger than 200 characters.") + .setDescription(String.format("Description for the new group. A group description cannot be larger than %d characters.", DESCRIPTION_MAX_LENGTH)) .setExampleValue("Default group for new users"); } @@ -82,53 +75,22 @@ public class CreateAction implements UserGroupsWsAction { String name = request.mandatoryParam(PARAM_NAME); String description = request.param(PARAM_DESCRIPTION); - validateName(name); - validateDescription(description); + groupUpdater.validateName(name); + if (description != null) { + groupUpdater.validateDescription(description); + } - GroupDto newGroup = new GroupDto().setName(name).setDescription(description); DbSession session = dbClient.openSession(false); try { - checkNameIsUnique(name, session); - newGroup = dbClient.groupDao().insert(session, new GroupDto().setName(name).setDescription(description)); + groupUpdater.checkNameIsUnique(name, session); + GroupDto newGroup = dbClient.groupDao().insert(session, new GroupDto().setName(name).setDescription(description)); session.commit(); + + JsonWriter json = response.newJsonWriter().beginObject(); + groupUpdater.writeGroup(json, newGroup, 0); + json.endObject().close(); } finally { closeQuietly(session); } - - response.newJsonWriter().beginObject().name("group").beginObject() - .prop("id", newGroup.getId().toString()) - .prop(PARAM_NAME, newGroup.getName()) - .prop(PARAM_DESCRIPTION, newGroup.getDescription()) - .prop("membersCount", 0) - .endObject().endObject().close(); - } - - private void validateName(String name) { - checkNameLength(name); - checkNameNotAnyone(name); - } - - private void checkNameLength(String name) { - Preconditions.checkArgument(!name.isEmpty(), "Name cannot be empty"); - Preconditions.checkArgument(name.length() <= NAME_MAX_LENGTH, String.format("Name cannot be longer than %d characters", NAME_MAX_LENGTH)); - } - - private void checkNameNotAnyone(String name) { - Preconditions.checkArgument(!DefaultGroups.isAnyone(name), String.format("Name '%s' is reserved (regardless of case)", DefaultGroups.ANYONE)); - } - - private void checkNameIsUnique(String name, DbSession session) { - // There is no database constraint on column groups.name - // because MySQL cannot create a unique index - // on a UTF-8 VARCHAR larger than 255 characters on InnoDB - if (dbClient.groupDao().selectNullableByKey(session, name) != null) { - throw new ServerException(HttpURLConnection.HTTP_CONFLICT, String.format("Name '%s' is already taken", name)); - } - } - - private void validateDescription(@Nullable String description) { - if (description != null) { - Preconditions.checkArgument(description.length() <= DESCRIPTION_MAX_LENGTH, String.format("Description cannot be longer than %d characters", DESCRIPTION_MAX_LENGTH)); - } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/GroupUpdater.java b/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/GroupUpdater.java new file mode 100644 index 00000000000..31175d541b9 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/GroupUpdater.java @@ -0,0 +1,86 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.usergroups.ws; + +import com.google.common.base.Preconditions; +import java.net.HttpURLConnection; +import org.sonar.api.security.DefaultGroups; +import org.sonar.api.server.ServerSide; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.user.GroupDto; +import org.sonar.server.db.DbClient; +import org.sonar.server.exceptions.ServerException; + +@ServerSide +public class GroupUpdater { + + static final String PARAM_ID = "id"; + static final String PARAM_DESCRIPTION = "description"; + static final String PARAM_NAME = "name"; + + // Database column size should be 500 (since migration #353), + // but on some instances, column size is still 255, + // hence the validation is done with 255 + static final int NAME_MAX_LENGTH = 255; + static final int DESCRIPTION_MAX_LENGTH = 200; + + private final DbClient dbClient; + + public GroupUpdater(DbClient dbClient) { + this.dbClient = dbClient; + } + + protected void validateName(String name) { + checkNameLength(name); + checkNameNotAnyone(name); + } + + private void checkNameLength(String name) { + Preconditions.checkArgument(!name.isEmpty(), "Name cannot be empty"); + Preconditions.checkArgument(name.length() <= NAME_MAX_LENGTH, String.format("Name cannot be longer than %d characters", NAME_MAX_LENGTH)); + } + + private void checkNameNotAnyone(String name) { + Preconditions.checkArgument(!DefaultGroups.isAnyone(name), String.format("Name '%s' is reserved (regardless of case)", DefaultGroups.ANYONE)); + } + + protected void checkNameIsUnique(String name, DbSession session) { + // There is no database constraint on column groups.name + // because MySQL cannot create a unique index + // on a UTF-8 VARCHAR larger than 255 characters on InnoDB + if (dbClient.groupDao().selectNullableByKey(session, name) != null) { + throw new ServerException(HttpURLConnection.HTTP_CONFLICT, String.format("Name '%s' is already taken", name)); + } + } + + protected void validateDescription(String description) { + Preconditions.checkArgument(description.length() <= DESCRIPTION_MAX_LENGTH, String.format("Description cannot be longer than %d characters", DESCRIPTION_MAX_LENGTH)); + } + + protected void writeGroup(JsonWriter json, GroupDto group, int membersCount) { + json.name("group").beginObject() + .prop(PARAM_ID, group.getId().toString()) + .prop(PARAM_NAME, group.getName()) + .prop(PARAM_DESCRIPTION, group.getDescription()) + .prop("membersCount", membersCount) + .endObject(); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/UpdateAction.java b/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/UpdateAction.java new file mode 100644 index 00000000000..3aaae84d1b5 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/UpdateAction.java @@ -0,0 +1,107 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.usergroups.ws; + +import java.util.Arrays; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService.NewAction; +import org.sonar.api.server.ws.WebService.NewController; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.user.GroupDto; +import org.sonar.server.db.DbClient; +import org.sonar.server.user.UserSession; + +import static org.sonar.core.persistence.MyBatis.closeQuietly; +import static org.sonar.server.usergroups.ws.GroupUpdater.DESCRIPTION_MAX_LENGTH; +import static org.sonar.server.usergroups.ws.GroupUpdater.NAME_MAX_LENGTH; +import static org.sonar.server.usergroups.ws.GroupUpdater.PARAM_DESCRIPTION; +import static org.sonar.server.usergroups.ws.GroupUpdater.PARAM_ID; +import static org.sonar.server.usergroups.ws.GroupUpdater.PARAM_NAME; + +public class UpdateAction implements UserGroupsWsAction { + + private final DbClient dbClient; + private final UserSession userSession; + private final GroupUpdater groupUpdater; + + public UpdateAction(DbClient dbClient, UserSession userSession, GroupUpdater groupUpdater) { + this.dbClient = dbClient; + this.groupUpdater = groupUpdater; + this.userSession = userSession; + } + + @Override + public void define(NewController context) { + NewAction action = context.createAction("update") + .setDescription("Update a group.") + .setHandler(this) + .setPost(true) + .setResponseExample(getClass().getResource("example-update.json")) + .setSince("5.2"); + + action.createParam(PARAM_ID) + .setDescription("Identifier of the group.") + .setExampleValue("42") + .setRequired(true); + + action.createParam(PARAM_NAME) + .setDescription(String.format("New name for the group. A group name cannot be larger than %d characters and must be unique. " + + "The value 'anyone' (whatever the case) is reserved and cannot be used.", NAME_MAX_LENGTH)) + .setExampleValue("sonar-users"); + + action.createParam(PARAM_DESCRIPTION) + .setDescription(String.format("New description for the group. A group description cannot be larger than %d characters.", DESCRIPTION_MAX_LENGTH)) + .setExampleValue("Default group for new users"); + } + + @Override + public void handle(Request request, Response response) throws Exception { + userSession.checkLoggedIn().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN); + + Long groupId = request.mandatoryParamAsLong(PARAM_ID); + String name = request.param(PARAM_NAME); + String description = request.param(PARAM_DESCRIPTION); + + DbSession dbSession = dbClient.openSession(false); + try { + groupUpdater.checkNameIsUnique(name, dbSession); + GroupDto group = dbClient.groupDao().selectById(dbSession, groupId); + if (name != null) { + groupUpdater.validateName(name); + group.setName(name); + } + if (description != null) { + groupUpdater.validateDescription(description); + group.setDescription(description); + } + dbClient.groupDao().update(dbSession, group); + dbSession.commit(); + + JsonWriter json = response.newJsonWriter().beginObject(); + groupUpdater.writeGroup(json, group, dbClient.groupMembershipDao().countUsersByGroups(dbSession, Arrays.asList(groupId)).get(group.getName())); + json.endObject().close(); + } finally { + closeQuietly(dbSession); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/UserGroupsModule.java b/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/UserGroupsModule.java new file mode 100644 index 00000000000..8c2406e1c91 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/UserGroupsModule.java @@ -0,0 +1,37 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.usergroups.ws; + +import org.sonar.core.component.Module; + +public class UserGroupsModule extends Module { + + @Override + protected void configureModule() { + add( + GroupUpdater.class, + UserGroupsWs.class, + SearchAction.class, + CreateAction.class, + DeleteAction.class, + UpdateAction.class); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/db/GroupDaoTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/db/GroupDaoTest.java index c30587992b5..70a1f674b53 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/db/GroupDaoTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/db/GroupDaoTest.java @@ -110,6 +110,23 @@ public class GroupDaoTest { dbTester.assertDbUnit(getClass(), "insert-result.xml", "groups"); } + @Test + public void update() { + when(system2.now()).thenReturn(DateUtils.parseDate("2013-07-25").getTime()); + + dbTester.prepareDbUnit(getClass(), "update.xml"); + + GroupDto dto = new GroupDto() + .setId(1L) + .setName("new-name") + .setDescription("New Description"); + + dao.update(session, dto); + session.commit(); + + dbTester.assertDbUnit(getClass(), "update-result.xml", "groups"); + } + @Test public void select_by_query() { dbTester.prepareDbUnit(getClass(), "select_by_query.xml"); diff --git a/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/CreateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/CreateActionTest.java index 1967cb1ca01..44e47770954 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/CreateActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/CreateActionTest.java @@ -68,7 +68,7 @@ public class CreateActionTest { DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), groupDao); - tester = new WsTester(new UserGroupsWs(new CreateAction(dbClient, userSession))); + tester = new WsTester(new UserGroupsWs(new CreateAction(dbClient, userSession, new GroupUpdater(dbClient)))); session = dbClient.openSession(false); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/UpdateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/UpdateActionTest.java new file mode 100644 index 00000000000..90ad8c4d3eb --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/UpdateActionTest.java @@ -0,0 +1,247 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.usergroups.ws; + +import java.net.HttpURLConnection; +import org.apache.commons.lang.StringUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.ExpectedException; +import org.sonar.api.utils.System2; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.DbTester; +import org.sonar.core.user.GroupDto; +import org.sonar.core.user.GroupMembershipDao; +import org.sonar.core.user.UserGroupDto; +import org.sonar.server.db.DbClient; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.exceptions.ServerException; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.user.db.GroupDao; +import org.sonar.server.user.db.UserGroupDao; +import org.sonar.server.ws.WsTester; +import org.sonar.test.DbTests; + +@Category(DbTests.class) +public class UpdateActionTest { + + @ClassRule + public static final DbTester dbTester = new DbTester(); + + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private WsTester tester; + + private GroupDao groupDao; + + private DbSession session; + + private UserGroupDao userGroupDao; + + @Before + public void setUp() { + dbTester.truncateTables(); + + groupDao = new GroupDao(System2.INSTANCE); + + userGroupDao = new UserGroupDao(); + + DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), + groupDao, new GroupMembershipDao(dbTester.myBatis()), userGroupDao); + + tester = new WsTester(new UserGroupsWs(new UpdateAction(dbClient, userSession, new GroupUpdater(dbClient)))); + + session = dbClient.openSession(false); + } + + @After + public void after() { + session.close(); + } + + @Test + public void update_nominal() throws Exception { + GroupDto existingGroup = groupDao.insert(session, new GroupDto().setName("old-name").setDescription("Old Description")); + userGroupDao.insert(session, new UserGroupDto().setGroupId(existingGroup.getId()).setUserId(42L)); + + session.commit(); + + loginAsAdmin(); + tester.newPostRequest("api/usergroups", "update") + .setParam("id", existingGroup.getId().toString()) + .setParam("name", "new-name") + .setParam("description", "New Description") + .execute().assertJson("{" + + " \"group\": {" + + " \"name\": \"new-name\"," + + " \"description\": \"New Description\"," + + " \"membersCount\": 1" + + " }" + + "}"); + } + + @Test + public void update_only_name() throws Exception { + GroupDto existingGroup = groupDao.insert(session, new GroupDto().setName("old-name").setDescription("Old Description")); + session.commit(); + + loginAsAdmin(); + tester.newPostRequest("api/usergroups", "update") + .setParam("id", existingGroup.getId().toString()) + .setParam("name", "new-name") + .execute().assertJson("{" + + " \"group\": {" + + " \"name\": \"new-name\"," + + " \"description\": \"Old Description\"," + + " \"membersCount\": 0" + + " }" + + "}"); + } + + @Test + public void update_only_description() throws Exception { + GroupDto existingGroup = groupDao.insert(session, new GroupDto().setName("old-name").setDescription("Old Description")); + session.commit(); + + loginAsAdmin(); + tester.newPostRequest("api/usergroups", "update") + .setParam("id", existingGroup.getId().toString()) + .setParam("description", "New Description") + .execute().assertJson("{" + + " \"group\": {" + + " \"name\": \"old-name\"," + + " \"description\": \"New Description\"," + + " \"membersCount\": 0" + + " }" + + "}"); + } + + @Test + public void require_admin_permission() throws Exception { + expectedException.expect(ForbiddenException.class); + + userSession.login("not-admin"); + tester.newPostRequest("api/usergroups", "update") + .setParam("id", "42") + .setParam("name", "some-product-bu") + .setParam("description", "Business Unit for Some Awesome Product") + .execute(); + } + + @Test + public void name_too_short() throws Exception { + GroupDto existingGroup = groupDao.insert(session, new GroupDto().setName("old-name").setDescription("Old Description")); + session.commit(); + + expectedException.expect(IllegalArgumentException.class); + + loginAsAdmin(); + tester.newPostRequest("api/usergroups", "update") + .setParam("id", existingGroup.getId().toString()) + .setParam("name", "") + .execute(); + } + + @Test + public void name_too_long() throws Exception { + GroupDto existingGroup = groupDao.insert(session, new GroupDto().setName("old-name").setDescription("Old Description")); + session.commit(); + + expectedException.expect(IllegalArgumentException.class); + + loginAsAdmin(); + tester.newPostRequest("api/usergroups", "update") + .setParam("id", existingGroup.getId().toString()) + .setParam("name", StringUtils.repeat("a", 255 + 1)) + .execute(); + } + + @Test + public void forbidden_name() throws Exception { + GroupDto existingGroup = groupDao.insert(session, new GroupDto().setName("old-name").setDescription("Old Description")); + session.commit(); + + expectedException.expect(IllegalArgumentException.class); + + loginAsAdmin(); + tester.newPostRequest("api/usergroups", "update") + .setParam("id", existingGroup.getId().toString()) + .setParam("name", "AnYoNe") + .execute(); + } + + @Test + public void non_unique_name() throws Exception { + GroupDto existingGroup = groupDao.insert(session, new GroupDto().setName("old-name").setDescription("Old Description")); + String groupName = "conflicting-name"; + groupDao.insert(session, new GroupDto() + .setName(groupName)); + session.commit(); + + expectedException.expect(ServerException.class); + expectedException.expectMessage("already taken"); + + loginAsAdmin(); + tester.newPostRequest("api/usergroups", "update") + .setParam("id", existingGroup.getId().toString()) + .setParam("name", groupName) + .execute().assertStatus(HttpURLConnection.HTTP_CONFLICT); + } + + @Test + public void description_too_long() throws Exception { + GroupDto existingGroup = groupDao.insert(session, new GroupDto().setName("old-name").setDescription("Old Description")); + session.commit(); + + expectedException.expect(IllegalArgumentException.class); + + loginAsAdmin(); + tester.newPostRequest("api/usergroups", "update") + .setParam("id", existingGroup.getId().toString()) + .setParam("name", "long-group-description-is-looooooooooooong") + .setParam("description", StringUtils.repeat("a", 200 + 1)) + .execute(); + } + + @Test + public void unknown_group() throws Exception { + expectedException.expect(NotFoundException.class); + + loginAsAdmin(); + tester.newPostRequest("api/usergroups", "update") + .setParam("id", "42") + .execute(); + } + + private void loginAsAdmin() { + userSession.login("admin").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/UserGroupsWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/UserGroupsWsTest.java index e0d9d4e060c..d739480aa9a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/UserGroupsWsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/UserGroupsWsTest.java @@ -41,7 +41,7 @@ public class UserGroupsWsTest { public void setUp() { WsTester tester = new WsTester(new UserGroupsWs( new SearchAction(mock(DbClient.class)), - new CreateAction(mock(DbClient.class), mock(UserSession.class)))); + new CreateAction(mock(DbClient.class), mock(UserSession.class), mock(GroupUpdater.class)))); controller = tester.controller("api/usergroups"); } diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/db/GroupDaoTest/update-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/db/GroupDaoTest/update-result.xml new file mode 100644 index 00000000000..7fdf00ca9d0 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/db/GroupDaoTest/update-result.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/db/GroupDaoTest/update.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/db/GroupDaoTest/update.xml new file mode 100644 index 00000000000..df225b534d4 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/db/GroupDaoTest/update.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/sonar-core/src/main/java/org/sonar/core/user/GroupMapper.java b/sonar-core/src/main/java/org/sonar/core/user/GroupMapper.java index de36b36af7c..607b97b4758 100644 --- a/sonar-core/src/main/java/org/sonar/core/user/GroupMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/user/GroupMapper.java @@ -36,10 +36,11 @@ public interface GroupMapper { void insert(GroupDto groupDto); + void update(GroupDto item); + List selectByQuery(String query, RowBounds rowBounds); int countByQuery(String query); void deleteById(long groupId); - } diff --git a/sonar-core/src/main/resources/org/sonar/core/user/GroupMapper.xml b/sonar-core/src/main/resources/org/sonar/core/user/GroupMapper.xml index 132edb40784..2f7cc2d3f16 100644 --- a/sonar-core/src/main/resources/org/sonar/core/user/GroupMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/user/GroupMapper.xml @@ -50,6 +50,14 @@ VALUES (#{name}, #{description}, #{createdAt}, #{updatedAt}) + + UPDATE groups SET + name=#{name}, + description=#{description}, + updated_at=#{updatedAt} + WHERE id=#{id} + +