mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 10:04:21 +00:00
Merge pull request #9197 from DSpace/backport-9125-to-dspace-7_x
[Port dspace-7_x] Add `isNotMemberOf` searches for Groups and EPersons (for improved performance on Edit Group pages)
This commit is contained in:
@@ -83,13 +83,14 @@ public abstract class AbstractHibernateDSODAO<T extends DSpaceObject> extends Ab
|
||||
if (CollectionUtils.isNotEmpty(metadataFields) || StringUtils.isNotBlank(additionalWhere)) {
|
||||
//Add the where query on metadata
|
||||
query.append(" WHERE ");
|
||||
// Group the 'OR' clauses below in outer parentheses, e.g. "WHERE (clause1 OR clause2 OR clause3)".
|
||||
// Grouping these 'OR' clauses allows for later code to append 'AND' clauses without unexpected behaviors
|
||||
query.append("(");
|
||||
for (int i = 0; i < metadataFields.size(); i++) {
|
||||
MetadataField metadataField = metadataFields.get(i);
|
||||
if (StringUtils.isNotBlank(operator)) {
|
||||
query.append(" (");
|
||||
query.append("lower(STR(" + metadataField.toString()).append(".value)) ").append(operator)
|
||||
.append(" lower(:queryParam)");
|
||||
query.append(")");
|
||||
if (i < metadataFields.size() - 1) {
|
||||
query.append(" OR ");
|
||||
}
|
||||
@@ -102,6 +103,7 @@ public abstract class AbstractHibernateDSODAO<T extends DSpaceObject> extends Ab
|
||||
}
|
||||
query.append(additionalWhere);
|
||||
}
|
||||
query.append(")");
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -184,32 +184,98 @@ public class EPersonServiceImpl extends DSpaceObjectServiceImpl<EPerson> impleme
|
||||
|
||||
@Override
|
||||
public List<EPerson> search(Context context, String query, int offset, int limit) throws SQLException {
|
||||
try {
|
||||
List<EPerson> ePerson = new ArrayList<>();
|
||||
EPerson person = find(context, UUID.fromString(query));
|
||||
if (person != null) {
|
||||
ePerson.add(person);
|
||||
}
|
||||
return ePerson;
|
||||
} catch (IllegalArgumentException e) {
|
||||
List<EPerson> ePersons = new ArrayList<>();
|
||||
UUID uuid = UUIDUtils.fromString(query);
|
||||
if (uuid == null) {
|
||||
// Search by firstname & lastname (NOTE: email will also be included automatically)
|
||||
MetadataField firstNameField = metadataFieldService.findByElement(context, "eperson", "firstname", null);
|
||||
MetadataField lastNameField = metadataFieldService.findByElement(context, "eperson", "lastname", null);
|
||||
if (StringUtils.isBlank(query)) {
|
||||
query = null;
|
||||
}
|
||||
return ePersonDAO.search(context, query, Arrays.asList(firstNameField, lastNameField),
|
||||
ePersons = ePersonDAO.search(context, query, Arrays.asList(firstNameField, lastNameField),
|
||||
Arrays.asList(firstNameField, lastNameField), offset, limit);
|
||||
} else {
|
||||
// Search by UUID
|
||||
EPerson person = find(context, uuid);
|
||||
if (person != null) {
|
||||
ePersons.add(person);
|
||||
}
|
||||
}
|
||||
return ePersons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int searchResultCount(Context context, String query) throws SQLException {
|
||||
int result = 0;
|
||||
UUID uuid = UUIDUtils.fromString(query);
|
||||
if (uuid == null) {
|
||||
// Count results found by firstname & lastname (email is also included automatically)
|
||||
MetadataField firstNameField = metadataFieldService.findByElement(context, "eperson", "firstname", null);
|
||||
MetadataField lastNameField = metadataFieldService.findByElement(context, "eperson", "lastname", null);
|
||||
if (StringUtils.isBlank(query)) {
|
||||
query = null;
|
||||
}
|
||||
return ePersonDAO.searchResultCount(context, query, Arrays.asList(firstNameField, lastNameField));
|
||||
result = ePersonDAO.searchResultCount(context, query, Arrays.asList(firstNameField, lastNameField));
|
||||
} else {
|
||||
// Search by UUID
|
||||
EPerson person = find(context, uuid);
|
||||
if (person != null) {
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EPerson> searchNonMembers(Context context, String query, Group excludeGroup, int offset, int limit)
|
||||
throws SQLException {
|
||||
List<EPerson> ePersons = new ArrayList<>();
|
||||
UUID uuid = UUIDUtils.fromString(query);
|
||||
if (uuid == null) {
|
||||
// Search by firstname & lastname (NOTE: email will also be included automatically)
|
||||
MetadataField firstNameField = metadataFieldService.findByElement(context, "eperson", "firstname", null);
|
||||
MetadataField lastNameField = metadataFieldService.findByElement(context, "eperson", "lastname", null);
|
||||
if (StringUtils.isBlank(query)) {
|
||||
query = null;
|
||||
}
|
||||
ePersons = ePersonDAO.searchNotMember(context, query, Arrays.asList(firstNameField, lastNameField),
|
||||
excludeGroup, Arrays.asList(firstNameField, lastNameField),
|
||||
offset, limit);
|
||||
} else {
|
||||
// Search by UUID
|
||||
EPerson person = find(context, uuid);
|
||||
// Verify EPerson is NOT a member of the given excludeGroup before adding
|
||||
if (person != null && !groupService.isDirectMember(excludeGroup, person)) {
|
||||
ePersons.add(person);
|
||||
}
|
||||
}
|
||||
|
||||
return ePersons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int searchNonMembersCount(Context context, String query, Group excludeGroup) throws SQLException {
|
||||
int result = 0;
|
||||
UUID uuid = UUIDUtils.fromString(query);
|
||||
if (uuid == null) {
|
||||
// Count results found by firstname & lastname (email is also included automatically)
|
||||
MetadataField firstNameField = metadataFieldService.findByElement(context, "eperson", "firstname", null);
|
||||
MetadataField lastNameField = metadataFieldService.findByElement(context, "eperson", "lastname", null);
|
||||
if (StringUtils.isBlank(query)) {
|
||||
query = null;
|
||||
}
|
||||
result = ePersonDAO.searchNotMemberCount(context, query, Arrays.asList(firstNameField, lastNameField),
|
||||
excludeGroup);
|
||||
} else {
|
||||
// Search by UUID
|
||||
EPerson person = find(context, uuid);
|
||||
// Verify EPerson is NOT a member of the given excludeGroup before counting
|
||||
if (person != null && !groupService.isDirectMember(excludeGroup, person)) {
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -460,17 +460,17 @@ public class GroupServiceImpl extends DSpaceObjectServiceImpl<Group> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Group> search(Context context, String groupIdentifier) throws SQLException {
|
||||
return search(context, groupIdentifier, -1, -1);
|
||||
public List<Group> search(Context context, String query) throws SQLException {
|
||||
return search(context, query, -1, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Group> search(Context context, String groupIdentifier, int offset, int limit) throws SQLException {
|
||||
public List<Group> search(Context context, String query, int offset, int limit) throws SQLException {
|
||||
List<Group> groups = new ArrayList<>();
|
||||
UUID uuid = UUIDUtils.fromString(groupIdentifier);
|
||||
UUID uuid = UUIDUtils.fromString(query);
|
||||
if (uuid == null) {
|
||||
//Search by group name
|
||||
groups = groupDAO.findByNameLike(context, groupIdentifier, offset, limit);
|
||||
groups = groupDAO.findByNameLike(context, query, offset, limit);
|
||||
} else {
|
||||
//Search by group id
|
||||
Group group = find(context, uuid);
|
||||
@@ -483,12 +483,12 @@ public class GroupServiceImpl extends DSpaceObjectServiceImpl<Group> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public int searchResultCount(Context context, String groupIdentifier) throws SQLException {
|
||||
public int searchResultCount(Context context, String query) throws SQLException {
|
||||
int result = 0;
|
||||
UUID uuid = UUIDUtils.fromString(groupIdentifier);
|
||||
UUID uuid = UUIDUtils.fromString(query);
|
||||
if (uuid == null) {
|
||||
//Search by group name
|
||||
result = groupDAO.countByNameLike(context, groupIdentifier);
|
||||
result = groupDAO.countByNameLike(context, query);
|
||||
} else {
|
||||
//Search by group id
|
||||
Group group = find(context, uuid);
|
||||
@@ -500,6 +500,44 @@ public class GroupServiceImpl extends DSpaceObjectServiceImpl<Group> implements
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Group> searchNonMembers(Context context, String query, Group excludeParentGroup,
|
||||
int offset, int limit) throws SQLException {
|
||||
List<Group> groups = new ArrayList<>();
|
||||
UUID uuid = UUIDUtils.fromString(query);
|
||||
if (uuid == null) {
|
||||
// Search by group name
|
||||
groups = groupDAO.findByNameLikeAndNotMember(context, query, excludeParentGroup, offset, limit);
|
||||
} else if (!uuid.equals(excludeParentGroup.getID())) {
|
||||
// Search by group id
|
||||
Group group = find(context, uuid);
|
||||
// Verify it is NOT a member of the given excludeParentGroup before adding
|
||||
if (group != null && !isMember(excludeParentGroup, group)) {
|
||||
groups.add(group);
|
||||
}
|
||||
}
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int searchNonMembersCount(Context context, String query, Group excludeParentGroup) throws SQLException {
|
||||
int result = 0;
|
||||
UUID uuid = UUIDUtils.fromString(query);
|
||||
if (uuid == null) {
|
||||
// Search by group name
|
||||
result = groupDAO.countByNameLikeAndNotMember(context, query, excludeParentGroup);
|
||||
} else if (!uuid.equals(excludeParentGroup.getID())) {
|
||||
// Search by group id
|
||||
Group group = find(context, uuid);
|
||||
// Verify it is NOT a member of the given excludeParentGroup before adding
|
||||
if (group != null && !isMember(excludeParentGroup, group)) {
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Context context, Group group) throws SQLException {
|
||||
if (group.isPermanent()) {
|
||||
|
@@ -33,11 +33,68 @@ public interface EPersonDAO extends DSpaceObjectDAO<EPerson>, DSpaceObjectLegacy
|
||||
|
||||
public EPerson findByNetid(Context context, String netid) throws SQLException;
|
||||
|
||||
/**
|
||||
* Search all EPersons by the given MetadataField objects, sorting by the given sort fields.
|
||||
* <P>
|
||||
* NOTE: As long as a query is specified, the EPerson's email address is included in the search alongside any given
|
||||
* metadata fields.
|
||||
*
|
||||
* @param context DSpace context
|
||||
* @param query the text to search EPersons for
|
||||
* @param queryFields the metadata fields to search within (email is also included automatically)
|
||||
* @param sortFields the metadata field(s) to sort the results by
|
||||
* @param offset the position of the first result to return
|
||||
* @param limit how many results return
|
||||
* @return List of matching EPerson objects
|
||||
* @throws SQLException if an error occurs
|
||||
*/
|
||||
public List<EPerson> search(Context context, String query, List<MetadataField> queryFields,
|
||||
List<MetadataField> sortFields, int offset, int limit) throws SQLException;
|
||||
|
||||
/**
|
||||
* Count number of EPersons who match a search on the given metadata fields. This returns the count of total
|
||||
* results for the same query using the 'search()', and therefore can be used to provide pagination.
|
||||
*
|
||||
* @param context DSpace context
|
||||
* @param query the text to search EPersons for
|
||||
* @param queryFields the metadata fields to search within (email is also included automatically)
|
||||
* @return total number of EPersons who match the query
|
||||
* @throws SQLException if an error occurs
|
||||
*/
|
||||
public int searchResultCount(Context context, String query, List<MetadataField> queryFields) throws SQLException;
|
||||
|
||||
/**
|
||||
* Search all EPersons via their firstname, lastname, email (fuzzy match), limited to those EPersons which are NOT
|
||||
* a member of the given group. This may be used to search across EPersons which are valid to add as members to the
|
||||
* given group.
|
||||
*
|
||||
* @param context The DSpace context
|
||||
* @param query the text to search EPersons for
|
||||
* @param queryFields the metadata fields to search within (email is also included automatically)
|
||||
* @param excludeGroup Group to exclude results from. Members of this group will never be returned.
|
||||
* @param offset the position of the first result to return
|
||||
* @param limit how many results return
|
||||
* @return EPersons matching the query (which are not members of the given group)
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
List<EPerson> searchNotMember(Context context, String query, List<MetadataField> queryFields, Group excludeGroup,
|
||||
List<MetadataField> sortFields, int offset, int limit) throws SQLException;
|
||||
|
||||
/**
|
||||
* Count number of EPersons that match a given search (fuzzy match) across firstname, lastname and email. This
|
||||
* search is limited to those EPersons which are NOT a member of the given group. This may be used
|
||||
* (with searchNotMember()) to perform a paginated search across EPersons which are valid to add to the given group.
|
||||
*
|
||||
* @param context The DSpace context
|
||||
* @param query querystring to fuzzy match against.
|
||||
* @param queryFields the metadata fields to search within (email is also included automatically)
|
||||
* @param excludeGroup Group to exclude results from. Members of this group will never be returned.
|
||||
* @return Groups matching the query (which are not members of the given parent)
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
int searchNotMemberCount(Context context, String query, List<MetadataField> queryFields, Group excludeGroup)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* Find all EPersons who are a member of one or more of the listed groups in a paginated fashion. This returns
|
||||
* EPersons ordered by UUID.
|
||||
|
@@ -135,6 +135,38 @@ public interface GroupDAO extends DSpaceObjectDAO<Group>, DSpaceObjectLegacySupp
|
||||
*/
|
||||
int countByNameLike(Context context, String groupName) throws SQLException;
|
||||
|
||||
/**
|
||||
* Search all groups via their name (fuzzy match), limited to those groups which are NOT a member of the given
|
||||
* parent group. This may be used to search across groups which are valid to add to the given parent group.
|
||||
* <P>
|
||||
* NOTE: The parent group itself is also excluded from the search.
|
||||
*
|
||||
* @param context The DSpace context
|
||||
* @param groupName Group name to fuzzy match against.
|
||||
* @param excludeParent Parent Group to exclude results from. Groups under this parent will never be returned.
|
||||
* @param offset Offset to use for pagination (-1 to disable)
|
||||
* @param limit The maximum number of results to return (-1 to disable)
|
||||
* @return Groups matching the query (which are not members of the given parent)
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
List<Group> findByNameLikeAndNotMember(Context context, String groupName, Group excludeParent,
|
||||
int offset, int limit) throws SQLException;
|
||||
|
||||
/**
|
||||
* Count number of groups that match a given name (fuzzy match), limited to those groups which are NOT a member of
|
||||
* the given parent group. This may be used (with findByNameLikeAndNotMember()) to search across groups which are
|
||||
* valid to add to the given parent group.
|
||||
* <P>
|
||||
* NOTE: The parent group itself is also excluded from the count.
|
||||
*
|
||||
* @param context The DSpace context
|
||||
* @param groupName Group name to fuzzy match against.
|
||||
* @param excludeParent Parent Group to exclude results from. Groups under this parent will never be returned.
|
||||
* @return Groups matching the query (which are not members of the given parent)
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
int countByNameLikeAndNotMember(Context context, String groupName, Group excludeParent) throws SQLException;
|
||||
|
||||
/**
|
||||
* Find a group by its name and the membership of the given EPerson
|
||||
*
|
||||
|
@@ -70,17 +70,9 @@ public class EPersonDAOImpl extends AbstractHibernateDSODAO<EPerson> implements
|
||||
String queryString = "SELECT " + EPerson.class.getSimpleName()
|
||||
.toLowerCase() + " FROM EPerson as " + EPerson.class
|
||||
.getSimpleName().toLowerCase() + " ";
|
||||
if (query != null) {
|
||||
query = "%" + query.toLowerCase() + "%";
|
||||
}
|
||||
Query hibernateQuery = getSearchQuery(context, queryString, query, queryFields, sortFields, null);
|
||||
|
||||
if (0 <= offset) {
|
||||
hibernateQuery.setFirstResult(offset);
|
||||
}
|
||||
if (0 <= limit) {
|
||||
hibernateQuery.setMaxResults(limit);
|
||||
}
|
||||
Query hibernateQuery = getSearchQuery(context, queryString, query, queryFields, null,
|
||||
sortFields, null, limit, offset);
|
||||
return list(hibernateQuery);
|
||||
}
|
||||
|
||||
@@ -92,6 +84,28 @@ public class EPersonDAOImpl extends AbstractHibernateDSODAO<EPerson> implements
|
||||
return count(hibernateQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EPerson> searchNotMember(Context context, String query, List<MetadataField> queryFields,
|
||||
Group excludeGroup, List<MetadataField> sortFields,
|
||||
int offset, int limit) throws SQLException {
|
||||
String queryString = "SELECT " + EPerson.class.getSimpleName()
|
||||
.toLowerCase() + " FROM EPerson as " + EPerson.class
|
||||
.getSimpleName().toLowerCase() + " ";
|
||||
|
||||
Query hibernateQuery = getSearchQuery(context, queryString, query, queryFields, excludeGroup,
|
||||
sortFields, null, limit, offset);
|
||||
return list(hibernateQuery);
|
||||
}
|
||||
|
||||
public int searchNotMemberCount(Context context, String query, List<MetadataField> queryFields,
|
||||
Group excludeGroup) throws SQLException {
|
||||
String queryString = "SELECT count(*) FROM EPerson as " + EPerson.class.getSimpleName().toLowerCase();
|
||||
|
||||
Query hibernateQuery = getSearchQuery(context, queryString, query, queryFields, excludeGroup,
|
||||
Collections.EMPTY_LIST, null, -1, -1);
|
||||
return count(hibernateQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EPerson> findAll(Context context, MetadataField metadataSortField, String sortField, int pageSize,
|
||||
int offset) throws SQLException {
|
||||
@@ -105,8 +119,8 @@ public class EPersonDAOImpl extends AbstractHibernateDSODAO<EPerson> implements
|
||||
sortFields = Collections.singletonList(metadataSortField);
|
||||
}
|
||||
|
||||
Query query = getSearchQuery(context, queryString, null, ListUtils.EMPTY_LIST, sortFields, sortField, pageSize,
|
||||
offset);
|
||||
Query query = getSearchQuery(context, queryString, null, ListUtils.EMPTY_LIST, null,
|
||||
sortFields, sortField, pageSize, offset);
|
||||
return list(query);
|
||||
|
||||
}
|
||||
@@ -178,43 +192,88 @@ public class EPersonDAOImpl extends AbstractHibernateDSODAO<EPerson> implements
|
||||
protected Query getSearchQuery(Context context, String queryString, String queryParam,
|
||||
List<MetadataField> queryFields, List<MetadataField> sortFields, String sortField)
|
||||
throws SQLException {
|
||||
return getSearchQuery(context, queryString, queryParam, queryFields, sortFields, sortField, -1, -1);
|
||||
return getSearchQuery(context, queryString, queryParam, queryFields, null, sortFields, sortField, -1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a search query across EPersons based on the given metadata fields and sorted based on the given metadata
|
||||
* field(s) or database column.
|
||||
* <P>
|
||||
* NOTE: the EPerson's email address is included in the search alongside any given metadata fields.
|
||||
*
|
||||
* @param context DSpace Context
|
||||
* @param queryString String which defines the beginning "SELECT" for the SQL query
|
||||
* @param queryParam Actual text being searched for
|
||||
* @param queryFields List of metadata fields to search within
|
||||
* @param excludeGroup Optional Group which should be excluded from search. Any EPersons who are members
|
||||
* of this group will not be included in the results.
|
||||
* @param sortFields Optional List of metadata fields to sort by (should not be specified if sortField is used)
|
||||
* @param sortField Optional database column to sort on (should not be specified if sortFields is used)
|
||||
* @param pageSize how many results return
|
||||
* @param offset the position of the first result to return
|
||||
* @return built Query object
|
||||
* @throws SQLException if error occurs
|
||||
*/
|
||||
protected Query getSearchQuery(Context context, String queryString, String queryParam,
|
||||
List<MetadataField> queryFields, List<MetadataField> sortFields, String sortField,
|
||||
List<MetadataField> queryFields, Group excludeGroup,
|
||||
List<MetadataField> sortFields, String sortField,
|
||||
int pageSize, int offset) throws SQLException {
|
||||
|
||||
// Initialize SQL statement using the passed in "queryString"
|
||||
StringBuilder queryBuilder = new StringBuilder();
|
||||
queryBuilder.append(queryString);
|
||||
|
||||
Set<MetadataField> metadataFieldsToJoin = new LinkedHashSet<>();
|
||||
metadataFieldsToJoin.addAll(queryFields);
|
||||
metadataFieldsToJoin.addAll(sortFields);
|
||||
|
||||
// Append necessary join information for MetadataFields we will search within
|
||||
if (!CollectionUtils.isEmpty(metadataFieldsToJoin)) {
|
||||
addMetadataLeftJoin(queryBuilder, EPerson.class.getSimpleName().toLowerCase(), metadataFieldsToJoin);
|
||||
}
|
||||
if (queryParam != null) {
|
||||
// Always append a search on EPerson "email" based on query
|
||||
if (StringUtils.isNotBlank(queryParam)) {
|
||||
addMetadataValueWhereQuery(queryBuilder, queryFields, "like",
|
||||
EPerson.class.getSimpleName().toLowerCase() + ".email like :queryParam");
|
||||
}
|
||||
// If excludeGroup is specified, exclude members of that group from results
|
||||
// This uses a subquery to find the excluded group & verify that it is not in the EPerson list of "groups"
|
||||
if (excludeGroup != null) {
|
||||
// If query params exist, then we already have a WHERE clause (see above) and just need to append an AND
|
||||
if (StringUtils.isNotBlank(queryParam)) {
|
||||
queryBuilder.append(" AND ");
|
||||
} else {
|
||||
// no WHERE clause yet, so this is the start of the WHERE
|
||||
queryBuilder.append(" WHERE ");
|
||||
}
|
||||
queryBuilder.append("(FROM Group g where g.id = :group_id) NOT IN elements (")
|
||||
.append(EPerson.class.getSimpleName().toLowerCase()).append(".groups)");
|
||||
}
|
||||
// Add sort/order by info to query, if specified
|
||||
if (!CollectionUtils.isEmpty(sortFields) || StringUtils.isNotBlank(sortField)) {
|
||||
addMetadataSortQuery(queryBuilder, sortFields, Collections.singletonList(sortField));
|
||||
}
|
||||
|
||||
// Create the final SQL SELECT statement (based on included params above)
|
||||
Query query = createQuery(context, queryBuilder.toString());
|
||||
// Set pagesize & offset for pagination
|
||||
if (pageSize > 0) {
|
||||
query.setMaxResults(pageSize);
|
||||
}
|
||||
if (offset > 0) {
|
||||
query.setFirstResult(offset);
|
||||
}
|
||||
// Set all parameters to the SQL SELECT statement (based on included params above)
|
||||
if (StringUtils.isNotBlank(queryParam)) {
|
||||
query.setParameter("queryParam", "%" + queryParam.toLowerCase() + "%");
|
||||
}
|
||||
for (MetadataField metadataField : metadataFieldsToJoin) {
|
||||
query.setParameter(metadataField.toString(), metadataField.getID());
|
||||
}
|
||||
if (excludeGroup != null) {
|
||||
query.setParameter("group_id", excludeGroup.getID());
|
||||
}
|
||||
|
||||
query.setHint("org.hibernate.cacheable", Boolean.TRUE);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
@@ -164,6 +164,41 @@ public class GroupDAOImpl extends AbstractHibernateDSODAO<Group> implements Grou
|
||||
return count(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Group> findByNameLikeAndNotMember(Context context, String groupName, Group excludeParent,
|
||||
int offset, int limit) throws SQLException {
|
||||
Query query = createQuery(context,
|
||||
"FROM Group " +
|
||||
"WHERE lower(name) LIKE lower(:group_name) " +
|
||||
"AND id != :parent_id " +
|
||||
"AND (from Group g where g.id = :parent_id) not in elements (parentGroups)");
|
||||
query.setParameter("parent_id", excludeParent.getID());
|
||||
query.setParameter("group_name", "%" + StringUtils.trimToEmpty(groupName) + "%");
|
||||
|
||||
if (0 <= offset) {
|
||||
query.setFirstResult(offset);
|
||||
}
|
||||
if (0 <= limit) {
|
||||
query.setMaxResults(limit);
|
||||
}
|
||||
query.setHint("org.hibernate.cacheable", Boolean.TRUE);
|
||||
|
||||
return list(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countByNameLikeAndNotMember(Context context, String groupName, Group excludeParent) throws SQLException {
|
||||
Query query = createQuery(context,
|
||||
"SELECT count(*) FROM Group " +
|
||||
"WHERE lower(name) LIKE lower(:group_name) " +
|
||||
"AND id != :parent_id " +
|
||||
"AND (from Group g where g.id = :parent_id) not in elements (parentGroups)");
|
||||
query.setParameter("parent_id", excludeParent.getID());
|
||||
query.setParameter("group_name", "%" + StringUtils.trimToEmpty(groupName) + "%");
|
||||
|
||||
return count(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Context context, Group group) throws SQLException {
|
||||
Query query = getHibernateSession(context)
|
||||
@@ -213,6 +248,7 @@ public class GroupDAOImpl extends AbstractHibernateDSODAO<Group> implements Grou
|
||||
return list(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countByParent(Context context, Group parent) throws SQLException {
|
||||
Query query = createQuery(context, "SELECT count(g) FROM Group g JOIN g.parentGroups pg " +
|
||||
"WHERE pg.id = :parent_id");
|
||||
|
@@ -98,9 +98,9 @@ public interface EPersonService extends DSpaceObjectService<EPerson>, DSpaceObje
|
||||
*
|
||||
* @param context The relevant DSpace Context.
|
||||
* @param query The search string
|
||||
* @param offset Inclusive offset
|
||||
* @param offset Inclusive offset (the position of the first result to return)
|
||||
* @param limit Maximum number of matches returned
|
||||
* @return array of EPerson objects
|
||||
* @return List of matching EPerson objects
|
||||
* @throws SQLException An exception that provides information on a database access error or other errors.
|
||||
*/
|
||||
public List<EPerson> search(Context context, String query, int offset, int limit)
|
||||
@@ -118,6 +118,34 @@ public interface EPersonService extends DSpaceObjectService<EPerson>, DSpaceObje
|
||||
public int searchResultCount(Context context, String query)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* Find the EPersons that match the search query which are NOT currently members of the given Group. The search
|
||||
* query is run against firstname, lastname or email.
|
||||
*
|
||||
* @param context DSpace context
|
||||
* @param query The search string
|
||||
* @param excludeGroup Group to exclude results from. Members of this group will never be returned.
|
||||
* @param offset Inclusive offset (the position of the first result to return)
|
||||
* @param limit Maximum number of matches returned
|
||||
* @return List of matching EPerson objects
|
||||
* @throws SQLException if error
|
||||
*/
|
||||
List<EPerson> searchNonMembers(Context context, String query, Group excludeGroup,
|
||||
int offset, int limit) throws SQLException;
|
||||
|
||||
/**
|
||||
* Returns the total number of EPersons that match the search query which are NOT currently members of the given
|
||||
* Group. The search query is run against firstname, lastname or email. Can be used with searchNonMembers() to
|
||||
* support pagination
|
||||
*
|
||||
* @param context DSpace context
|
||||
* @param query The search string
|
||||
* @param excludeGroup Group to exclude results from. Members of this group will never be returned.
|
||||
* @return List of matching EPerson objects
|
||||
* @throws SQLException if error
|
||||
*/
|
||||
int searchNonMembersCount(Context context, String query, Group excludeGroup) throws SQLException;
|
||||
|
||||
/**
|
||||
* Find all the {@code EPerson}s in a specific order by field.
|
||||
* The sortable fields are:
|
||||
|
@@ -261,37 +261,67 @@ public interface GroupService extends DSpaceObjectService<Group>, DSpaceObjectLe
|
||||
public List<Group> findAll(Context context, int sortField) throws SQLException;
|
||||
|
||||
/**
|
||||
* Find the groups that match the search query across eperson_group_id or name
|
||||
* Find the Groups that match the query across both Group name and Group ID. This is an unpaginated search,
|
||||
* which means it will load all matching groups into memory at once. This may provide POOR PERFORMANCE when a large
|
||||
* number of groups are matched.
|
||||
*
|
||||
* @param context DSpace context
|
||||
* @param groupIdentifier The group name or group ID
|
||||
* @return array of Group objects
|
||||
* @param query The search string used to search across group name or group ID
|
||||
* @return List of matching Group objects
|
||||
* @throws SQLException if error
|
||||
*/
|
||||
public List<Group> search(Context context, String groupIdentifier) throws SQLException;
|
||||
List<Group> search(Context context, String query) throws SQLException;
|
||||
|
||||
/**
|
||||
* Find the groups that match the search query across eperson_group_id or name
|
||||
* Find the Groups that match the query across both Group name and Group ID. This method supports pagination,
|
||||
* which provides better performance than the above non-paginated search() method.
|
||||
*
|
||||
* @param context DSpace context
|
||||
* @param groupIdentifier The group name or group ID
|
||||
* @param offset Inclusive offset
|
||||
* @param query The search string used to search across group name or group ID
|
||||
* @param offset Inclusive offset (the position of the first result to return)
|
||||
* @param limit Maximum number of matches returned
|
||||
* @return array of Group objects
|
||||
* @return List of matching Group objects
|
||||
* @throws SQLException if error
|
||||
*/
|
||||
public List<Group> search(Context context, String groupIdentifier, int offset, int limit) throws SQLException;
|
||||
List<Group> search(Context context, String query, int offset, int limit) throws SQLException;
|
||||
|
||||
/**
|
||||
* Returns the total number of groups returned by a specific query, without the overhead
|
||||
* of creating the Group objects to store the results.
|
||||
* Returns the total number of Groups returned by a specific query. Search is performed based on Group name
|
||||
* and Group ID. May be used with search() above to support pagination of matching Groups.
|
||||
*
|
||||
* @param context DSpace context
|
||||
* @param query The search string
|
||||
* @param query The search string used to search across group name or group ID
|
||||
* @return the number of groups matching the query
|
||||
* @throws SQLException if error
|
||||
*/
|
||||
public int searchResultCount(Context context, String query) throws SQLException;
|
||||
int searchResultCount(Context context, String query) throws SQLException;
|
||||
|
||||
/**
|
||||
* Find the groups that match the search query which are NOT currently members (subgroups)
|
||||
* of the given parentGroup
|
||||
*
|
||||
* @param context DSpace context
|
||||
* @param query The search string used to search across group name or group ID
|
||||
* @param excludeParentGroup Parent group to exclude results from
|
||||
* @param offset Inclusive offset (the position of the first result to return)
|
||||
* @param limit Maximum number of matches returned
|
||||
* @return List of matching Group objects
|
||||
* @throws SQLException if error
|
||||
*/
|
||||
List<Group> searchNonMembers(Context context, String query, Group excludeParentGroup,
|
||||
int offset, int limit) throws SQLException;
|
||||
|
||||
/**
|
||||
* Returns the total number of groups that match the search query which are NOT currently members (subgroups)
|
||||
* of the given parentGroup. Can be used with searchNonMembers() to support pagination.
|
||||
*
|
||||
* @param context DSpace context
|
||||
* @param query The search string used to search across group name or group ID
|
||||
* @param excludeParentGroup Parent group to exclude results from
|
||||
* @return the number of Groups matching the query
|
||||
* @throws SQLException if error
|
||||
*/
|
||||
int searchNonMembersCount(Context context, String query, Group excludeParentGroup) throws SQLException;
|
||||
|
||||
/**
|
||||
* Return true if group has no direct or indirect members
|
||||
|
@@ -8,6 +8,7 @@
|
||||
package org.dspace.eperson;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -15,6 +16,8 @@ import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -277,63 +280,184 @@ public class EPersonTest extends AbstractUnitTest {
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test of search method, of class EPerson.
|
||||
* Test of search() and searchResultCount() methods of EPersonService
|
||||
* NOTE: Pagination is not verified here because it is tested in EPersonRestRepositoryIT
|
||||
*/
|
||||
/*
|
||||
@Test
|
||||
public void testSearch_Context_String()
|
||||
throws Exception
|
||||
{
|
||||
System.out.println("search");
|
||||
Context context = null;
|
||||
String query = "";
|
||||
EPerson[] expResult = null;
|
||||
EPerson[] result = EPerson.search(context, query);
|
||||
assertEquals(expResult, result);
|
||||
// TODO review the generated test code and remove the default call to fail.
|
||||
fail("The test case is a prototype.");
|
||||
public void testSearchAndCountByNameEmail() throws SQLException, AuthorizeException, IOException {
|
||||
List<EPerson> allEPeopleAdded = new ArrayList<>();
|
||||
Group testGroup = createGroup("TestingGroup");
|
||||
try {
|
||||
// Create 4 EPersons. Add a few to a test group to verify group membership doesn't matter
|
||||
EPerson eperson1 = createEPersonAndAddToGroup("eperson1@example.com", "Jane", "Doe", testGroup);
|
||||
EPerson eperson2 = createEPerson("eperson2@example.com", "John", "Doe");
|
||||
EPerson eperson3 = createEPersonAndAddToGroup("eperson3@example.com", "John", "Smith", testGroup);
|
||||
EPerson eperson4 = createEPerson("eperson4@example.com", "Doe", "Smith");
|
||||
allEPeopleAdded.addAll(Arrays.asList(eperson1, eperson2, eperson3, eperson4));
|
||||
|
||||
List<EPerson> allJohns = Arrays.asList(eperson2, eperson3);
|
||||
List<EPerson> searchJohnResults = ePersonService.search(context, "John", -1, -1);
|
||||
assertTrue(searchJohnResults.containsAll(allJohns));
|
||||
assertEquals(searchJohnResults.size(), ePersonService.searchResultCount(context, "John"));
|
||||
|
||||
List<EPerson> allDoes = Arrays.asList(eperson1, eperson2, eperson4);
|
||||
List<EPerson> searchDoeResults = ePersonService.search(context, "Doe", -1, -1);
|
||||
assertTrue(searchDoeResults.containsAll(allDoes));
|
||||
assertEquals(searchDoeResults.size(), ePersonService.searchResultCount(context, "Doe"));
|
||||
|
||||
List<EPerson> allSmiths = Arrays.asList(eperson3, eperson4);
|
||||
List<EPerson> searchSmithResults = ePersonService.search(context, "Smith", -1, -1);
|
||||
assertTrue(searchSmithResults.containsAll(allSmiths));
|
||||
assertEquals(searchSmithResults.size(), ePersonService.searchResultCount(context, "Smith"));
|
||||
|
||||
// Assert search on example.com returns everyone
|
||||
List<EPerson> searchEmailResults = ePersonService.search(context, "example.com", -1, -1);
|
||||
assertTrue(searchEmailResults.containsAll(allEPeopleAdded));
|
||||
assertEquals(searchEmailResults.size(), ePersonService.searchResultCount(context, "example.com"));
|
||||
|
||||
// Assert exact email search returns just one
|
||||
List<EPerson> exactEmailResults = ePersonService.search(context, "eperson1@example.com", -1, -1);
|
||||
assertTrue(exactEmailResults.contains(eperson1));
|
||||
assertEquals(exactEmailResults.size(), ePersonService.searchResultCount(context, "eperson1@example.com"));
|
||||
|
||||
// Assert UUID search returns exact match
|
||||
List<EPerson> uuidResults = ePersonService.search(context, eperson4.getID().toString(), -1, -1);
|
||||
assertTrue(uuidResults.contains(eperson4));
|
||||
assertEquals(1, uuidResults.size());
|
||||
assertEquals(uuidResults.size(), ePersonService.searchResultCount(context, eperson4.getID().toString()));
|
||||
} finally {
|
||||
// Remove all Groups & EPersons we added for this test
|
||||
context.turnOffAuthorisationSystem();
|
||||
groupService.delete(context, testGroup);
|
||||
for (EPerson ePerson : allEPeopleAdded) {
|
||||
ePersonService.delete(context, ePerson);
|
||||
}
|
||||
context.restoreAuthSystemState();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test of search method, of class EPerson.
|
||||
* Test of searchNonMembers() and searchNonMembersCount() methods of EPersonService
|
||||
* NOTE: Pagination is not verified here because it is tested in EPersonRestRepositoryIT
|
||||
*/
|
||||
/*
|
||||
@Test
|
||||
public void testSearch_4args()
|
||||
throws Exception
|
||||
{
|
||||
System.out.println("search");
|
||||
Context context = null;
|
||||
String query = "";
|
||||
int offset = 0;
|
||||
int limit = 0;
|
||||
EPerson[] expResult = null;
|
||||
EPerson[] result = EPerson.search(context, query, offset, limit);
|
||||
assertEquals(expResult, result);
|
||||
// TODO review the generated test code and remove the default call to fail.
|
||||
fail("The test case is a prototype.");
|
||||
}
|
||||
*/
|
||||
public void testSearchAndCountByNameEmailNonMembers() throws SQLException, AuthorizeException, IOException {
|
||||
List<EPerson> allEPeopleAdded = new ArrayList<>();
|
||||
Group testGroup1 = createGroup("TestingGroup1");
|
||||
Group testGroup2 = createGroup("TestingGroup2");
|
||||
Group testGroup3 = createGroup("TestingGroup3");
|
||||
try {
|
||||
// Create two EPersons in Group 1
|
||||
EPerson eperson1 = createEPersonAndAddToGroup("eperson1@example.com", "Jane", "Doe", testGroup1);
|
||||
EPerson eperson2 = createEPersonAndAddToGroup("eperson2@example.com", "John", "Smith", testGroup1);
|
||||
|
||||
/**
|
||||
* Test of searchResultCount method, of class EPerson.
|
||||
*/
|
||||
/*
|
||||
@Test
|
||||
public void testSearchResultCount()
|
||||
throws Exception
|
||||
{
|
||||
System.out.println("searchResultCount");
|
||||
Context context = null;
|
||||
String query = "";
|
||||
int expResult = 0;
|
||||
int result = EPerson.searchResultCount(context, query);
|
||||
assertEquals(expResult, result);
|
||||
// TODO review the generated test code and remove the default call to fail.
|
||||
fail("The test case is a prototype.");
|
||||
// Create one more EPerson, and add it and a previous EPerson to Group 2
|
||||
EPerson eperson3 = createEPersonAndAddToGroup("eperson3@example.com", "John", "Doe", testGroup2);
|
||||
context.turnOffAuthorisationSystem();
|
||||
groupService.addMember(context, testGroup2, eperson2);
|
||||
groupService.update(context, testGroup2);
|
||||
ePersonService.update(context, eperson2);
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
// Create 2 more EPersons with no group memberships
|
||||
EPerson eperson4 = createEPerson("eperson4@example.com", "John", "Anthony");
|
||||
EPerson eperson5 = createEPerson("eperson5@example.org", "Smith", "Doe");
|
||||
allEPeopleAdded.addAll(Arrays.asList(eperson1, eperson2, eperson3, eperson4, eperson5));
|
||||
|
||||
// FIRST, test search by last name
|
||||
// Verify all Does match a nonMember search of Group3 (which is an empty group)
|
||||
List<EPerson> allDoes = Arrays.asList(eperson1, eperson3, eperson5);
|
||||
List<EPerson> searchDoeResults = ePersonService.searchNonMembers(context, "Doe", testGroup3, -1, -1);
|
||||
assertTrue(searchDoeResults.containsAll(allDoes));
|
||||
assertEquals(searchDoeResults.size(), ePersonService.searchNonMembersCount(context, "Doe", testGroup3));
|
||||
|
||||
// Verify searching "Doe" with Group 2 *excludes* the one which is already a member
|
||||
List<EPerson> allNonMemberDoes = Arrays.asList(eperson1, eperson5);
|
||||
List<EPerson> searchNonMemberDoeResults = ePersonService.searchNonMembers(context, "Doe", testGroup2,
|
||||
-1, -1);
|
||||
assertTrue(searchNonMemberDoeResults.containsAll(allNonMemberDoes));
|
||||
assertFalse(searchNonMemberDoeResults.contains(eperson3));
|
||||
assertEquals(searchNonMemberDoeResults.size(), ePersonService.searchNonMembersCount(context, "Doe",
|
||||
testGroup2));
|
||||
|
||||
// Verify searching "Doe" with Group 1 *excludes* the one which is already a member
|
||||
allNonMemberDoes = Arrays.asList(eperson3, eperson5);
|
||||
searchNonMemberDoeResults = ePersonService.searchNonMembers(context, "Doe", testGroup1, -1, -1);
|
||||
assertTrue(searchNonMemberDoeResults.containsAll(allNonMemberDoes));
|
||||
assertFalse(searchNonMemberDoeResults.contains(eperson1));
|
||||
assertEquals(searchNonMemberDoeResults.size(), ePersonService.searchNonMembersCount(context, "Doe",
|
||||
testGroup1));
|
||||
|
||||
// SECOND, test search by first name
|
||||
// Verify all Johns match a nonMember search of Group3 (which is an empty group)
|
||||
List<EPerson> allJohns = Arrays.asList(eperson2, eperson3, eperson4);
|
||||
List<EPerson> searchJohnResults = ePersonService.searchNonMembers(context, "John",
|
||||
testGroup3, -1, -1);
|
||||
assertTrue(searchJohnResults.containsAll(allJohns));
|
||||
assertEquals(searchJohnResults.size(), ePersonService.searchNonMembersCount(context, "John",
|
||||
testGroup3));
|
||||
|
||||
// Verify searching "John" with Group 2 *excludes* the two who are already a member
|
||||
List<EPerson> allNonMemberJohns = Arrays.asList(eperson4);
|
||||
List<EPerson> searchNonMemberJohnResults = ePersonService.searchNonMembers(context, "John",
|
||||
testGroup2, -1, -1);
|
||||
assertTrue(searchNonMemberJohnResults.containsAll(allNonMemberJohns));
|
||||
assertFalse(searchNonMemberJohnResults.contains(eperson2));
|
||||
assertFalse(searchNonMemberJohnResults.contains(eperson3));
|
||||
assertEquals(searchNonMemberJohnResults.size(), ePersonService.searchNonMembersCount(context, "John",
|
||||
testGroup2));
|
||||
|
||||
// FINALLY, test search by email
|
||||
// Assert search on example.com excluding Group 1 returns just those not in that group
|
||||
List<EPerson> exampleNonMembers = Arrays.asList(eperson3, eperson4);
|
||||
List<EPerson> searchEmailResults = ePersonService.searchNonMembers(context, "example.com",
|
||||
testGroup1, -1, -1);
|
||||
assertTrue(searchEmailResults.containsAll(exampleNonMembers));
|
||||
assertFalse(searchEmailResults.contains(eperson1));
|
||||
assertFalse(searchEmailResults.contains(eperson2));
|
||||
assertEquals(searchEmailResults.size(), ePersonService.searchNonMembersCount(context, "example.com",
|
||||
testGroup1));
|
||||
|
||||
// Assert exact email search returns just one (if not in group)
|
||||
List<EPerson> exactEmailResults = ePersonService.searchNonMembers(context, "eperson1@example.com",
|
||||
testGroup2, -1, -1);
|
||||
assertTrue(exactEmailResults.contains(eperson1));
|
||||
assertEquals(exactEmailResults.size(), ePersonService.searchNonMembersCount(context, "eperson1@example.com",
|
||||
testGroup2));
|
||||
// But, change the group to one they are a member of, and they won't be included
|
||||
exactEmailResults = ePersonService.searchNonMembers(context, "eperson1@example.com",
|
||||
testGroup1, -1, -1);
|
||||
assertFalse(exactEmailResults.contains(eperson1));
|
||||
assertEquals(exactEmailResults.size(), ePersonService.searchNonMembersCount(context, "eperson1@example.com",
|
||||
testGroup1));
|
||||
|
||||
// Assert UUID search returns exact match (if not in group)
|
||||
List<EPerson> uuidResults = ePersonService.searchNonMembers(context, eperson3.getID().toString(),
|
||||
testGroup1, -1, -1);
|
||||
assertTrue(uuidResults.contains(eperson3));
|
||||
assertEquals(1, uuidResults.size());
|
||||
assertEquals(uuidResults.size(), ePersonService.searchNonMembersCount(context, eperson3.getID().toString(),
|
||||
testGroup1));
|
||||
// But, change the group to one they are a member of, and you'll get no results
|
||||
uuidResults = ePersonService.searchNonMembers(context, eperson3.getID().toString(),
|
||||
testGroup2, -1, -1);
|
||||
assertFalse(uuidResults.contains(eperson3));
|
||||
assertEquals(0, uuidResults.size());
|
||||
assertEquals(uuidResults.size(), ePersonService.searchNonMembersCount(context, eperson3.getID().toString(),
|
||||
testGroup2));
|
||||
|
||||
} finally {
|
||||
// Remove all Groups & EPersons we added for this test
|
||||
context.turnOffAuthorisationSystem();
|
||||
groupService.delete(context, testGroup1);
|
||||
groupService.delete(context, testGroup2);
|
||||
groupService.delete(context, testGroup3);
|
||||
for (EPerson ePerson : allEPeopleAdded) {
|
||||
ePersonService.delete(context, ePerson);
|
||||
}
|
||||
context.restoreAuthSystemState();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test of findAll method, of class EPerson.
|
||||
@@ -1149,6 +1273,17 @@ public class EPersonTest extends AbstractUnitTest {
|
||||
return ePerson;
|
||||
}
|
||||
|
||||
protected EPerson createEPersonAndAddToGroup(String email, String firstname, String lastname, Group group)
|
||||
throws SQLException, AuthorizeException {
|
||||
context.turnOffAuthorisationSystem();
|
||||
EPerson ePerson = createEPerson(email, firstname, lastname);
|
||||
groupService.addMember(context, group, ePerson);
|
||||
groupService.update(context, group);
|
||||
ePersonService.update(context, ePerson);
|
||||
context.restoreAuthSystemState();
|
||||
return ePerson;
|
||||
}
|
||||
|
||||
protected EPerson createEPerson(String email) throws SQLException, AuthorizeException {
|
||||
context.turnOffAuthorisationSystem();
|
||||
EPerson ePerson = ePersonService.create(context);
|
||||
@@ -1157,4 +1292,15 @@ public class EPersonTest extends AbstractUnitTest {
|
||||
context.restoreAuthSystemState();
|
||||
return ePerson;
|
||||
}
|
||||
protected EPerson createEPerson(String email, String firstname, String lastname)
|
||||
throws SQLException, AuthorizeException {
|
||||
context.turnOffAuthorisationSystem();
|
||||
EPerson ePerson = ePersonService.create(context);
|
||||
ePerson.setEmail(email);
|
||||
ePerson.setFirstName(context, firstname);
|
||||
ePerson.setLastName(context, lastname);
|
||||
ePersonService.update(context, ePerson);
|
||||
context.restoreAuthSystemState();
|
||||
return ePerson;
|
||||
}
|
||||
}
|
||||
|
@@ -680,6 +680,109 @@ public class GroupTest extends AbstractUnitTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
// Tests searchNonMembers() and searchNonMembersCount()
|
||||
// NOTE: This does not test pagination as that is tested in GroupRestRepositoryIT in server-webapp
|
||||
public void searchAndCountNonMembers() throws SQLException, AuthorizeException, IOException {
|
||||
// Create a parent group with 2 child groups
|
||||
Group parentGroup = createGroup("Some Parent Group");
|
||||
Group someStaffGroup = createGroup("Some Other Staff");
|
||||
Group someStudentsGroup = createGroup("Some Students");
|
||||
groupService.addMember(context, parentGroup, someStaffGroup);
|
||||
groupService.addMember(context, parentGroup, someStudentsGroup);
|
||||
groupService.update(context, parentGroup);
|
||||
|
||||
// Create a separate parent which is not a member of the first & add two child groups to it
|
||||
Group studentsNotInParentGroup = createGroup("Students not in Parent");
|
||||
Group otherStudentsNotInParentGroup = createGroup("Other Students");
|
||||
Group someOtherStudentsNotInParentGroup = createGroup("Some Other Students");
|
||||
groupService.addMember(context, studentsNotInParentGroup, otherStudentsNotInParentGroup);
|
||||
groupService.addMember(context, studentsNotInParentGroup, someOtherStudentsNotInParentGroup);
|
||||
groupService.update(context, studentsNotInParentGroup);
|
||||
|
||||
try {
|
||||
// Assert that all Groups *not* in parent group match an empty search
|
||||
List<Group> notInParent = Arrays.asList(studentsNotInParentGroup, otherStudentsNotInParentGroup,
|
||||
someOtherStudentsNotInParentGroup);
|
||||
List<Group> nonMembersSearch = groupService.searchNonMembers(context, "", parentGroup, -1, -1);
|
||||
// NOTE: Because others unit tests create groups, this search will return an undetermined number of results.
|
||||
// Therefore, we just verify that our expected groups are included and others are NOT included.
|
||||
assertTrue(nonMembersSearch.containsAll(notInParent));
|
||||
// Verify it does NOT contain members of parentGroup
|
||||
assertFalse(nonMembersSearch.contains(someStaffGroup));
|
||||
assertFalse(nonMembersSearch.contains(someStudentsGroup));
|
||||
// Verify it also does NOT contain the parentGroup itself
|
||||
assertFalse(nonMembersSearch.contains(parentGroup));
|
||||
// Verify the count for empty search matches the size of the search results
|
||||
assertEquals(nonMembersSearch.size(), groupService.searchNonMembersCount(context, "", parentGroup));
|
||||
|
||||
// Assert a search on "Students" matches all those same groups (as they all include that word in their name)
|
||||
nonMembersSearch = groupService.searchNonMembers(context, "Students", parentGroup, -1, -1);
|
||||
assertTrue(nonMembersSearch.containsAll(notInParent));
|
||||
//Verify an existing member group with "Students" in its name does NOT get returned
|
||||
assertFalse(nonMembersSearch.contains(someStudentsGroup));
|
||||
assertEquals(nonMembersSearch.size(),
|
||||
groupService.searchNonMembersCount(context, "Students", parentGroup));
|
||||
|
||||
|
||||
// Assert a search on "other" matches just two groups
|
||||
// (this also tests search is case insensitive)
|
||||
nonMembersSearch = groupService.searchNonMembers(context, "other", parentGroup, -1, -1);
|
||||
assertTrue(nonMembersSearch.containsAll(
|
||||
Arrays.asList(otherStudentsNotInParentGroup, someOtherStudentsNotInParentGroup)));
|
||||
// Verify an existing member group with "Other" in its name does NOT get returned
|
||||
assertFalse(nonMembersSearch.contains(someStaffGroup));
|
||||
assertEquals(nonMembersSearch.size(), groupService.searchNonMembersCount(context, "other", parentGroup));
|
||||
|
||||
// Assert a search on "Parent" matches just one group
|
||||
nonMembersSearch = groupService.searchNonMembers(context, "Parent", parentGroup, -1, -1);
|
||||
assertTrue(nonMembersSearch.contains(studentsNotInParentGroup));
|
||||
// Verify Parent Group itself does NOT get returned
|
||||
assertFalse(nonMembersSearch.contains(parentGroup));
|
||||
assertEquals(nonMembersSearch.size(), groupService.searchNonMembersCount(context, "Parent", parentGroup));
|
||||
|
||||
// Assert a UUID search matching a non-member group will return just that one group
|
||||
nonMembersSearch = groupService.searchNonMembers(context,
|
||||
someOtherStudentsNotInParentGroup.getID().toString(),
|
||||
parentGroup, -1, -1);
|
||||
assertEquals(1, nonMembersSearch.size());
|
||||
assertTrue(nonMembersSearch.contains(someOtherStudentsNotInParentGroup));
|
||||
assertEquals(nonMembersSearch.size(),
|
||||
groupService.searchNonMembersCount(context,
|
||||
someOtherStudentsNotInParentGroup.getID().toString(),
|
||||
parentGroup));
|
||||
|
||||
// Assert a UUID search matching an EXISTING member will return NOTHING
|
||||
// (as this group is excluded from the search)
|
||||
nonMembersSearch = groupService.searchNonMembers(context, someStudentsGroup.getID().toString(),
|
||||
parentGroup,-1, -1);
|
||||
assertEquals(0, nonMembersSearch.size());
|
||||
assertEquals(nonMembersSearch.size(),
|
||||
groupService.searchNonMembersCount(context, someStudentsGroup.getID().toString(),
|
||||
parentGroup));
|
||||
|
||||
// Assert a UUID search matching Parent Group *itself* will return NOTHING
|
||||
// (as this group is excluded from the search)
|
||||
nonMembersSearch = groupService.searchNonMembers(context, parentGroup.getID().toString(),
|
||||
parentGroup,-1, -1);
|
||||
assertEquals(0, nonMembersSearch.size());
|
||||
assertEquals(nonMembersSearch.size(),
|
||||
groupService.searchNonMembersCount(context, parentGroup.getID().toString(),
|
||||
parentGroup));
|
||||
} finally {
|
||||
// Clean up our data
|
||||
context.turnOffAuthorisationSystem();
|
||||
groupService.delete(context, parentGroup);
|
||||
groupService.delete(context, someStaffGroup);
|
||||
groupService.delete(context, someStudentsGroup);
|
||||
groupService.delete(context, studentsNotInParentGroup);
|
||||
groupService.delete(context, otherStudentsNotInParentGroup);
|
||||
groupService.delete(context, someOtherStudentsNotInParentGroup);
|
||||
context.restoreAuthSystemState();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected Group createGroup(String name) throws SQLException, AuthorizeException {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
@@ -38,9 +38,11 @@ import org.dspace.authorize.service.ValidatePasswordService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.EmptyWorkflowGroupException;
|
||||
import org.dspace.eperson.Group;
|
||||
import org.dspace.eperson.RegistrationData;
|
||||
import org.dspace.eperson.service.AccountService;
|
||||
import org.dspace.eperson.service.EPersonService;
|
||||
import org.dspace.eperson.service.GroupService;
|
||||
import org.dspace.eperson.service.RegistrationDataService;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -79,6 +81,9 @@ public class EPersonRestRepository extends DSpaceObjectRestRepository<EPerson, E
|
||||
@Autowired
|
||||
private RegistrationDataService registrationDataService;
|
||||
|
||||
@Autowired
|
||||
private GroupService groupService;
|
||||
|
||||
private final EPersonService es;
|
||||
|
||||
|
||||
@@ -289,6 +294,35 @@ public class EPersonRestRepository extends DSpaceObjectRestRepository<EPerson, E
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the EPersons matching the query parameter which are NOT a member of the given Group.
|
||||
* The search is delegated to the
|
||||
* {@link EPersonService#searchNonMembers(Context, String, Group, int, int)} method
|
||||
*
|
||||
* @param groupUUID the *required* group UUID to exclude results from
|
||||
* @param query is the *required* query string
|
||||
* @param pageable contains the pagination information
|
||||
* @return a Page of EPersonRest instances matching the user query
|
||||
*/
|
||||
@PreAuthorize("hasAuthority('ADMIN') || hasAuthority('MANAGE_ACCESS_GROUP')")
|
||||
@SearchRestMethod(name = "isNotMemberOf")
|
||||
public Page<EPersonRest> findIsNotMemberOf(@Parameter(value = "group", required = true) UUID groupUUID,
|
||||
@Parameter(value = "query", required = true) String query,
|
||||
Pageable pageable) {
|
||||
|
||||
try {
|
||||
Context context = obtainContext();
|
||||
Group excludeGroup = groupService.find(context, groupUUID);
|
||||
long total = es.searchNonMembersCount(context, query, excludeGroup);
|
||||
List<EPerson> epersons = es.searchNonMembers(context, query, excludeGroup,
|
||||
Math.toIntExact(pageable.getOffset()),
|
||||
Math.toIntExact(pageable.getPageSize()));
|
||||
return converter.toRestPage(epersons, pageable, total, utils.obtainProjection());
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasPermission(#uuid, 'EPERSON', #patch)")
|
||||
protected void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID uuid,
|
||||
|
@@ -148,6 +148,35 @@ public class GroupRestRepository extends DSpaceObjectRestRepository<Group, Group
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the Groups matching the query parameter which are NOT a member of the given parent Group.
|
||||
* The search is delegated to the
|
||||
* {@link GroupService#searchNonMembers(Context, String, Group, int, int)} method
|
||||
*
|
||||
* @param groupUUID the parent group UUID
|
||||
* @param query is the *required* query string
|
||||
* @param pageable contains the pagination information
|
||||
* @return a Page of GroupRest instances matching the user query
|
||||
*/
|
||||
@PreAuthorize("hasAuthority('ADMIN') || hasAuthority('MANAGE_ACCESS_GROUP')")
|
||||
@SearchRestMethod(name = "isNotMemberOf")
|
||||
public Page<GroupRest> findIsNotMemberOf(@Parameter(value = "group", required = true) UUID groupUUID,
|
||||
@Parameter(value = "query", required = true) String query,
|
||||
Pageable pageable) {
|
||||
|
||||
try {
|
||||
Context context = obtainContext();
|
||||
Group excludeParentGroup = gs.find(context, groupUUID);
|
||||
long total = gs.searchNonMembersCount(context, query, excludeParentGroup);
|
||||
List<Group> groups = gs.searchNonMembers(context, query, excludeParentGroup,
|
||||
Math.toIntExact(pageable.getOffset()),
|
||||
Math.toIntExact(pageable.getPageSize()));
|
||||
return converter.toRestPage(groups, pageable, total, utils.obtainProjection());
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<GroupRest> getDomainClass() {
|
||||
return GroupRest.class;
|
||||
|
@@ -79,6 +79,7 @@ import org.dspace.eperson.Group;
|
||||
import org.dspace.eperson.PasswordHash;
|
||||
import org.dspace.eperson.service.AccountService;
|
||||
import org.dspace.eperson.service.EPersonService;
|
||||
import org.dspace.eperson.service.GroupService;
|
||||
import org.dspace.eperson.service.RegistrationDataService;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.hamcrest.Matchers;
|
||||
@@ -96,6 +97,9 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||
@Autowired
|
||||
private EPersonService ePersonService;
|
||||
|
||||
@Autowired
|
||||
private GroupService groupService;
|
||||
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
||||
|
||||
@@ -775,6 +779,242 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
// Test of /epersons/search/isNotMemberOf pagination
|
||||
// NOTE: Additional tests of 'isNotMemberOf' search functionality can be found in EPersonTest in 'dspace-api'
|
||||
@Test
|
||||
public void searchIsNotMemberOfPaginationTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
Group group = GroupBuilder.createGroup(context)
|
||||
.withName("Test Parent group")
|
||||
.build();
|
||||
// Create two EPerson in main group. These SHOULD NOT be included in pagination
|
||||
EPersonBuilder.createEPerson(context)
|
||||
.withNameInMetadata("Test", "Person")
|
||||
.withEmail("test@example.com")
|
||||
.withGroupMembership(group)
|
||||
.build();
|
||||
EPersonBuilder.createEPerson(context)
|
||||
.withNameInMetadata("Test2", "Person")
|
||||
.withEmail("test2@example.com")
|
||||
.withGroupMembership(group)
|
||||
.build();
|
||||
|
||||
// Create five EPersons who are NOT members of that group. These SHOULD be included in pagination
|
||||
EPersonBuilder.createEPerson(context)
|
||||
.withNameInMetadata("Test3", "Person")
|
||||
.withEmail("test3@example.com")
|
||||
.build();
|
||||
EPersonBuilder.createEPerson(context)
|
||||
.withNameInMetadata("Test4", "Person")
|
||||
.withEmail("test4@example.com")
|
||||
.build();
|
||||
EPersonBuilder.createEPerson(context)
|
||||
.withNameInMetadata("Test5", "Person")
|
||||
.withEmail("test5@example.com")
|
||||
.build();
|
||||
EPersonBuilder.createEPerson(context)
|
||||
.withNameInMetadata("Test6", "Person")
|
||||
.withEmail("test6@example.com")
|
||||
.build();
|
||||
EPersonBuilder.createEPerson(context)
|
||||
.withNameInMetadata("Test7", "Person")
|
||||
.withEmail("test7@example.com")
|
||||
.build();
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String authTokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||
getClient(authTokenAdmin).perform(get("/api/eperson/epersons/search/isNotMemberOf")
|
||||
.param("group", group.getID().toString())
|
||||
.param("query", "person")
|
||||
.param("page", "0")
|
||||
.param("size", "2"))
|
||||
.andExpect(status().isOk()).andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.epersons", Matchers.everyItem(
|
||||
hasJsonPath("$.type", is("eperson")))
|
||||
))
|
||||
.andExpect(jsonPath("$._embedded.epersons").value(Matchers.hasSize(2)))
|
||||
.andExpect(jsonPath("$.page.size", is(2)))
|
||||
.andExpect(jsonPath("$.page.number", is(0)))
|
||||
.andExpect(jsonPath("$.page.totalPages", is(3)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(5)));
|
||||
|
||||
getClient(authTokenAdmin).perform(get("/api/eperson/epersons/search/isNotMemberOf")
|
||||
.param("group", group.getID().toString())
|
||||
.param("query", "person")
|
||||
.param("page", "1")
|
||||
.param("size", "2"))
|
||||
.andExpect(status().isOk()).andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.epersons", Matchers.everyItem(
|
||||
hasJsonPath("$.type", is("eperson")))
|
||||
))
|
||||
.andExpect(jsonPath("$._embedded.epersons").value(Matchers.hasSize(2)))
|
||||
.andExpect(jsonPath("$.page.size", is(2)))
|
||||
.andExpect(jsonPath("$.page.number", is(1)))
|
||||
.andExpect(jsonPath("$.page.totalPages", is(3)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(5)));
|
||||
|
||||
getClient(authTokenAdmin).perform(get("/api/eperson/epersons/search/isNotMemberOf")
|
||||
.param("group", group.getID().toString())
|
||||
.param("query", "person")
|
||||
.param("page", "2")
|
||||
.param("size", "2"))
|
||||
.andExpect(status().isOk()).andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.epersons", Matchers.everyItem(
|
||||
hasJsonPath("$.type", is("eperson")))
|
||||
))
|
||||
.andExpect(jsonPath("$._embedded.epersons").value(Matchers.hasSize(1)))
|
||||
.andExpect(jsonPath("$.page.size", is(2)))
|
||||
.andExpect(jsonPath("$.page.number", is(2)))
|
||||
.andExpect(jsonPath("$.page.totalPages", is(3)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(5)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchIsNotMemberOfByEmail() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
Group group = GroupBuilder.createGroup(context)
|
||||
.withName("Test group")
|
||||
.build();
|
||||
Group group2 = GroupBuilder.createGroup(context)
|
||||
.withName("Test another group")
|
||||
.build();
|
||||
EPerson ePerson = EPersonBuilder.createEPerson(context)
|
||||
.withNameInMetadata("John", "Doe")
|
||||
.withEmail("Johndoe@example.com")
|
||||
.withGroupMembership(group)
|
||||
.build();
|
||||
|
||||
EPerson ePerson2 = EPersonBuilder.createEPerson(context)
|
||||
.withNameInMetadata("Jane", "Smith")
|
||||
.withEmail("janesmith@example.com")
|
||||
.build();
|
||||
|
||||
EPerson ePerson3 = EPersonBuilder.createEPerson(context)
|
||||
.withNameInMetadata("Tom", "Doe")
|
||||
.withEmail("tomdoe@example.com")
|
||||
.build();
|
||||
|
||||
EPerson ePerson4 = EPersonBuilder.createEPerson(context)
|
||||
.withNameInMetadata("Harry", "Prefix-Doe")
|
||||
.withEmail("harrydoeprefix@example.com")
|
||||
.build();
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String authToken = getAuthToken(admin.getEmail(), password);
|
||||
// Search for exact email in a group the person already belongs to. Should return zero results.
|
||||
getClient(authToken).perform(get("/api/eperson/epersons/search/isNotMemberOf")
|
||||
.param("query", ePerson.getEmail())
|
||||
.param("group", group.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
|
||||
// Search for exact email in a group the person does NOT belong to. Should return the person
|
||||
getClient(authToken).perform(get("/api/eperson/epersons/search/isNotMemberOf")
|
||||
.param("query", ePerson.getEmail())
|
||||
.param("group", group2.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.epersons", Matchers.contains(
|
||||
EPersonMatcher.matchEPersonEntry(ePerson)
|
||||
)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(1)));
|
||||
|
||||
// Search partial email should return all the people created above.
|
||||
getClient(authToken).perform(get("/api/eperson/epersons/search/isNotMemberOf")
|
||||
.param("query", "example.com")
|
||||
.param("group", group2.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.epersons", Matchers.containsInAnyOrder(
|
||||
EPersonMatcher.matchEPersonEntry(ePerson),
|
||||
EPersonMatcher.matchEPersonEntry(ePerson2),
|
||||
EPersonMatcher.matchEPersonEntry(ePerson3),
|
||||
EPersonMatcher.matchEPersonEntry(ePerson4)
|
||||
)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchIsNotMemberOfByUUID() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
Group group = GroupBuilder.createGroup(context)
|
||||
.withName("Test group")
|
||||
.build();
|
||||
Group group2 = GroupBuilder.createGroup(context)
|
||||
.withName("Test another group")
|
||||
.build();
|
||||
EPerson ePerson = EPersonBuilder.createEPerson(context)
|
||||
.withNameInMetadata("John", "Doe")
|
||||
.withEmail("Johndoe@example.com")
|
||||
.withGroupMembership(group)
|
||||
.build();
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String authToken = getAuthToken(admin.getEmail(), password);
|
||||
// Search for UUID in a group the person already belongs to. Should return zero results.
|
||||
getClient(authToken).perform(get("/api/eperson/epersons/search/isNotMemberOf")
|
||||
.param("query", ePerson.getID().toString())
|
||||
.param("group", group.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
|
||||
// Search for exact email in a group the person does NOT belong to. Should return the person
|
||||
getClient(authToken).perform(get("/api/eperson/epersons/search/isNotMemberOf")
|
||||
.param("query", ePerson.getID().toString())
|
||||
.param("group", group2.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.epersons", Matchers.contains(
|
||||
EPersonMatcher.matchEPersonEntry(ePerson)
|
||||
)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchIsNotMemberOfUnauthorized() throws Exception {
|
||||
Group adminGroup = groupService.findByName(context, Group.ADMIN);
|
||||
getClient().perform(get("/api/eperson/epersons/search/isNotMemberOf")
|
||||
.param("query", eperson.getID().toString())
|
||||
.param("group", adminGroup.getID().toString()))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchIsNotMemberOfForbidden() throws Exception {
|
||||
Group adminGroup = groupService.findByName(context, Group.ADMIN);
|
||||
String authToken = getAuthToken(eperson.getEmail(), password);
|
||||
getClient(authToken).perform(get("/api/eperson/epersons/search/isNotMemberOf")
|
||||
.param("query", eperson.getID().toString())
|
||||
.param("group", adminGroup.getID().toString()))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchIsNotMemberOfMissingOrInvalidParameter() throws Exception {
|
||||
Group adminGroup = groupService.findByName(context, Group.ADMIN);
|
||||
String authToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(authToken).perform(get("/api/eperson/epersons/search/isNotMemberOf"))
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
getClient(authToken).perform(get("/api/eperson/epersons/search/isNotMemberOf")
|
||||
.param("query", eperson.getID().toString()))
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
getClient(authToken).perform(get("/api/eperson/epersons/search/isNotMemberOf")
|
||||
.param("group", adminGroup.getID().toString()))
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
// Test invalid group UUID
|
||||
getClient(authToken).perform(get("/api/eperson/epersons/search/isNotMemberOf")
|
||||
.param("query", eperson.getID().toString())
|
||||
.param("group", "not-a-uuid"))
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteOne() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
@@ -3242,6 +3242,192 @@ public class GroupRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||
.andExpect(jsonPath("$.page.totalElements", is(5)));
|
||||
}
|
||||
|
||||
// Test of /groups/search/isNotMemberOf pagination
|
||||
// NOTE: Additional tests of 'isNotMemberOf' search functionality can be found in GroupTest in 'dspace-api'
|
||||
@Test
|
||||
public void searchIsNotMemberOfPaginationTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
Group group = GroupBuilder.createGroup(context)
|
||||
.withName("Test Parent group")
|
||||
.build();
|
||||
// Create two subgroups of main group. These SHOULD NOT be included in pagination
|
||||
GroupBuilder.createGroup(context)
|
||||
.withParent(group)
|
||||
.withName("Test group 1")
|
||||
.build();
|
||||
GroupBuilder.createGroup(context)
|
||||
.withParent(group)
|
||||
.withName("Test group 2")
|
||||
.build();
|
||||
|
||||
// Create five non-member groups. These SHOULD be included in pagination
|
||||
GroupBuilder.createGroup(context)
|
||||
.withName("Test group 3")
|
||||
.build();
|
||||
GroupBuilder.createGroup(context)
|
||||
.withName("Test group 4")
|
||||
.build();
|
||||
GroupBuilder.createGroup(context)
|
||||
.withName("Test group 5")
|
||||
.build();
|
||||
GroupBuilder.createGroup(context)
|
||||
.withName("Test group 6")
|
||||
.build();
|
||||
GroupBuilder.createGroup(context)
|
||||
.withName("Test group 7")
|
||||
.build();
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String authTokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||
getClient(authTokenAdmin).perform(get("/api/eperson/groups/search/isNotMemberOf")
|
||||
.param("group", group.getID().toString())
|
||||
.param("query", "test group")
|
||||
.param("page", "0")
|
||||
.param("size", "2"))
|
||||
.andExpect(status().isOk()).andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.groups", Matchers.everyItem(
|
||||
hasJsonPath("$.type", is("group")))
|
||||
))
|
||||
.andExpect(jsonPath("$._embedded.groups").value(Matchers.hasSize(2)))
|
||||
.andExpect(jsonPath("$.page.size", is(2)))
|
||||
.andExpect(jsonPath("$.page.number", is(0)))
|
||||
.andExpect(jsonPath("$.page.totalPages", is(3)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(5)));
|
||||
|
||||
getClient(authTokenAdmin).perform(get("/api/eperson/groups/search/isNotMemberOf")
|
||||
.param("group", group.getID().toString())
|
||||
.param("query", "test group")
|
||||
.param("page", "1")
|
||||
.param("size", "2"))
|
||||
.andExpect(status().isOk()).andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.groups", Matchers.everyItem(
|
||||
hasJsonPath("$.type", is("group")))
|
||||
))
|
||||
.andExpect(jsonPath("$._embedded.groups").value(Matchers.hasSize(2)))
|
||||
.andExpect(jsonPath("$.page.size", is(2)))
|
||||
.andExpect(jsonPath("$.page.number", is(1)))
|
||||
.andExpect(jsonPath("$.page.totalPages", is(3)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(5)));
|
||||
|
||||
getClient(authTokenAdmin).perform(get("/api/eperson/groups/search/isNotMemberOf")
|
||||
.param("group", group.getID().toString())
|
||||
.param("query", "test group")
|
||||
.param("page", "2")
|
||||
.param("size", "2"))
|
||||
.andExpect(status().isOk()).andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.groups", Matchers.everyItem(
|
||||
hasJsonPath("$.type", is("group")))
|
||||
))
|
||||
.andExpect(jsonPath("$._embedded.groups").value(Matchers.hasSize(1)))
|
||||
.andExpect(jsonPath("$.page.size", is(2)))
|
||||
.andExpect(jsonPath("$.page.number", is(2)))
|
||||
.andExpect(jsonPath("$.page.totalPages", is(3)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(5)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchIsNotMemberOfByUUID() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
// Create two groups which have no parent group
|
||||
Group group1 = GroupBuilder.createGroup(context)
|
||||
.withName("Test Parent group 1")
|
||||
.build();
|
||||
|
||||
Group group2 = GroupBuilder.createGroup(context)
|
||||
.withName("Test Parent group 2")
|
||||
.build();
|
||||
|
||||
// Create a subgroup of parent group 1
|
||||
Group group3 = GroupBuilder.createGroup(context)
|
||||
.withParent(group1)
|
||||
.withName("Test subgroup")
|
||||
.build();
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String authTokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||
// Search for UUID in a group that the subgroup already belongs to. Should return ZERO results.
|
||||
getClient(authTokenAdmin).perform(get("/api/eperson/groups/search/isNotMemberOf")
|
||||
.param("group", group1.getID().toString())
|
||||
.param("query", group3.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
|
||||
// Search for UUID in a group that the subgroup does NOT belong to. Should return group via exact match
|
||||
getClient(authTokenAdmin).perform(get("/api/eperson/groups/search/isNotMemberOf")
|
||||
.param("group", group2.getID().toString())
|
||||
.param("query", group3.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.groups", Matchers.contains(
|
||||
GroupMatcher.matchGroupEntry(group3.getID(), group3.getName())
|
||||
)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(1)));
|
||||
|
||||
// Search for UUID of the group in the "group" param. Should return ZERO results, as "group" param is excluded
|
||||
getClient(authTokenAdmin).perform(get("/api/eperson/groups/search/isNotMemberOf")
|
||||
.param("group", group1.getID().toString())
|
||||
.param("query", group1.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchIsNotMemberOfUnauthorized() throws Exception {
|
||||
// To avoid creating data, just use the Admin & Anon groups for this test
|
||||
GroupService groupService = EPersonServiceFactory.getInstance().getGroupService();
|
||||
Group adminGroup = groupService.findByName(context, Group.ADMIN);
|
||||
Group anonGroup = groupService.findByName(context, Group.ANONYMOUS);
|
||||
|
||||
getClient().perform(get("/api/eperson/groups/search/isNotMemberOf")
|
||||
.param("query", anonGroup.getID().toString())
|
||||
.param("group", adminGroup.getID().toString()))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchIsNotMemberOfForbidden() throws Exception {
|
||||
// To avoid creating data, just use the Admin & Anon groups for this test
|
||||
GroupService groupService = EPersonServiceFactory.getInstance().getGroupService();
|
||||
Group adminGroup = groupService.findByName(context, Group.ADMIN);
|
||||
Group anonGroup = groupService.findByName(context, Group.ANONYMOUS);
|
||||
|
||||
String authToken = getAuthToken(eperson.getEmail(), password);
|
||||
getClient(authToken).perform(get("/api/eperson/groups/search/isNotMemberOf")
|
||||
.param("query", anonGroup.getID().toString())
|
||||
.param("group", adminGroup.getID().toString()))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchIsNotMemberOfMissingOrInvalidParameter() throws Exception {
|
||||
// To avoid creating data, just use the Admin & Anon groups for this test
|
||||
GroupService groupService = EPersonServiceFactory.getInstance().getGroupService();
|
||||
Group adminGroup = groupService.findByName(context, Group.ADMIN);
|
||||
Group anonGroup = groupService.findByName(context, Group.ANONYMOUS);
|
||||
|
||||
String authToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(authToken).perform(get("/api/eperson/groups/search/isNotMemberOf"))
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
getClient(authToken).perform(get("/api/eperson/groups/search/isNotMemberOf")
|
||||
.param("query", anonGroup.getID().toString()))
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
getClient(authToken).perform(get("/api/eperson/groups/search/isNotMemberOf")
|
||||
.param("group", adminGroup.getID().toString()))
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
// Test invalid group UUID
|
||||
getClient(authToken).perform(get("/api/eperson/groups/search/isNotMemberOf")
|
||||
.param("query", anonGroup.getID().toString())
|
||||
.param("group", "not-a-uuid"))
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void commAdminAndColAdminCannotExploitItemReadGroupTest() throws Exception {
|
||||
|
||||
|
Reference in New Issue
Block a user