add autocomplete endpoint

This commit is contained in:
Mohamed Karray
2018-10-08 13:39:33 +02:00
parent 0f08f2f7d5
commit ddcc21c1a8
27 changed files with 664 additions and 14 deletions

View File

@@ -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);
}

View File

@@ -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}
*

View File

@@ -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)
{

View File

@@ -44,7 +44,7 @@ import java.io.Serializable;
*/
public interface ModelObject
extends TypedObject, LastModifiedAware, Cloneable, Validateable,
Serializable
Serializable, ReducedModelObject
{
/**

View 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();
}

View File

@@ -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.
*

View File

@@ -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");

View File

@@ -60,11 +60,12 @@ 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")
public class Repository extends BasicPropertiesAware implements ModelObject, PermissionObject {
public class Repository extends BasicPropertiesAware implements ModelObject, PermissionObject{
private static final long serialVersionUID = 3486560714961909711L;
@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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(); }

View File

@@ -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
*

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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
*

View File

@@ -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();

View File

@@ -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();
}

View File

@@ -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
*

View File

@@ -105,6 +105,11 @@ public class AbstractManagerResourceTest {
return id;
}
@Override
public String getDisplayName() {
return id;
}
@Override
public void setLastModified(Long timestamp) {

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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) {