diff --git a/source/java/org/alfresco/rest/api/People.java b/source/java/org/alfresco/rest/api/People.java index 5a89222408..bae375bfec 100644 --- a/source/java/org/alfresco/rest/api/People.java +++ b/source/java/org/alfresco/rest/api/People.java @@ -26,6 +26,8 @@ package org.alfresco.rest.api; import org.alfresco.rest.api.model.Person; +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; +import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.security.NoSuchPersonException; @@ -33,6 +35,10 @@ public interface People { String DEFAULT_USER = "-me-"; + String PARAM_FIRST_NAME = "firstName"; + String PARAM_LAST_NAME = "lastName"; + String PARAM_USER_NAME = "userName"; + String validatePerson(String personId); String validatePerson(String personId, boolean validateIsCurrentUser); NodeRef getAvatar(String personId); @@ -59,4 +65,11 @@ public interface People * @return The updated person details. */ Person update(String personId, Person person); + + /** + * Get people list + * + * @return CollectionWithPagingInfo + */ + CollectionWithPagingInfo getPeople(Parameters parameters); } diff --git a/source/java/org/alfresco/rest/api/impl/PeopleImpl.java b/source/java/org/alfresco/rest/api/impl/PeopleImpl.java index 671b885709..3357ba73fc 100644 --- a/source/java/org/alfresco/rest/api/impl/PeopleImpl.java +++ b/source/java/org/alfresco/rest/api/impl/PeopleImpl.java @@ -25,7 +25,17 @@ */ package org.alfresco.rest.api.impl; +import java.io.Serializable; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.alfresco.model.ContentModel; +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.rest.api.Nodes; @@ -35,8 +45,17 @@ import org.alfresco.rest.api.model.Person; import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; +import org.alfresco.rest.framework.resource.parameters.Paging; import org.alfresco.rest.framework.resource.parameters.Parameters; -import org.alfresco.service.cmr.repository.*; +import org.alfresco.rest.framework.resource.parameters.SortColumn; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.MutableAuthenticationService; import org.alfresco.service.cmr.security.NoSuchPersonException; @@ -45,11 +64,7 @@ import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.thumbnail.ThumbnailService; import org.alfresco.service.cmr.usage.ContentUsageService; import org.alfresco.service.namespace.QName; - -import java.io.Serializable; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import org.alfresco.util.Pair; /** * Centralises access to people services and maps between representations. @@ -70,6 +85,16 @@ public class PeopleImpl implements People protected ContentService contentService; protected ThumbnailService thumbnailService; + private final static Map sort_params_to_qnames; + static + { + Map aMap = new HashMap<>(3); + aMap.put(PARAM_FIRST_NAME, ContentModel.PROP_FIRSTNAME); + aMap.put(PARAM_LAST_NAME, ContentModel.PROP_LASTNAME); + aMap.put(PARAM_USER_NAME, ContentModel.PROP_USERNAME); + sort_params_to_qnames = Collections.unmodifiableMap(aMap); + } + public void setSites(Sites sites) { this.sites = sites; @@ -236,47 +261,112 @@ public class PeopleImpl implements People /** * - * @throws NoSuchPersonException if personId does not exist + * @throws NoSuchPersonException + * if personId does not exist */ public Person getPerson(String personId) { - Person person = null; + personId = validatePerson(personId); + Person person = getPersonWithProperties(personId); - personId = validatePerson(personId); - NodeRef personNode = personService.getPerson(personId, false); - if (personNode != null) - { - Map nodeProps = nodeService.getProperties(personNode); - processPersonProperties(nodeProps); - // TODO this needs to be run as admin but should we do this here? - final String pId = personId; - Boolean enabled = AuthenticationUtil.runAsSystem(new RunAsWork() - { - public Boolean doWork() throws Exception - { - return authenticationService.getAuthenticationEnabled(pId); - } - }); - person = new Person(personNode, nodeProps, enabled); + return person; + } - // get avatar information - if(hasAvatar(personNode)) - { - try - { - NodeRef avatar = getAvatar(personId); - person.setAvatarId(avatar); - } - catch(EntityNotFoundException e) - { - // shouldn't happen, but ok - } - } - } - else - { - throw new EntityNotFoundException(personId); - } + public CollectionWithPagingInfo getPeople(final Parameters parameters) + { + Paging paging = parameters.getPaging(); + PagingRequest pagingRequest = Util.getPagingRequest(paging); + + List> sortProps = getSortProps(parameters); + + // For now the results are not filtered + // please see REPO-555 + final PagingResults pagingResult = personService.getPeople(null, null, sortProps, pagingRequest); + + final List page = pagingResult.getPage(); + int totalItems = pagingResult.getTotalResultCount().getFirst(); + final String personId = AuthenticationUtil.getFullyAuthenticatedUser(); + List people = new AbstractList() + { + @Override + public Person get(int index) + { + PersonService.PersonInfo personInfo = page.get(index); + Person person = getPersonWithProperties(personInfo.getUserName()); + return person; + } + + @Override + public int size() + { + return page.size(); + } + }; + + return CollectionWithPagingInfo.asPaged(paging, people, pagingResult.hasMoreItems(), totalItems); + } + + private List> getSortProps(Parameters parameters) + { + List> sortProps = new ArrayList<>(); + List sortCols = parameters.getSorting(); + if ((sortCols != null) && (sortCols.size() > 0)) + { + for (SortColumn sortCol : sortCols) + { + QName sortPropQName = sort_params_to_qnames.get(sortCol.column); + if (sortPropQName == null) + { + throw new InvalidArgumentException("Invalid sort field: " + sortCol.column); + } + sortProps.add(new Pair<>(sortPropQName, (sortCol.asc ? Boolean.TRUE : Boolean.FALSE))); + } + } + else + { + // default sort order + sortProps.add(new Pair<>(ContentModel.PROP_USERNAME, Boolean.TRUE)); + } + return sortProps; + } + + private Person getPersonWithProperties(String personId) + { + Person person = null; + NodeRef personNode = personService.getPerson(personId, false); + if (personNode != null) + { + Map nodeProps = nodeService.getProperties(personNode); + processPersonProperties(nodeProps); + // TODO this needs to be run as admin but should we do this here? + final String pId = personId; + Boolean enabled = AuthenticationUtil.runAsSystem(new RunAsWork() + { + public Boolean doWork() throws Exception + { + return authenticationService.getAuthenticationEnabled(pId); + } + }); + person = new Person(personNode, nodeProps, enabled); + + // get avatar information + if (hasAvatar(personNode)) + { + try + { + NodeRef avatar = getAvatar(personId); + person.setAvatarId(avatar); + } + catch (EntityNotFoundException e) + { + // shouldn't happen, but ok + } + } + } + else + { + throw new EntityNotFoundException(personId); + } return person; } diff --git a/source/java/org/alfresco/rest/api/people/PeopleEntityResource.java b/source/java/org/alfresco/rest/api/people/PeopleEntityResource.java index 0cdab97dd5..8d2e5f34c7 100644 --- a/source/java/org/alfresco/rest/api/people/PeopleEntityResource.java +++ b/source/java/org/alfresco/rest/api/people/PeopleEntityResource.java @@ -33,6 +33,7 @@ import org.alfresco.rest.framework.core.ResourceParameter; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; import org.alfresco.rest.framework.resource.EntityResource; import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction; +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.util.ParameterCheck; import org.apache.commons.logging.Log; @@ -49,7 +50,7 @@ import java.util.List; * @author Gethin James */ @EntityResource(name="people", title = "People") -public class PeopleEntityResource implements EntityResourceAction.ReadById, EntityResourceAction.Create, EntityResourceAction.Update, InitializingBean +public class PeopleEntityResource implements EntityResourceAction.ReadById, EntityResourceAction.Create, EntityResourceAction.Update,EntityResourceAction.Read, InitializingBean { private static Log logger = LogFactory.getLog(PeopleEntityResource.class); @@ -175,4 +176,11 @@ public class PeopleEntityResource implements EntityResourceAction.ReadById readAll(Parameters params) + { + return people.getPeople(params); + } } \ No newline at end of file diff --git a/source/test-java/org/alfresco/rest/api/tests/TestPeople.java b/source/test-java/org/alfresco/rest/api/tests/TestPeople.java index 8b5437b30d..28a3cff48b 100644 --- a/source/test-java/org/alfresco/rest/api/tests/TestPeople.java +++ b/source/test-java/org/alfresco/rest/api/tests/TestPeople.java @@ -25,9 +25,22 @@ */ package org.alfresco.rest.api.tests; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + import org.alfresco.rest.api.tests.RepoService.TestNetwork; import org.alfresco.rest.api.tests.client.HttpResponse; import org.alfresco.rest.api.tests.client.Pair; +import org.alfresco.rest.api.tests.client.PublicApiClient; import org.alfresco.rest.api.tests.client.PublicApiClient.People; import org.alfresco.rest.api.tests.client.PublicApiException; import org.alfresco.rest.api.tests.client.RequestContext; @@ -40,30 +53,24 @@ import org.json.simple.JSONObject; import org.junit.Before; import org.junit.Test; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - public class TestPeople extends EnterpriseTestApi { - private People people; - private Iterator accountsIt; - private TestNetwork account1; - private TestNetwork account2; + private People people; + private Iterator accountsIt; + private TestNetwork account1; + private TestNetwork account2; private TestNetwork account3; - private Iterator account1PersonIt; - private Iterator account2PersonIt; + private TestNetwork account4; + private Iterator account1PersonIt; + private Iterator account2PersonIt; private Iterator account3PersonIt; - private String account1Admin; - private String account2Admin; + private Iterator account4PersonIt; + private String account1Admin; + private String account2Admin; private String account3Admin; + private String account4Admin; + private Person personAlice; + private Person personBen; @Before public void setUp() throws Exception @@ -73,9 +80,11 @@ public class TestPeople extends EnterpriseTestApi account1 = accountsIt.next(); account2 = accountsIt.next(); account3 = createNetwork("account3"); + account4 = createNetwork("account4"); account1Admin = "admin@" + account1.getId(); account2Admin = "admin@" + account2.getId(); account3Admin = "admin@" + account3.getId(); + account4Admin = "admin@" + account4.getId(); account1PersonIt = account1.getPersonIds().iterator(); account2PersonIt = account2.getPersonIds().iterator(); @@ -694,4 +703,123 @@ public class TestPeople extends EnterpriseTestApi "Expected 400 response when updating " + personId, 400); } } + + private PublicApiClient.ListResponse listPeople(final PublicApiClient.Paging paging, String sortColumn, boolean asc) throws Exception + { + final PublicApiClient.People peopleProxy = publicApiClient.people(); + + // sort params + final Map params = new HashMap<>(); + if (sortColumn != null) + { + params.put("orderBy", sortColumn + " " + (asc ? "ASC" : "DESC")); + } + + return peopleProxy.getPeople(createParams(paging, params)); + } + + /** + * Tests the capability to sort and paginate the list of people orderBy = + * firstName ASC skip = 1, count = 2 + * + * @throws Exception + */ + @Test + public void testPagingAndSortingByFirstNameAsc() throws Exception + { + initializeContextForGetPeople(); + + // paging + int skipCount = 1; + int maxItems = 2; + int totalResults = 4; + PublicApiClient.Paging paging = getPaging(skipCount, maxItems, totalResults, totalResults); + + // orderBy=firstName ASC + PublicApiClient.ListResponse resp = listPeople(paging, "firstName", true); + + List expectedList = new LinkedList<>(); + expectedList.add(personAlice); + expectedList.add(personBen); + + checkList(expectedList, paging.getExpectedPaging(), resp); + } + + /** + * Tests the capability to sort and paginate the list of people orderBy = + * firstName DESC skip = 1, count = 2 + * + * @throws Exception + */ + @Test + public void testPagingAndSortingByFirstNameDesc() throws Exception + { + initializeContextForGetPeople(); + + // paging + int skipCount = 1; + int maxItems = 2; + int totalResults = 4; + PublicApiClient.Paging paging = getPaging(skipCount, maxItems, totalResults, totalResults); + + // orderBy=firstName DESC + PublicApiClient.ListResponse resp = listPeople(paging, "firstName", false); + + List expectedList = new LinkedList<>(); + expectedList.add((Person) personBen); + expectedList.add((Person) personAlice); + + checkList(expectedList, paging.getExpectedPaging(), resp); + } + + /** + * Tests the capability to sort and paginate the list of people verifies + * default sorting, skip = 1, count = 2 + * + * @throws Exception + */ + @Test + public void testPagingAndDefaultSorting() throws Exception + { + initializeContextForGetPeople(); + + // paging + int skipCount = 1; + int maxItems = 2; + int totalResults = 4; + PublicApiClient.Paging paging = getPaging(skipCount, maxItems, totalResults, totalResults); + + // orderBy=firstName DESC + PublicApiClient.ListResponse resp = listPeople(paging, null, false); + + List expectedList = new LinkedList<>(); + expectedList.add((Person) personAlice); + expectedList.add((Person) personBen); + + checkList(expectedList, paging.getExpectedPaging(), resp); + } + + private void initializeContextForGetPeople() throws PublicApiException + { + publicApiClient.setRequestContext(new RequestContext(account4.getId(), account4Admin, "admin")); + personAlice = new Person(); + personAlice.setUserName("alice@" + account4.getId()); + personAlice.setId("alice@" + account4.getId()); + personAlice.setFirstName("Alice"); + personAlice.setLastName("Smith"); + personAlice.setEmail("alison.smith@example.com"); + personAlice.setPassword("password"); + personAlice.setEnabled(true); + people.create(personAlice); + + personBen = new Person(); + personBen.setUserName("ben@" + account4.getId()); + personBen.setId("ben@" + account4.getId()); + personBen.setFirstName("Ben"); + personBen.setLastName("Smythe"); + personBen.setEmail("ben.smythe@example.com"); + personBen.setPassword("password"); + personBen.setEnabled(true); + people.create(personBen); + } }