diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 1c6879a5d70..a7d1fa104ed 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/MapConverterValueEnhancer.java b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/MapConverterValueEnhancer.java new file mode 100644 index 00000000000..73c6f66a13a --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/MapConverterValueEnhancer.java @@ -0,0 +1,143 @@ +/** + * 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.impl; + +import java.sql.SQLException; +import java.util.Objects; + +import org.apache.commons.lang.StringUtils; +import org.dspace.content.Item; +import org.dspace.content.MetadataField; +import org.dspace.content.enhancer.AbstractItemEnhancer; +import org.dspace.content.enhancer.ItemEnhancer; +import org.dspace.content.service.ItemService; +import org.dspace.content.service.MetadataFieldService; +import org.dspace.core.Context; +import org.dspace.services.ConfigurationService; +import org.dspace.util.SimpleMapConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Implementation of {@link ItemEnhancer} that add metadata values on the given + * item from the result of some mapConverter + * Only consider 1 to 1 value enhancement and no additional security/authority/language settings form the origin value. + * e.g. dc.type (dspace) -> dc.type (coar) + * + * @author Florian Gantner (florian.gantner@uni-bamberg.de) + * + */ +public class MapConverterValueEnhancer extends AbstractItemEnhancer { + + private static final Logger LOGGER = LoggerFactory.getLogger(MapConverterValueEnhancer.class); + + @Autowired + private ItemService itemService; + + @Autowired + private MetadataFieldService metadatafieldService; + + @Autowired + private ConfigurationService configurationService; + + private String sourceEntityType; + + private String sourceItemMetadataField; + private String targetItemMetadataField; + + private boolean useDefaultLanguage; + + private SimpleMapConverter converter; + + @Override + public boolean canEnhance(Context context, Item item) { + return sourceEntityType == null || sourceEntityType.equals(itemService.getEntityType(item)); + } + + @Override + public boolean enhance(Context context, Item item, boolean deepMode) { + try { + if (StringUtils.isBlank(sourceItemMetadataField) || Objects.isNull(converter) || + StringUtils.isBlank(targetItemMetadataField)) { + return false; + } + String sourceval; + String targetval; + String calculatedval; + sourceval = itemService.getMetadata(item, sourceItemMetadataField); + targetval = itemService.getMetadata(item, targetItemMetadataField); + if (StringUtils.isNotBlank(sourceval)) { + calculatedval = converter.getValue(sourceval); + if (StringUtils.isNotBlank(targetval) && !targetval.contentEquals(calculatedval)) { + // replace mdv if it's different + removeTargetMetadata(context, item); + addTargetMetadata(context, item, calculatedval); + return true; + } else if (StringUtils.isBlank(targetval)) { + // set new value + addTargetMetadata(context, item, calculatedval); + return true; + } else if (StringUtils.isNotBlank(sourceval) && StringUtils.isNotBlank(targetval) + && sourceval.contentEquals(targetval) && deepMode) { + //When both values are equal and deepMode is active, recalculate the value + removeTargetMetadata(context, item); + addTargetMetadata(context, item, calculatedval); + return true; + } + } else if (StringUtils.isBlank(sourceval) && StringUtils.isNotBlank(targetval)) { + // remove value + removeTargetMetadata(context, item); + return true; + } + } catch (Exception e) { + LOGGER.error("An error occurs enhancing item with id {}: {}", item.getID(), e.getMessage(), e); + //throw new SQLRuntimeException(e); + } + return false; + } + private void addTargetMetadata(Context context, Item item, String value) throws Exception { + MetadataField targetmd = metadatafieldService.findByString(context, targetItemMetadataField, '.'); + if (targetmd != null) { + String lang = (this.useDefaultLanguage) ? this.configurationService.getProperty("default.language") : null; + itemService.addMetadata(context, item, targetmd, lang, value); + } else { + LOGGER.error("No valid metadatavalue to enhance specified"); + } + } + + private void removeTargetMetadata(Context context, Item item) throws SQLException { + MetadataField targetmd = metadatafieldService.findByString(context, targetItemMetadataField, '.'); + if (targetmd != null) { + itemService.clearMetadata(context, item, targetmd.getMetadataSchema().getName(), + targetmd.getElement(), targetmd.getQualifier(), Item.ANY); + } else { + LOGGER.error("No valid metadatavalue to enhance specified"); + } + } + + public void setSourceEntityType(String sourceEntityType) { + this.sourceEntityType = sourceEntityType; + } + + public void setTargetItemMetadataField(String targetItemMetadataField) { + this.targetItemMetadataField = targetItemMetadataField; + } + + public void setSourceItemMetadataField(String sourceItemMetadataField) { + this.sourceItemMetadataField = sourceItemMetadataField; + } + + public void setConverter(SimpleMapConverter converter) { + this.converter = converter; + } + + public void setUseDefaultLanguage(boolean useDefaultLanguage) { + this.useDefaultLanguage = useDefaultLanguage; + } +} diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/NameEnhancer.java b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/NameEnhancer.java new file mode 100644 index 00000000000..3678c661a06 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/NameEnhancer.java @@ -0,0 +1,187 @@ +/** + * 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.impl; + +import java.sql.SQLException; +import java.util.List; +import java.util.Objects; + +import org.apache.commons.lang.StringUtils; +import org.dspace.content.Item; +import org.dspace.content.MetadataField; +import org.dspace.content.MetadataValue; +import org.dspace.content.enhancer.AbstractItemEnhancer; +import org.dspace.content.enhancer.ItemEnhancer; +import org.dspace.content.service.ItemService; +import org.dspace.content.service.MetadataFieldService; +import org.dspace.core.Context; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Implementation of {@link ItemEnhancer} that add metadata values on the given + * item from a list of other metadatavalues . + * The first value of the list that is not null matches and overwrites the existing value. + * Some default value can be specified if no fields were found + * + * @author Florian Gantner (florian.gantner@uni-bamberg.de) + * + */ +public class NameEnhancer extends AbstractItemEnhancer { + + private static final Logger LOGGER = LoggerFactory.getLogger(NameEnhancer.class); + + @Autowired + private ItemService itemService; + + @Autowired + private MetadataFieldService metadatafieldService; + + private String sourceEntityType; + + private String targetItemMetadataField; + + private List relatedItemMetadataFields; + private String defaultValue; + + @Override + public boolean canEnhance(Context context, Item item) { + return sourceEntityType == null || sourceEntityType.equals(itemService.getEntityType(item)); + } + + @Override + public boolean enhance(Context context, Item item, boolean deepMode) { + try { + if (Objects.isNull(relatedItemMetadataFields) || relatedItemMetadataFields.isEmpty() || + StringUtils.isBlank(targetItemMetadataField)) { + return false; + } + return checkNames(context, item, deepMode); + } catch (Exception e) { + LOGGER.error("An error occurs enhancing item with id {}: {}", item.getID(), e.getMessage(), e); + //Exception handling not supported by ItemEnhancerService. Thus, just log to continue other enhancers + //throw new SQLRuntimeException(e); + } + return false; + } + + /** + * Check the names/values from the specified metadatafield and compare the first value found + * with the target metadatafield. Updates the target metadatafield when the value is different + * or when deepMode is set. + * When the target metadatafield has not been set before, then set this value. + * @param context current Context + * @param item current item + * @param deepMode boolean + * @return boolean value if some change/update has happened + * @throws Exception when some error occurs + */ + private boolean checkNames(Context context, Item item, boolean deepMode) throws Exception { + // ignore languages of Metadata here. Assume main title is not repeated + // Could be more simplified + List currentnames = itemService.getMetadataByMetadataString(item, targetItemMetadataField); + + if (!currentnames.isEmpty()) { + // some name is assigned yet + for (MetadataValue currentname : currentnames) { + String val = currentname.getValue(); + for (String field : relatedItemMetadataFields) { + List fieldnames = + itemService.getMetadataByMetadataString(item, field); + if (fieldnames.isEmpty()) { + continue ; //No Values, try next loop + } + for (MetadataValue fieldname : fieldnames) { + if (StringUtils.isNotBlank(fieldname.getValue()) + && fieldname.getValue().contentEquals(val)) { + //Values are the same. No Update necessary + if (deepMode) { + // value is recalculated in deepMode + return updateTargetMetadata(context, item, fieldname.getValue(), true); + } + return false; + } else { + //values differ. We must update the value + return updateTargetMetadata(context, item, fieldname.getValue(), true); + } + } + } + } + if (StringUtils.isNotBlank(defaultValue) + && !currentnames.get(0).getValue().contentEquals(defaultValue)) { + // None of the names above matches. Set Default-Value, if it exists. Otherwise, do nothing + return updateTargetMetadata(context, item, defaultValue, true); + } + + } else { + // No Name assigned yet + // Check existing names + for (String field : relatedItemMetadataFields) { + List fieldnames = itemService.getMetadataByMetadataString(item, field); + if (fieldnames.isEmpty()) { + continue; //No Values, try next loop + } + for (MetadataValue fieldname : fieldnames) { + if (StringUtils.isNotBlank(fieldname.getValue())) { + //Got some value + return updateTargetMetadata(context, item, fieldname.getValue(), false); + } + } + } + // If no name exist, set defaultvalue + if (StringUtils.isNotBlank(defaultValue)) { + return updateTargetMetadata(context, item, defaultValue, false); + } + // otherwise do not assign any value + } + return false; + } + + /** + * Update/Set the target metadata with option to clear/delete previous metadatavalues + * @param context current Context + * @param item item to set metadatavalue + * @param value value to set + * @param clear clear/delete existing values byfore + * @return boolean value if the value has been updated successfully + * @throws SQLException when some error occurs + */ + private boolean updateTargetMetadata(Context context, Item item, String value, boolean clear) throws SQLException { + MetadataField targetmd = metadatafieldService.findByString(context, targetItemMetadataField, '.'); + if (targetmd != null) { + if (clear) { + itemService.clearMetadata(context, item, targetmd.getMetadataSchema().getName(), targetmd.getElement(), + targetmd.getQualifier(), Item.ANY); + } + itemService.addMetadata(context, item, targetmd, null, value); + return true; + } else { + LOGGER.error("No valid metadatavalue to enhance specified"); + } + return false; + } + + public void setSourceEntityType(String sourceEntityType) { + this.sourceEntityType = sourceEntityType; + } + + public void setTargetItemMetadataField(String targetItemMetadataField) { + this.targetItemMetadataField = targetItemMetadataField; + } + + public void setRelatedItemMetadataFields(List relatedItemMetadataFields) { + this.relatedItemMetadataFields = relatedItemMetadataFields; + } + + public void setDefaultValue(String defaultvalue) { + this.defaultValue = defaultvalue; + } + +} + 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 00000000000..258884a02e3 --- /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 00000000000..e64814c4b16 --- /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 00000000000..7add8b2541d --- /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 00000000000..04451a08e9d --- /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 655ddf45b32..dff0ce058ba 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 00000000000..4faead26694 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerByDateScriptIT.java @@ -0,0 +1,863 @@ +/** + * 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 testItemEnhancementNameWithoutForce() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item firstPerson = ItemBuilder.createItem(context, persons) + .withMetadata("crisrp", "name", null, "Walter White") + .build(); + + Item secondPerson = ItemBuilder.createItem(context, persons) + .withMetadata("crisrp", "name", null, "Alois White") + .withMetadata("crisrp", "name", "translated", "Alois W. White") + .build(); + + Item thirdPerson = ItemBuilder.createItem(context, persons) + .withMetadata("crisrp", "name", "translated", "Walt Alternative") + .build(); + + context.commit(); + + + assertThat(getMetadataValues(firstPerson, "dc.title"), empty()); + assertThat(getMetadataValues(secondPerson, "dc.title"), empty()); + assertThat(getMetadataValues(thirdPerson, "dc.title"), empty()); + + TestDSpaceRunnableHandler runnableHandler = runScript(false); + + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + firstPerson = reload(firstPerson); + secondPerson = reload(secondPerson); + thirdPerson = reload(thirdPerson); + + assertThat(getMetadataValues(firstPerson, "dc.title"), hasSize(1)); + assertThat(getMetadataValues(secondPerson, "dc.title"), hasSize(1)); + assertThat(getMetadataValues(thirdPerson, "dc.title"), hasSize(1)); + + assertThat(firstPerson.getMetadata(), hasItem(with("dc.title", "Walter White"))); + assertThat(secondPerson.getMetadata(), hasItem(with("dc.title", "Alois White"))); + assertThat(thirdPerson.getMetadata(), hasItem(with("dc.title", "Walt Alternative"))); + + context.turnOffAuthorisationSystem(); + + MetadataValue nameToRemove = getMetadataValues(secondPerson, "crisrp.name").get(0); + itemService.removeMetadataValues(context, secondPerson, List.of(nameToRemove)); + + replaceMetadata(thirdPerson, "crisrp", "name", "translated", "Walt D. Alternative"); + + context.restoreAuthSystemState(); + + runnableHandler = runScript(false); + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + firstPerson = reload(firstPerson); + secondPerson = reload(secondPerson); + thirdPerson = reload(thirdPerson); + + assertThat(firstPerson.getMetadata(), hasItem(with("dc.title", "Walter White"))); + assertThat(secondPerson.getMetadata(), hasItem(with("dc.title", "Alois W. White"))); + assertThat(thirdPerson.getMetadata(), hasItem(with("dc.title", "Walt D. Alternative"))); + } + + @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 00000000000..3a703198c6a --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerEntityTypeScriptIT.java @@ -0,0 +1,898 @@ +/** + * 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 publications; + + private Collection publications2; + + private Collection publications3; + + 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(); + + 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 Person") + .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 testItemEnhancementNameWithoutForce() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item firstPerson = ItemBuilder.createItem(context, persons) + .withMetadata("crisrp", "name", null, "Walter White") + .build(); + + Item secondPerson = ItemBuilder.createItem(context, persons) + .withMetadata("crisrp", "name", null, "Alois White") + .withMetadata("crisrp", "name", "translated", "Alois W. White") + .build(); + + Item thirdPerson = ItemBuilder.createItem(context, persons) + .withMetadata("crisrp", "name", "translated", "Walt Alternative") + .build(); + + context.commit(); + + + assertThat(getMetadataValues(firstPerson, "dc.title"), empty()); + assertThat(getMetadataValues(secondPerson, "dc.title"), empty()); + assertThat(getMetadataValues(thirdPerson, "dc.title"), empty()); + + TestDSpaceRunnableHandler runnableHandler = runScript(false); + + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + firstPerson = reload(firstPerson); + secondPerson = reload(secondPerson); + thirdPerson = reload(thirdPerson); + + assertThat(getMetadataValues(firstPerson, "dc.title"), hasSize(1)); + assertThat(getMetadataValues(secondPerson, "dc.title"), hasSize(1)); + assertThat(getMetadataValues(thirdPerson, "dc.title"), hasSize(1)); + + assertThat(firstPerson.getMetadata(), hasItem(with("dc.title", "Walter White"))); + assertThat(secondPerson.getMetadata(), hasItem(with("dc.title", "Alois White"))); + assertThat(thirdPerson.getMetadata(), hasItem(with("dc.title", "Walt Alternative"))); + + context.turnOffAuthorisationSystem(); + + MetadataValue nameToRemove = getMetadataValues(secondPerson, "crisrp.name").get(0); + itemService.removeMetadataValues(context, secondPerson, List.of(nameToRemove)); + + replaceMetadata(thirdPerson, "crisrp", "name", "translated", "Walt D. Alternative"); + + context.restoreAuthSystemState(); + + runnableHandler = runScript(false); + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + firstPerson = reload(firstPerson); + secondPerson = reload(secondPerson); + thirdPerson = reload(thirdPerson); + + assertThat(firstPerson.getMetadata(), hasItem(with("dc.title", "Walter White"))); + assertThat(secondPerson.getMetadata(), hasItem(with("dc.title", "Alois W. White"))); + assertThat(thirdPerson.getMetadata(), hasItem(with("dc.title", "Walt D. Alternative"))); + } + + @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-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerScriptIT.java b/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerScriptIT.java index 33913368b0a..b95242910f4 100644 --- a/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerScriptIT.java +++ b/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerScriptIT.java @@ -58,6 +58,7 @@ public class ItemEnhancerScriptIT extends AbstractIntegrationTestWithDatabase { private Collection collection; + private Collection persons; /** * This method will be run before the first test as per @BeforeClass. It will @@ -100,6 +101,12 @@ public void setup() { collection = CollectionBuilder.createCollection(context, parentCommunity) .withName("Collection") .build(); + + persons = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection") + .withEntityType("Person") + .build(); + context.restoreAuthSystemState(); } @@ -321,6 +328,70 @@ public void testItemEnhancementWithForce() throws Exception { } + @Test + public void testItemEnhancementNameWithoutForce() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item firstPerson = ItemBuilder.createItem(context, persons) + .withMetadata("crisrp", "name", null, "Walter White") + .build(); + + Item secondPerson = ItemBuilder.createItem(context, persons) + .withMetadata("crisrp", "name", null, "Alois White") + .withMetadata("crisrp", "name", "translated", "Alois W. White") + .build(); + + Item thirdPerson = ItemBuilder.createItem(context, persons) + .withMetadata("crisrp", "name", "translated", "Walt Alternative") + .build(); + + context.commit(); + + + assertThat(getMetadataValues(firstPerson, "dc.title"), empty()); + assertThat(getMetadataValues(secondPerson, "dc.title"), empty()); + assertThat(getMetadataValues(thirdPerson, "dc.title"), empty()); + + TestDSpaceRunnableHandler runnableHandler = runScript(false); + + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + firstPerson = reload(firstPerson); + secondPerson = reload(secondPerson); + thirdPerson = reload(thirdPerson); + + assertThat(getMetadataValues(firstPerson, "dc.title"), hasSize(1)); + assertThat(getMetadataValues(secondPerson, "dc.title"), hasSize(1)); + assertThat(getMetadataValues(thirdPerson, "dc.title"), hasSize(1)); + + assertThat(firstPerson.getMetadata(), hasItem(with("dc.title", "Walter White"))); + assertThat(secondPerson.getMetadata(), hasItem(with("dc.title", "Alois White"))); + assertThat(thirdPerson.getMetadata(), hasItem(with("dc.title", "Walt Alternative"))); + + context.turnOffAuthorisationSystem(); + + MetadataValue nameToRemove = getMetadataValues(secondPerson, "crisrp.name").get(0); + itemService.removeMetadataValues(context, secondPerson, List.of(nameToRemove)); + + replaceMetadata(thirdPerson, "crisrp", "name", "translated", "Walt D. Alternative"); + + context.restoreAuthSystemState(); + + runnableHandler = runScript(false); + assertThat(runnableHandler.getErrorMessages(), empty()); + assertThat(runnableHandler.getInfoMessages(), hasItem("Enhancement completed with success")); + + firstPerson = reload(firstPerson); + secondPerson = reload(secondPerson); + thirdPerson = reload(thirdPerson); + + assertThat(firstPerson.getMetadata(), hasItem(with("dc.title", "Walter White"))); + assertThat(secondPerson.getMetadata(), hasItem(with("dc.title", "Alois W. White"))); + assertThat(thirdPerson.getMetadata(), hasItem(with("dc.title", "Walt D. Alternative"))); + } + @Test public void testItemEnhancementMetadataPositions() throws Exception { diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 3b56ba53e83..f6846b955fb 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 76718f44ba3..a3ab33551c0 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index eb63a67e457..83af00bc343 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index 257d0b3a91f..a77c960283e 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -3,7 +3,7 @@ org.dspace dspace-rest war - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 DSpace (Deprecated) REST Webapp DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED. Please consider using the REST API in the dspace-server-webapp instead! @@ -12,7 +12,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index c83aa538698..a26174341ef 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -14,7 +14,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 39e2ccf36f3..8b67a90a8ea 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 10a44f3615f..803eba85911 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index e76bfca65b9..1c8103016e5 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace/config/spring/api/metadata-enhancers.xml b/dspace/config/spring/api/metadata-enhancers.xml index e36727959c2..acc44f57acf 100644 --- a/dspace/config/spring/api/metadata-enhancers.xml +++ b/dspace/config/spring/api/metadata-enhancers.xml @@ -57,6 +57,38 @@ + + + + + + + + crisrp.name + crisrp.name.translated + crisrp.name.variants + + + + + + + + + + + + + + + + + + + + + + diff --git a/dspace/config/spring/api/scripts.xml b/dspace/config/spring/api/scripts.xml index a7851655c5a..9278fa97619 100644 --- a/dspace/config/spring/api/scripts.xml +++ b/dspace/config/spring/api/scripts.xml @@ -135,6 +135,16 @@ + + + + + + + + + + diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index f5ae804f8b8..db2f30166a7 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index 00f691235bc..5a4426dddfd 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 ../../pom.xml diff --git a/dspace/modules/rest/pom.xml b/dspace/modules/rest/pom.xml index 4dfa2939bf9..8b80b402166 100644 --- a/dspace/modules/rest/pom.xml +++ b/dspace/modules/rest/pom.xml @@ -13,7 +13,7 @@ org.dspace modules - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace/modules/server-boot/pom.xml b/dspace/modules/server-boot/pom.xml index ee23c8ee7bc..90ceff73554 100644 --- a/dspace/modules/server-boot/pom.xml +++ b/dspace/modules/server-boot/pom.xml @@ -11,7 +11,7 @@ modules org.dspace - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index 3797e809dca..0d26cdb5fa1 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -7,7 +7,7 @@ modules org.dspace - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace/pom.xml b/dspace/pom.xml index 85b98dbb10d..b8cd8ad6c37 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 ../pom.xml diff --git a/pom.xml b/pom.xml index 8ec60b3a99d..c7b81d3eeb3 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 DSpace Parent Project DSpace-CRIS is an open source extension of DSpace (http://www.dspace.org) providing out of box support for the CRIS / RIMS and moder Institution Repository use cases with advanced features and optimized configurations @@ -979,14 +979,14 @@ org.dspace dspace-rest - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 jar classes org.dspace dspace-rest - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 war @@ -1137,62 +1137,62 @@ org.dspace dspace-api - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 org.dspace dspace-api test-jar - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 test org.dspace.modules additions - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 org.dspace dspace-sword - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 org.dspace dspace-swordv2 - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 org.dspace dspace-oai - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 org.dspace dspace-services - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 org.dspace dspace-server-webapp test-jar - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 test org.dspace dspace-rdf - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 org.dspace dspace-iiif - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 org.dspace dspace-server-webapp - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 @@ -2037,7 +2037,7 @@ scm:git:git@github.com:4Science/DSpace.git scm:git:git@github.com:4Science/DSpace.git git@github.com:4Science/DSpace.git - dspace-cris-2023.02.02-SNAPSHOT + dspace-cris-2023.02.02