Skip to content
Permalink
Browse files

Users api v2 - bulk delete

  • Loading branch information...
yankurk authored and ketan committed Aug 18, 2017
1 parent eb7305e commit e4d041b2340ee6b7928a5fb11b6d3d787665dd1f
Showing with 1,221 additions and 36 deletions.
  1. +6 −4 server/src/com/thoughtworks/go/server/dao/UserDao.java
  2. +17 −2 server/src/com/thoughtworks/go/server/dao/UserSqlMapDao.java
  3. +34 −0 server/src/com/thoughtworks/go/server/service/UserService.java
  4. +56 −0 server/src/com/thoughtworks/go/server/service/result/BulkDeletionFailureResult.java
  5. +31 −8 server/test/integration/com/thoughtworks/go/server/dao/UserSqlMapDaoIntegrationTest.java
  6. +12 −13 server/test/unit/com/thoughtworks/go/server/dao/UserSqlMapDaoTest.java
  7. +87 −5 server/test/unit/com/thoughtworks/go/server/service/UserServiceTest.java
  8. +29 −0 server/test/unit/com/thoughtworks/go/server/service/result/BulkDeletionFailureResultTest.java
  9. +1 −1 server/webapp/WEB-INF/rails.new/app/controllers/api_v2/base_controller.rb
  10. +88 −0 server/webapp/WEB-INF/rails.new/app/controllers/api_v2/users_controller.rb
  11. +46 −0 server/webapp/WEB-INF/rails.new/app/helpers/api_v2/users_helper.rb
  12. +1 −1 server/webapp/WEB-INF/rails.new/app/presenters/api_v2/base_representer.rb
  13. +32 −0 server/webapp/WEB-INF/rails.new/app/presenters/api_v2/bulk_deletion_failure_result_representer.rb
  14. +44 −0 server/webapp/WEB-INF/rails.new/app/presenters/api_v2/user_representer.rb
  15. +41 −0 server/webapp/WEB-INF/rails.new/app/presenters/api_v2/user_summary_representer.rb
  16. +39 −0 server/webapp/WEB-INF/rails.new/app/presenters/api_v2/users_representer.rb
  17. +4 −0 server/webapp/WEB-INF/rails.new/config/routes.rb
  18. +2 −1 server/webapp/WEB-INF/rails.new/lib/java_imports.rb
  19. +477 −0 server/webapp/WEB-INF/rails.new/spec/controllers/api_v2/users_controller_spec.rb
  20. +55 −0 .../webapp/WEB-INF/rails.new/spec/presenters/api_v2/bulk_deletion_failure_result_representer_spec.rb
  21. +44 −0 server/webapp/WEB-INF/rails.new/spec/presenters/api_v2/user_representer_spec.rb
  22. +37 −0 server/webapp/WEB-INF/rails.new/spec/presenters/api_v2/user_summary_representer_spec.rb
  23. +35 −0 server/webapp/WEB-INF/rails.new/spec/presenters/api_v2/users_representer_spec.rb
  24. +3 −1 server/webapp/localize.xml
@@ -1,5 +1,5 @@
/*************************GO-LICENSE-START*********************************
* Copyright 2014 ThoughtWorks, Inc.
* Copyright 2017 ThoughtWorks, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,12 +16,12 @@

package com.thoughtworks.go.server.dao;

import java.util.List;
import java.util.Set;

import com.thoughtworks.go.domain.User;
import com.thoughtworks.go.domain.Users;

import java.util.List;
import java.util.Set;

public interface UserDao {
void saveOrUpdate(User user);

@@ -46,4 +46,6 @@
User load(long id);

boolean deleteUser(String username);

boolean deleteUsers(List<String> userNames);
}
@@ -1,5 +1,5 @@
/*************************GO-LICENSE-START*********************************
* Copyright 2014 ThoughtWorks, Inc.
* Copyright 2017 ThoughtWorks, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -49,7 +49,7 @@
import org.springframework.transaction.support.TransactionSynchronizationAdapter;

@Component
public class UserSqlMapDao extends HibernateDaoSupport implements UserDao {
public class UserSqlMapDao extends HibernateDaoSupport implements UserDao {
private SessionFactory sessionFactory;
private TransactionTemplate transactionTemplate;
private GoCache goCache;
@@ -164,6 +164,7 @@ public void enableUsers(List<String> usernames) {
changeEnabledStatus(usernames, true);
}

@Override
public List<User> enabledUsers() {
List<User> enabledUsers = new ArrayList<>();
for (User user : allUsers()) {
@@ -212,6 +213,20 @@ public Object doInTransaction(TransactionStatus status) {
});
}

@Override
public boolean deleteUsers(List<String> userNames) {
return (Boolean) transactionTemplate.execute(new TransactionCallback() {
@Override
public Object doInTransaction(TransactionStatus status) {
String queryString = "delete from User where name in (:userNames)";
Query query = sessionFactory.getCurrentSession().createQuery(queryString);
query.setParameterList("userNames", userNames);
query.executeUpdate();
return Boolean.TRUE;
}
});
}

private void clearEnabledUserCountFromCache() {
synchronized (ENABLED_USER_COUNT_CACHE_KEY) {
goCache.remove(ENABLED_USER_COUNT_CACHE_KEY);
@@ -30,6 +30,7 @@
import com.thoughtworks.go.server.exceptions.UserNotFoundException;
import com.thoughtworks.go.server.persistence.OauthRepository;
import com.thoughtworks.go.server.security.OnlyKnownUsersAllowedException;
import com.thoughtworks.go.server.service.result.BulkDeletionFailureResult;
import com.thoughtworks.go.server.service.result.HttpLocalizedOperationResult;
import com.thoughtworks.go.server.service.result.LocalizedOperationResult;
import com.thoughtworks.go.server.transaction.TransactionTemplate;
@@ -257,6 +258,39 @@ public void deleteUser(String username, HttpLocalizedOperationResult result) {
}
}

public BulkDeletionFailureResult deleteUsers(List<String> userNames, HttpLocalizedOperationResult result) {
if (userNames == null || userNames.isEmpty()) {
result.badRequest(LocalizedMessage.string("NO_USERS_SELECTED"));
return null;
}
synchronized (enableUserMutex) {
BulkDeletionFailureResult bulkDeletionFailureResult = deletionValidation(userNames);

if (bulkDeletionFailureResult.isEmpty()) {
userDao.deleteUsers(userNames);
result.setMessage(LocalizedMessage.string("RESOURCES_DELETE_SUCCESSFUL", "users", userNames));
} else {
result.unprocessableEntity(LocalizedMessage.string("USER_ENABLED_OR_NOT_FOUND"));
}
return bulkDeletionFailureResult;
}
}

private BulkDeletionFailureResult deletionValidation(List<String> userNames) {
BulkDeletionFailureResult bulkDeletionFailureResult = new BulkDeletionFailureResult();
for (String userName : userNames) {
User user = userDao.findUser(userName);
if (user instanceof NullUser) {
bulkDeletionFailureResult.addNonExistentUserName(userName);
continue;
}
if (user.isEnabled()) {
bulkDeletionFailureResult.addEnabledUserName(userName);
}
}
return bulkDeletionFailureResult;
}

public enum SortableColumn {
EMAIL {
protected String get(UserModel model) {
@@ -0,0 +1,56 @@
/*
* Copyright 2017 ThoughtWorks, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.thoughtworks.go.server.service.result;

import java.util.ArrayList;
import java.util.List;
public class BulkDeletionFailureResult {
private List<String> nonExistentUsers;
private List<String> enabledUsers;

public BulkDeletionFailureResult() {
nonExistentUsers = new ArrayList<>();
enabledUsers = new ArrayList<>();
}

public BulkDeletionFailureResult(List<String> nonExistentUsers, List<String> enabledUsers) {
this.nonExistentUsers = new ArrayList<>();
this.nonExistentUsers = nonExistentUsers;
this.enabledUsers = new ArrayList<>();
this.enabledUsers = enabledUsers;
}

public List<String> getNonExistentUsers(){
return nonExistentUsers;
}

public List<String> getEnabledUsers(){
return enabledUsers;
}

public void addNonExistentUserName(String userName) {
nonExistentUsers.add(userName);
}

public void addEnabledUserName(String userName) {
enabledUsers.add(userName);
}

public boolean isEmpty() {
return nonExistentUsers.isEmpty() && enabledUsers.isEmpty();
}
}
@@ -43,16 +43,20 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.matchers.JUnitMatchers.hasItems;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
"classpath:WEB-INF/applicationContext-global.xml",
"classpath:WEB-INF/applicationContext-dataLocalAccess.xml",
"classpath:WEB-INF/applicationContext-acegi-security.xml"
})
public class UserSqlMapDaoIntegrationTest {
@Autowired private UserSqlMapDao userDao;
@Autowired private DatabaseAccessHelper dbHelper;
@Autowired private SessionFactory sessionFactory;
@Autowired
private UserSqlMapDao userDao;
@Autowired
private DatabaseAccessHelper dbHelper;
@Autowired
private SessionFactory sessionFactory;

@Before
public void setup() throws Exception {
@@ -77,15 +81,15 @@ public void shouldSaveUsersIntoDatabase() throws Exception {
}

@Test
public void shouldSaveLoginAsDisplayNameIfDisplayNameIsNotPresent(){
public void shouldSaveLoginAsDisplayNameIfDisplayNameIsNotPresent() {
User user = new User("loser");
userDao.saveOrUpdate(user);

assertThat(userDao.findUser("loser").getDisplayName(), is("loser"));
}

@Test
public void shouldNotUpdateDisplayNameToNullOrBlank(){
public void shouldNotUpdateDisplayNameToNullOrBlank() {
User user = new User("loser", "moocow", "moocow@example.com");
userDao.saveOrUpdate(user);
user.setDisplayName("");
@@ -193,7 +197,7 @@ public void shouldUpdateUserWhenUserExist() throws Exception {
}

@Test
public void shouldUpdateUserWithEnabledStatusWhenUserExist(){
public void shouldUpdateUserWithEnabledStatusWhenUserExist() {
User user = new User("user", "my name", "user2@mail.com");
userDao.saveOrUpdate(user);
final User foundUser = userDao.findUser("user");
@@ -311,7 +315,7 @@ public void findUserShouldIgnoreCaseOfUserName() {
}

@Test
public void shouldLoadSubscribersOfNotification(){
public void shouldLoadSubscribersOfNotification() {
User user1 = new User("user1");
user1.addNotificationFilter(new NotificationFilter("pipeline", "stage", StageEvent.Fails, true));
userDao.saveOrUpdate(user1);
@@ -348,7 +352,7 @@ public int compare(User user1, User user2) {


@Test
public void shouldDeleteNotificationOnAUser(){
public void shouldDeleteNotificationOnAUser() {
User user = new User("user1");
user.addNotificationFilter(new NotificationFilter("pipeline1", "stage", StageEvent.Fails, true));
user.addNotificationFilter(new NotificationFilter("pipeline2", "stage", StageEvent.Fails, true));
@@ -425,6 +429,25 @@ public void shouldAddNewUserWhenDeleteQueryForTheUserHasCachedANullUser() {
assertThat(userDao.deleteUser(userName), is(true));
}

@Test
public void shouldDeleteUsers() {
User john = new User("john");
john.disable();
User joan = new User("joan");
joan.disable();

List<String> userNames = Arrays.asList("john", "joan");

userDao.saveOrUpdate(john);
userDao.saveOrUpdate(joan);

boolean result = userDao.deleteUsers(userNames);
assertThat(result, is(true));

Users users = userDao.allUsers();
assertThat(users, is(empty()));
}

private User anUser() {
return user("user");
}
@@ -16,9 +16,6 @@

package com.thoughtworks.go.server.dao;

import java.util.Arrays;
import java.util.Set;

import com.thoughtworks.go.domain.User;
import com.thoughtworks.go.server.cache.GoCache;
import com.thoughtworks.go.server.service.StubGoCache;
@@ -34,24 +31,26 @@
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.transaction.support.TransactionCallback;

import java.util.Arrays;
import java.util.Set;

import static com.thoughtworks.go.util.DataStructureUtils.s;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.hasItems;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;

public class UserSqlMapDaoTest {
@Mock private SessionFactory sessionFactory;
@Mock private TransactionTemplate transactionTemplate;
@Mock private HibernateTemplate mockHibernateTemplate;
@Mock private TransactionSynchronizationManager transactionSynchronizationManager;
@Mock
private SessionFactory sessionFactory;
@Mock
private TransactionTemplate transactionTemplate;
@Mock
private HibernateTemplate mockHibernateTemplate;
@Mock
private TransactionSynchronizationManager transactionSynchronizationManager;
private StubGoCache goCache;
private UserSqlMapDao dao;

0 comments on commit e4d041b

Please sign in to comment.
You can’t perform that action at this time.