Merged 5.2.N (5.2.2) to HEAD (5.2)

133574 cturlica: REPO-1302: Retrieve list of group members
      - added group members api
      - fixed order by issue when using post processing ordering by id


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@137309 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Andrei Rebegea
2017-06-14 16:44:03 +00:00
parent 80ad58c3f4
commit 5851af3029
8 changed files with 644 additions and 4 deletions

View File

@@ -1356,4 +1356,8 @@
<bean class="org.alfresco.rest.api.groups.GroupsEntityResource">
<property name="groups" ref="Groups" />
</bean>
<bean class="org.alfresco.rest.api.groups.GroupMembersRelation">
<property name="groups" ref="Groups" />
</bean>
</beans>

View File

@@ -26,6 +26,7 @@
package org.alfresco.rest.api;
import org.alfresco.rest.api.model.Group;
import org.alfresco.rest.api.model.GroupMember;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
@@ -41,6 +42,9 @@ public interface Groups
String PARAM_INCLUDE_PARENT_IDS = "parentIds";
String PARAM_INCLUDE_ZONES = "zones";
String PARAM_IS_ROOT = "isRoot";
String PARAM_MEMBER_TYPE = "memberType";
String PARAM_MEMBER_TYPE_GROUP = "GROUP";
String PARAM_MEMBER_TYPE_PERSON = "PERSON";
/**
* Gets a list of groups.
@@ -53,4 +57,15 @@ public interface Groups
*/
CollectionWithPagingInfo<Group> getGroups(Parameters parameters);
/**
* Gets a list of groups.
*
* @param groupId the identifier of a group.
* @param parameters the {@link Parameters} object to get the parameters passed into the request
* including:
* - filter, sort & paging params (where, orderBy, skipCount, maxItems)
* - incFiles, incFolders (both true by default)
* @return a paged list of {@code org.alfresco.rest.api.model.GroupMember} objects
*/
CollectionWithPagingInfo<GroupMember> getGroupMembers(String groupId, Parameters parameters);
}

View File

@@ -0,0 +1,64 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.groups;
import org.alfresco.rest.api.Groups;
import org.alfresco.rest.api.model.GroupMember;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.util.ParameterCheck;
import org.springframework.beans.factory.InitializingBean;
/**
* @author cturlica
*/
@RelationshipResource(name = "members", entityResource = GroupsEntityResource.class, title = "Group Members")
public class GroupMembersRelation implements RelationshipResourceAction.Read<GroupMember>, InitializingBean
{
private Groups groups;
public void setGroups(Groups groups)
{
this.groups = groups;
}
@Override
public void afterPropertiesSet()
{
ParameterCheck.mandatory("groups", this.groups);
}
@Override
@WebApiDescription(title="A paged list of all the members of the group 'groupId'.")
public CollectionWithPagingInfo<GroupMember> readAll(String groupId, Parameters params)
{
return groups.getGroupMembers(groupId, params);
}
}

View File

@@ -47,6 +47,7 @@ import org.alfresco.repo.security.authority.UnknownAuthorityException;
import org.alfresco.rest.antlr.WhereClauseParser;
import org.alfresco.rest.api.Groups;
import org.alfresco.rest.api.model.Group;
import org.alfresco.rest.api.model.GroupMember;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
@@ -85,6 +86,8 @@ public class GroupsImpl implements Groups
// List groups filtering (via where clause)
private final static Set<String> LIST_GROUPS_EQUALS_QUERY_PROPERTIES = new HashSet<>(Arrays.asList(new String[] { PARAM_IS_ROOT }));
private final static Set<String> LIST_GROUP_MEMBERS_QUERY_PROPERTIES = new HashSet<>(Arrays.asList(new String[] { PARAM_MEMBER_TYPE }));
protected AuthorityService authorityService;
public AuthorityService getAuthorityService()
@@ -355,11 +358,11 @@ public class GroupsImpl implements Groups
if (v == null)
{
// Get the value from the group
if (PARAM_DISPLAY_NAME.equals(sortBy))
if (DISPLAY_NAME.equals(sortBy))
{
v = g.getAuthorityDisplayName();
}
else if (PARAM_ID.equals(sortBy))
else if (SHORT_NAME.equals(sortBy))
{
v = g.getAuthorityName();
}
@@ -377,4 +380,131 @@ public class GroupsImpl implements Groups
return v;
}
}
public CollectionWithPagingInfo<GroupMember> getGroupMembers(String groupId, final Parameters parameters)
{
validateGroupId(groupId);
Paging paging = parameters.getPaging();
// Retrieve sort column. This is limited for now to sort column due to
// v0 api implementation. Should be improved in the future.
Pair<String, Boolean> sortProp = getGroupsSortProp(parameters);
AuthorityType authorityType = null;
// Parse where clause properties.
Query q = parameters.getQuery();
if (q != null)
{
MapBasedQueryWalkerOrSupported propertyWalker = new MapBasedQueryWalkerOrSupported(LIST_GROUP_MEMBERS_QUERY_PROPERTIES, null);
QueryHelper.walk(q, propertyWalker);
String memberTypeStr = propertyWalker.getProperty(PARAM_MEMBER_TYPE, WhereClauseParser.EQUALS, String.class);
if (memberTypeStr != null && !memberTypeStr.isEmpty())
{
switch (memberTypeStr)
{
case PARAM_MEMBER_TYPE_GROUP:
authorityType = AuthorityType.GROUP;
break;
case PARAM_MEMBER_TYPE_PERSON:
authorityType = AuthorityType.USER;
break;
default:
throw new InvalidArgumentException("MemberType is invalid (expected eg. GROUP, PERSON)");
}
}
}
PagingResults<AuthorityInfo> pagingResult = getAuthoritiesInfo(authorityType, groupId, sortProp, paging);
// Create response.
final List<AuthorityInfo> page = pagingResult.getPage();
int totalItems = pagingResult.getTotalResultCount().getFirst();
List<GroupMember> groupMembers = new AbstractList<GroupMember>()
{
@Override
public GroupMember get(int index)
{
AuthorityInfo authorityInfo = page.get(index);
return getGroupMember(authorityInfo);
}
@Override
public int size()
{
return page.size();
}
};
return CollectionWithPagingInfo.asPaged(paging, groupMembers, pagingResult.hasMoreItems(), totalItems);
}
private PagingResults<AuthorityInfo> getAuthoritiesInfo(AuthorityType authorityType, String groupId, Pair<String, Boolean> sortProp, Paging paging)
{
Set<String> authorities;
try
{
authorities = authorityService.findAuthorities(authorityType, groupId, true, null, null);
}
catch (UnknownAuthorityException e)
{
authorities = Collections.emptySet();
}
List<AuthorityInfo> authorityInfoList = new ArrayList<>(authorities.size());
authorityInfoList.addAll(authorities.stream().map(this::getAuthorityInfo).collect(Collectors.toList()));
// Post process sorting - this should be moved to service
// layer. It is done here because sorting is not supported at
// service layer.
AuthorityInfoComparator authorityComparator = new AuthorityInfoComparator(sortProp.getFirst(), sortProp.getSecond());
Collections.sort(authorityInfoList, authorityComparator);
// Post process paging - this should be moved to service layer.
return Util.wrapPagingResults(paging, authorityInfoList);
}
private GroupMember getGroupMember(AuthorityInfo authorityInfo)
{
if (authorityInfo == null)
{
return null;
}
GroupMember groupMember = new GroupMember();
groupMember.setId(authorityInfo.getAuthorityName());
groupMember.setDisplayName(authorityInfo.getAuthorityDisplayName());
String memberType = null;
AuthorityType authorityType = AuthorityType.getAuthorityType(authorityInfo.getAuthorityName());
switch (authorityType)
{
case GROUP:
memberType = PARAM_MEMBER_TYPE_GROUP;
break;
case USER:
memberType = PARAM_MEMBER_TYPE_PERSON;
break;
default:
}
groupMember.setMemberType(memberType);
return groupMember;
}
private void validateGroupId(String groupId)
{
if (groupId == null || groupId.isEmpty())
{
throw new InvalidArgumentException("groupId is null or empty");
}
if (!authorityService.authorityExists(groupId))
{
throw new EntityNotFoundException(groupId);
}
}
}

View File

@@ -0,0 +1,116 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.model;
import org.alfresco.rest.framework.resource.UniqueId;
/**
* Represents a group member.
*
* @author cturlica
*
*/
public class GroupMember implements Comparable<GroupMember>
{
private String id; // group id (aka authority name)
private String displayName;
private String memberType;
@UniqueId
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public String getDisplayName()
{
return displayName;
}
public void setDisplayName(String displayName)
{
this.displayName = displayName;
}
public String getMemberType()
{
return memberType;
}
public void setMemberType(String memberType)
{
this.memberType = memberType;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
GroupMember other = (GroupMember) obj;
return id.equals(other.id);
}
@Override
public int compareTo(GroupMember group)
{
return id.compareTo(group.getId());
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public String toString()
{
return "GroupMember [id=" + id + ", displayName=" + displayName + ", memberType=" + memberType + "]";
}
}

View File

@@ -44,6 +44,7 @@ import org.alfresco.rest.api.tests.client.PublicApiClient.Groups;
import org.alfresco.rest.api.tests.client.PublicApiClient.ListResponse;
import org.alfresco.rest.api.tests.client.PublicApiClient.Paging;
import org.alfresco.rest.api.tests.client.data.Group;
import org.alfresco.rest.api.tests.client.data.GroupMember;
import org.alfresco.rest.framework.resource.parameters.SortColumn;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
@@ -64,6 +65,8 @@ public class GroupsTest extends AbstractSingleNetworkSiteTest
private String rootGroupName = null;
private Group groupA = null;
private Group groupB = null;
private GroupMember groupMemberA = null;
private GroupMember groupMemberB = null;
@Before
public void setup() throws Exception
@@ -351,6 +354,12 @@ public class GroupsTest extends AbstractSingleNetworkSiteTest
groupB = new Group();
groupB.setId(groupBAuthorityName);
groupMemberA = new GroupMember();
groupMemberA.setId(groupAAuthorityName);
groupMemberB = new GroupMember();
groupMemberB.setId(groupBAuthorityName);
}
}
@@ -377,4 +386,182 @@ public class GroupsTest extends AbstractSingleNetworkSiteTest
assertNull(group.getParentIds());
assertNull(group.getZones());
}
private ListResponse<GroupMember> getGroupMembers(String groupId, final PublicApiClient.Paging paging, Map<String, String> otherParams, String errorMessage, int expectedStatus) throws Exception
{
final Groups groupsProxy = publicApiClient.groups();
return groupsProxy.getGroupMembers(groupId, createParams(paging, otherParams), errorMessage, expectedStatus);
}
private ListResponse<GroupMember> getGroupMembers(String groupId, final PublicApiClient.Paging paging, Map<String, String> otherParams) throws Exception
{
return getGroupMembers(groupId, paging, otherParams, "Failed to get group members", HttpServletResponse.SC_OK);
}
@Test
public void testGetGroupMembers() throws Exception
{
try
{
createAuthorityContext(user1);
setRequestContext(user1);
testGetGroupMembersByGroupId();
testGetGroupMembersSorting();
testGetGroupMembersSkipPaging();
testGetGroupsByMemberType();
}
finally
{
clearAuthorityContext();
}
}
private void testGetGroupMembersByGroupId() throws Exception
{
Paging paging = getPaging(0, 4);
getGroupMembers(null, paging, null, "", HttpServletResponse.SC_METHOD_NOT_ALLOWED);
getGroupMembers("", paging, null, "", HttpServletResponse.SC_BAD_REQUEST);
getGroupMembers("invalidGroupId", paging, null, "", HttpServletResponse.SC_NOT_FOUND);
}
private void testGetGroupMembersSorting() throws Exception
{
// orderBy=sortColumn should be the same to orderBy=sortColumn ASC
{
// paging
Paging paging = getPaging(0, Integer.MAX_VALUE);
Map<String, String> otherParams = new HashMap<>();
// Default order.
addOrderBy(otherParams, org.alfresco.rest.api.Groups.PARAM_DISPLAY_NAME, null);
ListResponse<GroupMember> resp = getGroupMembers(rootGroupName, paging, otherParams);
List<GroupMember> groupMembers = resp.getList();
assertTrue("group members order not valid", groupMembers.indexOf(groupMemberA) < groupMembers.indexOf(groupMemberB));
// Ascending order
addOrderBy(otherParams, org.alfresco.rest.api.Groups.PARAM_DISPLAY_NAME, true);
ListResponse<GroupMember> respOrderAsc = getGroupMembers(rootGroupName, paging, otherParams);
checkList(respOrderAsc.getList(), resp.getPaging(), resp);
}
// Sort by displayName.
{
// paging
Paging paging = getPaging(0, Integer.MAX_VALUE);
Map<String, String> otherParams = new HashMap<>();
// Default order.
addOrderBy(otherParams, org.alfresco.rest.api.Groups.PARAM_DISPLAY_NAME, true);
ListResponse<GroupMember> resp = getGroupMembers(rootGroupName, paging, otherParams);
List<GroupMember> groupMembers = resp.getList();
assertTrue("group members order not valid", groupMembers.indexOf(groupMemberA) < groupMembers.indexOf(groupMemberB));
}
// Sort by id.
{
// paging
Paging paging = getPaging(0, Integer.MAX_VALUE);
Map<String, String> otherParams = new HashMap<>();
addOrderBy(otherParams, org.alfresco.rest.api.Groups.PARAM_ID, false);
// list sites
ListResponse<GroupMember> resp = getGroupMembers(rootGroupName, paging, otherParams);
List<GroupMember> groupMembers = resp.getList();
assertTrue("group members order not valid", groupMembers.indexOf(groupMemberB) < groupMembers.indexOf(groupMemberA));
}
// Multiple sort fields not allowed.
{
// paging
Paging paging = getPaging(0, Integer.MAX_VALUE);
Map<String, String> otherParams = new HashMap<>();
otherParams.put("orderBy", org.alfresco.rest.api.Groups.PARAM_ID + " ASC," + org.alfresco.rest.api.Groups.PARAM_DISPLAY_NAME + " ASC");
getGroupMembers(rootGroupName, paging, otherParams, "", HttpServletResponse.SC_BAD_REQUEST);
}
}
private void testGetGroupMembersSkipPaging() throws Exception
{
// +ve: check skip count.
{
// Sort params
Map<String, String> otherParams = new HashMap<>();
addOrderBy(otherParams, org.alfresco.rest.api.Groups.PARAM_DISPLAY_NAME, false);
// Paging and list groups
int skipCount = 0;
int maxItems = 2;
Paging paging = getPaging(skipCount, maxItems);
ListResponse<GroupMember> resp = getGroupMembers(rootGroupName, paging, otherParams);
// Paging and list groups with skip count.
skipCount = 1;
maxItems = 1;
paging = getPaging(skipCount, maxItems);
ListResponse<GroupMember> sublistResponse = getGroupMembers(rootGroupName, paging, otherParams);
List<GroupMember> expectedSublist = sublist(resp.getList(), skipCount, maxItems);
checkList(expectedSublist, sublistResponse.getPaging(), sublistResponse);
}
// -ve: check skip count.
{
getGroupMembers(rootGroupName, getPaging(-1, null), null, "", HttpServletResponse.SC_BAD_REQUEST);
}
}
private void testGetGroupsByMemberType() throws Exception
{
testGetGroupsByMemberType(rootGroupName, org.alfresco.rest.api.Groups.PARAM_MEMBER_TYPE_GROUP);
testGetGroupsByMemberType(groupB.getId(), org.alfresco.rest.api.Groups.PARAM_MEMBER_TYPE_PERSON);
// Invalid member type
{
Map<String, String> otherParams = new HashMap<>();
otherParams.put("where", "(memberType=invalidMemberType)");
getGroupMembers(rootGroupName, getPaging(0, 4), otherParams, "", HttpServletResponse.SC_BAD_REQUEST);
}
}
private void testGetGroupsByMemberType(String groupId, String memberType) throws Exception
{
// Sort params
Map<String, String> otherParams = new HashMap<>();
otherParams.put("where", "(memberType=" + memberType + ")");
// Paging
Paging paging = getPaging(0, 4);
ListResponse<GroupMember> resp = getGroupMembers(groupId, paging, otherParams);
resp.getList().forEach(groupMember -> {
validateGroupMemberDefaultFields(groupMember);
assertEquals("memberType was expected to be " + memberType, memberType, groupMember.getMemberType());
});
}
private void validateGroupMemberDefaultFields(GroupMember groupMember)
{
assertNotNull(groupMember);
assertNotNull(groupMember.getId());
assertNotNull(groupMember.getDisplayName());
assertNotNull(groupMember.getMemberType());
}
}

View File

@@ -57,6 +57,7 @@ import org.alfresco.rest.api.tests.client.data.Favourite;
import org.alfresco.rest.api.tests.client.data.FavouriteSite;
import org.alfresco.rest.api.tests.client.data.FolderNode;
import org.alfresco.rest.api.tests.client.data.Group;
import org.alfresco.rest.api.tests.client.data.GroupMember;
import org.alfresco.rest.api.tests.client.data.JSONAble;
import org.alfresco.rest.api.tests.client.data.MemberOfSite;
import org.alfresco.rest.api.tests.client.data.NodeRating;
@@ -2262,9 +2263,25 @@ public class PublicApiClient
return null;
}
public ListResponse<Group> getGroups(Map<String, String> params) throws PublicApiException, ParseException
public ListResponse<GroupMember> getGroupMembers(String groupId, Map<String, String> params) throws PublicApiException, ParseException
{
return getGroups(params, "Failed to get groups", HttpServletResponse.SC_OK);
return getGroupMembers(groupId, params, "Failed to get groups", HttpServletResponse.SC_OK);
}
public ListResponse<GroupMember> getGroupMembers(String groupId, Map<String, String> params, String errorMessage, int expectedStatus)
throws PublicApiException, ParseException
{
HttpResponse response = getAll("groups", groupId, "members", null, params, errorMessage, expectedStatus);
if (response != null && response.getJsonResponse() != null)
{
JSONObject jsonList = (JSONObject) response.getJsonResponse().get("list");
if (jsonList != null)
{
return GroupMember.parseGroupMembers(response.getJsonResponse());
}
}
return null;
}
}
}

View File

@@ -0,0 +1,107 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.tests.client.data;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.rest.api.tests.client.PublicApiClient.ExpectedPaging;
import org.alfresco.rest.api.tests.client.PublicApiClient.ListResponse;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
/**
* Represents a group member.
*
* @author cturlica
*
*/
public class GroupMember extends org.alfresco.rest.api.model.GroupMember implements Serializable, ExpectedComparison
{
@Override
public void expected(Object o)
{
assertTrue("o is an instance of " + o.getClass(), o instanceof GroupMember);
GroupMember other = (GroupMember) o;
AssertUtil.assertEquals("id", getId(), other.getId());
AssertUtil.assertEquals("displayName", getDisplayName(), other.getDisplayName());
AssertUtil.assertEquals("memberType", getMemberType(), other.getMemberType());
}
public JSONObject toJSON()
{
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", getId());
jsonObject.put("displayName", getDisplayName());
jsonObject.put("memberType", getMemberType());
return jsonObject;
}
public static GroupMember parseGroupMember(JSONObject jsonObject)
{
String id = (String) jsonObject.get("id");
String displayName = (String) jsonObject.get("displayName");
String memberType = (String) jsonObject.get("memberType");
GroupMember group = new GroupMember();
group.setId(id);
group.setDisplayName(displayName);
group.setMemberType(memberType);
return group;
}
public static ListResponse<GroupMember> parseGroupMembers(JSONObject jsonObject)
{
List<GroupMember> groupMembers = new ArrayList<>();
JSONObject jsonList = (JSONObject) jsonObject.get("list");
assertNotNull(jsonList);
JSONArray jsonEntries = (JSONArray) jsonList.get("entries");
assertNotNull(jsonEntries);
for (int i = 0; i < jsonEntries.size(); i++)
{
JSONObject jsonEntry = (JSONObject) jsonEntries.get(i);
JSONObject entry = (JSONObject) jsonEntry.get("entry");
groupMembers.add(parseGroupMember(entry));
}
ExpectedPaging paging = ExpectedPaging.parsePagination(jsonList);
ListResponse<GroupMember> resp = new ListResponse<>(paging, groupMembers);
return resp;
}
}