Introduce own manager for reduced model object

Move autocomplete functionality to own managers and add functions to
load reduced model objects by id
This commit is contained in:
René Pfeuffer
2019-04-01 13:51:34 +02:00
parent c2e385f950
commit ae79a5ad9d
21 changed files with 214 additions and 98 deletions

View File

@@ -0,0 +1,19 @@
package sonia.scm;
import java.util.Collection;
import java.util.Optional;
public interface DisplayManager<T extends ReducedModelObject> {
int DEFAULT_LIMIT = 5;
/**
* Returns a {@link Collection} of filtered objects
*
* @param filter the searched string
* @return filtered object from the store
*/
Collection<T> autocomplete(String filter);
Optional<T> get(String id);
}

View File

@@ -47,8 +47,6 @@ public interface Manager<T extends ModelObject>
extends HandlerBase<T>, LastModifiedAware extends HandlerBase<T>, LastModifiedAware
{ {
int DEFAULT_LIMIT = 5;
/** /**
* Reloads a object from store and overwrites all changes. * Reloads a object from store and overwrites all changes.

View File

@@ -39,8 +39,10 @@ package sonia.scm;
* @author Sebastian Sdorra * @author Sebastian Sdorra
* *
* @param <T> type of objects to transform * @param <T> type of objects to transform
* @param <R> result type of the transformation
*/ */
public interface TransformFilter<T> @FunctionalInterface
public interface TransformFilter<T, R>
{ {
/** /**
@@ -52,5 +54,5 @@ public interface TransformFilter<T>
* *
* @return tranformed object * @return tranformed object
*/ */
public T accept(T item); R accept(T item);
} }

View File

@@ -0,0 +1,26 @@
package sonia.scm.group;
import sonia.scm.ReducedModelObject;
public class DisplayGroup implements ReducedModelObject {
private final String id;
private final String displayName;
public static DisplayGroup from(Group group) {
return new DisplayGroup(group.getId(), group.getDescription());
}
private DisplayGroup(String id, String displayName) {
this.id = id;
this.displayName = displayName;
}
public String getId() {
return id;
}
public String getDisplayName() {
return displayName;
}
}

View File

@@ -0,0 +1,6 @@
package sonia.scm.group;
import sonia.scm.DisplayManager;
public interface GroupDisplayManager extends DisplayManager<DisplayGroup> {
}

View File

@@ -61,14 +61,4 @@ public interface GroupManager
* @return all groups assigned to the given member * @return all groups assigned to the given member
*/ */
public Collection<Group> getGroupsForMember(String member); public Collection<Group> getGroupsForMember(String member);
/**
* Returns a {@link java.util.Collection} of filtered objects
*
* @param filter the searched string
* @return filtered object from the store
*/
Collection<Group> autocomplete(String filter);
} }

View File

@@ -109,11 +109,6 @@ public class GroupManagerDecorator
return decorated.getGroupsForMember(member); return decorated.getGroupsForMember(member);
} }
@Override
public Collection<Group> autocomplete(String filter) {
return decorated.autocomplete(filter);
}
//~--- fields --------------------------------------------------------------- //~--- fields ---------------------------------------------------------------
/** Field description */ /** Field description */

View File

@@ -159,17 +159,17 @@ public final class SearchUtil
* *
* @return * @return
*/ */
public static <T> Collection<T> search(SearchRequest searchRequest, public static <T, R> Collection<R> search(SearchRequest searchRequest,
Collection<T> collection, TransformFilter<T> filter) Collection<T> collection, TransformFilter<T, R> filter)
{ {
List<T> items = new ArrayList<T>(); List<R> items = new ArrayList<>();
int index = 0; int index = 0;
int counter = 0; int counter = 0;
Iterator<T> it = collection.iterator(); Iterator<T> it = collection.iterator();
while (it.hasNext()) while (it.hasNext())
{ {
T item = filter.accept(it.next()); R item = filter.accept(it.next());
if (item != null) if (item != null)
{ {

View File

@@ -0,0 +1,32 @@
package sonia.scm.user;
import sonia.scm.ReducedModelObject;
public class DisplayUser implements ReducedModelObject {
private final String id;
private final String displayName;
private final String mail;
public static DisplayUser from(User user) {
return new DisplayUser(user.getId(), user.getDisplayName(), user.getMail());
}
private DisplayUser(String id, String displayName, String mail) {
this.id = id;
this.displayName = displayName;
this.mail = mail;
}
public String getId() {
return id;
}
public String getDisplayName() {
return displayName;
}
public String getMail() {
return mail;
}
}

View File

@@ -0,0 +1,6 @@
package sonia.scm.user;
import sonia.scm.DisplayManager;
public interface UserDisplayManager extends DisplayManager<DisplayUser> {
}

View File

@@ -75,14 +75,6 @@ public interface UserManager
return getDefaultType().equals(user.getType()); return getDefaultType().equals(user.getType());
} }
/**
* Returns a {@link java.util.Collection} of filtered objects
*
* @param filter the searched string
* @return filtered object from the store
*/
Collection<User> autocomplete(String filter);
/** /**
* Changes the password of the logged in user. * Changes the password of the logged in user.
* @param oldPassword The current encrypted password of the user. * @param oldPassword The current encrypted password of the user.

View File

@@ -121,11 +121,6 @@ public class UserManagerDecorator extends ManagerDecorator<User>
return decorated.getDefaultType(); return decorated.getDefaultType();
} }
@Override
public Collection<User> autocomplete(String filter) {
return decorated.autocomplete(filter);
}
@Override @Override
public void changePasswordForLoggedInUser(String oldPassword, String newPassword) { public void changePasswordForLoggedInUser(String oldPassword, String newPassword) {
decorated.changePasswordForLoggedInUser(oldPassword, newPassword); decorated.changePasswordForLoggedInUser(oldPassword, newPassword);

View File

@@ -0,0 +1,42 @@
package sonia.scm;
import sonia.scm.search.SearchRequest;
import sonia.scm.search.SearchUtil;
import java.util.Collection;
import java.util.Optional;
import java.util.function.Function;
import static java.util.Optional.ofNullable;
import static sonia.scm.group.DisplayGroup.from;
public abstract class GenericDisplayManager<D, T extends ReducedModelObject> implements DisplayManager<T> {
private final GenericDAO<D> dao;
private final Function<D, T> transform;
protected GenericDisplayManager(GenericDAO<D> dao, Function<D, T> transform) {
this.dao = dao;
this.transform = transform;
}
@Override
public Collection<T> autocomplete(String filter) {
checkPermission();
SearchRequest searchRequest = new SearchRequest(filter, true, DEFAULT_LIMIT);
return SearchUtil.search(
searchRequest,
dao.getAll(),
object -> matches(searchRequest, object)? transform.apply(object): null
);
}
protected abstract void checkPermission();
protected abstract boolean matches(SearchRequest searchRequest, D object);
@Override
public Optional<T> get(String id) {
return ofNullable(dao.get(id)).map(transform);
}
}

View File

@@ -46,8 +46,10 @@ import sonia.scm.cache.CacheManager;
import sonia.scm.cache.GuavaCacheManager; import sonia.scm.cache.GuavaCacheManager;
import sonia.scm.config.ScmConfiguration; import sonia.scm.config.ScmConfiguration;
import sonia.scm.event.ScmEventBus; import sonia.scm.event.ScmEventBus;
import sonia.scm.group.DefaultGroupDisplayManager;
import sonia.scm.group.DefaultGroupManager; import sonia.scm.group.DefaultGroupManager;
import sonia.scm.group.GroupDAO; import sonia.scm.group.GroupDAO;
import sonia.scm.group.GroupDisplayManager;
import sonia.scm.group.GroupManager; import sonia.scm.group.GroupManager;
import sonia.scm.group.GroupManagerProvider; import sonia.scm.group.GroupManagerProvider;
import sonia.scm.group.xml.XmlGroupDAO; import sonia.scm.group.xml.XmlGroupDAO;
@@ -102,8 +104,10 @@ import sonia.scm.template.MustacheTemplateEngine;
import sonia.scm.template.TemplateEngine; import sonia.scm.template.TemplateEngine;
import sonia.scm.template.TemplateEngineFactory; import sonia.scm.template.TemplateEngineFactory;
import sonia.scm.template.TemplateServlet; import sonia.scm.template.TemplateServlet;
import sonia.scm.user.DefaultUserDisplayManager;
import sonia.scm.user.DefaultUserManager; import sonia.scm.user.DefaultUserManager;
import sonia.scm.user.UserDAO; import sonia.scm.user.UserDAO;
import sonia.scm.user.UserDisplayManager;
import sonia.scm.user.UserManager; import sonia.scm.user.UserManager;
import sonia.scm.user.UserManagerProvider; import sonia.scm.user.UserManagerProvider;
import sonia.scm.user.xml.XmlUserDAO; import sonia.scm.user.xml.XmlUserDAO;
@@ -268,8 +272,11 @@ public class ScmServletModule extends ServletModule
RepositoryManagerProvider.class); RepositoryManagerProvider.class);
bindDecorated(UserManager.class, DefaultUserManager.class, bindDecorated(UserManager.class, DefaultUserManager.class,
UserManagerProvider.class); UserManagerProvider.class);
bind(UserDisplayManager.class, DefaultUserDisplayManager.class);
bindDecorated(GroupManager.class, DefaultGroupManager.class, bindDecorated(GroupManager.class, DefaultGroupManager.class,
GroupManagerProvider.class); GroupManagerProvider.class);
bind(GroupDisplayManager.class, DefaultGroupDisplayManager.class);
bind(CGIExecutorFactory.class, DefaultCGIExecutorFactory.class); bind(CGIExecutorFactory.class, DefaultCGIExecutorFactory.class);
// bind sslcontext provider // bind sslcontext provider

View File

@@ -4,8 +4,8 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.NotEmpty;
import sonia.scm.ReducedModelObject; import sonia.scm.ReducedModelObject;
import sonia.scm.group.GroupManager; import sonia.scm.group.GroupDisplayManager;
import sonia.scm.user.UserManager; import sonia.scm.user.UserDisplayManager;
import sonia.scm.web.VndMediaType; import sonia.scm.web.VndMediaType;
import javax.inject.Inject; import javax.inject.Inject;
@@ -30,14 +30,14 @@ public class AutoCompleteResource {
private ReducedObjectModelToDtoMapper mapper; private ReducedObjectModelToDtoMapper mapper;
private UserManager userManager; private UserDisplayManager userDisplayManager;
private GroupManager groupManager; private GroupDisplayManager groupDisplayManager;
@Inject @Inject
public AutoCompleteResource(ReducedObjectModelToDtoMapper mapper, UserManager userManager, GroupManager groupManager) { public AutoCompleteResource(ReducedObjectModelToDtoMapper mapper, UserDisplayManager userDisplayManager, GroupDisplayManager groupDisplayManager) {
this.mapper = mapper; this.mapper = mapper;
this.userManager = userManager; this.userDisplayManager = userDisplayManager;
this.groupManager = groupManager; this.groupDisplayManager = groupDisplayManager;
} }
@GET @GET
@@ -51,7 +51,7 @@ public class AutoCompleteResource {
@ResponseCode(code = 500, condition = "internal server error") @ResponseCode(code = 500, condition = "internal server error")
}) })
public List<ReducedObjectModelDto> searchUser(@NotEmpty(message = PARAMETER_IS_REQUIRED) @Size(min = MIN_SEARCHED_CHARS, message = INVALID_PARAMETER_LENGTH) @QueryParam("q") String filter) { public List<ReducedObjectModelDto> searchUser(@NotEmpty(message = PARAMETER_IS_REQUIRED) @Size(min = MIN_SEARCHED_CHARS, message = INVALID_PARAMETER_LENGTH) @QueryParam("q") String filter) {
return map(userManager.autocomplete(filter)); return map(userDisplayManager.autocomplete(filter));
} }
@GET @GET
@@ -65,7 +65,7 @@ public class AutoCompleteResource {
@ResponseCode(code = 500, condition = "internal server error") @ResponseCode(code = 500, condition = "internal server error")
}) })
public List<ReducedObjectModelDto> searchGroup(@NotEmpty(message = PARAMETER_IS_REQUIRED) @Size(min = MIN_SEARCHED_CHARS, message = INVALID_PARAMETER_LENGTH) @QueryParam("q") String filter) { public List<ReducedObjectModelDto> searchGroup(@NotEmpty(message = PARAMETER_IS_REQUIRED) @Size(min = MIN_SEARCHED_CHARS, message = INVALID_PARAMETER_LENGTH) @QueryParam("q") String filter) {
return map(groupManager.autocomplete(filter)); return map(groupDisplayManager.autocomplete(filter));
} }
private <T extends ReducedModelObject> List<ReducedObjectModelDto> map(Collection<T> autocomplete) { private <T extends ReducedModelObject> List<ReducedObjectModelDto> map(Collection<T> autocomplete) {

View File

@@ -6,6 +6,7 @@ import com.webcohesion.enunciate.metadata.rs.TypeHint;
import org.apache.shiro.authc.credential.PasswordService; import org.apache.shiro.authc.credential.PasswordService;
import sonia.scm.user.User; import sonia.scm.user.User;
import sonia.scm.user.UserManager; import sonia.scm.user.UserManager;
import sonia.scm.user.UserPermissions;
import sonia.scm.web.VndMediaType; import sonia.scm.web.VndMediaType;
import javax.inject.Inject; import javax.inject.Inject;

View File

@@ -0,0 +1,25 @@
package sonia.scm.group;
import sonia.scm.GenericDisplayManager;
import sonia.scm.search.SearchRequest;
import sonia.scm.search.SearchUtil;
import javax.inject.Inject;
public class DefaultGroupDisplayManager extends GenericDisplayManager<Group, DisplayGroup> implements GroupDisplayManager {
@Inject
public DefaultGroupDisplayManager(GroupDAO groupDAO) {
super(groupDAO, DisplayGroup::from);
}
@Override
protected void checkPermission() {
GroupPermissions.autocomplete().check();
}
@Override
protected boolean matches(SearchRequest searchRequest, Group group) {
return SearchUtil.matchesOne(searchRequest, group.getName(), group.getDescription());
}
}

View File

@@ -195,7 +195,7 @@ public class DefaultGroupManager extends AbstractGroupManager
final PermissionActionCheck<Group> check = GroupPermissions.read(); final PermissionActionCheck<Group> check = GroupPermissions.read();
return SearchUtil.search(searchRequest, groupDAO.getAll(), return SearchUtil.search(searchRequest, groupDAO.getAll(),
new TransformFilter<Group>() new TransformFilter<Group, Group>()
{ {
@Override @Override
public Group accept(Group group) public Group accept(Group group)
@@ -241,13 +241,6 @@ public class DefaultGroupManager extends AbstractGroupManager
return group; return group;
} }
@Override
public Collection<Group> autocomplete(String filter) {
GroupPermissions.autocomplete().check();
SearchRequest searchRequest = new SearchRequest(filter, true, DEFAULT_LIMIT);
return SearchUtil.search(searchRequest, groupDAO.getAll(), group -> matches(searchRequest,group)?group:null);
}
/** /**
* Method description * Method description
* *

View File

@@ -0,0 +1,25 @@
package sonia.scm.user;
import sonia.scm.GenericDisplayManager;
import sonia.scm.search.SearchRequest;
import sonia.scm.search.SearchUtil;
import javax.inject.Inject;
public class DefaultUserDisplayManager extends GenericDisplayManager<User, DisplayUser> implements UserDisplayManager {
@Inject
public DefaultUserDisplayManager(UserDAO userDAO) {
super(userDAO, DisplayUser::from);
}
@Override
protected void checkPermission() {
UserPermissions.autocomplete().check();
}
@Override
protected boolean matches(SearchRequest searchRequest, User user) {
return SearchUtil.matchesOne(searchRequest, user.getName(), user.getDisplayName(), user.getMail());
}
}

View File

@@ -212,13 +212,6 @@ public class DefaultUserManager extends AbstractUserManager
fresh.copyProperties(user); fresh.copyProperties(user);
} }
@Override
public Collection<User> autocomplete(String filter) {
UserPermissions.autocomplete().check();
SearchRequest searchRequest = new SearchRequest(filter, true, DEFAULT_LIMIT);
return SearchUtil.search(searchRequest, userDAO.getAll(), user -> matches(searchRequest,user)?user:null);
}
/** /**
* Method description * Method description
* *
@@ -236,7 +229,7 @@ public class DefaultUserManager extends AbstractUserManager
} }
final PermissionActionCheck<User> check = UserPermissions.read(); final PermissionActionCheck<User> check = UserPermissions.read();
return SearchUtil.search(searchRequest, userDAO.getAll(), new TransformFilter<User>() { return SearchUtil.search(searchRequest, userDAO.getAll(), new TransformFilter<User, User>() {
@Override @Override
public User accept(User user) public User accept(User user)
{ {
@@ -415,35 +408,6 @@ public class DefaultUserManager extends AbstractUserManager
this.modify(user); this.modify(user);
} }
/**
* Method description
*
*
* @param unmarshaller
* @param path
*/
private void createDefaultAccount(Unmarshaller unmarshaller, String path)
{
InputStream input = DefaultUserManager.class.getResourceAsStream(path);
try
{
User user = (User) unmarshaller.unmarshal(input);
user.setType(userDAO.getType());
user.setCreationDate(System.currentTimeMillis());
userDAO.add(user);
}
catch (Exception ex)
{
logger.error("could not create account", ex);
}
finally
{
IOUtil.close(input);
}
}
//~--- fields --------------------------------------------------------------- //~--- fields ---------------------------------------------------------------
private final UserDAO userDAO; private final UserDAO userDAO;

View File

@@ -14,16 +14,14 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.Manager; import sonia.scm.DisplayManager;
import sonia.scm.group.DefaultGroupManager; import sonia.scm.group.DefaultGroupDisplayManager;
import sonia.scm.group.Group; import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.group.xml.XmlGroupDAO; import sonia.scm.group.xml.XmlGroupDAO;
import sonia.scm.store.ConfigurationStore; import sonia.scm.store.ConfigurationStore;
import sonia.scm.store.ConfigurationStoreFactory; import sonia.scm.store.ConfigurationStoreFactory;
import sonia.scm.user.DefaultUserManager; import sonia.scm.user.DefaultUserDisplayManager;
import sonia.scm.user.User; import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.user.xml.XmlUserDAO; import sonia.scm.user.xml.XmlUserDAO;
import sonia.scm.web.VndMediaType; import sonia.scm.web.VndMediaType;
import sonia.scm.xml.XmlDatabase; import sonia.scm.xml.XmlDatabase;
@@ -51,7 +49,7 @@ public class AutoCompleteResourceTest {
public final ShiroRule shiroRule = new ShiroRule(); public final ShiroRule shiroRule = new ShiroRule();
public static final String URL = "/" + AutoCompleteResource.PATH; public static final String URL = "/" + AutoCompleteResource.PATH;
private final Integer defaultLimit = Manager.DEFAULT_LIMIT; private final Integer defaultLimit = DisplayManager.DEFAULT_LIMIT;
private Dispatcher dispatcher; private Dispatcher dispatcher;
private XmlUserDAO userDao; private XmlUserDAO userDao;
@@ -73,8 +71,8 @@ public class AutoCompleteResourceTest {
XmlGroupDAO groupDAO = new XmlGroupDAO(storeFactory); XmlGroupDAO groupDAO = new XmlGroupDAO(storeFactory);
groupDao = spy(groupDAO); groupDao = spy(groupDAO);
ReducedObjectModelToDtoMapperImpl mapper = new ReducedObjectModelToDtoMapperImpl(); ReducedObjectModelToDtoMapperImpl mapper = new ReducedObjectModelToDtoMapperImpl();
UserManager userManager = new DefaultUserManager(this.userDao); DefaultUserDisplayManager userManager = new DefaultUserDisplayManager(this.userDao);
GroupManager groupManager = new DefaultGroupManager(groupDao); DefaultGroupDisplayManager groupManager = new DefaultGroupDisplayManager(groupDao);
AutoCompleteResource autoCompleteResource = new AutoCompleteResource(mapper, userManager, groupManager); AutoCompleteResource autoCompleteResource = new AutoCompleteResource(mapper, userManager, groupManager);
dispatcher = createDispatcher(autoCompleteResource); dispatcher = createDispatcher(autoCompleteResource);
} }