Merge branch 'main' into CST-7754

This commit is contained in:
corrado lombardi
2023-02-03 18:49:57 +01:00
21 changed files with 1789 additions and 63 deletions

View File

@@ -806,10 +806,11 @@
<scope>test</scope>
</dependency>
<dependency>
<dependency>
<groupId>org.apache.bcel</groupId>
<artifactId>bcel</artifactId>
<version>6.4.0</version>
<version>6.6.0</version>
<scope>test</scope>
</dependency>
<!-- required for openaire api integration -->

View File

@@ -8,8 +8,8 @@
package org.dspace.browse;
import java.util.List;
import java.util.UUID;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
/**
@@ -140,21 +140,21 @@ public interface BrowseDAO {
public void setAscending(boolean ascending);
/**
* Get the database ID of the container object. The container object will be a
* Get the container object. The container object will be a
* Community or a Collection.
*
* @return the database id of the container, or -1 if none is set
* @return the container, or null if none is set
*/
public UUID getContainerID();
public DSpaceObject getContainer();
/**
* Set the database id of the container object. This should be the id of a
* Community or Collection. This will constrain the results of the browse
* to only items or values within items that appear in the given container.
* Set the container object. This should be a Community or Collection.
* This will constrain the results of the browse to only items or values within items that appear in the given
* container and add the related configuration default filters.
*
* @param containerID community/collection internal ID (UUID)
* @param container community/collection
*/
public void setContainerID(UUID containerID);
public void setContainer(DSpaceObject container);
/**
* get the name of the field in which to look for the container id. This is

View File

@@ -141,12 +141,12 @@ public class BrowseEngine {
Collection col = (Collection) scope.getBrowseContainer();
dao.setContainerTable("collection2item");
dao.setContainerIDField("collection_id");
dao.setContainerID(col.getID());
dao.setContainer(col);
} else if (scope.inCommunity()) {
Community com = (Community) scope.getBrowseContainer();
dao.setContainerTable("communities2item");
dao.setContainerIDField("community_id");
dao.setContainerID(com.getID());
dao.setContainer(com);
}
}
@@ -247,12 +247,12 @@ public class BrowseEngine {
Collection col = (Collection) scope.getBrowseContainer();
dao.setContainerTable("collection2item");
dao.setContainerIDField("collection_id");
dao.setContainerID(col.getID());
dao.setContainer(col);
} else if (scope.inCommunity()) {
Community com = (Community) scope.getBrowseContainer();
dao.setContainerTable("communities2item");
dao.setContainerIDField("community_id");
dao.setContainerID(com.getID());
dao.setContainer(com);
}
}
@@ -413,12 +413,12 @@ public class BrowseEngine {
Collection col = (Collection) scope.getBrowseContainer();
dao.setContainerTable("collection2item");
dao.setContainerIDField("collection_id");
dao.setContainerID(col.getID());
dao.setContainer(col);
} else if (scope.inCommunity()) {
Community com = (Community) scope.getBrowseContainer();
dao.setContainerTable("communities2item");
dao.setContainerIDField("community_id");
dao.setContainerID(com.getID());
dao.setContainer(com);
}
}

View File

@@ -13,13 +13,13 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.apache.solr.client.solrj.util.ClientUtils;
import org.dspace.authorize.factory.AuthorizeServiceFactory;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.discovery.DiscoverFacetField;
@@ -31,6 +31,8 @@ import org.dspace.discovery.DiscoverResult.SearchDocument;
import org.dspace.discovery.IndexableObject;
import org.dspace.discovery.SearchService;
import org.dspace.discovery.SearchServiceException;
import org.dspace.discovery.SearchUtils;
import org.dspace.discovery.configuration.DiscoveryConfiguration;
import org.dspace.discovery.configuration.DiscoveryConfigurationParameters;
import org.dspace.discovery.indexobject.IndexableItem;
import org.dspace.services.factory.DSpaceServicesFactory;
@@ -124,9 +126,9 @@ public class SolrBrowseDAO implements BrowseDAO {
private String containerIDField = null;
/**
* the database id of the container we are constraining to
* the container we are constraining to
*/
private UUID containerID = null;
private DSpaceObject container = null;
/**
* the column that we are sorting results by
@@ -176,6 +178,7 @@ public class SolrBrowseDAO implements BrowseDAO {
if (sResponse == null) {
DiscoverQuery query = new DiscoverQuery();
addLocationScopeFilter(query);
addDefaultFilterQueries(query);
addStatusFilter(query);
if (distinct) {
DiscoverFacetField dff;
@@ -240,15 +243,20 @@ public class SolrBrowseDAO implements BrowseDAO {
}
private void addLocationScopeFilter(DiscoverQuery query) {
if (containerID != null) {
if (container != null) {
if (containerIDField.startsWith("collection")) {
query.addFilterQueries("location.coll:" + containerID);
query.addFilterQueries("location.coll:" + container.getID());
} else if (containerIDField.startsWith("community")) {
query.addFilterQueries("location.comm:" + containerID);
query.addFilterQueries("location.comm:" + container.getID());
}
}
}
private void addDefaultFilterQueries(DiscoverQuery query) {
DiscoveryConfiguration discoveryConfiguration = SearchUtils.getDiscoveryConfiguration(container);
discoveryConfiguration.getDefaultFilterQueries().forEach(query::addFilterQueries);
}
@Override
public int doCountQuery() throws BrowseException {
DiscoverResult resp = getSolrResponse();
@@ -337,6 +345,7 @@ public class SolrBrowseDAO implements BrowseDAO {
throws BrowseException {
DiscoverQuery query = new DiscoverQuery();
addLocationScopeFilter(query);
addDefaultFilterQueries(query);
addStatusFilter(query);
query.setMaxResults(0);
query.addFilterQueries("search.resourcetype:" + IndexableItem.TYPE);
@@ -398,8 +407,8 @@ public class SolrBrowseDAO implements BrowseDAO {
* @see org.dspace.browse.BrowseDAO#getContainerID()
*/
@Override
public UUID getContainerID() {
return containerID;
public DSpaceObject getContainer() {
return container;
}
/*
@@ -561,8 +570,8 @@ public class SolrBrowseDAO implements BrowseDAO {
* @see org.dspace.browse.BrowseDAO#setContainerID(int)
*/
@Override
public void setContainerID(UUID containerID) {
this.containerID = containerID;
public void setContainer(DSpaceObject container) {
this.container = container;
}

View File

@@ -51,8 +51,14 @@ import org.dspace.content.virtual.VirtualMetadataPopulator;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogHelper;
import org.dspace.discovery.DiscoverQuery;
import org.dspace.discovery.DiscoverResult;
import org.dspace.discovery.SearchService;
import org.dspace.discovery.SearchServiceException;
import org.dspace.discovery.indexobject.IndexableItem;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.eperson.service.GroupService;
import org.dspace.event.Event;
import org.dspace.harvest.HarvestedItem;
import org.dspace.harvest.service.HarvestedItemService;
@@ -93,6 +99,8 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
@Autowired(required = true)
protected CommunityService communityService;
@Autowired(required = true)
protected GroupService groupService;
@Autowired(required = true)
protected AuthorizeService authorizeService;
@Autowired(required = true)
protected BundleService bundleService;
@@ -105,6 +113,8 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
@Autowired(required = true)
protected InstallItemService installItemService;
@Autowired(required = true)
protected SearchService searchService;
@Autowired(required = true)
protected ResourcePolicyService resourcePolicyService;
@Autowired(required = true)
protected CollectionService collectionService;
@@ -1065,6 +1075,53 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
return collectionService.canEditBoolean(context, item.getOwningCollection(), false);
}
/**
* Finds all Indexed Items where the current user has edit rights. If the user is an Admin,
* this is all Indexed Items. Otherwise, it includes those Items where
* an indexed "edit" policy lists either the eperson or one of the eperson's groups
*
* @param context DSpace context
* @param discoverQuery
* @return discovery search result objects
* @throws SQLException if something goes wrong
* @throws SearchServiceException if search error
*/
private DiscoverResult retrieveItemsWithEdit(Context context, DiscoverQuery discoverQuery)
throws SQLException, SearchServiceException {
EPerson currentUser = context.getCurrentUser();
if (!authorizeService.isAdmin(context)) {
String userId = currentUser != null ? "e" + currentUser.getID().toString() : "e";
Stream<String> groupIds = groupService.allMemberGroupsSet(context, currentUser).stream()
.map(group -> "g" + group.getID());
String query = Stream.concat(Stream.of(userId), groupIds)
.collect(Collectors.joining(" OR ", "edit:(", ")"));
discoverQuery.addFilterQueries(query);
}
return searchService.search(context, discoverQuery);
}
@Override
public List<Item> findItemsWithEdit(Context context, int offset, int limit)
throws SQLException, SearchServiceException {
DiscoverQuery discoverQuery = new DiscoverQuery();
discoverQuery.setDSpaceObjectFilter(IndexableItem.TYPE);
discoverQuery.setStart(offset);
discoverQuery.setMaxResults(limit);
DiscoverResult resp = retrieveItemsWithEdit(context, discoverQuery);
return resp.getIndexableObjects().stream()
.map(solrItems -> ((IndexableItem) solrItems).getIndexedObject())
.collect(Collectors.toList());
}
@Override
public int countItemsWithEdit(Context context) throws SQLException, SearchServiceException {
DiscoverQuery discoverQuery = new DiscoverQuery();
discoverQuery.setMaxResults(0);
discoverQuery.setDSpaceObjectFilter(IndexableItem.TYPE);
DiscoverResult resp = retrieveItemsWithEdit(context, discoverQuery);
return (int) resp.getTotalSearchResults();
}
/**
* Check if the item is an inprogress submission
*

View File

@@ -28,6 +28,7 @@ import org.dspace.content.MetadataValue;
import org.dspace.content.Thumbnail;
import org.dspace.content.WorkspaceItem;
import org.dspace.core.Context;
import org.dspace.discovery.SearchServiceException;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
@@ -768,6 +769,27 @@ public interface ItemService
*/
int countWithdrawnItems(Context context) throws SQLException;
/**
* finds all items for which the current user has editing rights
* @param context DSpace context object
* @param offset page offset
* @param limit page size limit
* @return list of items for which the current user has editing rights
* @throws SQLException
* @throws SearchServiceException
*/
public List<Item> findItemsWithEdit(Context context, int offset, int limit)
throws SQLException, SearchServiceException;
/**
* counts all items for which the current user has editing rights
* @param context DSpace context object
* @return list of items for which the current user has editing rights
* @throws SQLException
* @throws SearchServiceException
*/
public int countItemsWithEdit(Context context) throws SQLException, SearchServiceException;
/**
* Check if the supplied item is an inprogress submission
*

View File

@@ -0,0 +1,119 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.discovery;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.DSpaceObject;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.core.Context;
/**
* Util methods used by indexing.
*
* @author Koen Pauwels (koen.pauwels at atmire dot com)
*/
public class IndexingUtils {
private IndexingUtils() {
}
/**
* Retrieve all ancestor communities of a given community, with the first one being the given community and the
* last one being the root.
* <p>
*
* @param context DSpace context object
* @param community Community for which we search the ancestors
* @return A list of ancestor communities.
* @throws SQLException if database error
*/
static List<Community> getAncestorCommunities(Context context, Community community) throws SQLException {
ArrayList<Community> communities = new ArrayList<>();
while (community != null) {
communities.add(community);
community = (Community) ContentServiceFactory.getInstance().getDSpaceObjectService(community)
.getParentObject(context, community);
}
return communities;
}
/**
* Retrieve the ids of all groups that have ADMIN rights to the given community, either directly
* (through direct resource policy) or indirectly (through a policy on an ancestor community).
*
* @param context DSpace context object
* @param community Community for which we search the admin group IDs
* @return A list of admin group IDs
* @throws SQLException if database error
*/
static List<UUID> findTransitiveAdminGroupIds(Context context, Community community) throws SQLException {
return getAncestorCommunities(context, community).stream()
.filter(parent -> parent.getAdministrators() != null)
.map(parent -> parent.getAdministrators().getID())
.collect(Collectors.toList());
}
/**
* Retrieve the ids of all groups that have ADMIN rights to the given collection, either directly
* (through direct resource policy) or indirectly (through a policy on its community, or one of
* its ancestor communities).
*
* @param context DSpace context object
* @param collection Collection for which we search the admin group IDs
* @return A list of admin group IDs
* @throws SQLException if database error
*/
static List<UUID> findTransitiveAdminGroupIds(Context context, Collection collection) throws SQLException {
List<UUID> ids = new ArrayList<>();
if (collection.getAdministrators() != null) {
ids.add(collection.getAdministrators().getID());
}
for (Community community : collection.getCommunities()) {
for (UUID id : findTransitiveAdminGroupIds(context, community)) {
ids.add(id);
}
}
return ids;
}
/**
* Retrieve group and eperson IDs for all groups and eperson who have _any_ of the given authorizations
* on the given DSpaceObject. The resulting IDs are prefixed with "e" in the case of an eperson ID, and "g" in the
* case of a group ID.
*
* @param authService The authentication service
* @param context DSpace context object
* @param obj DSpaceObject for which we search the admin group IDs
* @return A stream of admin group IDs as Strings, prefixed with either "e" or "g", depending on whether it is a
* group or eperson ID.
* @throws SQLException if database error
*/
static List<String> findDirectlyAuthorizedGroupAndEPersonPrefixedIds(
AuthorizeService authService, Context context, DSpaceObject obj, int[] authorizations)
throws SQLException {
ArrayList<String> prefixedIds = new ArrayList<>();
for (int auth : authorizations) {
for (ResourcePolicy policy : authService.getPoliciesActionFilter(context, obj, auth)) {
String prefixedId = policy.getGroup() == null
? "e" + policy.getEPerson().getID()
: "g" + policy.getGroup().getID();
prefixedIds.add(prefixedId);
context.uncacheEntity(policy);
}
}
return prefixedIds;
}
}

View File

@@ -7,16 +7,17 @@
*/
package org.dspace.discovery;
import static org.dspace.discovery.IndexingUtils.findDirectlyAuthorizedGroupAndEPersonPrefixedIds;
import static org.dspace.discovery.IndexingUtils.findTransitiveAdminGroupIds;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
import org.apache.logging.log4j.Logger;
import org.apache.solr.common.SolrInputDocument;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogHelper;
@@ -42,29 +43,21 @@ public class SolrServiceIndexCollectionSubmittersPlugin implements SolrServiceIn
Collection col = ((IndexableCollection) idxObj).getIndexedObject();
if (col != null) {
try {
String fieldValue = null;
Community parent = (Community) ContentServiceFactory.getInstance().getDSpaceObjectService(col)
.getParentObject(context, col);
while (parent != null) {
if (parent.getAdministrators() != null) {
fieldValue = "g" + parent.getAdministrators().getID();
document.addField("submit", fieldValue);
}
parent = (Community) ContentServiceFactory.getInstance().getDSpaceObjectService(parent)
.getParentObject(context, parent);
// Index groups with ADMIN rights on the Collection, on
// Communities containing those Collections, and recursively on any Community containing such a
// Community.
// TODO: Strictly speaking we should also check for epersons who received admin rights directly,
// without being part of the admin group. Finding them may be a lot slower though.
for (UUID unprefixedId : findTransitiveAdminGroupIds(context, col)) {
document.addField("submit", "g" + unprefixedId);
}
List<ResourcePolicy> policies = authorizeService.getPoliciesActionFilter(context,col,Constants.ADD);
policies.addAll(authorizeService.getPoliciesActionFilter(context, col, Constants.ADMIN));
for (ResourcePolicy resourcePolicy : policies) {
if (resourcePolicy.getGroup() != null) {
fieldValue = "g" + resourcePolicy.getGroup().getID();
} else {
fieldValue = "e" + resourcePolicy.getEPerson().getID();
}
document.addField("submit", fieldValue);
context.uncacheEntity(resourcePolicy);
// Index groups and epersons with ADD or ADMIN rights on the Collection.
List<String> prefixedIds = findDirectlyAuthorizedGroupAndEPersonPrefixedIds(
authorizeService, context, col, new int[] {Constants.ADD, Constants.ADMIN}
);
for (String prefixedId : prefixedIds) {
document.addField("submit", prefixedId);
}
} catch (SQLException e) {
log.error(LogHelper.getHeader(context, "Error while indexing resource policies",
@@ -73,5 +66,4 @@ public class SolrServiceIndexCollectionSubmittersPlugin implements SolrServiceIn
}
}
}
}
}

View File

@@ -0,0 +1,71 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.discovery;
import static org.dspace.discovery.IndexingUtils.findDirectlyAuthorizedGroupAndEPersonPrefixedIds;
import static org.dspace.discovery.IndexingUtils.findTransitiveAdminGroupIds;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
import org.apache.logging.log4j.Logger;
import org.apache.solr.common.SolrInputDocument;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Collection;
import org.dspace.content.Item;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogHelper;
import org.dspace.discovery.indexobject.IndexableItem;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Indexes policies that yield write access to items.
*
* @author Koen Pauwels at atmire.com
*/
public class SolrServiceIndexItemEditorsPlugin implements SolrServiceIndexPlugin {
private static final Logger log = org.apache.logging.log4j.LogManager
.getLogger(SolrServiceIndexItemEditorsPlugin.class);
@Autowired(required = true)
protected AuthorizeService authorizeService;
@Override
public void additionalIndex(Context context, IndexableObject idxObj, SolrInputDocument document) {
if (idxObj instanceof IndexableItem) {
Item item = ((IndexableItem) idxObj).getIndexedObject();
if (item != null) {
try {
// Index groups with ADMIN rights on Collections containing the Item, on
// Communities containing those Collections, and recursively on any Community containing ssuch a
// Community.
// TODO: Strictly speaking we should also check for epersons who received admin rights directly,
// without being part of the admin group. Finding them may be a lot slower though.
for (Collection collection : item.getCollections()) {
for (UUID unprefixedId : findTransitiveAdminGroupIds(context, collection)) {
document.addField("edit", "g" + unprefixedId);
}
}
// Index groups and epersons with WRITE or direct ADMIN rights on the Item.
List<String> prefixedIds = findDirectlyAuthorizedGroupAndEPersonPrefixedIds(
authorizeService, context, item, new int[] {Constants.WRITE, Constants.ADMIN}
);
for (String prefixedId : prefixedIds) {
document.addField("edit", prefixedId);
}
} catch (SQLException e) {
log.error(LogHelper.getHeader(context, "Error while indexing resource policies",
"Item: (id " + item.getID() + " name " + item.getName() + ")" ));
}
}
}
}
}

View File

@@ -20,12 +20,15 @@ import java.util.stream.Collectors;
import org.apache.logging.log4j.Logger;
import org.dspace.AbstractIntegrationTestWithDatabase;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.EntityTypeBuilder;
import org.dspace.builder.GroupBuilder;
import org.dspace.builder.ItemBuilder;
import org.dspace.builder.RelationshipBuilder;
import org.dspace.builder.RelationshipTypeBuilder;
import org.dspace.builder.ResourcePolicyBuilder;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.EntityType;
@@ -35,6 +38,8 @@ import org.dspace.content.Relationship;
import org.dspace.content.RelationshipType;
import org.dspace.content.WorkspaceItem;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.core.Constants;
import org.dspace.eperson.Group;
import org.dspace.versioning.Version;
import org.dspace.versioning.factory.VersionServiceFactory;
import org.dspace.versioning.service.VersioningService;
@@ -473,6 +478,82 @@ public class ItemServiceTest extends AbstractIntegrationTestWithDatabase {
context.restoreAuthSystemState();
}
@Test
public void testFindItemsWithEditNoRights() throws Exception {
context.setCurrentUser(eperson);
List<Item> result = itemService.findItemsWithEdit(context, 0, 10);
int count = itemService.countItemsWithEdit(context);
assertThat(result.size(), equalTo(0));
assertThat(count, equalTo(0));
}
@Test
public void testFindAndCountItemsWithEditEPerson() throws Exception {
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withUser(eperson)
.withDspaceObject(item)
.withAction(Constants.WRITE)
.build();
context.setCurrentUser(eperson);
List<Item> result = itemService.findItemsWithEdit(context, 0, 10);
int count = itemService.countItemsWithEdit(context);
assertThat(result.size(), equalTo(1));
assertThat(count, equalTo(1));
}
@Test
public void testFindAndCountItemsWithAdminEPerson() throws Exception {
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withUser(eperson)
.withDspaceObject(item)
.withAction(Constants.ADMIN)
.build();
context.setCurrentUser(eperson);
List<Item> result = itemService.findItemsWithEdit(context, 0, 10);
int count = itemService.countItemsWithEdit(context);
assertThat(result.size(), equalTo(1));
assertThat(count, equalTo(1));
}
@Test
public void testFindAndCountItemsWithEditGroup() throws Exception {
context.turnOffAuthorisationSystem();
Group group = GroupBuilder.createGroup(context)
.addMember(eperson)
.build();
context.restoreAuthSystemState();
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withGroup(group)
.withDspaceObject(item)
.withAction(Constants.WRITE)
.build();
context.setCurrentUser(eperson);
List<Item> result = itemService.findItemsWithEdit(context, 0, 10);
int count = itemService.countItemsWithEdit(context);
assertThat(result.size(), equalTo(1));
assertThat(count, equalTo(1));
}
@Test
public void testFindAndCountItemsWithAdminGroup() throws Exception {
context.turnOffAuthorisationSystem();
Group group = GroupBuilder.createGroup(context)
.addMember(eperson)
.build();
context.restoreAuthSystemState();
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withGroup(group)
.withDspaceObject(item)
.withAction(Constants.ADMIN)
.build();
context.setCurrentUser(eperson);
List<Item> result = itemService.findItemsWithEdit(context, 0, 10);
int count = itemService.countItemsWithEdit(context);
assertThat(result.size(), equalTo(1));
assertThat(count, equalTo(1));
}
private void assertMetadataValue(String authorQualifier, String contributorElement, String dcSchema, String value,
String authority, int place, MetadataValue metadataValue) {
assertThat(metadataValue.getValue(), equalTo(value));