diff --git a/dspace-api/src/main/java/org/dspace/curate/CurationCli.java b/dspace-api/src/main/java/org/dspace/curate/CurationCli.java index 3832ddf3eca0..8f5d91cc1cf5 100644 --- a/dspace-api/src/main/java/org/dspace/curate/CurationCli.java +++ b/dspace-api/src/main/java/org/dspace/curate/CurationCli.java @@ -8,21 +8,22 @@ package org.dspace.curate; import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; import java.io.FileReader; +import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintStream; import java.io.Writer; +import java.sql.SQLException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.HelpFormatter; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.PosixParser; import org.apache.commons.io.output.NullOutputStream; +import org.dspace.authorize.AuthorizeException; +import org.dspace.content.DSpaceObject; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.core.Context; import org.dspace.core.factory.CoreServiceFactory; @@ -30,246 +31,332 @@ import org.dspace.eperson.EPerson; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.EPersonService; +import org.dspace.handle.factory.HandleServiceFactory; +import org.dspace.handle.service.HandleService; +import org.dspace.scripts.DSpaceRunnable; +import org.dspace.utils.DSpace; /** * CurationCli provides command-line access to Curation tools and processes. * * @author richardrodgers */ -public class CurationCli { +public class CurationCli extends DSpaceRunnable { - /** - * Default constructor - */ - private CurationCli() { } - - public static void main(String[] args) throws Exception { - // create an options object and populate it - CommandLineParser parser = new PosixParser(); - - Options options = new Options(); - - options.addOption("t", "task", true, - "curation task name"); - options.addOption("T", "taskfile", true, - "file containing curation task names"); - options.addOption("i", "id", true, - "Id (handle) of object to perform task on, or 'all' to perform on whole repository"); - options.addOption("p", "parameter", true, - "a task parameter 'NAME=VALUE'"); - options.addOption("q", "queue", true, - "name of task queue to process"); - options.addOption("e", "eperson", true, - "email address of curating eperson"); - options.addOption("r", "reporter", true, - "relative or absolute path to the desired report file. " - + "Use '-' to report to console. " - + "If absent, no reporting"); - options.addOption("s", "scope", true, - "transaction scope to impose: use 'object', 'curation', or 'open'. If absent, 'open' " + - "applies"); - options.addOption("v", "verbose", false, - "report activity to stdout"); - options.addOption("h", "help", false, "help"); + private EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - CommandLine line = parser.parse(options, args); + private Context context; + private CurationClientOptions curationClientOptions; - String taskName = null; - String taskFileName = null; - String idName = null; - String taskQueueName = null; - String ePersonName = null; - String reporterName = null; - String scope = null; - boolean verbose = false; - final Map parameters = new HashMap<>(); + private String task; + private String taskFile; + private String id; + private String queue; + private String scope; + private String reporter; + private Map parameters; + private boolean verbose; - if (line.hasOption('h')) { - HelpFormatter help = new HelpFormatter(); - help.printHelp("CurationCli\n", options); - System.out - .println("\nwhole repo: CurationCli -t estimate -i all"); - System.out - .println("single item: CurationCli -t generate -i itemId"); - System.out - .println("task queue: CurationCli -q monthly"); - System.exit(0); + @Override + public void internalRun() throws Exception { + if (curationClientOptions == CurationClientOptions.HELP) { + printHelp(); + return; } - if (line.hasOption('t')) { // task - taskName = line.getOptionValue('t'); - } + Curator curator = initCurator(); - if (line.hasOption('T')) { // task file - taskFileName = line.getOptionValue('T'); + // load curation tasks + if (curationClientOptions == CurationClientOptions.TASK) { + long start = System.currentTimeMillis(); + handleCurationTask(curator); + this.endScript(start); } - if (line.hasOption('i')) { // id - idName = line.getOptionValue('i'); + // process task queue + if (curationClientOptions == CurationClientOptions.QUEUE) { + // process the task queue + TaskQueue taskQueue = (TaskQueue) CoreServiceFactory.getInstance().getPluginService() + .getSinglePlugin(TaskQueue.class); + if (taskQueue == null) { + super.handler.logError("No implementation configured for queue"); + throw new UnsupportedOperationException("No queue service available"); + } + long timeRun = this.runQueue(taskQueue, curator); + this.endScript(timeRun); } + } - if (line.hasOption('q')) { // task queue - taskQueueName = line.getOptionValue('q'); + /** + * Does the curation task (-t) or the task in the given file (-T). + * Checks: + * - if required option -i is missing. + * - if option -t has a valid task option + */ + private void handleCurationTask(Curator curator) throws IOException, SQLException { + String taskName; + if (commandLine.hasOption('t')) { + if (verbose) { + handler.logInfo("Adding task: " + this.task); + } + curator.addTask(this.task); + if (verbose && !curator.hasTask(this.task)) { + handler.logInfo("Task: " + this.task + " not resolved"); + } + } else if (commandLine.hasOption('T')) { + // load taskFile + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader(this.taskFile)); + while ((taskName = reader.readLine()) != null) { + if (verbose) { + super.handler.logInfo("Adding task: " + taskName); + } + curator.addTask(taskName); + } + } finally { + if (reader != null) { + reader.close(); + } + } } - - if (line.hasOption('e')) { // eperson - ePersonName = line.getOptionValue('e'); + // run tasks against object + if (verbose) { + super.handler.logInfo("Starting curation"); + super.handler.logInfo("Curating id: " + this.id); } + if ("all".equals(this.id)) { + // run on whole Site + curator.curate(context, + ContentServiceFactory.getInstance().getSiteService().findSite(context).getHandle()); + } else { + curator.curate(context, this.id); + } + } - if (line.hasOption('p')) { // parameter - for (String parameter : line.getOptionValues('p')) { - String[] parts = parameter.split("=", 2); - String name = parts[0].trim(); - String value; - if (parts.length > 1) { - value = parts[1].trim(); - } else { - value = "true"; + /** + * Runs task queue (-q set) + * + * @param queue The task queue + * @param curator The curator + * @return Time when queue started + */ + private long runQueue(TaskQueue queue, Curator curator) throws SQLException, AuthorizeException, IOException { + // use current time as our reader 'ticket' + long ticket = System.currentTimeMillis(); + Iterator entryIter = queue.dequeue(this.queue, ticket).iterator(); + while (entryIter.hasNext()) { + TaskQueueEntry entry = entryIter.next(); + if (verbose) { + super.handler.logInfo("Curating id: " + entry.getObjectId()); + } + curator.clear(); + // does entry relate to a DSO or workflow object? + if (entry.getObjectId().indexOf('/') > 0) { + for (String taskName : entry.getTaskNames()) { + curator.addTask(taskName); + } + curator.curate(context, entry.getObjectId()); + } else { + // make eperson who queued task the effective user + EPerson agent = ePersonService.findByEmail(context, entry.getEpersonId()); + if (agent != null) { + context.setCurrentUser(agent); } - parameters.put(name, value); + CurateServiceFactory.getInstance().getWorkflowCuratorService() + .curate(curator, context, entry.getObjectId()); } } - if (line.hasOption('r')) { // report file - reporterName = line.getOptionValue('r'); - } - + queue.release(this.queue, ticket, true); + return ticket; + } - if (line.hasOption('s')) { // transaction scope - scope = line.getOptionValue('s'); + /** + * End of curation script; logs script time if -v verbose is set + * + * @param timeRun Time script was started + * @throws SQLException If DSpace contextx can't complete + */ + private void endScript(long timeRun) throws SQLException { + context.complete(); + if (verbose) { + long elapsed = System.currentTimeMillis() - timeRun; + this.handler.logInfo("Ending curation. Elapsed time: " + elapsed); } + } - if (line.hasOption('v')) { // verbose - verbose = true; + /** + * Initialize the curator with command line variables + * + * @return Initialised curator + * @throws FileNotFoundException If file of command line variable -r reporter is not found + */ + private Curator initCurator() throws FileNotFoundException { + Curator curator = new Curator(); + OutputStream reporterStream; + if (null == this.reporter) { + reporterStream = new NullOutputStream(); + } else if ("-".equals(this.reporter)) { + reporterStream = System.out; + } else { + reporterStream = new PrintStream(this.reporter); } + Writer reportWriter = new OutputStreamWriter(reporterStream); + curator.setReporter(reportWriter); - // now validate the args - if (idName == null && taskQueueName == null) { - System.out.println("Id must be specified: a handle, 'all', or a task queue (-h for help)"); - System.exit(1); + if (this.scope != null) { + Curator.TxScope txScope = Curator.TxScope.valueOf(this.scope.toUpperCase()); + curator.setTransactionScope(txScope); } - if (taskName == null && taskFileName == null && taskQueueName == null) { - System.out.println("A curation task or queue must be specified (-h for help)"); - System.exit(1); - } + curator.addParameters(parameters); + // we are operating in batch mode, if anyone cares. + curator.setInvoked(Curator.Invoked.BATCH); + return curator; + } - if (scope != null && Curator.TxScope.valueOf(scope.toUpperCase()) == null) { - System.out.println("Bad transaction scope '" + scope + "': only 'object', 'curation' or 'open' recognized"); - System.exit(1); - } - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); + @Override + public void printHelp() { + super.printHelp(); + super.handler.logInfo("\nwhole repo: CurationCli -t estimate -i all"); + super.handler.logInfo("single item: CurationCli -t generate -i itemId"); + super.handler.logInfo("task queue: CurationCli -q monthly"); + } - Context c = new Context(Context.Mode.BATCH_EDIT); - if (ePersonName != null) { - EPerson ePerson = ePersonService.findByEmail(c, ePersonName); - if (ePerson == null) { - System.out.println("EPerson not found: " + ePersonName); - System.exit(1); + @Override + public CurationScriptConfiguration getScriptConfiguration() { + return new DSpace().getServiceManager().getServiceByName("curate", CurationScriptConfiguration.class); + } + + @Override + public void setup() { + if (this.commandLine.hasOption('e')) { + String ePersonEmail = this.commandLine.getOptionValue('e'); + this.context = new Context(Context.Mode.BATCH_EDIT); + try { + EPerson ePerson = ePersonService.findByEmail(this.context, ePersonEmail); + if (ePerson == null) { + super.handler.logError("EPerson not found: " + ePersonEmail); + throw new IllegalArgumentException("Unable to find a user with email: " + ePersonEmail); + } + this.context.setCurrentUser(ePerson); + } catch (SQLException e) { + throw new IllegalArgumentException("SQLException trying to find user with email: " + ePersonEmail); } - c.setCurrentUser(ePerson); } else { - c.turnOffAuthorisationSystem(); + throw new IllegalArgumentException("Needs an -e to set eperson (admin)"); } + this.curationClientOptions = CurationClientOptions.getClientOption(commandLine); - Curator curator = new Curator(); - OutputStream reporter; - if (null == reporterName) { - reporter = new NullOutputStream(); - } else if ("-".equals(reporterName)) { - reporter = System.out; + if (this.curationClientOptions != null) { + this.initGeneralLineOptionsAndCheckIfValid(); + if (curationClientOptions == CurationClientOptions.TASK) { + this.initTaskLineOptionsAndCheckIfValid(); + } else if (curationClientOptions == CurationClientOptions.QUEUE) { + this.queue = this.commandLine.getOptionValue('q'); + } } else { - reporter = new PrintStream(reporterName); + throw new IllegalArgumentException("[--help || --task|--taskfile <> -identifier <> || -queue <> ] must be" + + " specified"); } - Writer reportWriter = new OutputStreamWriter(reporter); - curator.setReporter(reportWriter); + } - if (scope != null) { - Curator.TxScope txScope = Curator.TxScope.valueOf(scope.toUpperCase()); - curator.setTransactionScope(txScope); + /** + * Fills in some optional command line options. + * Checks if there are missing required options or invalid values for options. + */ + private void initGeneralLineOptionsAndCheckIfValid() { + // report file + if (this.commandLine.hasOption('r')) { + this.reporter = this.commandLine.getOptionValue('r'); } - curator.addParameters(parameters); - // we are operating in batch mode, if anyone cares. - curator.setInvoked(Curator.Invoked.BATCH); - // load curation tasks - if (taskName != null) { - if (verbose) { - System.out.println("Adding task: " + taskName); - } - curator.addTask(taskName); - if (verbose && !curator.hasTask(taskName)) { - System.out.println("Task: " + taskName + " not resolved"); - } - } else if (taskQueueName == null) { - // load taskFile - BufferedReader reader = null; - try { - reader = new BufferedReader(new FileReader(taskFileName)); - while ((taskName = reader.readLine()) != null) { - if (verbose) { - System.out.println("Adding task: " + taskName); - } - curator.addTask(taskName); - } - } finally { - if (reader != null) { - reader.close(); + + // parameters + this.parameters = new HashMap<>(); + if (this.commandLine.hasOption('p')) { + for (String parameter : this.commandLine.getOptionValues('p')) { + String[] parts = parameter.split("=", 2); + String name = parts[0].trim(); + String value; + if (parts.length > 1) { + value = parts[1].trim(); + } else { + value = "true"; } + this.parameters.put(name, value); } } - // run tasks against object - long start = System.currentTimeMillis(); - if (verbose) { - System.out.println("Starting curation"); + + // verbose + verbose = false; + if (commandLine.hasOption('v')) { + verbose = true; } - if (idName != null) { - if (verbose) { - System.out.println("Curating id: " + idName); + + // scope + if (this.commandLine.getOptionValue('s') != null) { + this.scope = this.commandLine.getOptionValue('s'); + if (this.scope != null && Curator.TxScope.valueOf(this.scope.toUpperCase()) == null) { + this.handler.logError("Bad transaction scope '" + this.scope + "': only 'object', 'curation' or " + + "'open' recognized"); + throw new IllegalArgumentException( + "Bad transaction scope '" + this.scope + "': only 'object', 'curation' or " + + "'open' recognized"); } - if ("all".equals(idName)) { - // run on whole Site - curator.curate(c, ContentServiceFactory.getInstance().getSiteService().findSite(c).getHandle()); - } else { - curator.curate(c, idName); + } + } + + /** + * Fills in required command line options for the task or taskFile option. + * Checks if there are is a missing required -i option and if -i is either 'all' or a valid dso handle. + * Checks if -t task has a valid task option. + * Checks if -T taskfile is a valid file. + */ + private void initTaskLineOptionsAndCheckIfValid() { + // task or taskFile + if (this.commandLine.hasOption('t')) { + this.task = this.commandLine.getOptionValue('t'); + if (!CurationClientOptions.getTaskOptions().contains(this.task)) { + super.handler + .logError("-t task must be one of: " + CurationClientOptions.getTaskOptions()); + throw new IllegalArgumentException( + "-t task must be one of: " + CurationClientOptions.getTaskOptions()); } - } else { - // process the task queue - TaskQueue queue = (TaskQueue) CoreServiceFactory.getInstance().getPluginService() - .getSinglePlugin(TaskQueue.class); - if (queue == null) { - System.out.println("No implementation configured for queue"); - throw new UnsupportedOperationException("No queue service available"); + } else if (this.commandLine.hasOption('T')) { + this.taskFile = this.commandLine.getOptionValue('T'); + if (!(new File(this.taskFile).isFile())) { + super.handler + .logError("-T taskFile must be valid file: " + this.taskFile); + throw new IllegalArgumentException("-T taskFile must be valid file: " + this.taskFile); } - // use current time as our reader 'ticket' - long ticket = System.currentTimeMillis(); - Iterator entryIter = queue.dequeue(taskQueueName, ticket).iterator(); - while (entryIter.hasNext()) { - TaskQueueEntry entry = entryIter.next(); - if (verbose) { - System.out.println("Curating id: " + entry.getObjectId()); + } + + if (this.commandLine.hasOption('i')) { + this.id = this.commandLine.getOptionValue('i').toLowerCase(); + if (!this.id.equalsIgnoreCase("all")) { + HandleService handleService = HandleServiceFactory.getInstance().getHandleService(); + DSpaceObject dso; + try { + dso = handleService.resolveToObject(this.context, id); + } catch (SQLException e) { + super.handler.logError("SQLException trying to resolve handle " + id + " to a valid dso"); + throw new IllegalArgumentException( + "SQLException trying to resolve handle " + id + " to a valid dso"); } - curator.clear(); - // does entry relate to a DSO or workflow object? - if (entry.getObjectId().indexOf("/") > 0) { - for (String task : entry.getTaskNames()) { - curator.addTask(task); - } - curator.curate(c, entry.getObjectId()); - } else { - // make eperson who queued task the effective user - EPerson agent = ePersonService.findByEmail(c, entry.getEpersonId()); - if (agent != null) { - c.setCurrentUser(agent); - } - CurateServiceFactory.getInstance().getWorkflowCuratorService() - .curate(curator, c, entry.getObjectId()); + if (dso == null) { + super.handler.logError("Id must be specified: a valid dso handle or 'all'; " + this.id + " could " + + "not be resolved to valid dso handle"); + throw new IllegalArgumentException( + "Id must be specified: a valid dso handle or 'all'; " + this.id + " could " + + "not be resolved to valid dso handle"); } } - queue.release(taskQueueName, ticket, true); - } - c.complete(); - if (verbose) { - long elapsed = System.currentTimeMillis() - start; - System.out.println("Ending curation. Elapsed time: " + elapsed); + } else { + super.handler.logError("Id must be specified: a handle, 'all', or no -i and a -q task queue (-h for " + + "help)"); + throw new IllegalArgumentException( + "Id must be specified: a handle, 'all', or no -i and a -q task queue (-h for " + + "help)"); } } } diff --git a/dspace-api/src/main/java/org/dspace/curate/CurationClientOptions.java b/dspace-api/src/main/java/org/dspace/curate/CurationClientOptions.java new file mode 100644 index 000000000000..7daf107aadda --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/curate/CurationClientOptions.java @@ -0,0 +1,85 @@ +/** + * 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.curate; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Options; +import org.apache.commons.lang3.StringUtils; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; + +/** + * This Enum holds all the possible options and combinations for the Curation script + * + * @author Maria Verdonck (Atmire) on 23/06/2020 + */ +public enum CurationClientOptions { + TASK, + QUEUE, + HELP; + + private static List taskOptions; + + /** + * This method resolves the CommandLine parameters to figure out which action the curation script should perform + * + * @param commandLine The relevant CommandLine for the curation script + * @return The curation option to be ran, parsed from the CommandLine + */ + protected static CurationClientOptions getClientOption(CommandLine commandLine) { + if (commandLine.hasOption("h")) { + return CurationClientOptions.HELP; + } else if (commandLine.hasOption("t") || commandLine.hasOption("T")) { + return CurationClientOptions.TASK; + } else if (commandLine.hasOption("q")) { + return CurationClientOptions.QUEUE; + } + return null; + } + + protected static Options constructOptions() { + Options options = new Options(); + + options.addOption("t", "task", true, "curation task name; options: " + getTaskOptions()); + options.addOption("T", "taskfile", true, "file containing curation task names"); + options.addOption("i", "id", true, + "Id (handle) of object to perform task on, or 'all' to perform on whole repository"); + options.addOption("p", "parameter", true, "a task parameter 'NAME=VALUE'"); + options.addOption("q", "queue", true, "name of task queue to process"); + options.addOption("e", "eperson", true, "email address of curating eperson"); + options.addOption("r", "reporter", true, + "relative or absolute path to the desired report file. Use '-' to report to console. If absent, no " + + "reporting"); + options.addOption("s", "scope", true, + "transaction scope to impose: use 'object', 'curation', or 'open'. If absent, 'open' applies"); + options.addOption("v", "verbose", false, "report activity to stdout"); + options.addOption("h", "help", false, "help"); + + return options; + } + + /** + * Creates list of the taskOptions' keys from the configs of plugin.named.org.dspace.curate.CurationTask + * + * @return List of the taskOptions' keys from the configs of plugin.named.org.dspace.curate.CurationTask + */ + public static List getTaskOptions() { + if (taskOptions == null) { + ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + String[] taskConfigs = configurationService.getArrayProperty("plugin.named.org.dspace.curate.CurationTask"); + taskOptions = new ArrayList<>(); + for (String taskConfig : taskConfigs) { + taskOptions.add(StringUtils.substringAfterLast(taskConfig, "=").trim()); + } + } + return taskOptions; + } +} diff --git a/dspace-api/src/main/java/org/dspace/curate/CurationScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/curate/CurationScriptConfiguration.java new file mode 100644 index 000000000000..785926908ed6 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/curate/CurationScriptConfiguration.java @@ -0,0 +1,61 @@ +/** + * 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.curate; + +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; + +/** + * The {@link ScriptConfiguration} for the {@link CurationCli} script + * + * @author Maria Verdonck (Atmire) on 23/06/2020 + */ +public class CurationScriptConfiguration extends ScriptConfiguration { + + @Autowired + private AuthorizeService authorizeService; + + private Class dspaceRunnableClass; + + @Override + public Class getDspaceRunnableClass() { + return this.dspaceRunnableClass; + } + + @Override + public void setDspaceRunnableClass(Class dspaceRunnableClass) { + this.dspaceRunnableClass = dspaceRunnableClass; + } + + /** + * Only admin can run Curation script via the scripts and processes endpoints. + * @param context The relevant DSpace context + * @return True if currentUser is admin, otherwise false + */ + @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) { + super.options = CurationClientOptions.constructOptions(); + } + return options; + } +} diff --git a/dspace-api/src/main/java/org/dspace/curate/Curator.java b/dspace-api/src/main/java/org/dspace/curate/Curator.java index 44733174dfde..8f12750baea3 100644 --- a/dspace-api/src/main/java/org/dspace/curate/Curator.java +++ b/dspace-api/src/main/java/org/dspace/curate/Curator.java @@ -98,6 +98,7 @@ public Curator() { communityService = ContentServiceFactory.getInstance().getCommunityService(); itemService = ContentServiceFactory.getInstance().getItemService(); handleService = HandleServiceFactory.getInstance().getHandleService(); + resolver = new TaskResolver(); } /** @@ -142,10 +143,10 @@ public Curator addTask(String taskName) { // performance order currently FIFO - to be revisited perfList.add(taskName); } catch (IOException ioE) { - log.error("Task: '" + taskName + "' initialization failure: " + ioE.getMessage()); + System.out.println("Task: '" + taskName + "' initialization failure: " + ioE.getMessage()); } } else { - log.error("Task: '" + taskName + "' does not resolve"); + System.out.println("Task: '" + taskName + "' does not resolve"); } return this; } @@ -259,13 +260,6 @@ public void curate(Context c, String id) throws IOException { /** * Performs all configured tasks upon DSpace object * (Community, Collection or Item). - *

- * Note: Site-wide tasks will default to running as - * an Anonymous User unless you call the Site-wide task - * via the {@link curate(Context,String)} or - * {@link #curate(Context, DSpaceObject)} method with an - * authenticated Context object. - * * @param dso the DSpace object * @throws IOException if IO error */ @@ -325,7 +319,7 @@ public void queue(Context c, String id, String queueId) throws IOException { taskQ.enqueue(queueId, new TaskQueueEntry(c.getCurrentUser().getName(), System.currentTimeMillis(), perfList, id)); } else { - log.error("curate - no TaskQueue implemented"); + System.out.println("curate - no TaskQueue implemented"); } } @@ -346,7 +340,7 @@ public void report(String message) { try { reporter.append(message); } catch (IOException ex) { - log.error("Task reporting failure", ex); + System.out.println("Task reporting failure: " + ex); } } @@ -552,7 +546,7 @@ public boolean run(DSpaceObject dso) throws IOException { return !suspend(statusCode); } catch (IOException ioe) { //log error & pass exception upwards - log.error("Error executing curation task '" + task.getName() + "'", ioe); + System.out.println("Error executing curation task '" + task.getName() + "'; " + ioe); throw ioe; } } @@ -568,7 +562,7 @@ public boolean run(Context c, String id) throws IOException { return !suspend(statusCode); } catch (IOException ioe) { //log error & pass exception upwards - log.error("Error executing curation task '" + task.getName() + "'", ioe); + System.out.println("Error executing curation task '" + task.getName() + "'; " + ioe); throw ioe; } } diff --git a/dspace-api/src/test/data/dspaceFolder/assetstore/curate.txt b/dspace-api/src/test/data/dspaceFolder/assetstore/curate.txt new file mode 100644 index 000000000000..ff2cb89ef6b9 --- /dev/null +++ b/dspace-api/src/test/data/dspaceFolder/assetstore/curate.txt @@ -0,0 +1,2 @@ +checklinks +requiredmetadata 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 30fb69a3c414..76cb57a40d10 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 @@ -19,6 +19,12 @@ + + + + + + diff --git a/dspace-api/src/test/java/org/dspace/curate/CuratorTest.java b/dspace-api/src/test/java/org/dspace/curate/CuratorTest.java index 8ca6b6c1727e..4890fef26f30 100644 --- a/dspace-api/src/test/java/org/dspace/curate/CuratorTest.java +++ b/dspace-api/src/test/java/org/dspace/curate/CuratorTest.java @@ -8,23 +8,27 @@ package org.dspace.curate; import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.Map; import org.dspace.AbstractUnitTest; import org.dspace.content.DSpaceObject; +import org.dspace.content.Item; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.SiteService; +import org.dspace.core.factory.CoreServiceFactory; +import org.dspace.ctask.general.NoOpCurationTask; import org.dspace.services.ConfigurationService; import org.junit.Test; /** - * * @author mhwood */ -public class CuratorTest - extends AbstractUnitTest { +public class CuratorTest extends AbstractUnitTest { + private static final SiteService SITE_SERVICE = ContentServiceFactory.getInstance().getSiteService(); static final String RUN_PARAMETER_NAME = "runParameter"; @@ -32,20 +36,24 @@ public class CuratorTest static final String TASK_PROPERTY_NAME = "taskProperty"; static final String TASK_PROPERTY_VALUE = "a property"; - /** Value of a known runtime parameter, if any. */ + /** + * Value of a known runtime parameter, if any. + */ static String runParameter; - /** Value of a known task property, if any. */ + /** + * Value of a known task property, if any. + */ static String taskProperty; /** * Test of curate method, of class Curator. * Currently this just tests task properties and run parameters. + * * @throws java.lang.Exception passed through. */ @Test - public void testCurate_DSpaceObject() - throws Exception { + public void testCurate_DSpaceObject() throws Exception { System.out.println("curate"); final String TASK_NAME = "dummyTask"; @@ -53,7 +61,7 @@ public void testCurate_DSpaceObject() // Configure the task to be run. ConfigurationService cfg = kernelImpl.getConfigurationService(); cfg.setProperty("plugin.named.org.dspace.curate.CurationTask", - DummyTask.class.getName() + " = " + TASK_NAME); + DummyTask.class.getName() + " = " + TASK_NAME); cfg.setProperty(TASK_NAME + '.' + TASK_PROPERTY_NAME, TASK_PROPERTY_VALUE); // Get and configure a Curator. @@ -72,12 +80,40 @@ public void testCurate_DSpaceObject() // Check the result. System.out.format("Task %s result was '%s'%n", - TASK_NAME, instance.getResult(TASK_NAME)); + TASK_NAME, instance.getResult(TASK_NAME)); System.out.format("Task %s status was %d%n", - TASK_NAME, instance.getStatus(TASK_NAME)); + TASK_NAME, instance.getStatus(TASK_NAME)); assertEquals("Unexpected task status", - Curator.CURATE_SUCCESS, instance.getStatus(TASK_NAME)); + Curator.CURATE_SUCCESS, instance.getStatus(TASK_NAME)); assertEquals("Wrong run parameter", RUN_PARAMETER_VALUE, runParameter); assertEquals("Wrong task property", TASK_PROPERTY_VALUE, taskProperty); } + + @Test + public void testCurate_NoOpTask() throws Exception { + + CoreServiceFactory.getInstance().getPluginService().clearNamedPluginClasses(); + + final String TASK_NAME = "noop"; + + // Configure the noop task to be run. + ConfigurationService cfg = kernelImpl.getConfigurationService(); + cfg.setProperty("plugin.named.org.dspace.curate.CurationTask", + NoOpCurationTask.class.getName() + " = " + TASK_NAME); + + // Get and configure a Curator. + Curator curator = new Curator(); + + StringBuilder reporterOutput = new StringBuilder(); + curator.setReporter(reporterOutput); // Send any report to our StringBuilder. + + curator.addTask(TASK_NAME); + Item item = mock(Item.class); + when(item.getType()).thenReturn(2); + when(item.getHandle()).thenReturn("testHandle"); + curator.curate(context, item); + + assertEquals(Curator.CURATE_SUCCESS, curator.getStatus(TASK_NAME)); + assertEquals(reporterOutput.toString(), "No operation performed on testHandle"); + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScriptRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScriptRestRepositoryIT.java index c1ba31305e17..aeecd4bbe2d3 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScriptRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScriptRestRepositoryIT.java @@ -82,7 +82,9 @@ public void findAllScriptsTest() throws Exception { ScriptMatcher.matchScript(scriptConfigurations.get(2).getName(), scriptConfigurations.get(2).getDescription()), ScriptMatcher.matchScript(scriptConfigurations.get(3).getName(), - scriptConfigurations.get(3).getDescription()) + scriptConfigurations.get(3).getDescription()), + ScriptMatcher.matchScript(scriptConfigurations.get(4).getName(), + scriptConfigurations.get(4).getDescription()) ))); } @@ -139,7 +141,7 @@ public void findOneScriptByNameTest() throws Exception { getClient(token).perform(get("/api/system/scripts/mock-script")) .andExpect(status().isOk()) .andExpect(jsonPath("$", ScriptMatcher - .matchMockScript(scriptConfigurations.get(3).getOptions()))); + .matchMockScript(scriptConfigurations.get(scriptConfigurations.size() - 1).getOptions()))); } @Test diff --git a/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java b/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java new file mode 100644 index 000000000000..4a8e907c5e8f --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java @@ -0,0 +1,441 @@ +/** + * 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.curate; + +import static com.jayway.jsonpath.JsonPath.read; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.containsStringIgnoringCase; +import static org.hamcrest.Matchers.is; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.io.File; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import com.google.gson.Gson; +import org.dspace.app.rest.builder.CollectionBuilder; +import org.dspace.app.rest.builder.CommunityBuilder; +import org.dspace.app.rest.builder.ItemBuilder; +import org.dspace.app.rest.builder.ProcessBuilder; +import org.dspace.app.rest.converter.DSpaceRunnableParameterConverter; +import org.dspace.app.rest.matcher.ProcessMatcher; +import org.dspace.app.rest.model.ParameterValueRest; +import org.dspace.app.rest.model.ProcessRest; +import org.dspace.app.rest.model.ScriptRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.Item; +import org.dspace.content.ProcessStatus; +import org.dspace.scripts.DSpaceCommandLineParameter; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * IT for {@link CurationCli} + * + * @author Maria Verdonck (Atmire) on 24/06/2020 + */ +public class CurationScriptIT extends AbstractControllerIntegrationTest { + + @Autowired + private DSpaceRunnableParameterConverter dSpaceRunnableParameterConverter; + + private final static String SCRIPTS_ENDPOINT = "/api/" + ScriptRest.CATEGORY + "/" + ScriptRest.PLURAL_NAME; + private final static String CURATE_SCRIPT_ENDPOINT = SCRIPTS_ENDPOINT + "/curate/" + ProcessRest.PLURAL_NAME; + + @Test + public void curateScript_invalidTaskOption() throws Exception { + context.turnOffAuthorisationSystem(); + + String token = getAuthToken(admin.getEmail(), password); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); + + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + LinkedList parameters = new LinkedList<>(); + + parameters.add(new DSpaceCommandLineParameter("-e", admin.getEmail())); + parameters.add(new DSpaceCommandLineParameter("-i", publicItem1.getHandle())); + parameters.add(new DSpaceCommandLineParameter("-t", "invalidTaskOption")); + + List list = parameters.stream() + .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter + .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) + .collect(Collectors.toList()); + + context.restoreAuthSystemState(); + + // Request with -t + getClient(token) + .perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data") + .param("properties", + new Gson().toJson(list))) + // Illegal Argument Exception + .andExpect(status().isBadRequest()) + // Contains the valid options + .andExpect(status().reason(containsString(CurationClientOptions.getTaskOptions().toString()))); + } + + @Test + public void curateScript_MissingEperson() throws Exception { + context.turnOffAuthorisationSystem(); + + String token = getAuthToken(admin.getEmail(), password); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); + + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + LinkedList parameters = new LinkedList<>(); + + parameters.add(new DSpaceCommandLineParameter("-i", publicItem1.getHandle())); + parameters.add(new DSpaceCommandLineParameter("-t", CurationClientOptions.getTaskOptions().get(0))); + + List list = parameters.stream() + .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter + .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) + .collect(Collectors.toList()); + + context.restoreAuthSystemState(); + + // Request with missing required -e + getClient(token) + .perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data") + .param("properties", + new Gson().toJson(list))) + // Illegal Argument Exception + .andExpect(status().isBadRequest()) + // Contains -e (the missing required cl option + .andExpect(status().reason(containsString("-e"))); + } + + @Test + public void curateScript_NonExistentEPerson() throws Exception { + context.turnOffAuthorisationSystem(); + + String token = getAuthToken(admin.getEmail(), password); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); + + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + LinkedList parameters = new LinkedList<>(); + + parameters.add(new DSpaceCommandLineParameter("-e", "nonExistentEmail@test.com")); + parameters.add(new DSpaceCommandLineParameter("-i", publicItem1.getHandle())); + parameters.add(new DSpaceCommandLineParameter("-t", CurationClientOptions.getTaskOptions().get(0))); + + List list = parameters.stream() + .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter + .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) + .collect(Collectors.toList()); + + context.restoreAuthSystemState(); + + // Request with -e + getClient(token) + .perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data") + .param("properties", + new Gson().toJson(list))) + // Illegal Argument Exception + .andExpect(status().isBadRequest()) + // Contains email + .andExpect(status().reason(containsString("email"))); + } + + @Test + public void curateScript_MissingHandle() throws Exception { + String token = getAuthToken(admin.getEmail(), password); + + LinkedList parameters = new LinkedList<>(); + + parameters.add(new DSpaceCommandLineParameter("-e", admin.getEmail())); + parameters.add(new DSpaceCommandLineParameter("-t", CurationClientOptions.getTaskOptions().get(0))); + + List list = parameters.stream() + .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter + .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) + .collect(Collectors.toList()); + + // Request with missing required -i + getClient(token) + .perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data") + .param("properties", + new Gson().toJson(list))) + // Illegal Argument Exception + .andExpect(status().isBadRequest()) + // Contains handle + .andExpect(status().reason(containsString("handle"))); + } + + @Test + public void curateScript_invalidHandle() throws Exception { + String token = getAuthToken(admin.getEmail(), password); + + LinkedList parameters = new LinkedList<>(); + + parameters.add(new DSpaceCommandLineParameter("-i", "invalidhandle")); + parameters.add(new DSpaceCommandLineParameter("-e", admin.getEmail())); + parameters.add(new DSpaceCommandLineParameter("-t", CurationClientOptions.getTaskOptions().get(0))); + + List list = parameters.stream() + .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter + .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) + .collect(Collectors.toList()); + + // Request with missing required -i + getClient(token) + .perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data") + .param("properties", + new Gson().toJson(list))) + // Illegal Argument Exception + .andExpect(status().isBadRequest()) + // Contains invalidHandle + .andExpect(status().reason(containsStringIgnoringCase("invalidhandle"))); + } + + @Test + public void curateScript_MissingTaskOrTaskFile() throws Exception { + context.turnOffAuthorisationSystem(); + + String token = getAuthToken(admin.getEmail(), password); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); + + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + LinkedList parameters = new LinkedList<>(); + + parameters.add(new DSpaceCommandLineParameter("-e", admin.getEmail())); + parameters.add(new DSpaceCommandLineParameter("-i", publicItem1.getHandle())); + + List list = parameters.stream() + .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter + .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) + .collect(Collectors.toList()); + + context.restoreAuthSystemState(); + + // Request without -t or -T (and no -q ) + getClient(token) + .perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data") + .param("properties", + new Gson().toJson(list))) + // Illegal Argument Exception + .andExpect(status().isBadRequest()) + // Contains task + .andExpect(status().reason(containsString("task"))); + } + + @Test + public void curateScript_InvalidScope() throws Exception { + String token = getAuthToken(admin.getEmail(), password); + + LinkedList parameters = new LinkedList<>(); + + parameters.add(new DSpaceCommandLineParameter("-e", admin.getEmail())); + parameters.add(new DSpaceCommandLineParameter("-i", "all")); + parameters.add(new DSpaceCommandLineParameter("-s", "invalidScope")); + + List list = parameters.stream() + .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter + .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) + .collect(Collectors.toList()); + + // Request with invalid -s ; must be object, curation or open + getClient(token) + .perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data") + .param("properties", + new Gson().toJson(list))) + // Illegal Argument Exception + .andExpect(status().isBadRequest()); + } + + @Test + public void curateScript_InvalidTaskFile() throws Exception { + String token = getAuthToken(admin.getEmail(), password); + + LinkedList parameters = new LinkedList<>(); + + parameters.add(new DSpaceCommandLineParameter("-e", admin.getEmail())); + parameters.add(new DSpaceCommandLineParameter("-i", "all")); + parameters.add(new DSpaceCommandLineParameter("-T", "invalidTaskFile")); + + List list = parameters.stream() + .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter + .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) + .collect(Collectors.toList()); + + // Request with invalid -s ; must be object, curation or open + getClient(token) + .perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data") + .param("properties", + new Gson().toJson(list))) + // Illegal Argument Exception + .andExpect(status().isBadRequest()) + // Contains taskFile + .andExpect(status().reason(containsString("taskFile"))); + } + + @Test + public void curateScript_validRequest_Task() throws Exception { + context.turnOffAuthorisationSystem(); + + String token = getAuthToken(admin.getEmail(), password); + AtomicReference idRef = new AtomicReference<>(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); + + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + LinkedList parameters = new LinkedList<>(); + + parameters.add(new DSpaceCommandLineParameter("-e", admin.getEmail())); + parameters.add(new DSpaceCommandLineParameter("-i", publicItem1.getHandle())); + parameters.add(new DSpaceCommandLineParameter("-t", CurationClientOptions.getTaskOptions().get(0))); + + List list = parameters.stream() + .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter + .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) + .collect(Collectors.toList()); + + context.restoreAuthSystemState(); + + try { + getClient(token) + .perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data") + .param("properties", + new Gson().toJson(list))) + .andExpect(status().isAccepted()) + .andExpect(jsonPath("$", is( + ProcessMatcher.matchProcess("curate", + String.valueOf(admin.getID()), parameters, + ProcessStatus.SCHEDULED)))) + .andDo(result -> idRef + .set(read(result.getResponse().getContentAsString(), "$.processId"))); + } finally { + ProcessBuilder.deleteProcess(idRef.get()); + } + } + + @Test + public void curateScript_validRequest_TaskFile() throws Exception { + context.turnOffAuthorisationSystem(); + + String token = getAuthToken(admin.getEmail(), password); + AtomicReference idRef = new AtomicReference<>(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); + + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + File taskFile = new File(testProps.get("test.curateTaskFile").toString()); + + LinkedList parameters = new LinkedList<>(); + parameters.add(new DSpaceCommandLineParameter("-e", admin.getEmail())); + parameters.add(new DSpaceCommandLineParameter("-i", publicItem1.getHandle())); + parameters.add(new DSpaceCommandLineParameter("-T", taskFile.getAbsolutePath())); + + List list = parameters.stream() + .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter + .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) + .collect(Collectors.toList()); + + context.restoreAuthSystemState(); + + try { + getClient(token) + .perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data") + .param("properties", + new Gson().toJson(list))) + .andExpect(status().isAccepted()) + .andExpect(jsonPath("$", is( + ProcessMatcher.matchProcess("curate", + String.valueOf(admin.getID()), parameters, + ProcessStatus.SCHEDULED)))) + .andDo(result -> idRef + .set(read(result.getResponse().getContentAsString(), "$.processId"))); + } finally { + ProcessBuilder.deleteProcess(idRef.get()); + } + } + +} diff --git a/dspace-server-webapp/src/test/resources/test-config.properties b/dspace-server-webapp/src/test/resources/test-config.properties index 273d93c96874..3af96b20fcf4 100644 --- a/dspace-server-webapp/src/test/resources/test-config.properties +++ b/dspace-server-webapp/src/test/resources/test-config.properties @@ -11,3 +11,6 @@ test.folder.assetstore = ./target/testing/dspace/assetstore #Path for a test file to create bitstreams test.bitstream = ./target/testing/dspace/assetstore/ConstitutionofIreland.pdf + +#Path for a test Taskfile for the curate script +test.curateTaskFile = ./target/testing/dspace/assetstore/curate.txt diff --git a/dspace/config/launcher.xml b/dspace/config/launcher.xml index f06225e5cc53..4dd49ac14611 100644 --- a/dspace/config/launcher.xml +++ b/dspace/config/launcher.xml @@ -54,13 +54,6 @@ org.dspace.administer.CreateAdministrator - - curate - Perform curation tasks on DSpace objects - - org.dspace.curate.CurationCli - - database Perform database tasks like test database connection, migrate/repair database, remove database diff --git a/dspace/config/modules/curate.cfg b/dspace/config/modules/curate.cfg index cf1a25410cc9..df6d4f855add 100644 --- a/dspace/config/modules/curate.cfg +++ b/dspace/config/modules/curate.cfg @@ -11,8 +11,8 @@ plugin.named.org.dspace.curate.CurationTask = org.dspace.ctask.general.NoOpCurationTask = noop plugin.named.org.dspace.curate.CurationTask = org.dspace.ctask.general.ProfileFormats = profileformats plugin.named.org.dspace.curate.CurationTask = org.dspace.ctask.general.RequiredMetadata = requiredmetadata -plugin.named.org.dspace.curate.CurationTask = org.dspace.ctask.general.ClamScan = vscan -plugin.named.org.dspace.curate.CurationTask = org.dspace.ctask.general.MicrosoftTranslator = translate +#plugin.named.org.dspace.curate.CurationTask = org.dspace.ctask.general.ClamScan = vscan +#plugin.named.org.dspace.curate.CurationTask = org.dspace.ctask.general.MicrosoftTranslator = translate plugin.named.org.dspace.curate.CurationTask = org.dspace.ctask.general.MetadataValueLinkChecker = checklinks # add new tasks here (or in additional config files) @@ -25,30 +25,6 @@ curate.taskqueue.dir = ${dspace.dir}/ctqueues # (optional) directory location of scripted (non-java) tasks # curate.script.dir = ${dspace.dir}/ctscripts -# Friendly names for curation tasks to appear in admin UI -# Also acts as a filter - i.e. tasks not enumerated here can still -# be invoked on cmd line, etc - just not in UI -curate.ui.tasknames = profileformats = Profile Bitstream Formats -curate.ui.tasknames = requiredmetadata = Check for Required Metadata -curate.ui.tasknames = checklinks = Check Links in Metadata - -# Tasks may be organized into named groups which display together in UI drop-downs -# curate.ui.taskgroups = \ -# general = General Purpose Tasks, - -# Group membership is defined using comma-separated lists of task names, one property per group -# curate.ui.taskgroup.general = profileformats, requiredmetadata, checklinks - -# Name of queue used when tasks queued in Admin UI -curate.ui.queuename = admin_ui - -# Localized names for curation status codes in Admin UI -curate.ui.statusmessages = \ - -3 = Unknown Task, \ - -2 = No Status Set, \ - -1 = Error, \ - 0 = Success, \ - 1 = Fail, \ - 2 = Skip, \ - other = Invalid Status +# Ensure list of Curation Tasks (defined above) is available via the REST API /api/config/properties endpoint +rest.properties.exposed = plugin.named.org.dspace.curate.CurationTask diff --git a/dspace/config/spring/api/scripts.xml b/dspace/config/spring/api/scripts.xml index 0713baad199f..31d457f09db9 100644 --- a/dspace/config/spring/api/scripts.xml +++ b/dspace/config/spring/api/scripts.xml @@ -18,4 +18,9 @@ + + + + +