mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-07 22:15:45 +01:00
add autocomplete endpoint
This commit is contained in:
@@ -114,4 +114,13 @@ public interface GenericDAO<T>
|
||||
* @return all items
|
||||
*/
|
||||
public Collection<T> getAll();
|
||||
|
||||
/**
|
||||
* Returns items containing the searched string
|
||||
*
|
||||
* @param searched the search character
|
||||
* @param limit the max count of the result entities. if limit is <= 0 return all filtered entities
|
||||
* @return searched items
|
||||
*/
|
||||
Collection<T> getFiltered(String searched, int limit);
|
||||
}
|
||||
|
||||
@@ -77,6 +77,15 @@ public interface Manager<T extends ModelObject>
|
||||
*/
|
||||
Collection<T> getAll();
|
||||
|
||||
/**
|
||||
* Returns a {@link java.util.Collection} of filtered objects
|
||||
*
|
||||
* @param filter the searched string
|
||||
* @param limit the max count of the result entities. if limit is <= 0 return all filtered entities
|
||||
* @return all object in the store
|
||||
*/
|
||||
Collection<T> getFiltered(String filter, int limit);
|
||||
|
||||
/**
|
||||
* Returns all object of the store sorted by the given {@link java.util.Comparator}
|
||||
*
|
||||
|
||||
@@ -91,6 +91,11 @@ public class ManagerDecorator<T extends ModelObject> implements Manager<T> {
|
||||
decorated.refresh(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<T> getFiltered(String filter, int limit) {
|
||||
return decorated.getFiltered(filter, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(String id)
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@ import java.io.Serializable;
|
||||
*/
|
||||
public interface ModelObject
|
||||
extends TypedObject, LastModifiedAware, Cloneable, Validateable,
|
||||
Serializable
|
||||
Serializable, ReducedModelObject
|
||||
{
|
||||
|
||||
/**
|
||||
|
||||
15
scm-core/src/main/java/sonia/scm/ReducedModelObject.java
Normal file
15
scm-core/src/main/java/sonia/scm/ReducedModelObject.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package sonia.scm;
|
||||
|
||||
|
||||
/**
|
||||
* This is a reduced form of a model object.
|
||||
* It can be used as search result to avoid returning the whole object properties.
|
||||
*
|
||||
* @author Mohamed Karray
|
||||
*/
|
||||
public interface ReducedModelObject {
|
||||
|
||||
String getId();
|
||||
|
||||
String getDisplayName();
|
||||
}
|
||||
@@ -60,7 +60,7 @@ import java.util.List;
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@StaticPermissions(value = "group", globalPermissions = {"create", "list"})
|
||||
@StaticPermissions(value = "group", globalPermissions = {"create", "list", "autocomplete"})
|
||||
@XmlRootElement(name = "groups")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class Group extends BasicPropertiesAware
|
||||
@@ -309,6 +309,11 @@ public class Group extends BasicPropertiesAware
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a timestamp of the last modified date of this group.
|
||||
*
|
||||
|
||||
@@ -256,6 +256,11 @@ public class Changeset extends BasicPropertiesAware implements ModelObject {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastModified(Long timestamp) {
|
||||
throw new UnsupportedOperationException("changesets are immutable");
|
||||
|
||||
@@ -60,7 +60,8 @@ import java.util.List;
|
||||
*/
|
||||
@StaticPermissions(
|
||||
value = "repository",
|
||||
permissions = {"read", "modify", "delete", "healthCheck", "pull", "push", "permissionRead", "permissionWrite"}
|
||||
permissions = {"read", "modify", "delete", "healthCheck", "pull", "push", "permissionRead", "permissionWrite"},
|
||||
globalPermissions = {"create", "autocomplete"}
|
||||
)
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlRootElement(name = "repositories")
|
||||
@@ -183,6 +184,11 @@ public class Repository extends BasicPropertiesAware implements ModelObject, Per
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return getNamespaceAndName().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLastModified() {
|
||||
return lastModified;
|
||||
|
||||
@@ -55,7 +55,7 @@ import java.security.Principal;
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@StaticPermissions(value = "user", globalPermissions = {"create", "list"})
|
||||
@StaticPermissions(value = "user", globalPermissions = {"create", "list", "autocomplete"})
|
||||
@XmlRootElement(name = "users")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class User extends BasicPropertiesAware implements Principal, ModelObject, PermissionObject
|
||||
|
||||
@@ -18,6 +18,7 @@ public class VndMediaType {
|
||||
public static final String INDEX = PREFIX + "index" + SUFFIX;
|
||||
public static final String USER = PREFIX + "user" + SUFFIX;
|
||||
public static final String GROUP = PREFIX + "group" + SUFFIX;
|
||||
public static final String AUTOCOMPLETE = PREFIX + "autocomplete" + SUFFIX;
|
||||
public static final String REPOSITORY = PREFIX + "repository" + SUFFIX;
|
||||
public static final String PERMISSION = PREFIX + "permission" + SUFFIX;
|
||||
public static final String CHANGESET = PREFIX + "changeset" + SUFFIX;
|
||||
|
||||
@@ -78,6 +78,11 @@ public class ManagerTest {
|
||||
return IntStream.range(0, givenItemCount).boxed().collect(toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection getFiltered(String filter, int limit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection getAll(Comparator comparator) { return getAll(); }
|
||||
|
||||
|
||||
@@ -41,11 +41,13 @@ import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.GenericDAO;
|
||||
import sonia.scm.ModelObject;
|
||||
import sonia.scm.group.xml.XmlGroupDAO;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
import sonia.scm.store.ConfigurationStore;
|
||||
import sonia.scm.util.AssertUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
import sonia.scm.store.ConfigurationStore;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -234,6 +236,16 @@ public abstract class AbstractXmlDAO<I extends ModelObject,
|
||||
return ImmutableList.copyOf(db.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<I> getFiltered(String searched, int limit) {
|
||||
int size = db.values().size();
|
||||
AssertUtil.assertIsNotEmpty(searched);
|
||||
return ImmutableList.copyOf(db.values().stream()
|
||||
.filter(item -> item.getId().contains(searched) || (item.getDisplayName() != null && item.getDisplayName().contains(searched)))
|
||||
.limit(limit <= 0 ? size : limit)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
public class AutoCompleteBadParamException extends Exception {
|
||||
|
||||
public static final String PARAMETER_IS_REQUIRED = "The parameter is required.";
|
||||
public static final String INVALID_PARAMETER_LENGTH = "Invalid parameter length.";
|
||||
|
||||
public AutoCompleteBadParamException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.ext.ExceptionMapper;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
@Provider
|
||||
public class AutoCompleteBadParamExceptionMapper implements ExceptionMapper<AutoCompleteBadParamException> {
|
||||
|
||||
@Override
|
||||
public Response toResponse(AutoCompleteBadParamException exception) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(exception.getMessage())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import sonia.scm.group.GroupManager;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.user.UserManager;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.validation.Valid;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static sonia.scm.api.v2.resources.AutoCompleteBadParamException.INVALID_PARAMETER_LENGTH;
|
||||
import static sonia.scm.api.v2.resources.AutoCompleteBadParamException.PARAMETER_IS_REQUIRED;
|
||||
|
||||
@Path(AutoCompleteResource.PATH)
|
||||
public class AutoCompleteResource {
|
||||
public static final String PATH = "v2/autocomplete/";
|
||||
public static final String DEFAULT_LIMIT = "5";
|
||||
public static final int MIN_SEARCHED_CHARS = 1;
|
||||
|
||||
private ReducedObjectModelToDtoMapper mapper;
|
||||
|
||||
private UserManager userManager;
|
||||
private GroupManager groupManager;
|
||||
private RepositoryManager repositoryManager;
|
||||
|
||||
@Inject
|
||||
public AutoCompleteResource(ReducedObjectModelToDtoMapper mapper, UserManager userManager, GroupManager groupManager, RepositoryManager repositoryManager) {
|
||||
this.mapper = mapper;
|
||||
this.userManager = userManager;
|
||||
this.groupManager = groupManager;
|
||||
this.repositoryManager = repositoryManager;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("user")
|
||||
@Produces(VndMediaType.AUTOCOMPLETE)
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 200, condition = "success"),
|
||||
@ResponseCode(code = 400, condition = "if the searched string contains less than 2 characters"),
|
||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||
@ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"user:autocomplete\" privilege"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
public Response searchUser(@QueryParam("filter") String filter,
|
||||
@DefaultValue(DEFAULT_LIMIT) @QueryParam("limit") Integer limit) throws AutoCompleteBadParamException {
|
||||
validateParams(filter);
|
||||
return Response.ok(userManager.getFiltered(filter, limit)
|
||||
.stream()
|
||||
.map(mapper::map)
|
||||
.collect(Collectors.toList()))
|
||||
.build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("group")
|
||||
@Produces(VndMediaType.AUTOCOMPLETE)
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 200, condition = "success"),
|
||||
@ResponseCode(code = 400, condition = "if the searched string contains less than 2 characters"),
|
||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||
@ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"group:autocomplete\" privilege"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
public Response searchGroup(@Valid @QueryParam("filter") String filter,
|
||||
@DefaultValue(DEFAULT_LIMIT) @QueryParam("limit") Integer limit) throws AutoCompleteBadParamException {
|
||||
validateParams(filter);
|
||||
return Response.ok(groupManager.getFiltered(filter, limit)
|
||||
.stream()
|
||||
.map(mapper::map)
|
||||
.collect(Collectors.toList()))
|
||||
.build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("repository")
|
||||
@Produces(VndMediaType.AUTOCOMPLETE)
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 200, condition = "success"),
|
||||
@ResponseCode(code = 400, condition = "if the searched string contains less than 2 characters"),
|
||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||
@ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"repository:autocomplete\" privilege"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
public Response searchRepo(@Valid @QueryParam("filter") String filter,
|
||||
@DefaultValue(DEFAULT_LIMIT) @QueryParam("limit") Integer limit) throws AutoCompleteBadParamException {
|
||||
validateParams(filter);
|
||||
return Response.ok(repositoryManager.getFiltered(filter, limit)
|
||||
.stream()
|
||||
.map(mapper::map)
|
||||
.collect(Collectors.toList()))
|
||||
.build();
|
||||
}
|
||||
|
||||
void validateParams(String filter) throws AutoCompleteBadParamException {
|
||||
if (StringUtils.isBlank(filter)) {
|
||||
throw new AutoCompleteBadParamException(PARAMETER_IS_REQUIRED);
|
||||
}
|
||||
if (filter.length() <= MIN_SEARCHED_CHARS) {
|
||||
throw new AutoCompleteBadParamException(INVALID_PARAMETER_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,6 +37,8 @@ public class MapperModule extends AbstractModule {
|
||||
bind(FileObjectToFileObjectDtoMapper.class).to(Mappers.getMapper(FileObjectToFileObjectDtoMapper.class).getClass());
|
||||
bind(ModificationsToDtoMapper.class).to(Mappers.getMapper(ModificationsToDtoMapper.class).getClass());
|
||||
|
||||
bind(ReducedObjectModelToDtoMapper.class).to(Mappers.getMapper(ReducedObjectModelToDtoMapper.class).getClass());
|
||||
|
||||
// no mapstruct required
|
||||
bind(UIPluginDtoMapper.class);
|
||||
bind(UIPluginDtoCollectionMapper.class);
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class ReducedObjectModelDto extends HalRepresentation {
|
||||
|
||||
private String id;
|
||||
|
||||
private String displayName;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import sonia.scm.ReducedModelObject;
|
||||
|
||||
@Mapper
|
||||
public abstract class ReducedObjectModelToDtoMapper {
|
||||
|
||||
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
|
||||
public abstract ReducedObjectModelDto map(ReducedModelObject modelObject);
|
||||
|
||||
}
|
||||
@@ -242,6 +242,11 @@ public class DefaultGroupManager extends AbstractGroupManager
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Group> getFiltered(String filter, int limit) {
|
||||
GroupPermissions.autocomplete().check();
|
||||
return groupDAO.getFiltered(filter, limit);
|
||||
}
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
|
||||
@@ -243,6 +243,12 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
|
||||
return repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Repository> getFiltered(String filter, int limit) {
|
||||
RepositoryPermissions.autocomplete().check();
|
||||
return repositoryDAO.getFiltered(filter, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Repository> getAll(Comparator<Repository> comparator) {
|
||||
List<Repository> repositories = Lists.newArrayList();
|
||||
|
||||
@@ -52,9 +52,11 @@ import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.cache.Cache;
|
||||
import sonia.scm.cache.CacheManager;
|
||||
import sonia.scm.group.GroupNames;
|
||||
import sonia.scm.group.GroupPermissions;
|
||||
import sonia.scm.plugin.Extension;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryDAO;
|
||||
import sonia.scm.repository.RepositoryPermissions;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.user.UserPermissions;
|
||||
import sonia.scm.util.Util;
|
||||
@@ -256,6 +258,9 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
||||
collectGlobalPermissions(builder, user, groups);
|
||||
collectRepositoryPermissions(builder, user, groups);
|
||||
builder.add(canReadOwnUser(user));
|
||||
builder.add(getUserAutocompletePermission());
|
||||
builder.add(getGroupAutocompletePermission());
|
||||
builder.add(getRepoAutocompletePermission());
|
||||
permissions = builder.build();
|
||||
}
|
||||
|
||||
@@ -264,6 +269,18 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
||||
return info;
|
||||
}
|
||||
|
||||
private String getRepoAutocompletePermission() {
|
||||
return RepositoryPermissions.autocomplete().asShiroString();
|
||||
}
|
||||
|
||||
private String getGroupAutocompletePermission() {
|
||||
return GroupPermissions.autocomplete().asShiroString();
|
||||
}
|
||||
|
||||
private String getUserAutocompletePermission() {
|
||||
return UserPermissions.autocomplete().asShiroString();
|
||||
}
|
||||
|
||||
private String canReadOwnUser(User user) {
|
||||
return UserPermissions.read(user.getName()).asShiroString();
|
||||
}
|
||||
|
||||
@@ -300,6 +300,12 @@ public class DefaultUserManager extends AbstractUserManager
|
||||
return getAll(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<User> getFiltered(String filter, int limit) {
|
||||
UserPermissions.autocomplete().check();
|
||||
return userDAO.getFiltered(filter, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
|
||||
@@ -105,6 +105,11 @@ public class AbstractManagerResourceTest {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastModified(Long timestamp) {
|
||||
|
||||
|
||||
@@ -0,0 +1,368 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.apache.shiro.subject.support.SubjectThreadState;
|
||||
import org.apache.shiro.util.ThreadContext;
|
||||
import org.apache.shiro.util.ThreadState;
|
||||
import org.assertj.core.util.Lists;
|
||||
import org.jboss.resteasy.core.Dispatcher;
|
||||
import org.jboss.resteasy.mock.MockHttpRequest;
|
||||
import org.jboss.resteasy.mock.MockHttpResponse;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.group.DefaultGroupManager;
|
||||
import sonia.scm.group.Group;
|
||||
import sonia.scm.group.GroupManager;
|
||||
import sonia.scm.group.xml.XmlGroupDAO;
|
||||
import sonia.scm.repository.DefaultRepositoryManager;
|
||||
import sonia.scm.repository.NamespaceStrategy;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.xml.XmlRepositoryDAO;
|
||||
import sonia.scm.security.KeyGenerator;
|
||||
import sonia.scm.store.ConfigurationStore;
|
||||
import sonia.scm.store.ConfigurationStoreFactory;
|
||||
import sonia.scm.user.DefaultUserManager;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.user.UserManager;
|
||||
import sonia.scm.user.xml.XmlUserDAO;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
import sonia.scm.xml.XmlDatabase;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||
public class AutoCompleteResourceTest {
|
||||
|
||||
public static final String URL = "/" + AutoCompleteResource.PATH;
|
||||
private final Integer defaultLimit = Integer.valueOf(AutoCompleteResource.DEFAULT_LIMIT);
|
||||
private Dispatcher dispatcher;
|
||||
|
||||
private final Subject subject = mock(Subject.class);
|
||||
private final ThreadState subjectThreadState = new SubjectThreadState(subject);
|
||||
|
||||
|
||||
private XmlUserDAO userDaoMock;
|
||||
|
||||
private XmlGroupDAO groupDaoMock;
|
||||
|
||||
private XmlRepositoryDAO repoDaoMock;
|
||||
private XmlDatabase xmlDB;
|
||||
private ObjectMapper jsonObjectMapper = new ObjectMapper();
|
||||
|
||||
|
||||
@Before
|
||||
public void prepareEnvironment() {
|
||||
initMocks(this);
|
||||
ConfigurationStoreFactory storeFactory = mock(ConfigurationStoreFactory.class);
|
||||
ConfigurationStore<Object> storeConfig = mock(ConfigurationStore.class);
|
||||
xmlDB = mock(XmlDatabase.class);
|
||||
when(storeConfig.get()).thenReturn(xmlDB);
|
||||
when(storeFactory.getStore(any(), any())).thenReturn(storeConfig);
|
||||
XmlUserDAO userDao = new XmlUserDAO(storeFactory);
|
||||
userDaoMock = spy(userDao);
|
||||
XmlGroupDAO groupDAO = new XmlGroupDAO(storeFactory);
|
||||
groupDaoMock = spy(groupDAO);
|
||||
XmlRepositoryDAO repoDao = new XmlRepositoryDAO(storeFactory);
|
||||
repoDaoMock = spy(repoDao);
|
||||
ReducedObjectModelToDtoMapperImpl mapper = new ReducedObjectModelToDtoMapperImpl();
|
||||
UserManager userManager = new DefaultUserManager(userDaoMock);
|
||||
GroupManager groupManager = new DefaultGroupManager(groupDaoMock);
|
||||
RepositoryManager repositoryManager = new DefaultRepositoryManager(mock(ScmConfiguration.class), mock(SCMContextProvider.class), mock(KeyGenerator.class), repoDaoMock, new HashSet<>(), mock(NamespaceStrategy.class));
|
||||
AutoCompleteResource autoCompleteResource = new AutoCompleteResource(mapper, userManager, groupManager, repositoryManager);
|
||||
dispatcher = createDispatcher(autoCompleteResource);
|
||||
subjectThreadState.bind();
|
||||
ThreadContext.bind(subject);
|
||||
when(subject.isPermitted(any(String.class))).thenReturn(true);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanupContext() {
|
||||
ThreadContext.unbindSubject();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGet400OnFailedParameterForUserSearch() throws Exception {
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get("/" + AutoCompleteResource.PATH + "user")
|
||||
.contentType(VndMediaType.AUTOCOMPLETE)
|
||||
.accept(VndMediaType.AUTOCOMPLETE);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(400, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGet400IfParameterLengthLessThan2CharsForUserSearch() throws Exception {
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get("/" + AutoCompleteResource.PATH + "user?filter=a")
|
||||
.contentType(VndMediaType.AUTOCOMPLETE)
|
||||
.accept(VndMediaType.AUTOCOMPLETE);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(400, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSearchUsers() throws Exception {
|
||||
ArrayList<User> users = Lists.newArrayList(createMockUser("YuCantFindMe", "ha ha"), createMockUser("user1", "User 1"), createMockUser("user2", "User 2"));
|
||||
String searched = "user";
|
||||
when(xmlDB.values()).thenReturn(users);
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get("/" + AutoCompleteResource.PATH + "user?filter=" + searched)
|
||||
.contentType(VndMediaType.AUTOCOMPLETE)
|
||||
.accept(VndMediaType.AUTOCOMPLETE);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
|
||||
assertResultSize(response, 2);
|
||||
assertTrue(response.getContentAsString().contains("\"id\":\"user1\""));
|
||||
assertTrue(response.getContentAsString().contains("\"displayName\":\"User 1\""));
|
||||
assertTrue(response.getContentAsString().contains("\"id\":\"user2\""));
|
||||
assertTrue(response.getContentAsString().contains("\"displayName\":\"User 2\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSearchUsersWithLimitLength() throws Exception {
|
||||
List<User> users = IntStream.range(0, 10).boxed().map(i -> createMockUser("user" + i, "User " + i)).collect(Collectors.toList());
|
||||
when(xmlDB.values()).thenReturn(users);
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get("/" + AutoCompleteResource.PATH + "user?filter=user&limit=1")
|
||||
.contentType(VndMediaType.AUTOCOMPLETE)
|
||||
.accept(VndMediaType.AUTOCOMPLETE);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
verify(userDaoMock).getFiltered(eq("user"), eq(1));
|
||||
assertResultSize(response, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSearchUsersWithDefaultLimitLength() throws Exception {
|
||||
List<User> userList = IntStream.range(0, 10).boxed().map(i -> createMockUser("user" + i, "User " + i)).collect(Collectors.toList());
|
||||
when(xmlDB.values()).thenReturn(userList);
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get("/" + AutoCompleteResource.PATH + "user?filter=user")
|
||||
.contentType(VndMediaType.AUTOCOMPLETE)
|
||||
.accept(VndMediaType.AUTOCOMPLETE);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
verify(userDaoMock).getFiltered(eq("user"), eq(defaultLimit));
|
||||
assertResultSize(response, defaultLimit);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGet400OnFailedParameterForGroupSearch() throws Exception {
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get("/" + AutoCompleteResource.PATH + "group")
|
||||
.contentType(VndMediaType.AUTOCOMPLETE)
|
||||
.accept(VndMediaType.AUTOCOMPLETE);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(400, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGet400IfParameterLengthLessThan2CharsForGroupSearch() throws Exception {
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get("/" + AutoCompleteResource.PATH + "group?filter=a")
|
||||
.contentType(VndMediaType.AUTOCOMPLETE)
|
||||
.accept(VndMediaType.AUTOCOMPLETE);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(400, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSearchGroups() throws Exception {
|
||||
ArrayList<Group> groups = Lists.newArrayList(createMockGroup("YuCantFindMe"), createMockGroup("group_1"), createMockGroup("group_2"));
|
||||
String searched = "group";
|
||||
when(xmlDB.values()).thenReturn(groups);
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get("/" + AutoCompleteResource.PATH + "group?filter=" + searched)
|
||||
.contentType(VndMediaType.AUTOCOMPLETE)
|
||||
.accept(VndMediaType.AUTOCOMPLETE);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
assertResultSize(response, 2);
|
||||
assertTrue(response.getContentAsString().contains("\"id\":\"group_1\""));
|
||||
assertTrue(response.getContentAsString().contains("\"displayName\":\"group_1\""));
|
||||
assertTrue(response.getContentAsString().contains("\"id\":\"group_2\""));
|
||||
assertTrue(response.getContentAsString().contains("\"displayName\":\"group_2\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSearchGroupsWithLimitLength() throws Exception {
|
||||
List<Group> groups = IntStream.range(0, 10).boxed().map(i -> createMockGroup("group_" + i)).collect(Collectors.toList());
|
||||
when(xmlDB.values()).thenReturn(groups);
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get("/" + AutoCompleteResource.PATH + "group?filter=group&limit=1")
|
||||
.contentType(VndMediaType.AUTOCOMPLETE)
|
||||
.accept(VndMediaType.AUTOCOMPLETE);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
verify(groupDaoMock).getFiltered(eq("group"), eq(1));
|
||||
assertResultSize(response, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSearchGroupsWithDefaultLimitLength() throws Exception {
|
||||
List<Group> groups = IntStream.range(0, 10).boxed().map(i -> createMockGroup("group_" + i)).collect(Collectors.toList());
|
||||
when(xmlDB.values()).thenReturn(groups);
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get("/" + AutoCompleteResource.PATH + "group?filter=group")
|
||||
.contentType(VndMediaType.AUTOCOMPLETE)
|
||||
.accept(VndMediaType.AUTOCOMPLETE);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
verify(groupDaoMock).getFiltered(eq("group"), eq(defaultLimit));
|
||||
assertResultSize(response, defaultLimit);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldGet400OnFailedParameterForRepoSearch() throws Exception {
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get("/" + AutoCompleteResource.PATH + "repository")
|
||||
.contentType(VndMediaType.AUTOCOMPLETE)
|
||||
.accept(VndMediaType.AUTOCOMPLETE);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(400, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGet400IfParameterLengthLessThan2CharsForRepoSearch() throws Exception {
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get("/" + AutoCompleteResource.PATH + "repository?filter=a")
|
||||
.contentType(VndMediaType.AUTOCOMPLETE)
|
||||
.accept(VndMediaType.AUTOCOMPLETE);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(400, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSearchRepos() throws Exception {
|
||||
List<Repository> repos = Lists.newArrayList(createMockRepo("YCannotFindMe"), createMockRepo("repo1"), createMockRepo("repo2"));
|
||||
when(xmlDB.values()).thenReturn(repos);
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get("/" + AutoCompleteResource.PATH + "repository?filter=repo")
|
||||
.contentType(VndMediaType.AUTOCOMPLETE)
|
||||
.accept(VndMediaType.AUTOCOMPLETE);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
assertResultSize(response, 2);
|
||||
assertTrue(response.getContentAsString().contains("\"displayName\":\"space/repo1\""));
|
||||
assertTrue(response.getContentAsString().contains("\"displayName\":\"space/repo2\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSearchReposWithLimitLength() throws Exception {
|
||||
List<Repository> repositories = IntStream.range(0, 10).boxed().map(i -> createMockRepo("repo" + i)).collect(Collectors.toList());
|
||||
when(xmlDB.values()).thenReturn(repositories);
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get("/" + AutoCompleteResource.PATH + "repository?filter=repo&limit=1")
|
||||
.contentType(VndMediaType.AUTOCOMPLETE)
|
||||
.accept(VndMediaType.AUTOCOMPLETE);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
verify(repoDaoMock).getFiltered(eq("repo"), eq(1));
|
||||
assertResultSize(response, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSearchReposWithDefaultLimitLength() throws Exception {
|
||||
List<Repository> repositories = IntStream.range(0, 10).boxed().map(i -> createMockRepo("repo" + i)).collect(Collectors.toList());
|
||||
when(xmlDB.values()).thenReturn(repositories);
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get("/" + AutoCompleteResource.PATH + "repository?filter=repo")
|
||||
.contentType(VndMediaType.AUTOCOMPLETE)
|
||||
.accept(VndMediaType.AUTOCOMPLETE);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
verify(repoDaoMock).getFiltered(eq("repo"), eq(defaultLimit));
|
||||
assertResultSize(response, defaultLimit);
|
||||
}
|
||||
|
||||
|
||||
private User createMockUser(String id, String name) {
|
||||
return new User(id, name, "em@l.de");
|
||||
}
|
||||
|
||||
|
||||
private Group createMockGroup(String name) {
|
||||
Group group = new Group("type", name);
|
||||
group.setDescription(name);
|
||||
return group;
|
||||
}
|
||||
|
||||
private Repository createMockRepo(String repository) {
|
||||
return new Repository("id", "git", "space", repository);
|
||||
}
|
||||
|
||||
private void assertResultSize(MockHttpResponse response, int size) throws java.io.IOException {
|
||||
ReducedObjectModelDto[] reducedObjectModelDtos = jsonObjectMapper.readValue(response.getContentAsString(), ReducedObjectModelDto[].class);
|
||||
assertTrue(reducedObjectModelDtos.length == size);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ public class DispatcherMock {
|
||||
dispatcher.getProviderFactory().registerProvider(InternalRepositoryExceptionMapper.class);
|
||||
dispatcher.getProviderFactory().registerProvider(ChangePasswordNotAllowedExceptionMapper.class);
|
||||
dispatcher.getProviderFactory().registerProvider(InvalidPasswordExceptionMapper.class);
|
||||
dispatcher.getProviderFactory().registerProvider(AutoCompleteBadParamExceptionMapper.class);
|
||||
return dispatcher;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import javax.servlet.http.HttpServletResponse;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.text.MessageFormat;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@@ -161,8 +161,8 @@ public class DefaultAuthorizationCollectorTest {
|
||||
|
||||
AuthorizationInfo authInfo = collector.collect();
|
||||
assertThat(authInfo.getRoles(), Matchers.contains(Role.USER));
|
||||
assertThat(authInfo.getStringPermissions(), hasSize(1));
|
||||
assertThat(authInfo.getStringPermissions(), contains("user:read:trillian"));
|
||||
assertThat(authInfo.getStringPermissions(), hasSize(4));
|
||||
assertThat(authInfo.getStringPermissions(), containsInAnyOrder("user:autocomplete", "group:autocomplete", "repository:autocomplete", "user:read:trillian"));
|
||||
assertThat(authInfo.getObjectPermissions(), nullValue());
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ public class DefaultAuthorizationCollectorTest {
|
||||
AuthorizationInfo authInfo = collector.collect();
|
||||
assertThat(authInfo.getRoles(), Matchers.containsInAnyOrder(Role.USER));
|
||||
assertThat(authInfo.getObjectPermissions(), nullValue());
|
||||
assertThat(authInfo.getStringPermissions(), containsInAnyOrder("repository:read,pull:one", "repository:read,pull,push:two", "user:read:trillian"));
|
||||
assertThat(authInfo.getStringPermissions(), containsInAnyOrder("user:autocomplete", "group:autocomplete","repository:autocomplete", "repository:read,pull:one", "repository:read,pull,push:two", "user:read:trillian"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,7 +230,7 @@ public class DefaultAuthorizationCollectorTest {
|
||||
AuthorizationInfo authInfo = collector.collect();
|
||||
assertThat(authInfo.getRoles(), Matchers.containsInAnyOrder(Role.USER));
|
||||
assertThat(authInfo.getObjectPermissions(), nullValue());
|
||||
assertThat(authInfo.getStringPermissions(), containsInAnyOrder("one:one", "two:two", "user:read:trillian"));
|
||||
assertThat(authInfo.getStringPermissions(), containsInAnyOrder("one:one", "two:two", "user:read:trillian", "user:autocomplete" , "group:autocomplete", "repository:autocomplete"));
|
||||
}
|
||||
|
||||
private void authenticate(User user, String group, String... groups) {
|
||||
|
||||
Reference in New Issue
Block a user