From bf3adb93d682dea8ce36cc330fa5a49e734de1cf Mon Sep 17 00:00:00 2001 From: "Gantner, Florian Klaus" Date: Wed, 6 Mar 2024 16:31:18 +0100 Subject: [PATCH] EnhancerScripts ItemEnhancerEntityType ItemEnhancerByDate --- .../script/ItemEnhancerByDateScript.java | 310 +++++++ ...ItemEnhancerByDateScriptConfiguration.java | 73 ++ .../script/ItemEnhancerEntityTypeScript.java | 391 ++++++++ ...EnhancerEntityTypeScriptConfiguration.java | 73 ++ .../config/spring/api/scripts.xml | 10 + .../script/ItemEnhancerByDateScriptIT.java | 799 +++++++++++++++++ .../ItemEnhancerEntityTypeScriptIT.java | 834 ++++++++++++++++++ dspace/config/spring/api/scripts.xml | 10 + 8 files changed, 2500 insertions(+) create mode 100644 dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerByDateScript.java create mode 100644 dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerByDateScriptConfiguration.java create mode 100644 dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerEntityTypeScript.java create mode 100644 dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerEntityTypeScriptConfiguration.java create mode 100644 dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerByDateScriptIT.java create mode 100644 dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerEntityTypeScriptIT.java diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerByDateScript.java b/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerByDateScript.java new file mode 100644 index 000000000000..258884a02e3d --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerByDateScript.java @@ -0,0 +1,310 @@ +/** + * 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.content.enhancer.script; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.apache.commons.cli.ParseException; +import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.client.solrj.response.SolrPingResponse; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrDocumentList; +import org.dspace.content.Item; +import org.dspace.content.enhancer.service.ItemEnhancerService; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.CollectionService; +import org.dspace.content.service.EntityTypeService; +import org.dspace.content.service.ItemService; +import org.dspace.core.Context; +import org.dspace.core.exception.SQLRuntimeException; +import org.dspace.discovery.SearchUtils; +import org.dspace.discovery.SolrSearchCore; +import org.dspace.eperson.EPerson; +import org.dspace.eperson.factory.EPersonServiceFactory; +import org.dspace.scripts.DSpaceRunnable; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.dspace.util.UUIDUtils; +import org.dspace.utils.DSpace; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Script that allows to enhance items, also forcing the updating of the + * calculated metadata with the enhancement. + * + * @author Luca Giamminonni (luca.giamminonni at 4science.it) + + * This Script uses the solr search to discover the subset of entities being processed. + * This offers extended functionalities, e.g. enhance only items modified since or between + * timestamps etc... which cannot be expressed by the database on some easy way. + * - dateupper/datelower: filterquery for items between dates on the lastModified Date + * - entity: filterquery for entitytype (search.resourcetype) + * - collection: filterquery for collection (location.coll) + * - query: free hand search query, e.g. -cris.virtual.author:* . Best to use some criteria on already enhanced items + * - max: perform max items. Best for testing the entries. + * - limit: split result in smaller lists containint limit entries to avoid one big commit in the database + * and additional collection/entitytype queries as filterfacets. + * + * @author florian.gantner@uni-bamberg.de + * + */ +public class ItemEnhancerByDateScript + extends DSpaceRunnable> { + + private ItemService itemService; + private CollectionService collectionService; + + private ItemEnhancerService itemEnhancerService; + + protected SolrSearchCore solrSearchCore; + + private boolean force; + private UUID collection; + private String entitytype; + + private String query; + + private String dateupper; + + private String datelower; + + private Context context; + + private int max; + + private int limit; + + private int counter = 0; + + private int countertotal = 0; + + private EntityTypeService entityTypeService; + + private static final Logger log = LoggerFactory.getLogger(ItemEnhancerByDateScript.class); + + @Override + public void setup() throws ParseException { + + this.itemService = ContentServiceFactory.getInstance().getItemService(); + this.collectionService = ContentServiceFactory.getInstance().getCollectionService(); + this.entityTypeService = ContentServiceFactory.getInstance().getEntityTypeService(); + itemEnhancerService = new DSpace().getSingletonService(ItemEnhancerService.class); + this.solrSearchCore = + DSpaceServicesFactory.getInstance().getServiceManager().getServicesByType(SolrSearchCore.class).get(0); + + this.force = commandLine.hasOption('f'); + if (commandLine.hasOption('c')) { + this.collection = UUIDUtils.fromString(commandLine.getOptionValue('c').trim()); + } + if (commandLine.hasOption('e')) { + this.entitytype = commandLine.getOptionValue('e').trim(); + } + if (commandLine.hasOption('q')) { + this.query = commandLine.getOptionValue('q').trim(); + } + if (commandLine.hasOption('d')) { + this.dateupper = commandLine.getOptionValue('d').trim(); + } + if (commandLine.hasOption('s')) { + this.datelower = commandLine.getOptionValue('s').trim(); + } + if (commandLine.hasOption('m')) { + try { + this.max = Integer.parseInt(commandLine.getOptionValue('m').trim()); + } catch (Exception e) { + handler.logError(e.getMessage()); + } + } + if (commandLine.hasOption('l')) { + try { + this.limit = Integer.parseInt(commandLine.getOptionValue('l').trim()); + } catch (Exception e) { + handler.logError(e.getMessage()); + } + } + } + + @Override + public void internalRun() throws Exception { + context = new Context(); + assignCurrentUserInContext(); + assignSpecialGroupsInContext(); + if (commandLine.hasOption('e') && Objects.isNull(entityTypeService.findByEntityType(context, entitytype))) { + throw new Exception("unknown EntityType " + entitytype); + } + if (commandLine.hasOption('c') && (Objects.isNull(collection) + || Objects.isNull(this.collectionService.find(context, collection)))) { + throw new Exception("specified Collection does not exist"); + } + SolrPingResponse ping = solrSearchCore.getSolr().ping(); + if (ping.getStatus() > 299) { + throw new Exception("Solr seems not to be available. Status" + ping.getStatus()); + } + + context.turnOffAuthorisationSystem(); + try { + searchItems(); + context.complete(); + handler.logInfo("Enhancement completed with success"); + } catch (Exception e) { + handler.handleException("An error occurs during enhancement. The process is aborted", e); + context.abort(); + } finally { + context.restoreAuthSystemState(); + } + } + + + private void searchItems() { + int maximum = 0; //maximum items to be processed + int total = 0; //results of search/query + List items = new ArrayList<>(); + try { + SolrDocumentList results = searchItemsInSolr(this.query, this.dateupper, this.datelower); + for (SolrDocument doc : results) { + String resourceid = (String) doc.getFieldValue(SearchUtils.RESOURCE_ID_FIELD); + if (Objects.nonNull(resourceid) && Objects.nonNull(UUIDUtils.fromString(resourceid))) { + items.add(resourceid); + } + } + } catch (SolrServerException | IOException e) { + handler.logError(e.getMessage(), e); + log.error(e.getMessage()); + } + total = items.size(); + if (total == 0) { + handler.logInfo("No results in solr-Query"); + log.info("No results in solr-Query"); + return; + } else if (this.max > 0) { + maximum = this.max; + if (this.max < items.size()) { + items = items.subList(0, (this.max - 1)); + total = this.max - 1; + } + } + + // split list and commit after limit entries + if (this.limit > 0) { + if (limit > total) { + limit = total; + } + // counting variables for pagination + int tempcounter = 0; + int start = 0; + int end = 0; + while (tempcounter < total) { + start = tempcounter; + end = tempcounter + limit; + if (end > total) { + end = total; + limit = total - tempcounter; + } + try { + this.itemService.findByIds(context, items.subList(start, end)).forEachRemaining(this::enhanceItem); + tempcounter += limit; + context.commit(); + handler.logInfo("enhanced " + tempcounter + " out of max " + maximum + " items"); + log.info("enhanced " + tempcounter + " out of max " + maximum + " items"); + } catch (Exception e) { + tempcounter += limit; + handler.logError(e.getMessage()); + handler.logInfo("enhanced " + tempcounter + " out of max " + maximum + " items"); + log.info("enhanced " + tempcounter + " out of max " + maximum + " items"); + } + } + + } else { + // enhance all found items + try { + this.itemService.findByIds(context, items).forEachRemaining(this::enhanceItem); + } catch (SQLException e) { + handler.logError(e.getMessage()); + } + } + handler.logInfo("enhanced " + counter + " items"); + log.info("enhanced " + counter + " items"); + } + + private SolrDocumentList searchItemsInSolr(String query, String datequeryupper, String datequerylower) + throws SolrServerException, IOException { + SolrQuery sQuery; + if (Objects.nonNull(query)) { + sQuery = new SolrQuery(query); + } else { + sQuery = new SolrQuery("*"); + } + if (Objects.nonNull(datequeryupper) && Objects.nonNull(datequerylower)) { + sQuery.addFilterQuery("lastModified:[" + datequerylower + " TO " + datequeryupper + "]"); + } else if (Objects.nonNull(datequeryupper)) { + sQuery.addFilterQuery("lastModified:[* TO " + datequeryupper + "]"); + } else if (Objects.nonNull(datequerylower)) { + sQuery.addFilterQuery("lastModified:[" + datequerylower + " TO *]"); + } + if (Objects.nonNull(entitytype)) { + sQuery.addFilterQuery("search.entitytype:" + entitytype); + } + sQuery.addFilterQuery(SearchUtils.RESOURCE_TYPE_FIELD + ":Item"); + if (Objects.nonNull(collection)) { + sQuery.addFilterQuery("location.coll:" + UUIDUtils.toString(collection)); + } + sQuery.addField(SearchUtils.RESOURCE_ID_FIELD); + if (max > 0) { + sQuery.setRows(this.max); + } else { + sQuery.setRows(Integer.MAX_VALUE); + } + sQuery.setSort("lastModified_dt",SolrQuery.ORDER.asc); + handler.logInfo("Query Params:" + sQuery.toString()); + QueryResponse qResp = solrSearchCore.getSolr().query(sQuery); + return qResp.getResults(); + } + + private void enhanceItem(Item item) { + counter++; + itemEnhancerService.enhance(context, item, force); + uncacheItem(item); + } + + private void uncacheItem(Item item) { + try { + context.uncacheEntity(item); + } catch (SQLException e) { + throw new SQLRuntimeException(e); + } + } + + private void assignCurrentUserInContext() throws SQLException { + UUID uuid = getEpersonIdentifier(); + if (uuid != null) { + EPerson ePerson = EPersonServiceFactory.getInstance().getEPersonService().find(context, uuid); + context.setCurrentUser(ePerson); + } + } + + private void assignSpecialGroupsInContext() { + for (UUID uuid : handler.getSpecialGroups()) { + context.setSpecialGroup(uuid); + } + } + + @Override + @SuppressWarnings("unchecked") + public ItemEnhancerByDateScriptConfiguration getScriptConfiguration() { + return new DSpace().getServiceManager().getServiceByName("item-enhancer-date", + ItemEnhancerByDateScriptConfiguration.class); + } + +} diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerByDateScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerByDateScriptConfiguration.java new file mode 100644 index 000000000000..e64814c4b16b --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerByDateScriptConfiguration.java @@ -0,0 +1,73 @@ +/** + * 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.content.enhancer.script; + +import java.sql.SQLException; + +import org.apache.commons.cli.Options; +import org.dspace.authorize.service.AuthorizeService; +import org.dspace.core.Context; +import org.dspace.scripts.configuration.ScriptConfiguration; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Script configuration of {@link ItemEnhancerEntityTypeScript}. + * + * @author Luca Giamminonni (luca.giamminonni at 4science.it) + * @author Florian Gantner (florian.gantner@uni-bamberg.de) + */ +public class ItemEnhancerByDateScriptConfiguration extends ScriptConfiguration { + + @Autowired + private AuthorizeService authorizeService; + + private Class dspaceRunnableClass; + + @Override + public boolean isAllowedToExecute(Context context) { + try { + return authorizeService.isAdmin(context); + } catch (SQLException e) { + throw new RuntimeException("SQLException occurred when checking if the current user is an admin", e); + } + } + + @Override + public Options getOptions() { + if (options == null) { + Options options = new Options(); + + options.addOption("f", "force", false, "force the usage of the deep mode" + + " (always compute the enhanced metadata to verify if the item need an update)"); + options.addOption("c", "collection", true, + "uuid of the collection. If the collection does not exist the script aborts."); + options.addOption("e", "entity", true, "Entity type of the items"); + options.addOption("d", "dateupper", true, + "iso date as upper range of date query for lastModified. e.g. 2022-10-27T12:12:17.369Z "); + options.addOption("s", "datelower", true, "iso date as lower range of date query for lastModified "); + options.addOption("m", "max", true, "--max results/rows from solr"); + options.addOption("l", "limit", true, "commit after --limit entities processed"); + options.addOption("q", "query", true, + "additional filterquery for the entities. this can f.e. be the exclusion of already enhanced metadata"); + + super.options = options; + } + return options; + } + + @Override + public Class getDspaceRunnableClass() { + return dspaceRunnableClass; + } + + @Override + public void setDspaceRunnableClass(Class dspaceRunnableClass) { + this.dspaceRunnableClass = dspaceRunnableClass; + } + +} diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerEntityTypeScript.java b/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerEntityTypeScript.java new file mode 100644 index 000000000000..7add8b2541d6 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerEntityTypeScript.java @@ -0,0 +1,391 @@ +/** + * 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.content.enhancer.script; + +import java.sql.SQLException; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.apache.commons.cli.ParseException; +import org.apache.commons.lang3.StringUtils; +import org.dspace.content.Collection; +import org.dspace.content.Item; +import org.dspace.content.enhancer.service.ItemEnhancerService; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.CollectionService; +import org.dspace.content.service.EntityTypeService; +import org.dspace.content.service.ItemService; +import org.dspace.core.Context; +import org.dspace.core.exception.SQLRuntimeException; +import org.dspace.eperson.EPerson; +import org.dspace.eperson.factory.EPersonServiceFactory; +import org.dspace.scripts.DSpaceRunnable; +import org.dspace.util.UUIDUtils; +import org.dspace.utils.DSpace; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Script that allows to enhance items, also forcing the updating of the + * calculated metadata with the enhancement. + * + * @author Luca Giamminonni (luca.giamminonni at 4science.it) + * + * Extended to limit the item set to collection/entitytype to speed up process + * Extended to use pagination option with max/offset/limit options + * - max for testing purposes (process max x items per collection) + * - offset for pagination (start with item 0+offset per collection) + * - limit to make some intermediary commit between x items (recommended 100 steps) + * to make the process more error prone + * + * @author florian.gantner@uni-bamberg.de + * + */ +public class ItemEnhancerEntityTypeScript + extends DSpaceRunnable> { + + private ItemService itemService; + private CollectionService collectionService; + private ItemEnhancerService itemEnhancerService; + private boolean force; + private UUID collection; + private String entitytype; + + private Context context; + + private int limit; + + private int max; + + private int offset; + + private int counter; + + private EntityTypeService entityTypeService; + + private static final Logger log = LoggerFactory.getLogger(ItemEnhancerEntityTypeScript.class); + + @Override + public void setup() throws ParseException { + + this.itemService = ContentServiceFactory.getInstance().getItemService(); + this.collectionService = ContentServiceFactory.getInstance().getCollectionService(); + this.entityTypeService = ContentServiceFactory.getInstance().getEntityTypeService(); + itemEnhancerService = new DSpace().getSingletonService(ItemEnhancerService.class); + + this.force = commandLine.hasOption('f'); + if (commandLine.hasOption('c')) { + this.collection = UUIDUtils.fromString(commandLine.getOptionValue('c').trim()); + } + if (commandLine.hasOption('e')) { + this.entitytype = commandLine.getOptionValue('e').trim(); + } + if (commandLine.hasOption('l')) { + try { + this.limit = Integer.parseInt(commandLine.getOptionValue('l').trim()); + } catch (Exception e) { + handler.logError(e.getMessage()); + } + } + if (commandLine.hasOption('m')) { + try { + this.max = Integer.parseInt(commandLine.getOptionValue('m').trim()); + } catch (Exception e) { + handler.logError(e.getMessage()); + } + } + if (commandLine.hasOption('o')) { + try { + this.offset = Integer.parseInt(commandLine.getOptionValue('o').trim()); + } catch (Exception e) { + handler.logError(e.getMessage()); + } + } + } + + @Override + public void internalRun() throws Exception { + context = new Context(); + assignCurrentUserInContext(); + assignSpecialGroupsInContext(); + if (Objects.nonNull(entitytype) && Objects.isNull(entityTypeService.findByEntityType(context, entitytype))) { + throw new Exception("unknown EntityType " + entitytype); + } + if (Objects.nonNull(entitytype) && StringUtils.isNotBlank(entitytype) && + this.collectionService.findAll(context).stream() + .noneMatch(col -> col.getEntityType().contentEquals(entitytype))) { + throw new Exception("no Collections with EntityType " + entitytype); + } + if (commandLine.hasOption('c') && Objects.isNull(collection)) { + throw new Exception("invalid uuid in the specified Collection"); + } + if (Objects.nonNull(collection) && (Objects.isNull(this.collectionService.find(context, collection)))) { + throw new Exception("specified Collection does not exist"); + } + if (Objects.nonNull(collection) && (Objects.nonNull(entitytype)) && + !this.collectionService.find(context, collection).getEntityType().contentEquals(entitytype)) { + throw new Exception("the specified Collection does not match with the specified EntityType"); + } + + context.turnOffAuthorisationSystem(); + try { + enhanceItems(); + context.complete(); + handler.logInfo("Enhancement completed with success"); + } catch (Exception e) { + handler.handleException("An error occurs during enhancement. The process is aborted", e); + context.abort(); + } finally { + context.restoreAuthSystemState(); + } + } + + private void enhanceItems() throws SQLException { + if (Objects.nonNull(collection)) { + Collection coll = this.collectionService.find(context, collection); + findItemsToEnhance(coll); + } else if (Objects.nonNull(entitytype)) { + //for each collection with entity type + for (Collection coll : collectionService.findAll(context).stream() + .filter(collection1 -> collection1.getEntityType().contentEquals(entitytype)).collect( + Collectors.toList())) { + findItemsToEnhance(coll); + } + } else { + findItemsToEnhance(null); + } + } + + /** + * enhance the items in this collection with the given numeric restrictions + * @param coll + */ + private void findItemsToEnhance(Collection coll) { + int total = 0; + int maximal = 0; + if (Objects.nonNull(coll)) { + //Paginate through items in one (given) collection + try { + total = itemService.countItems(context, coll); + } catch (SQLException e) { + handler.logError(e.getMessage()); + return; + } + if (this.max > 0) { + total = this.max; + maximal = this.max; + } + if (this.offset > 0) { + //offset is being added to counter and offset + total += offset; + if (limit > 0) { + handler.logDebug("offset " + offset + " added. Range: [" + + counter + " to " + total + "] in " + limit + " steps"); + log.debug("offset " + offset + " added. Range: [" + + counter + " to " + total + "] in " + limit + " steps"); + } else { + handler.logDebug("offset " + offset + " added. Range: [" + + counter + " to " + total + "]"); + log.debug("offset " + offset + " added. Range: [" + + counter + " to " + total + "]"); + } + } else { + if (limit > 0) { + handler.logDebug("Range: [" + counter + " to " + + total + "] in " + limit + " steps"); + log.debug("Range: [" + counter + " to " + total + "] in " + limit + " steps"); + } else { + handler.logDebug("Range: [" + counter + " to " + + total + "]"); + log.debug("Range: [" + counter + " to " + total + "]"); + } + } + int tempcounter = 0; + if (limit > total) { + limit = total; + } + while (tempcounter < total) { + if (limit > 0) { + try { + itemService.findAllByCollection(context, coll, limit, tempcounter) + .forEachRemaining(this::enhanceItem); + context.commit(); + handler.logInfo("enhanced " + counter + " out of max " + maximal + " items"); + log.info("enhanced " + counter + " out of max " + maximal + " items"); + + tempcounter += limit; + } catch (SQLException e) { + handler.logError(e.getMessage()); + handler.logError("enhanced " + counter + " out of max " + maximal + " items"); + log.error("enhanced " + counter + " out of max " + maximal + " items"); + + tempcounter += limit; + } + } else { + try { + // no limit, so process all items in one commit + itemService.findAllByCollection(context, coll, total, 0) + .forEachRemaining(this::enhanceItem); + context.commit(); + handler.logInfo("enhanced " + counter + " out of max " + maximal + " items"); + log.info("enhanced " + counter + " out of max " + maximal + " items"); + + tempcounter += total; + } catch (SQLException e) { + handler.logError(e.getMessage()); + handler.logError("enhanced " + counter + " out of max " + maximal + " items"); + log.error("enhanced " + counter + " out of max " + maximal + " items"); + + tempcounter += total; + } + } + } + } else { + // operate over all items + try { + total = itemService.countTotal(context); + } catch (SQLException e) { + handler.logError(e.getMessage()); + return; + } + + if (this.max > 0) { + total = this.max; + maximal = this.max; + } + if (this.offset > 0) { + //offset is being added to counter and offset + total += offset; + if (limit > 0) { + handler.logDebug("offset " + offset + " added. Range: [" + + counter + " to " + total + "] in " + limit + " steps"); + log.debug("offset " + offset + " added. Range: [" + + counter + " to " + total + "] in " + limit + " steps"); + } else { + handler.logDebug("offset " + offset + " added. Range: [" + + counter + " to " + total + "]"); + log.debug("offset " + offset + " added. Range: [" + + counter + " to " + total + "]"); + } + } else { + if (limit > 0) { + handler.logDebug("Range: [" + counter + " to " + + total + "] in " + limit + " steps"); + log.debug("Range: [" + counter + " to " + total + "] in " + limit + " steps"); + } else { + handler.logDebug("Range: [" + counter + " to " + + total + "]"); + log.debug("Range: [" + counter + " to " + total + "]"); + } + } + //Counting variables for pagination + int tempcounter = 0; + while (tempcounter < total) { + if (limit > 0) { + try { + // Check for entity type in enhanceItem method + if (Objects.nonNull(this.entitytype)) { + itemService.findAll(context, limit, tempcounter) + .forEachRemaining(this::enhanceItemEntityCheck); + } else { + itemService.findAll(context, limit, tempcounter).forEachRemaining(this::enhanceItem); + } + context.commit(); + handler.logInfo("enhanced " + counter + " out of max " + maximal + " items"); + log.info("enhanced " + counter + " out of max " + maximal + " items"); + + tempcounter += limit; + } catch (SQLException e) { + handler.logError(e.getMessage()); + handler.logError("enhanced " + counter + " out of max " + maximal + " items"); + log.error("enhanced " + counter + " out of max " + maximal + " items"); + + tempcounter += limit; + } + } else { + try { + // Check for entity type in enhanceItem method + if (Objects.nonNull(this.entitytype)) { + itemService.findAll(context, total, 0).forEachRemaining(this::enhanceItemEntityCheck); + } else { + itemService.findAll(context, total, 0).forEachRemaining(this::enhanceItem); + } + context.commit(); + handler.logInfo("enhanced " + counter + " out of max " + maximal + " items"); + log.info("enhanced " + counter + " out of max " + maximal + " items"); + + tempcounter += total; + } catch (SQLException e) { + counter++; + handler.logError(e.getMessage()); + handler.logError("enhanced " + counter + " out of max " + maximal + " items"); + log.error("enhanced " + counter + " out of max " + maximal + " items"); + + tempcounter += total; + } + } + } + } + handler.logInfo("enhanced " + counter + " items"); + log.info("enhanced " + counter + " items"); + } + + private void enhanceItem(Item item) { + counter++; + itemEnhancerService.enhance(context, item, force); + uncacheItem(item); + } + + /** + * Additional Entity Check. Only applicable when operating over all entities + */ + private void enhanceItemEntityCheck(Item item) { + if (Objects.nonNull(entitytype)) { + if (entitytype.contentEquals(itemService.getEntityType(item))) { + counter++; + itemEnhancerService.enhance(context, item, force); + uncacheItem(item); + } + } else { + counter++; + itemEnhancerService.enhance(context, item, force); + uncacheItem(item); + } + } + + private void uncacheItem(Item item) { + try { + context.uncacheEntity(item); + } catch (SQLException e) { + throw new SQLRuntimeException(e); + } + } + + private void assignCurrentUserInContext() throws SQLException { + UUID uuid = getEpersonIdentifier(); + if (uuid != null) { + EPerson ePerson = EPersonServiceFactory.getInstance().getEPersonService().find(context, uuid); + context.setCurrentUser(ePerson); + } + } + + private void assignSpecialGroupsInContext() { + for (UUID uuid : handler.getSpecialGroups()) { + context.setSpecialGroup(uuid); + } + } + + @Override + @SuppressWarnings("unchecked") + public ItemEnhancerEntityTypeScriptConfiguration getScriptConfiguration() { + return new DSpace().getServiceManager().getServiceByName("item-enhancer-type", + ItemEnhancerEntityTypeScriptConfiguration.class); + } + +} diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerEntityTypeScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerEntityTypeScriptConfiguration.java new file mode 100644 index 000000000000..04451a08e9dc --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerEntityTypeScriptConfiguration.java @@ -0,0 +1,73 @@ +/** + * 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.content.enhancer.script; + +import java.sql.SQLException; + +import org.apache.commons.cli.Options; +import org.dspace.authorize.service.AuthorizeService; +import org.dspace.core.Context; +import org.dspace.scripts.configuration.ScriptConfiguration; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Script configuration of {@link ItemEnhancerEntityTypeScript}. + * + * @author Luca Giamminonni (luca.giamminonni at 4science.it) + * @author Florian Gantner (florian.gantner@uni-bamberg.de) + */ +public class ItemEnhancerEntityTypeScriptConfiguration + extends ScriptConfiguration { + + @Autowired + private AuthorizeService authorizeService; + + private Class dspaceRunnableClass; + + @Override + public boolean isAllowedToExecute(Context context) { + try { + return authorizeService.isAdmin(context); + } catch (SQLException e) { + throw new RuntimeException("SQLException occurred when checking if the current user is an admin", e); + } + } + + @Override + public Options getOptions() { + if (options == null) { + Options options = new Options(); + + options.addOption("f", "force", false, "force the usage of the deep mode" + + " (always compute the enhanced metadata to verify if the item need an update)"); + options.addOption("c", "collection", true, + "uuid of the collection. If the collection does not exist the script aborts."); + options.addOption("e", "entity", true, + "Entity type of the items. Processes all collections with the specific entity type "); + options.addOption("l", "limit", true, + "size for iterator --limit items and commit after --limit items"); + options.addOption("m", "max", true, "process max --max items (per collection)"); + options.addOption("o", "offset", true, + "offset of items to start --offset items from the start (per collection)"); + + super.options = options; + } + return options; + } + + @Override + public Class getDspaceRunnableClass() { + return dspaceRunnableClass; + } + + @Override + public void setDspaceRunnableClass(Class dspaceRunnableClass) { + this.dspaceRunnableClass = dspaceRunnableClass; + } + +} diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/scripts.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/scripts.xml index 655ddf45b323..dff0ce058baa 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/spring/api/scripts.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/scripts.xml @@ -135,6 +135,16 @@ + + + + + + + + + + diff --git a/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerByDateScriptIT.java b/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerByDateScriptIT.java new file mode 100644 index 000000000000..f681080d0c6f --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerByDateScriptIT.java @@ -0,0 +1,799 @@ +/** + * 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.content.enhancer.script; + +import static org.dspace.app.matcher.MetadataValueMatcher.with; +import static org.dspace.content.Item.ANY; +import static org.dspace.content.enhancer.consumer.ItemEnhancerConsumer.ITEMENHANCER_ENABLED; +import static org.dspace.core.CrisConstants.PLACEHOLDER_PARENT_METADATA_VALUE; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalToIgnoringCase; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.notNullValue; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.dspace.AbstractIntegrationTestWithDatabase; +import org.dspace.app.launcher.ScriptLauncher; +import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; +import org.dspace.authorize.AuthorizeException; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.ItemBuilder; +import org.dspace.builder.WorkspaceItemBuilder; +import org.dspace.content.Collection; +import org.dspace.content.Item; +import org.dspace.content.MetadataValue; +import org.dspace.content.WorkspaceItem; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.ItemService; +import org.dspace.core.ReloadableEntity; +import org.dspace.event.factory.EventServiceFactory; +import org.dspace.event.service.EventService; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ItemEnhancerByDateScriptIT extends AbstractIntegrationTestWithDatabase { + + private static ConfigurationService configService = DSpaceServicesFactory.getInstance().getConfigurationService(); + + private static final EventService eventService = EventServiceFactory.getInstance().getEventService(); + private static boolean isEnabled; + private static String[] consumers; + + private ItemService itemService; + + private Collection collection; + + private Collection persons; + + /** + * This method will be run before the first test as per @BeforeClass. It will + * configure the event.dispatcher.default.consumers property to remove the + * ItemEnhancerConsumer. + */ + @BeforeClass + public static void initConsumers() { + consumers = configService.getArrayProperty("event.dispatcher.default.consumers"); + Set consumersSet = new HashSet(Arrays.asList(consumers)); + if (!consumersSet.contains("itemenhancer")) { + consumersSet.add("itemenhancer"); + configService.setProperty("event.dispatcher.default.consumers", consumersSet.toArray()); + eventService.reloadConfiguration(); + } + } + + /** + * Reset the event.dispatcher.default.consumers property value. + */ + @AfterClass + public static void resetDefaultConsumers() { + configService.setProperty("event.dispatcher.default.consumers", consumers); + eventService.reloadConfiguration(); + } + + @Before + public void setup() { + + configService.setProperty(ITEMENHANCER_ENABLED, false); + + itemService = ContentServiceFactory.getInstance().getItemService(); + + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + collection = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection") + .build(); + + persons = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection") + .withEntityType("Person") + .build(); + + context.restoreAuthSystemState(); + + } + + @Test + public void testItemsEnhancement() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item firstAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Walter White") + .withPersonMainAffiliation("4Science") + .build(); + + String firstAuthorId = firstAuthor.getID().toString(); + + Item secondAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Jesse Pinkman") + .withPersonMainAffiliation("Company") + .build(); + + String secondAuthorId = secondAuthor.getID().toString(); + + Item firstPublication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication") + .withEntityType("Publication") + .withAuthor("Walter White", firstAuthorId) + .build(); + + Item secondPublication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2") + .withEntityType("Publication") + .withAuthor("Walter White", firstAuthorId) + .withAuthor("Jesse Pinkman", secondAuthorId) + .build(); + + WorkspaceItem thirdPublication = WorkspaceItemBuilder.createWorkspaceItem(context, collection) + .withTitle("Test publication 3") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman", secondAuthorId) + .build(); + + context.commit(); + + firstPublication = reload(firstPublication); + secondPublication = reload(secondPublication); + thirdPublication = reload(thirdPublication); + + assertThat(getMetadataValues(firstPublication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(firstPublication, "cris.virtualsource.department"), empty()); + assertThat(getMetadataValues(secondPublication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(secondPublication, "cris.virtualsource.department"), empty()); + assertThat(getMetadataValues(thirdPublication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(thirdPublication, "cris.virtualsource.department"), empty()); + + TestDSpaceRunnableHandler runnableHandler = runScript(false); + + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + firstPublication = reload(firstPublication); + secondPublication = reload(secondPublication); + + assertThat(getMetadataValues(firstPublication, "cris.virtual.department"), hasSize(1)); + assertThat(getMetadataValues(firstPublication, "cris.virtualsource.department"), hasSize(1)); + + assertThat(getMetadataValues(secondPublication, "cris.virtual.department"), hasSize(2)); + assertThat(getMetadataValues(secondPublication, "cris.virtualsource.department"), hasSize(2)); + + assertThat(firstPublication.getMetadata(), hasItem(with("cris.virtual.department", "4Science"))); + assertThat(firstPublication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); + assertThat(secondPublication.getMetadata(), hasItem(with("cris.virtual.department", "4Science"))); + assertThat(secondPublication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); + assertThat(secondPublication.getMetadata(), hasItem(with("cris.virtual.department", "Company", 1))); + assertThat(secondPublication.getMetadata(), hasItem(with("cris.virtualsource.department", secondAuthorId, 1))); + + assertThat(getMetadataValues(thirdPublication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(thirdPublication, "cris.virtualsource.department"), empty()); + + } + + @Test + public void testItemEnhancementWithoutForce() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item firstAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Walter White") + .withPersonMainAffiliation("4Science") + .build(); + + String firstAuthorId = firstAuthor.getID().toString(); + + Item secondAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Jesse Pinkman") + .withPersonMainAffiliation("Company") + .build(); + + String secondAuthorId = secondAuthor.getID().toString(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Walter White", firstAuthorId) + .withAuthor("Jesse Pinkman", secondAuthorId) + .build(); + + context.commit(); + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), empty()); + + TestDSpaceRunnableHandler runnableHandler = runScript(false); + + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(2)); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(2)); + + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "4Science"))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "Company", 1))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", secondAuthorId, 1))); + + context.turnOffAuthorisationSystem(); + + MetadataValue authorToRemove = getMetadataValues(publication, "dc.contributor.author").get(1); + itemService.removeMetadataValues(context, publication, List.of(authorToRemove)); + + replaceMetadata(firstAuthor, "person", "affiliation", "name", "University"); + + context.restoreAuthSystemState(); + + runnableHandler = runScript(false); + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(1)); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(1)); + + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "4Science"))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); + + } + + @Test + public void testItemEnhancementWithForce() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item firstAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Walter White") + .withPersonMainAffiliation("4Science") + .build(); + + String firstAuthorId = firstAuthor.getID().toString(); + + Item secondAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Jesse Pinkman") + .withPersonMainAffiliation("Company") + .build(); + + String secondAuthorId = secondAuthor.getID().toString(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Walter White", firstAuthorId) + .withAuthor("Jesse Pinkman", secondAuthorId) + .build(); + + context.commit(); + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), empty()); + + TestDSpaceRunnableHandler runnableHandler = runScript(false); + + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(2)); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(2)); + + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "4Science"))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "Company", 1))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", secondAuthorId, 1))); + + context.turnOffAuthorisationSystem(); + + MetadataValue authorToRemove = getMetadataValues(publication, "dc.contributor.author").get(1); + itemService.removeMetadataValues(context, publication, List.of(authorToRemove)); + + replaceMetadata(firstAuthor, "person", "affiliation", "name", "University"); + + context.restoreAuthSystemState(); + + runnableHandler = runScript(true); + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(1)); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(1)); + + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "University"))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); + + } + + @Test + public void testItemEnhancementMetadataPositions() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item firstAuthor = ItemBuilder.createItem(context, collection) + .withTitle("John Doe") + .build(); + String firstAuthorId = firstAuthor.getID().toString(); + + Item secondAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Walter White") + .withPersonMainAffiliation("4Science") + .build(); + String secondAuthorId = secondAuthor.getID().toString(); + + Item thirdAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Jesse Pinkman") + .withPersonMainAffiliation("Company") + .build(); + + String thirdAuthorId = thirdAuthor.getID().toString(); + + Item fourthAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Jesse Smith") + .build(); + + String fourthAuthorId = fourthAuthor.getID().toString(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("John Doe", firstAuthorId) + .withAuthor("Walter White", secondAuthorId) + .withAuthor("Jesse Pinkman", thirdAuthorId) + .withAuthor("Jesse Smith", fourthAuthorId) + .build(); + + context.commit(); + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), empty()); + + TestDSpaceRunnableHandler runnableHandler = runScript(false); + + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(4)); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(4)); + + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", + PLACEHOLDER_PARENT_METADATA_VALUE, 0))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId,0))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "4Science", 1))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", secondAuthorId,1))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "Company", 2))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", thirdAuthorId, 2))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", + PLACEHOLDER_PARENT_METADATA_VALUE, 3))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", fourthAuthorId,3))); + + } + + @Test + public void testItemEnhancementSourceWithoutAuthority() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item secondAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Jesse Smith") + .withPersonMainAffiliation("4Science") + .build(); + + String secondAuthorId = secondAuthor.getID().toString(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith", secondAuthorId) + .build(); + + context.commit(); + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), empty()); + + TestDSpaceRunnableHandler runnableHandler = runScript(false); + + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(2)); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(2)); + + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", + PLACEHOLDER_PARENT_METADATA_VALUE, 0))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", + PLACEHOLDER_PARENT_METADATA_VALUE,0))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "4Science", 1))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", secondAuthorId,1))); + + } + + @Test + public void testItemEnhancementWithoutAuthorities() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + context.commit(); + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), empty()); + + TestDSpaceRunnableHandler runnableHandler = runScript(false); + + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), empty()); + + } + + @Test + public void testItemEnhancementEntityTypeInvalidCollectionUUID() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + context.commit(); + publication = reload(publication); + + TestDSpaceRunnableHandler runnableHandler = + runScript(false, publication.getID().toString(), null, "*", null, null); + + assertThat(runnableHandler.getException(), notNullValue()); + assertThat(runnableHandler.getException().getMessage(), + equalToIgnoringCase("specified Collection does not exist")); + } + + @Test + public void testItemEnhancementEntityTypeAbortWhenInvalidSolrQuery() throws Exception { + + context.turnOffAuthorisationSystem(); + context.commit(); + String query = "(test : info"; + + TestDSpaceRunnableHandler runnableHandler = + runScript(false, null, null, query, null, null); + + assertThat(runnableHandler.getErrorMessages(), hasItems()); + assertThat(runnableHandler.getErrorMessages(), hasItem(containsString("ParseException"))); + assertThat(runnableHandler.getErrorMessages(), + hasItem("An error occurs during enhancement. The process is aborted")); + } + + @Test + public void testItemEnhancementEntityTypeAbortWhenEmptySolrQuery() throws Exception { + + context.turnOffAuthorisationSystem(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + context.commit(); + String query = "neverexistingdsolrindexfield:true"; + + TestDSpaceRunnableHandler runnableHandler = + runScript(false, null, null, query, null, null); + + assertThat(runnableHandler.getInfoMessages(), hasItems()); + assertThat(runnableHandler.getInfoMessages(), hasItem("No results in solr-Query")); + } + + @Test + public void testItemEnhancementEntityTypeInvalidEntityType() throws Exception { + + context.turnOffAuthorisationSystem(); + context.commit(); + + TestDSpaceRunnableHandler runnableHandler + = runScript(false, null, "ResearchData", "*", null, null); + + assertThat(runnableHandler.getException(), notNullValue()); + assertThat(runnableHandler.getException().getMessage(), equalToIgnoringCase("unknown EntityType ResearchData")); + } + + @Test + public void testItemEnhancementEntityTypeMaxMatches() throws Exception { + + context.turnOffAuthorisationSystem(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 3") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + context.commit(); + String max = "2"; + + TestDSpaceRunnableHandler runnableHandler + = runScript(false, collection.getID().toString(), null, "*", null, max); + + assertThat(runnableHandler.getInfoMessages(), hasItems()); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced " + max + " items")); + } + + @Test + public void testItemEnhancementEntityTypeLimitAndMaxMatches() throws Exception { + + context.turnOffAuthorisationSystem(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 3") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 4") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 5") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + context.commit(); + + String limit = "2"; + String max = "3"; + + TestDSpaceRunnableHandler runnableHandler + = runScript(false, collection.getID().toString(), null, "*", limit, max); + + assertThat(runnableHandler.getInfoMessages(), hasItems()); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced 2 out of max " + max + " items")); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced 3 out of max " + max + " items")); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced " + max + " items")); + } + + @Test + public void testItemEnhancementEntityTypeLimitAndMaxMatches2() throws Exception { + + context.turnOffAuthorisationSystem(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 3") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 4") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 5") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + context.commit(); + + String limit = "2"; + String max = "3"; + + TestDSpaceRunnableHandler runnableHandler + = runScript(false, collection.getID().toString(), null, "*", limit, max); + + assertThat(runnableHandler.getInfoMessages(), hasItems()); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced 2 out of max " + max + " items")); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced 3 out of max " + max + " items")); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced " + max + " items")); + } + + @Test + public void testItemEnhancementEntityTypeLimitAndMaxMatches3() throws Exception { + + context.turnOffAuthorisationSystem(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 3") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 4") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + context.commit(); + + String limit = "2"; + String max = "5"; + + TestDSpaceRunnableHandler runnableHandler + = runScript(false, collection.getID().toString(), null, "*", limit, max); + + assertThat(runnableHandler.getInfoMessages(), hasItems()); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced 2 out of max " + max + " items")); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced 4 out of max " + max + " items")); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced 4 items")); + } + + + private TestDSpaceRunnableHandler runScript(boolean force) throws InstantiationException, IllegalAccessException { + TestDSpaceRunnableHandler runnableHandler = new TestDSpaceRunnableHandler(); + String[] args = force ? new String[] { "item-enhancer-date", "-f" } : new String[] { "item-enhancer-date" }; + ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), runnableHandler, kernelImpl); + return runnableHandler; + } + + private TestDSpaceRunnableHandler runScript(boolean force, String collectionuuid, String entitytype, String query, + String limit, String max) + throws InstantiationException, IllegalAccessException { + TestDSpaceRunnableHandler runnableHandler = new TestDSpaceRunnableHandler(); + List argslist = new ArrayList<>(); + argslist.add("item-enhancer-date"); + if (force) { + argslist.add("-f"); + } + if (StringUtils.isNotBlank(collectionuuid)) { + argslist.add("-c " + collectionuuid); + } + if (StringUtils.isNotBlank(entitytype)) { + argslist.add("-e " + entitytype); + } + if (StringUtils.isNotBlank(query)) { + argslist.add("-q " + query); + } + if (StringUtils.isNotBlank(limit)) { + argslist.add("-l " + limit); + } + if (StringUtils.isNotBlank(max)) { + argslist.add("-m " + max); + } + String[] args = argslist.toArray(new String[0]); + + ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), runnableHandler, kernelImpl); + return runnableHandler; + } + + @SuppressWarnings("rawtypes") + private T reload(T entity) throws SQLException, AuthorizeException { + return context.reloadEntity(entity); + } + + private void replaceMetadata(Item item, String schema, String element, String qualifier, String newValue) + throws SQLException, AuthorizeException { + itemService.replaceMetadata(context, reload(item), schema, element, qualifier, ANY, newValue, null, -1, 0); + } + + private List getMetadataValues(Item item, String metadataField) { + return itemService.getMetadataByMetadataString(item, metadataField); + } + + private List getMetadataValues(WorkspaceItem item, String metadataField) { + return itemService.getMetadataByMetadataString(item.getItem(), metadataField); + } + +} diff --git a/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerEntityTypeScriptIT.java b/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerEntityTypeScriptIT.java new file mode 100644 index 000000000000..b65627fa5a15 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerEntityTypeScriptIT.java @@ -0,0 +1,834 @@ +/** + * 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.content.enhancer.script; + +import static org.dspace.app.matcher.MetadataValueMatcher.with; +import static org.dspace.content.Item.ANY; +import static org.dspace.content.enhancer.consumer.ItemEnhancerConsumer.ITEMENHANCER_ENABLED; +import static org.dspace.core.CrisConstants.PLACEHOLDER_PARENT_METADATA_VALUE; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalToIgnoringCase; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.notNullValue; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.dspace.AbstractIntegrationTestWithDatabase; +import org.dspace.app.launcher.ScriptLauncher; +import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; +import org.dspace.authorize.AuthorizeException; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.EntityTypeBuilder; +import org.dspace.builder.ItemBuilder; +import org.dspace.builder.WorkspaceItemBuilder; +import org.dspace.content.Collection; +import org.dspace.content.EntityType; +import org.dspace.content.Item; +import org.dspace.content.MetadataValue; +import org.dspace.content.WorkspaceItem; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.ItemService; +import org.dspace.core.ReloadableEntity; +import org.dspace.event.factory.EventServiceFactory; +import org.dspace.event.service.EventService; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ItemEnhancerEntityTypeScriptIT extends AbstractIntegrationTestWithDatabase { + + private static final ConfigurationService configService = + DSpaceServicesFactory.getInstance().getConfigurationService(); + private static final EventService eventService = EventServiceFactory.getInstance().getEventService(); + private static String[] consumers; + private static boolean isEnabled; + private ItemService itemService; + + private Collection collection; + + private Collection persons; + + private Collection publications; + + private Collection publications2; + + private Collection publications3; + + /** + * This method will be run before the first test as per @BeforeClass. It will + * configure the event.dispatcher.default.consumers property to remove the + * ItemEnhancerConsumer. + */ + @BeforeClass + public static void initConsumers() { + consumers = configService.getArrayProperty("event.dispatcher.default.consumers"); + Set consumersSet = new HashSet(Arrays.asList(consumers)); + if (!consumersSet.contains("itemenhancer")) { + consumersSet.add("itemenhancer"); + configService.setProperty("event.dispatcher.default.consumers", consumersSet.toArray()); + eventService.reloadConfiguration(); + } + } + + /** + * Reset the event.dispatcher.default.consumers property value. + */ + @AfterClass + public static void resetDefaultConsumers() { + configService.setProperty("event.dispatcher.default.consumers", consumers); + eventService.reloadConfiguration(); + } + + @Before + public void setup() { + + configService.setProperty(ITEMENHANCER_ENABLED, false); + + itemService = ContentServiceFactory.getInstance().getItemService(); + + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + collection = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection") + .build(); + + publications = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection Publication") + .withEntityType("Publication") + .build(); + + publications2 = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection Publication2") + .withEntityType("Publication") + .build(); + + publications3 = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection Publication3") + .withEntityType("Publication") + .build(); + + persons = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection") + .withEntityType("Person") + .build(); + + context.restoreAuthSystemState(); + + } + + @Test + public void testItemsEnhancement() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item firstAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Walter White") + .withPersonMainAffiliation("4Science") + .build(); + + String firstAuthorId = firstAuthor.getID().toString(); + + Item secondAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Jesse Pinkman") + .withPersonMainAffiliation("Company") + .build(); + + String secondAuthorId = secondAuthor.getID().toString(); + + Item firstPublication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication") + .withEntityType("Publication") + .withAuthor("Walter White", firstAuthorId) + .build(); + + Item secondPublication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2") + .withEntityType("Publication") + .withAuthor("Walter White", firstAuthorId) + .withAuthor("Jesse Pinkman", secondAuthorId) + .build(); + + WorkspaceItem thirdPublication = WorkspaceItemBuilder.createWorkspaceItem(context, collection) + .withTitle("Test publication 3") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman", secondAuthorId) + .build(); + + context.commit(); + + firstPublication = reload(firstPublication); + secondPublication = reload(secondPublication); + thirdPublication = reload(thirdPublication); + + assertThat(getMetadataValues(firstPublication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(firstPublication, "cris.virtualsource.department"), empty()); + assertThat(getMetadataValues(secondPublication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(secondPublication, "cris.virtualsource.department"), empty()); + assertThat(getMetadataValues(thirdPublication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(thirdPublication, "cris.virtualsource.department"), empty()); + + TestDSpaceRunnableHandler runnableHandler = runScript(false); + + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + firstPublication = reload(firstPublication); + secondPublication = reload(secondPublication); + + assertThat(getMetadataValues(firstPublication, "cris.virtual.department"), hasSize(1)); + assertThat(getMetadataValues(firstPublication, "cris.virtualsource.department"), hasSize(1)); + + assertThat(getMetadataValues(secondPublication, "cris.virtual.department"), hasSize(2)); + assertThat(getMetadataValues(secondPublication, "cris.virtualsource.department"), hasSize(2)); + + assertThat(firstPublication.getMetadata(), hasItem(with("cris.virtual.department", "4Science"))); + assertThat(firstPublication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); + assertThat(secondPublication.getMetadata(), hasItem(with("cris.virtual.department", "4Science"))); + assertThat(secondPublication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); + assertThat(secondPublication.getMetadata(), hasItem(with("cris.virtual.department", "Company", 1))); + assertThat(secondPublication.getMetadata(), hasItem(with("cris.virtualsource.department", secondAuthorId, 1))); + + assertThat(getMetadataValues(thirdPublication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(thirdPublication, "cris.virtualsource.department"), empty()); + + } + + @Test + public void testItemEnhancementWithoutForce() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item firstAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Walter White") + .withPersonMainAffiliation("4Science") + .build(); + + String firstAuthorId = firstAuthor.getID().toString(); + + Item secondAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Jesse Pinkman") + .withPersonMainAffiliation("Company") + .build(); + + String secondAuthorId = secondAuthor.getID().toString(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Walter White", firstAuthorId) + .withAuthor("Jesse Pinkman", secondAuthorId) + .build(); + + context.commit(); + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), empty()); + + TestDSpaceRunnableHandler runnableHandler = runScript(false); + + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(2)); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(2)); + + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "4Science"))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "Company", 1))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", secondAuthorId, 1))); + + context.turnOffAuthorisationSystem(); + + MetadataValue authorToRemove = getMetadataValues(publication, "dc.contributor.author").get(1); + itemService.removeMetadataValues(context, publication, List.of(authorToRemove)); + + replaceMetadata(firstAuthor, "person", "affiliation", "name", "University"); + + context.restoreAuthSystemState(); + + runnableHandler = runScript(false); + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(1)); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(1)); + + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "4Science"))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); + + } + + @Test + public void testItemEnhancementWithForce() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item firstAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Walter White") + .withPersonMainAffiliation("4Science") + .build(); + + String firstAuthorId = firstAuthor.getID().toString(); + + Item secondAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Jesse Pinkman") + .withPersonMainAffiliation("Company") + .build(); + + String secondAuthorId = secondAuthor.getID().toString(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Walter White", firstAuthorId) + .withAuthor("Jesse Pinkman", secondAuthorId) + .build(); + + context.commit(); + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), empty()); + + TestDSpaceRunnableHandler runnableHandler = runScript(false); + + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(2)); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(2)); + + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "4Science"))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "Company", 1))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", secondAuthorId, 1))); + + context.turnOffAuthorisationSystem(); + + MetadataValue authorToRemove = getMetadataValues(publication, "dc.contributor.author").get(1); + itemService.removeMetadataValues(context, publication, List.of(authorToRemove)); + + replaceMetadata(firstAuthor, "person", "affiliation", "name", "University"); + + context.restoreAuthSystemState(); + + runnableHandler = runScript(true); + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(1)); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(1)); + + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "University"))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); + + } + + @Test + public void testItemEnhancementMetadataPositions() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item firstAuthor = ItemBuilder.createItem(context, collection) + .withTitle("John Doe") + .build(); + String firstAuthorId = firstAuthor.getID().toString(); + + Item secondAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Walter White") + .withPersonMainAffiliation("4Science") + .build(); + String secondAuthorId = secondAuthor.getID().toString(); + + Item thirdAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Jesse Pinkman") + .withPersonMainAffiliation("Company") + .build(); + + String thirdAuthorId = thirdAuthor.getID().toString(); + + Item fourthAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Jesse Smith") + .build(); + + String fourthAuthorId = fourthAuthor.getID().toString(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("John Doe", firstAuthorId) + .withAuthor("Walter White", secondAuthorId) + .withAuthor("Jesse Pinkman", thirdAuthorId) + .withAuthor("Jesse Smith", fourthAuthorId) + .build(); + + context.commit(); + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), empty()); + + TestDSpaceRunnableHandler runnableHandler = runScript(false); + + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(4)); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(4)); + + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", + PLACEHOLDER_PARENT_METADATA_VALUE, 0))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId,0))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "4Science", 1))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", secondAuthorId,1))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "Company", 2))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", thirdAuthorId, 2))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", + PLACEHOLDER_PARENT_METADATA_VALUE, 3))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", fourthAuthorId,3))); + + } + + @Test + public void testItemEnhancementSourceWithoutAuthority() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item secondAuthor = ItemBuilder.createItem(context, collection) + .withTitle("Jesse Smith") + .withPersonMainAffiliation("4Science") + .build(); + + String secondAuthorId = secondAuthor.getID().toString(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith", secondAuthorId) + .build(); + + context.commit(); + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), empty()); + + TestDSpaceRunnableHandler runnableHandler = runScript(false); + + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(2)); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(2)); + + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", + PLACEHOLDER_PARENT_METADATA_VALUE, 0))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", + PLACEHOLDER_PARENT_METADATA_VALUE,0))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "4Science", 1))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", secondAuthorId,1))); + + } + + @Test + public void testItemEnhancementWithoutAuthorities() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + context.commit(); + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), empty()); + + TestDSpaceRunnableHandler runnableHandler = runScript(false); + + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + publication = reload(publication); + + assertThat(getMetadataValues(publication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), empty()); + + } + + @Test + public void testItemEnhancementEntityTypeInvalidCollectionUUID() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + context.commit(); + publication = reload(publication); + + TestDSpaceRunnableHandler runnableHandler = runScript(false, publication.getID().toString(), null, null, null); + + assertThat(runnableHandler.getException(), notNullValue()); + assertThat(runnableHandler.getException().getMessage(), + equalToIgnoringCase("specified Collection does not exist")); + } + + @Test + public void testItemEnhancementEntityTypeNoCollection() throws Exception { + context.turnOffAuthorisationSystem(); + EntityType projectType = EntityTypeBuilder.createEntityTypeBuilder(context, "Project").build(); + context.restoreAuthSystemState(); + + TestDSpaceRunnableHandler runnableHandler = runScript(false, null, projectType.getLabel(), null, null); + + assertThat(runnableHandler.getException(), notNullValue()); + assertThat(runnableHandler.getException().getMessage(), + equalToIgnoringCase("no Collections with EntityType " + projectType.getLabel())); + } + + @Test + public void testItemEnhancementEntityTypeInvalidForCollection() throws Exception { + context.turnOffAuthorisationSystem(); + EntityType personType = EntityTypeBuilder.createEntityTypeBuilder(context, "Person").build(); + context.commit(); + context.restoreAuthSystemState(); + + TestDSpaceRunnableHandler runnableHandler = runScript(false, publications.getID().toString(), + personType.getLabel(), null, null); + + assertThat(runnableHandler.getException(), notNullValue()); + assertThat(runnableHandler.getException().getMessage(), + equalToIgnoringCase("the specified Collection does not match with the specified EntityType")); + } + + @Test + public void testItemEnhancementEntityTypeInvalidEntityType() throws Exception { + + context.turnOffAuthorisationSystem(); + context.commit(); + + TestDSpaceRunnableHandler runnableHandler = runScript(false, null, "ResearchData", null, null); + + assertThat(runnableHandler.getException(), notNullValue()); + assertThat(runnableHandler.getException().getMessage(), equalToIgnoringCase("unknown EntityType ResearchData")); + } + + @Test + public void testItemEnhancementEntityTypeMaxMatches() throws Exception { + + context.turnOffAuthorisationSystem(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 3") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + context.commit(); + String max = "2"; + + TestDSpaceRunnableHandler runnableHandler + = runScript(false, collection.getID().toString(), null, null, max); + + assertThat(runnableHandler.getInfoMessages(), hasItems()); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced " + max + " items")); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + } + + @Test + public void testItemEnhancementEntityTypeLimitAndMaxMatches() throws Exception { + + context.turnOffAuthorisationSystem(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 3") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 4") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + ItemBuilder.createItem(context, collection) + .withTitle("Test publication 5") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build(); + + context.commit(); + + String limit = "2"; + String max = "4"; //smaller than collection size + + TestDSpaceRunnableHandler runnableHandler + = runScript(false, collection.getID().toString(), null, limit, max); + + assertThat(runnableHandler.getInfoMessages(), hasItems()); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced 2 out of max " + max + " items")); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced 4 out of max " + max + " items")); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced " + max + " items")); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + } + + @Test + public void testItemEnhancementEntityTypeLimitAndMaxMatches2() throws Exception { + + context.turnOffAuthorisationSystem(); + + List list = new ArrayList<>(); + + list.add(ItemBuilder.createItem(context, collection) + .withTitle("Test publication") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build()); + + list.add(ItemBuilder.createItem(context, collection) + .withTitle("Test publication 2 ") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build()); + + list.add(ItemBuilder.createItem(context, collection) + .withTitle("Test publication 3") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build()); + + list.add(ItemBuilder.createItem(context, collection) + .withTitle("Test publication 4") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build()); + + list.add(ItemBuilder.createItem(context, collection) + .withTitle("Test publication 5") + .withEntityType("Publication") + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build()); + + context.commit(); + + String limit = "3"; + String max = "7"; + + TestDSpaceRunnableHandler runnableHandler + = runScript(false, collection.getID().toString(), null, limit, max); + + assertThat(runnableHandler.getInfoMessages(), hasItems()); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced 3 out of max " + max + " items")); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced 5 out of max " + max + " items")); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced " + list.size() + " items")); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + } + + @Test + public void testItemEnhancementEntityTypeMaxMatchesPerCollectionsAndEntityType() throws Exception { + + context.turnOffAuthorisationSystem(); + EntityType publicationType = EntityTypeBuilder.createEntityTypeBuilder(context, "Publication").build(); + + List list = new ArrayList<>(); + + list.add(ItemBuilder.createItem(context, publications) + .withTitle("Test publication") + .withEntityType(publicationType.getLabel()) + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build()); + + list.add(ItemBuilder.createItem(context, publications2) + .withTitle("Test publication 2 ") + .withEntityType(publicationType.getLabel()) + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build()); + + list.add(ItemBuilder.createItem(context, publications2) + .withTitle("Test publication 3") + .withEntityType(publicationType.getLabel()) + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build()); + + list.add(ItemBuilder.createItem(context, publications2) + .withTitle("Test publication 4 ") + .withEntityType(publicationType.getLabel()) + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build()); + + list.add(ItemBuilder.createItem(context, publications3) + .withTitle("Test publication 5") + .withEntityType(publicationType.getLabel()) + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build()); + + list.add(ItemBuilder.createItem(context, publications3) + .withTitle("Test publication 6") + .withEntityType(publicationType.getLabel()) + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build()); + + list.add(ItemBuilder.createItem(context, publications3) + .withTitle("Test publication 7") + .withEntityType(publicationType.getLabel()) + .withAuthor("Jesse Pinkman") + .withAuthor("Jesse Smith") + .build()); + + context.commit(); + + // Max 2 per collection + String max = "2"; + + TestDSpaceRunnableHandler runnableHandler + = runScript(false, null, "Publication", null, max); + + // every collection max two items + assertThat(runnableHandler.getInfoMessages(), hasItems()); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced 1 out of max 2 items")); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced 3 out of max 2 items")); + assertThat(runnableHandler.getInfoMessages(), hasItem("enhanced 5 items")); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + } + + private TestDSpaceRunnableHandler runScript(boolean force) throws InstantiationException, IllegalAccessException { + TestDSpaceRunnableHandler runnableHandler = new TestDSpaceRunnableHandler(); + String[] args = force ? new String[] { "item-enhancer-type", "-f" } : new String[] { "item-enhancer-type" }; + ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), runnableHandler, kernelImpl); + return runnableHandler; + } + + private TestDSpaceRunnableHandler runScript(boolean force, String collectionuuid, String entitytype, String limit, + String max) throws InstantiationException, IllegalAccessException { + TestDSpaceRunnableHandler runnableHandler = new TestDSpaceRunnableHandler(); + List argslist = new ArrayList<>(); + argslist.add("item-enhancer-type"); + if (force) { + argslist.add("-f"); + } + if (StringUtils.isNotBlank(collectionuuid)) { + argslist.add("-c " + collectionuuid); + } + if (StringUtils.isNotBlank(entitytype)) { + argslist.add("-e " + entitytype); + } + if (StringUtils.isNotBlank(limit)) { + argslist.add("-l " + limit); + } + if (StringUtils.isNotBlank(max)) { + argslist.add("-m " + max); + } + String[] args = argslist.toArray(new String[0]); + + ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), runnableHandler, kernelImpl); + return runnableHandler; + } + + @SuppressWarnings("rawtypes") + private T reload(T entity) throws SQLException, AuthorizeException { + return context.reloadEntity(entity); + } + + private void replaceMetadata(Item item, String schema, String element, String qualifier, String newValue) + throws SQLException, AuthorizeException { + itemService.replaceMetadata(context, reload(item), schema, element, qualifier, ANY, newValue, null, -1, 0); + } + + private List getMetadataValues(Item item, String metadataField) { + return itemService.getMetadataByMetadataString(item, metadataField); + } + + private List getMetadataValues(WorkspaceItem item, String metadataField) { + return itemService.getMetadataByMetadataString(item.getItem(), metadataField); + } + +} diff --git a/dspace/config/spring/api/scripts.xml b/dspace/config/spring/api/scripts.xml index a7851655c5a5..9278fa976195 100644 --- a/dspace/config/spring/api/scripts.xml +++ b/dspace/config/spring/api/scripts.xml @@ -135,6 +135,16 @@ + + + + + + + + + +