diff --git a/.gitattributes b/.gitattributes index 03b42152e9..c00b03e686 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,12 @@ # Auto detect text files and perform LF normalization * text=auto +# Ensure Unix files always keep Unix line endings +*.sh text eol=lf + +# Ensure Windows files always keep Windows line endings +*.bat text eol=crlf + # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain diff --git a/.travis.yml b/.travis.yml index dd5c0b2b41..dfc4c31799 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,14 +2,14 @@ language: java sudo: false dist: trusty -env: +env: # Give Maven 1GB of memory to work with - MAVEN_OPTS=-Xmx1024M jdk: - # DS-3384 Oracle JDK 8 has DocLint enabled by default. + # DS-3384 Oracle JDK has DocLint enabled by default. # Let's use this to catch any newly introduced DocLint issues. - - oraclejdk8 + - oraclejdk11 ## Should we run into any problems with oraclejdk8 on Travis, we may try the following workaround. ## https://docs.travis-ci.com/user/languages/java#Testing-Against-Multiple-JDKs @@ -19,7 +19,6 @@ jdk: # packages: # - oracle-java8-installer -# Install prerequisites for building Mirage2 more rapidly before_install: # Remove outdated settings.xml from Travis builds. Workaround for https://github.com/travis-ci/travis-ci/issues/4629 - rm ~/.m2/settings.xml @@ -33,11 +32,12 @@ script: # license:check => Validate all source code license headers # -Dmaven.test.skip=false => Enable DSpace Unit Tests # -DskipITs=false => Enable DSpace Integration Tests + # -Pdspace-rest => Enable optional dspace-rest module as part of build # -P !assembly => Skip assembly of "dspace-installer" directory (as it can be memory intensive) # -B => Maven batch/non-interactive mode (recommended for CI) # -V => Display Maven version info before build # -Dsurefire.rerunFailingTestsCount=2 => try again for flakey tests, and keep track of/report on number of retries - - "mvn clean install license:check -Dmaven.test.skip=false -DskipITs=false -P !assembly -B -V -Dsurefire.rerunFailingTestsCount=2" + - "mvn clean install license:check -Dmaven.test.skip=false -DskipITs=false -Pdspace-rest -P !assembly -B -V -Dsurefire.rerunFailingTestsCount=2" # After a successful build and test (see 'script'), send code coverage reports to coveralls.io # These code coverage reports are generated by jacoco-maven-plugin (during test process above). diff --git a/Dockerfile.jdk8 b/Dockerfile similarity index 74% rename from Dockerfile.jdk8 rename to Dockerfile index aa486d47be..006f32f28e 100644 --- a/Dockerfile.jdk8 +++ b/Dockerfile @@ -1,12 +1,11 @@ # This image will be published as dspace/dspace -# See https://dspace-labs.github.io/DSpace-Docker-Images/ for usage details +# See https://github.com/DSpace/DSpace/tree/master/dspace/src/main/docker for usage details # -# This version is JDK8 compatible -# - tomcat:8-jre8 +# This version is JDK11 compatible +# - tomcat:8-jdk11 # - ANT 1.10.7 -# - maven:3-jdk-8 -# - note: -# - default tag for branch: dspace/dspace: dspace/dspace:dspace-7_x-jdk8 +# - maven:3-jdk-11 (see dspace-dependencies) +# - note: default tag for branch: dspace/dspace: dspace/dspace:dspace-7_x # Step 1 - Run Maven Build FROM dspace/dspace-dependencies:dspace-7_x as build @@ -24,13 +23,14 @@ USER dspace ADD --chown=dspace . /app/ COPY dspace/src/main/docker/local.cfg /app/local.cfg -# Build DSpace. Copy the dspace-install directory to /install. Clean up the build to keep the docker image small +# Build DSpace (note: this build doesn't include the optional, deprecated "dspace-rest" webapp) +# Copy the dspace-install directory to /install. Clean up the build to keep the docker image small RUN mvn package && \ mv /app/dspace/target/${TARGET_DIR}/* /install && \ mvn clean # Step 2 - Run Ant Deploy -FROM tomcat:8-jre8 as ant_build +FROM tomcat:8-jdk11 as ant_build ARG TARGET_DIR=dspace-installer COPY --from=build /install /dspace-src WORKDIR /dspace-src @@ -47,7 +47,7 @@ RUN ant init_installation update_configs update_code update_webapps # Step 3 - Run tomcat # Create a new tomcat image that does not retain the the build directory contents -FROM tomcat:8-jre8 +FROM tomcat:8-jdk11 ENV DSPACE_INSTALL=/dspace COPY --from=ant_build /dspace $DSPACE_INSTALL EXPOSE 8080 8009 @@ -55,12 +55,9 @@ EXPOSE 8080 8009 ENV JAVA_OPTS=-Xmx2000m # Run the "server" webapp off the /server path (e.g. http://localhost:8080/server/) -# and the v6.x (deprecated) REST API off the "/rest" path -RUN ln -s $DSPACE_INSTALL/webapps/server /usr/local/tomcat/webapps/server && \ - ln -s $DSPACE_INSTALL/webapps/rest /usr/local/tomcat/webapps/rest +RUN ln -s $DSPACE_INSTALL/webapps/server /usr/local/tomcat/webapps/server # If you wish to run "server" webapp off the ROOT path, then comment out the above RUN, and uncomment the below RUN. # You also MUST update the URL in dspace/src/main/docker/local.cfg # Please note that server webapp should only run on one path at a time. #RUN mv /usr/local/tomcat/webapps/ROOT /usr/local/tomcat/webapps/ROOT.bk && \ -# ln -s $DSPACE_INSTALL/webapps/server /usr/local/tomcat/webapps/ROOT && \ -# ln -s $DSPACE_INSTALL/webapps/rest /usr/local/tomcat/webapps/rest \ No newline at end of file +# ln -s $DSPACE_INSTALL/webapps/server /usr/local/tomcat/webapps/ROOT diff --git a/Dockerfile.cli.jdk8 b/Dockerfile.cli similarity index 80% rename from Dockerfile.cli.jdk8 rename to Dockerfile.cli index 4c41e7d83b..116b251f2d 100644 --- a/Dockerfile.cli.jdk8 +++ b/Dockerfile.cli @@ -1,12 +1,11 @@ # This image will be published as dspace/dspace-cli -# See https://dspace-labs.github.io/DSpace-Docker-Images/ for usage details +# See https://github.com/DSpace/DSpace/tree/master/dspace/src/main/docker for usage details # -# This version is JDK8 compatible -# - openjdk:8-jdk +# This version is JDK11 compatible +# - openjdk:11 # - ANT 1.10.7 -# - maven:3-jdk-8 -# - note: -# - default tag for branch: dspace/dspace-cli: dspace/dspace-cli:dspace-7_x +# - maven:3-jdk-11 (see dspace-dependencies) +# - note: default tag for branch: dspace/dspace-cli: dspace/dspace-cli:dspace-7_x # Step 1 - Run Maven Build FROM dspace/dspace-dependencies:dspace-7_x as build @@ -25,12 +24,12 @@ ADD --chown=dspace . /app/ COPY dspace/src/main/docker/local.cfg /app/local.cfg # Build DSpace. Copy the dspace-install directory to /install. Clean up the build to keep the docker image small -RUN mvn package -P'!dspace-rest' && \ +RUN mvn package && \ mv /app/dspace/target/${TARGET_DIR}/* /install && \ mvn clean # Step 2 - Run Ant Deploy -FROM openjdk:8-jdk as ant_build +FROM openjdk:11 as ant_build ARG TARGET_DIR=dspace-installer COPY --from=build /install /dspace-src WORKDIR /dspace-src @@ -47,7 +46,7 @@ RUN ant init_installation update_configs update_code # Step 3 - Run jdk # Create a new tomcat image that does not retain the the build directory contents -FROM openjdk:8-jdk +FROM openjdk:11 ENV DSPACE_INSTALL=/dspace COPY --from=ant_build /dspace $DSPACE_INSTALL diff --git a/Dockerfile.dependencies b/Dockerfile.dependencies index a7107d46b5..54647ebad1 100644 --- a/Dockerfile.dependencies +++ b/Dockerfile.dependencies @@ -1,8 +1,11 @@ # This image will be published as dspace/dspace-dependencies -# The purpose of this image is to make the build for dspace/dspace run faster +# The purpose of this image is to make the build for dspace/dspace run faster +# +# This version is JDK11 compatible +# - maven:3-jdk-11 # Step 1 - Run Maven Build -FROM maven:3-jdk-8 as build +FROM maven:3-jdk-11 as build ARG TARGET_DIR=dspace-installer WORKDIR /app @@ -16,9 +19,9 @@ ADD --chown=dspace . /app/ COPY dspace/src/main/docker/local.cfg /app/local.cfg # Trigger the installation of all maven dependencies -# Clean up the built artifacts in the same step to keep the docker image small -RUN mvn package && mvn clean +RUN mvn package -# Clear the contents of the /app directory so no artifacts are left when dspace:dspace is built +# Clear the contents of the /app directory (including all maven builds), so no artifacts remain. +# This ensures when dspace:dspace is built, it will just the Maven local cache (.m2) for dependencies USER root -RUN rm -rf /app/* \ No newline at end of file +RUN rm -rf /app/* diff --git a/Dockerfile.jdk8-test b/Dockerfile.test similarity index 77% rename from Dockerfile.jdk8-test rename to Dockerfile.test index 6bbd43c3ea..090f714e28 100644 --- a/Dockerfile.jdk8-test +++ b/Dockerfile.test @@ -1,12 +1,13 @@ # This image will be published as dspace/dspace -# See https://dspace-labs.github.io/DSpace-Docker-Images/ for usage details +# See https://github.com/DSpace/DSpace/tree/master/dspace/src/main/docker for usage details # -# This version is JDK8 compatible -# - tomcat:8-jre8 +# This version is JDK11 compatible +# - tomcat:8-jdk11 # - ANT 1.10.7 -# - maven:3-jdk-8 -# - note: -# - default tag for branch: dspace/dspace: dspace/dspace:dspace-7_x-jdk8-test +# - maven:3-jdk-11 (see dspace-dependencies) +# - note: default tag for branch: dspace/dspace: dspace/dspace:dspace-7_x-test +# +# This image is meant for TESTING/DEVELOPMENT ONLY as it deploys the old v6 REST API under HTTP (not HTTPS) # Step 1 - Run Maven Build FROM dspace/dspace-dependencies:dspace-7_x as build @@ -24,13 +25,14 @@ USER dspace ADD --chown=dspace . /app/ COPY dspace/src/main/docker/local.cfg /app/local.cfg -# Build DSpace. Copy the dspace-install directory to /install. Clean up the build to keep the docker image small -RUN mvn package && \ +# Build DSpace (including the optional, deprecated "dspace-rest" webapp) +# Copy the dspace-install directory to /install. Clean up the build to keep the docker image small +RUN mvn package -Pdspace-rest && \ mv /app/dspace/target/${TARGET_DIR}/* /install && \ mvn clean # Step 2 - Run Ant Deploy -FROM tomcat:8-jre8 as ant_build +FROM tomcat:8-jdk11 as ant_build ARG TARGET_DIR=dspace-installer COPY --from=build /install /dspace-src WORKDIR /dspace-src @@ -47,7 +49,7 @@ RUN ant init_installation update_configs update_code update_webapps # Step 3 - Run tomcat # Create a new tomcat image that does not retain the the build directory contents -FROM tomcat:8-jre8 +FROM tomcat:8-jdk11 ENV DSPACE_INSTALL=/dspace COPY --from=ant_build /dspace $DSPACE_INSTALL EXPOSE 8080 8009 @@ -67,4 +69,4 @@ RUN ln -s $DSPACE_INSTALL/webapps/server /usr/local/tomcat/webapps/server && # Overwrite the v6.x (deprecated) REST API's web.xml, so that we can run it on HTTP (defaults to requiring HTTPS) COPY dspace/src/main/docker/test/rest_web.xml $DSPACE_INSTALL/webapps/rest/WEB-INF/web.xml -RUN sed -i -e "s|\${dspace.dir}|$DSPACE_INSTALL|" $DSPACE_INSTALL/webapps/rest/WEB-INF/web.xml \ No newline at end of file +RUN sed -i -e "s|\${dspace.dir}|$DSPACE_INSTALL|" $DSPACE_INSTALL/webapps/rest/WEB-INF/web.xml diff --git a/docker-compose-cli.yml b/docker-compose-cli.yml index 386c582f34..1a53f36189 100644 --- a/docker-compose-cli.yml +++ b/docker-compose-cli.yml @@ -6,7 +6,7 @@ services: container_name: dspace-cli build: context: . - dockerfile: Dockerfile.cli.jdk8 + dockerfile: Dockerfile.cli #environment: volumes: - ./dspace/src/main/docker-compose/local.cfg:/dspace/config/local.cfg diff --git a/docker-compose.yml b/docker-compose.yml index fad1a0365c..358518a449 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,10 +4,10 @@ networks: services: dspace: container_name: dspace - image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-dspace-7_x-jdk8-test}" + image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-dspace-7_x-test}" build: context: . - dockerfile: Dockerfile.jdk8-test + dockerfile: Dockerfile.test depends_on: - dspacedb networks: diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 620934047e..b016f1bff6 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -50,10 +50,33 @@ true true - - org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor - - + + + + org.hibernate + hibernate-jpamodelgen + ${hibernate.version} + + + + javax.xml.bind + jaxb-api + ${jaxb-api.version} + + + + javax.annotation + javax.annotation-api + ${javax-annotation.version} + + + + com.google.errorprone + error_prone_core + ${errorprone.version} + + @@ -93,7 +116,7 @@ org.codehaus.mojo build-helper-maven-plugin - 1.9.1 + 3.0.0 validate @@ -161,7 +184,6 @@ install of DSpace, against which Tests can be run. --> maven-dependency-plugin - 2.8 ${project.build.directory}/testing @@ -196,7 +218,7 @@ (see: http://gmaven.codehaus.org/Executing+Groovy+Code ) We are generating a OS-agnostic version (agnostic.build.dir) of the ${project.build.directory} property (full path of target dir). - This is needed by the FileWeaver & Surefire plugins (see below) + This is needed by the Surefire & Failsafe plugins (see below) to initialize the Unit Test environment's dspace.cfg file. Otherwise, the Unit Test Framework will not work on Windows OS. This Groovy code was mostly borrowed from: @@ -205,19 +227,17 @@ org.codehaus.gmaven groovy-maven-plugin - 2.0 setproperty - generate-test-resources - + initialize execute project.properties['agnostic.build.dir'] = project.build.directory.replace(File.separator, '/'); - println("Initializing Maven property 'agnostic.build.dir' to: " + project.properties['agnostic.build.dir']); + log.info("Initializing Maven property 'agnostic.build.dir' to: {}", project.properties['agnostic.build.dir']); @@ -230,6 +250,7 @@ + ${agnostic.build.dir}/testing/dspace/ @@ -238,51 +259,13 @@ - - - org.codehaus.mojo - xml-maven-plugin - 1.0.1 - - - validate-ALL-xml-and-xsl - process-test-resources - - validate - - - - - - - - ${agnostic.build.dir}/testing - - **/*.xml - **/*.xsl - **/*.xconf - - - - - ${root.basedir} - - **/*.xml - **/*.xsl - **/*.xmap - - - - - - - maven-failsafe-plugin + ${agnostic.build.dir}/testing/dspace/ true @@ -456,11 +439,6 @@ org.dspace dspace-services - - org.jmockit - jmockit - test - junit junit @@ -481,7 +459,11 @@ mockito-core test - + + org.springframework + spring-test + test + org.rometools rome-modules @@ -609,19 +591,44 @@ jar + + + javax.xml.bind + jaxb-api + + + org.glassfish.jaxb + jaxb-runtime + + + org.apache.ws.commons.axiom axiom-impl - - 1.2.14 + ${axiom.version} + + + + org.apache.geronimo.specs + * + + - org.apache.ws.commons.axiom axiom-api - - 1.2.14 + ${axiom.version} + + + + org.apache.geronimo.specs + * + + + org.glassfish.jersey.core jersey-client @@ -664,7 +671,7 @@ org.xmlunit xmlunit-matchers - 2.6.2 + 2.6.3 test @@ -674,6 +681,12 @@ 1.0.0.Final + + org.apache.bcel + bcel + 6.4.0 + + diff --git a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java index d512358be0..50183e4311 100644 --- a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java +++ b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java @@ -212,6 +212,8 @@ public class StructBuilder { } importStructure(context, inputStream, outputStream); + // save changes from import + context.complete(); } System.exit(0); } @@ -304,8 +306,6 @@ public class StructBuilder { output, e.getMessage()); System.exit(1); } - - context.complete(); } /** diff --git a/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportServiceImpl.java index 73bd16c7d7..fc441df26f 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportServiceImpl.java @@ -929,7 +929,7 @@ public class ItemExportServiceImpl implements ItemExportService { Locale supportedLocale = I18nUtil.getEPersonLocale(eperson); Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, "export_success")); email.addRecipient(eperson.getEmail()); - email.addArgument(ConfigurationManager.getProperty("dspace.url") + "/exportdownload/" + fileName); + email.addArgument(ConfigurationManager.getProperty("dspace.ui.url") + "/exportdownload/" + fileName); email.addArgument(ConfigurationManager.getProperty("org.dspace.app.itemexport.life.span.hours")); email.send(); @@ -947,7 +947,7 @@ public class ItemExportServiceImpl implements ItemExportService { Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, "export_error")); email.addRecipient(eperson.getEmail()); email.addArgument(error); - email.addArgument(ConfigurationManager.getProperty("dspace.url") + "/feedback"); + email.addArgument(ConfigurationManager.getProperty("dspace.ui.url") + "/feedback"); email.send(); } catch (Exception e) { diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index 1083876d69..12fcd84d04 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java @@ -1797,7 +1797,7 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, "bte_batch_import_error")); email.addRecipient(eperson.getEmail()); email.addArgument(error); - email.addArgument(ConfigurationManager.getProperty("dspace.url") + "/feedback"); + email.addArgument(ConfigurationManager.getProperty("dspace.ui.url") + "/feedback"); email.send(); } catch (Exception e) { diff --git a/dspace-api/src/main/java/org/dspace/app/sitemap/GenerateSitemaps.java b/dspace-api/src/main/java/org/dspace/app/sitemap/GenerateSitemaps.java index bf8bddcd8a..bb35cd3ff9 100644 --- a/dspace-api/src/main/java/org/dspace/app/sitemap/GenerateSitemaps.java +++ b/dspace-api/src/main/java/org/dspace/app/sitemap/GenerateSitemaps.java @@ -152,11 +152,11 @@ public class GenerateSitemaps { */ public static void generateSitemaps(boolean makeHTMLMap, boolean makeSitemapOrg) throws SQLException, IOException { - String sitemapStem = configurationService.getProperty("dspace.url") + String sitemapStem = configurationService.getProperty("dspace.ui.url") + "/sitemap"; - String htmlMapStem = configurationService.getProperty("dspace.url") + String htmlMapStem = configurationService.getProperty("dspace.ui.url") + "/htmlmap"; - String handleURLStem = configurationService.getProperty("dspace.url") + String handleURLStem = configurationService.getProperty("dspace.ui.url") + "/handle/"; File outputDir = new File(configurationService.getProperty("sitemap.dir")); @@ -293,7 +293,7 @@ public class GenerateSitemaps { .getProperty("http.proxy.port")); } - String sitemapURL = configurationService.getProperty("dspace.url") + String sitemapURL = configurationService.getProperty("dspace.ui.url") + "/sitemap"; URL url = new URL(engineURL + URLEncoder.encode(sitemapURL, "UTF-8")); diff --git a/dspace-api/src/main/java/org/dspace/app/statistics/LogAnalyser.java b/dspace-api/src/main/java/org/dspace/app/statistics/LogAnalyser.java index f46d6cb569..c378f15543 100644 --- a/dspace-api/src/main/java/org/dspace/app/statistics/LogAnalyser.java +++ b/dspace-api/src/main/java/org/dspace/app/statistics/LogAnalyser.java @@ -33,6 +33,7 @@ import org.apache.commons.lang3.StringUtils; import org.dspace.core.ConfigurationManager; import org.dspace.core.Context; import org.dspace.core.LogManager; +import org.dspace.core.Utils; import org.dspace.discovery.DiscoverQuery; import org.dspace.discovery.SearchServiceException; import org.dspace.discovery.SearchUtils; @@ -581,9 +582,9 @@ public class LogAnalyser { } // now do the host name and url lookup - hostName = ConfigurationManager.getProperty("dspace.hostname").trim(); + hostName = Utils.getHostName(ConfigurationManager.getProperty("dspace.ui.url")); name = ConfigurationManager.getProperty("dspace.name").trim(); - url = ConfigurationManager.getProperty("dspace.url").trim(); + url = ConfigurationManager.getProperty("dspace.ui.url").trim(); if ((url != null) && (!url.endsWith("/"))) { url = url + "/"; } diff --git a/dspace-api/src/main/java/org/dspace/app/statistics/Stat.java b/dspace-api/src/main/java/org/dspace/app/statistics/Stat.java index 717c8d666c..ea7320549e 100644 --- a/dspace-api/src/main/java/org/dspace/app/statistics/Stat.java +++ b/dspace-api/src/main/java/org/dspace/app/statistics/Stat.java @@ -15,7 +15,7 @@ package org.dspace.app.statistics; * * @author Richard Jones */ -public class Stat implements Comparable { +public class Stat implements Comparable { // FIXME: this class is functional but a bit messy, and should be neatened // up and completed @@ -132,17 +132,17 @@ public class Stat implements Comparable { /** - * compare the current object to the given object returning -1 if o is less - * than the current object, 0 if they are the same, and +1 if o is greater - * than the current object. + * Compare the current Stat to the given Stat returning -1 if o is less + * than the current Stat, 0 if they are the same, and +1 if o is greater + * than the current Stat. * - * @param o the object to compare to the current one + * @param stat the Stat object to compare to the current one * @return +1, 0, -1 if o is less than, equal to, or greater than the * current object value. */ @Override - public int compareTo(Object o) { - int objectValue = ((Stat) o).getValue(); + public int compareTo(Stat stat) { + int objectValue = stat.getValue(); if (objectValue < this.getValue()) { return -1; diff --git a/dspace-api/src/main/java/org/dspace/app/util/AbstractDSpaceWebapp.java b/dspace-api/src/main/java/org/dspace/app/util/AbstractDSpaceWebapp.java index 12389ecfce..a27107a81a 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/AbstractDSpaceWebapp.java +++ b/dspace-api/src/main/java/org/dspace/app/util/AbstractDSpaceWebapp.java @@ -50,16 +50,16 @@ abstract public class AbstractDSpaceWebapp /** * Construct a particular kind of DSpace application. * - * @param kind what kind of application is this? (XMLUI, JSPUI, etc.) + * @param kind what kind of application is this? */ public AbstractDSpaceWebapp(String kind) { this.kind = kind; started = new Date(); - url = ConfigurationManager.getProperty("dspace.url"); + url = ConfigurationManager.getProperty("dspace.ui.url"); if (null == url) { - throw new IllegalStateException("dspace.url is undefined"); + throw new IllegalStateException("dspace.ui.url is undefined"); } } diff --git a/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java b/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java index bb7e8e43b8..04db15eb4a 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java +++ b/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java @@ -34,12 +34,6 @@ import org.dspace.core.Context; */ public class AuthorizeUtil { - private static final AuthorizeService authorizeService = - AuthorizeServiceFactory.getInstance().getAuthorizeService(); - private static final ItemService itemService = ContentServiceFactory.getInstance().getItemService(); - private static final CollectionService collectionService = - ContentServiceFactory.getInstance().getCollectionService(); - /** * Default constructor */ @@ -95,8 +89,9 @@ public class AuthorizeUtil { */ public static void authorizeManageItemPolicy(Context context, Item item) throws AuthorizeException, SQLException { + AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); if (AuthorizeConfiguration.canItemAdminManagePolicies()) { - authorizeService.authorizeAction(context, item, Constants.ADMIN); + AuthorizeServiceFactory.getInstance().getAuthorizeService().authorizeAction(context, item, Constants.ADMIN); } else if (AuthorizeConfiguration.canCollectionAdminManageItemPolicies()) { authorizeService.authorizeAction(context, item .getOwningCollection(), Constants.ADMIN); @@ -124,6 +119,7 @@ public class AuthorizeUtil { */ public static void authorizeManageCollectionPolicy(Context context, Collection collection) throws AuthorizeException, SQLException { + AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); if (AuthorizeConfiguration.canCollectionAdminManagePolicies()) { authorizeService.authorizeAction(context, collection, Constants.ADMIN); @@ -151,6 +147,7 @@ public class AuthorizeUtil { */ public static void authorizeManageCommunityPolicy(Context context, Community community) throws AuthorizeException, SQLException { + AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); if (AuthorizeConfiguration.canCommunityAdminManagePolicies()) { authorizeService.authorizeAction(context, community, Constants.ADMIN); @@ -171,6 +168,7 @@ public class AuthorizeUtil { */ public static void requireAdminRole(Context context) throws AuthorizeException, SQLException { + AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); if (!authorizeService.isAdmin(context)) { throw new AuthorizeException( "Only system admin are allowed to perform this action"); @@ -191,6 +189,8 @@ public class AuthorizeUtil { */ public static void authorizeManageCCLicense(Context context, Item item) throws AuthorizeException, SQLException { + AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); + ItemService itemService = ContentServiceFactory.getInstance().getItemService(); try { authorizeService.authorizeAction(context, item, Constants.ADD); authorizeService.authorizeAction(context, item, Constants.REMOVE); @@ -224,6 +224,8 @@ public class AuthorizeUtil { */ public static void authorizeManageTemplateItem(Context context, Collection collection) throws AuthorizeException, SQLException { + AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); + CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService(); boolean isAuthorized = collectionService.canEditBoolean(context, collection, false); if (!isAuthorized @@ -258,6 +260,7 @@ public class AuthorizeUtil { */ public static void authorizeManageSubmittersGroup(Context context, Collection collection) throws AuthorizeException, SQLException { + AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); if (AuthorizeConfiguration.canCollectionAdminManageSubmitters()) { authorizeService.authorizeAction(context, collection, Constants.ADMIN); @@ -285,6 +288,7 @@ public class AuthorizeUtil { */ public static void authorizeManageWorkflowsGroup(Context context, Collection collection) throws AuthorizeException, SQLException { + AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); if (AuthorizeConfiguration.canCollectionAdminManageWorkflows()) { authorizeService.authorizeAction(context, collection, Constants.ADMIN); @@ -313,6 +317,7 @@ public class AuthorizeUtil { */ public static void authorizeManageAdminGroup(Context context, Collection collection) throws AuthorizeException, SQLException { + AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); if (AuthorizeConfiguration.canCollectionAdminManageAdminGroup()) { authorizeService.authorizeAction(context, collection, Constants.ADMIN); @@ -341,6 +346,7 @@ public class AuthorizeUtil { */ public static void authorizeRemoveAdminGroup(Context context, Collection collection) throws AuthorizeException, SQLException { + AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); List parentCommunities = collection.getCommunities(); if (AuthorizeConfiguration .canCommunityAdminManageCollectionAdminGroup() @@ -368,6 +374,7 @@ public class AuthorizeUtil { */ public static void authorizeManageAdminGroup(Context context, Community community) throws AuthorizeException, SQLException { + AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); if (AuthorizeConfiguration.canCommunityAdminManageAdminGroup()) { authorizeService.authorizeAction(context, community, Constants.ADMIN); @@ -392,6 +399,7 @@ public class AuthorizeUtil { */ public static void authorizeRemoveAdminGroup(Context context, Community community) throws SQLException, AuthorizeException { + AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); List parentCommunities = community.getParentCommunities(); Community parentCommunity = null; if (0 < parentCommunities.size()) { @@ -458,6 +466,7 @@ public class AuthorizeUtil { public static void authorizeWithdrawItem(Context context, Item item) throws SQLException, AuthorizeException { boolean authorized = false; + AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); if (AuthorizeConfiguration.canCollectionAdminPerformItemWithdrawn()) { authorized = authorizeService.authorizeActionBoolean(context, item .getOwningCollection(), Constants.ADMIN); @@ -492,6 +501,7 @@ public class AuthorizeUtil { */ public static void authorizeReinstateItem(Context context, Item item) throws SQLException, AuthorizeException { + AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); List colls = item.getCollections(); for (Collection coll : colls) { diff --git a/dspace-api/src/main/java/org/dspace/app/util/GoogleMetadata.java b/dspace-api/src/main/java/org/dspace/app/util/GoogleMetadata.java index c2bd6633f0..4d7e078e1d 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/GoogleMetadata.java +++ b/dspace-api/src/main/java/org/dspace/app/util/GoogleMetadata.java @@ -889,7 +889,7 @@ public class GoogleMetadata { Bitstream bitstream = findLinkableFulltext(item); if (bitstream != null) { StringBuilder path = new StringBuilder(); - path.append(ConfigurationManager.getProperty("dspace.url")); + path.append(ConfigurationManager.getProperty("dspace.ui.url")); if (item.getHandle() != null) { path.append("/bitstream/"); diff --git a/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java index 8e306efae0..9132dbe311 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java @@ -89,7 +89,7 @@ public class OpenSearchServiceImpl implements OpenSearchService { * Get base search service URL (websvc.opensearch.svccontext) */ protected String getBaseSearchServiceURL() { - return configurationService.getProperty("dspace.url") + "/" + + return configurationService.getProperty("dspace.server.url") + "/" + configurationService.getProperty("websvc.opensearch.svccontext"); } @@ -97,7 +97,7 @@ public class OpenSearchServiceImpl implements OpenSearchService { * Get base search UI URL (websvc.opensearch.uicontext) */ protected String getBaseSearchUIURL() { - return configurationService.getProperty("dspace.url") + "/" + + return configurationService.getProperty("dspace.server.url") + "/" + configurationService.getProperty("websvc.opensearch.uicontext"); } diff --git a/dspace-api/src/main/java/org/dspace/app/util/SyndicationFeed.java b/dspace-api/src/main/java/org/dspace/app/util/SyndicationFeed.java index f72035b4e1..4bb798ad8d 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/SyndicationFeed.java +++ b/dspace-api/src/main/java/org/dspace/app/util/SyndicationFeed.java @@ -531,11 +531,9 @@ public class SyndicationFeed { if (dso == null) { if (baseURL == null) { if (request == null) { - baseURL = ConfigurationManager.getProperty("dspace.url"); + baseURL = ConfigurationManager.getProperty("dspace.ui.url"); } else { - baseURL = (request.isSecure()) ? "https://" : "http://"; - baseURL += ConfigurationManager.getProperty("dspace.hostname"); - baseURL += ":" + request.getServerPort(); + baseURL = ConfigurationManager.getProperty("dspace.ui.url"); baseURL += request.getContextPath(); } } diff --git a/dspace-api/src/main/java/org/dspace/app/util/Util.java b/dspace-api/src/main/java/org/dspace/app/util/Util.java index 564300358c..aa04c13be7 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/Util.java +++ b/dspace-api/src/main/java/org/dspace/app/util/Util.java @@ -360,9 +360,13 @@ public class Util { InputStream cis = null; try { cis = Util.class.getResourceAsStream("/META-INF/maven/org.dspace/dspace-api/pom.properties"); + if (cis == null) { + // pom.properties will not exist when running tests + return "unknown"; + } constants.load(cis); } catch (Exception e) { - log.error(e.getMessage(), e); + log.error("Could not open dspace-api's pom.properties", e); } finally { if (cis != null) { try { @@ -475,10 +479,10 @@ public class Util { /** * Split a list in an array of i sub-lists uniformly sized - * + * * @param idsList the list to split * @param i the number of sublists to return - * + * * @return an array of sub-lists of fixed size */ public static List[] splitList(List idsList, int i) { diff --git a/dspace-api/src/main/java/org/dspace/authority/AuthorityValueServiceImpl.java b/dspace-api/src/main/java/org/dspace/authority/AuthorityValueServiceImpl.java index 194da3eba5..fc62f0df19 100644 --- a/dspace-api/src/main/java/org/dspace/authority/AuthorityValueServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/authority/AuthorityValueServiceImpl.java @@ -153,7 +153,7 @@ public class AuthorityValueServiceImpl implements AuthorityValueService { public List findByValue(Context context, String schema, String element, String qualifier, String value) { String field = fieldParameter(schema, element, qualifier); - return findByValue(context, field, qualifier); + return findByValue(context, field, value); } @Override diff --git a/dspace-api/src/main/java/org/dspace/authority/PersonAuthorityValue.java b/dspace-api/src/main/java/org/dspace/authority/PersonAuthorityValue.java index f937b5f7ed..2ffacae2a1 100644 --- a/dspace-api/src/main/java/org/dspace/authority/PersonAuthorityValue.java +++ b/dspace-api/src/main/java/org/dspace/authority/PersonAuthorityValue.java @@ -11,8 +11,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Objects; -import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrInputDocument; @@ -140,8 +140,8 @@ public class PersonAuthorityValue extends AuthorityValue { @Override public void setValues(SolrDocument document) { super.setValues(document); - this.firstName = ObjectUtils.toString(document.getFieldValue("first_name")); - this.lastName = ObjectUtils.toString(document.getFieldValue("last_name")); + this.firstName = Objects.toString(document.getFieldValue("first_name"), ""); + this.lastName = Objects.toString(document.getFieldValue("last_name"), ""); nameVariants = new ArrayList(); Collection document_name_variant = document.getFieldValues("name_variant"); if (document_name_variant != null) { diff --git a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicy.java b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicy.java index f4208e5835..b84055b8b0 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicy.java +++ b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicy.java @@ -8,6 +8,7 @@ package org.dspace.authorize; import java.util.Date; +import java.util.Objects; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; @@ -23,7 +24,6 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; -import org.apache.commons.lang3.ObjectUtils; import org.dspace.content.DSpaceObject; import org.dspace.core.Context; import org.dspace.core.ReloadableEntity; @@ -123,16 +123,16 @@ public class ResourcePolicy implements ReloadableEntity { if (getAction() != other.getAction()) { return false; } - if (!ObjectUtils.equals(getEPerson(), other.getEPerson())) { + if (!Objects.equals(getEPerson(), other.getEPerson())) { return false; } - if (!ObjectUtils.equals(getGroup(), other.getGroup())) { + if (!Objects.equals(getGroup(), other.getGroup())) { return false; } - if (!ObjectUtils.equals(getStartDate(), other.getStartDate())) { + if (!Objects.equals(getStartDate(), other.getStartDate())) { return false; } - if (!ObjectUtils.equals(getEndDate(), other.getEndDate())) { + if (!Objects.equals(getEndDate(), other.getEndDate())) { return false; } return true; @@ -185,7 +185,7 @@ public class ResourcePolicy implements ReloadableEntity { /** * set the action this policy authorizes * - * @param myid action ID from {@link org.dspace.core.Constants#Constants Constants} + * @param myid action ID from {@link org.dspace.core.Constants Constants} */ public void setAction(int myid) { this.actionId = myid; diff --git a/dspace-api/src/main/java/org/dspace/checker/DailyReportEmailer.java b/dspace-api/src/main/java/org/dspace/checker/DailyReportEmailer.java index 350facaad5..b4688c4807 100644 --- a/dspace-api/src/main/java/org/dspace/checker/DailyReportEmailer.java +++ b/dspace-api/src/main/java/org/dspace/checker/DailyReportEmailer.java @@ -27,6 +27,7 @@ import org.dspace.checker.service.SimpleReporterService; import org.dspace.core.ConfigurationManager; import org.dspace.core.Context; import org.dspace.core.Email; +import org.dspace.core.Utils; /** *

@@ -62,7 +63,7 @@ public class DailyReportEmailer { public void sendReport(File attachment, int numberOfBitstreams) throws IOException, javax.mail.MessagingException { if (numberOfBitstreams > 0) { - String hostname = ConfigurationManager.getProperty("dspace.hostname"); + String hostname = Utils.getHostName(ConfigurationManager.getProperty("dspace.ui.url")); Email email = new Email(); email.setSubject( "Checksum checker Report - " + numberOfBitstreams + " Bitstreams found with POSSIBLE issues on " + diff --git a/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java index 363b3a8df0..cc6d32b8c3 100644 --- a/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java @@ -367,7 +367,7 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl i @Override public void setWorkflowGroup(Context context, Collection collection, int step, Group group) - throws SQLException, AuthorizeException { + throws SQLException { Workflow workflow = null; try { workflow = workflowFactory.getWorkflow(collection); @@ -889,4 +889,4 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl i throws SQLException { return collectionDAO.getCollectionsWithBitstreamSizesTotal(context); } -} \ No newline at end of file +} diff --git a/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java index ee188dc144..6886d41e1b 100644 --- a/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java @@ -694,9 +694,11 @@ public abstract class DSpaceObjectServiceImpl implements List list = getMetadata(dso, schema, element, qualifier); - if (from >= list.size()) { + if (from >= list.size() || to >= list.size() || to < 0 || from < 0) { throw new IllegalArgumentException( - "The \"from\" location MUST exist for the operation to be successful. Idx:" + from); + "The \"from\" and \"to\" locations MUST exist for the operation to be successful." + + "\n To and from indices must be between 0 and " + (list.size() - 1) + + "\n Idx from:" + from + " Idx to: " + to); } clearMetadata(context, dso, schema, element, qualifier, Item.ANY); @@ -757,4 +759,9 @@ public abstract class DSpaceObjectServiceImpl implements } } + @Override + public void setMetadataModified(T dso) { + dso.setMetadataModified(); + } + } diff --git a/dspace-api/src/main/java/org/dspace/content/MetadataFieldServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/MetadataFieldServiceImpl.java index e3ed9c8ae7..c71db2d131 100644 --- a/dspace-api/src/main/java/org/dspace/content/MetadataFieldServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/MetadataFieldServiceImpl.java @@ -12,11 +12,13 @@ import java.sql.SQLException; import java.util.List; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.dao.MetadataFieldDAO; import org.dspace.content.service.MetadataFieldService; +import org.dspace.content.service.MetadataSchemaService; import org.dspace.content.service.MetadataValueService; import org.dspace.core.Context; import org.dspace.core.LogManager; @@ -42,6 +44,8 @@ public class MetadataFieldServiceImpl implements MetadataFieldService { protected AuthorizeService authorizeService; @Autowired(required = true) protected MetadataValueService metadataValueService; + @Autowired(required = true) + protected MetadataSchemaService metadataSchemaService; protected MetadataFieldServiceImpl() { @@ -87,13 +91,25 @@ public class MetadataFieldServiceImpl implements MetadataFieldService { return metadataFieldDAO.findByElement(context, metadataSchema, element, qualifier); } - @Override public MetadataField findByElement(Context context, String metadataSchemaName, String element, String qualifier) throws SQLException { return metadataFieldDAO.findByElement(context, metadataSchemaName, element, qualifier); } + @Override + public MetadataField findByString(Context context, String mdString, char separator) throws SQLException { + String[] seq = StringUtils.split(mdString, separator); + String schema = seq.length > 1 ? seq[0] : null; + String element = seq.length > 1 ? seq[1] : null; + String qualifier = seq.length == 3 ? seq[2] : null; + if (schema == null || element == null) { + return null; + } else { + return this.findByElement(context, schema, element, qualifier); + } + } + @Override public List findFieldsByElementNameUnqualified(Context context, String metadataSchemaName, String element) throws SQLException { diff --git a/dspace-api/src/main/java/org/dspace/content/Site.java b/dspace-api/src/main/java/org/dspace/content/Site.java index ade0f62408..2704a3d518 100644 --- a/dspace-api/src/main/java/org/dspace/content/Site.java +++ b/dspace-api/src/main/java/org/dspace/content/Site.java @@ -56,7 +56,7 @@ public class Site extends DSpaceObject { } public String getURL() { - return ConfigurationManager.getProperty("dspace.url"); + return ConfigurationManager.getProperty("dspace.ui.url"); } private SiteService getSiteService() { diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/OREDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/OREDisseminationCrosswalk.java index f0e20cfffc..3dde093784 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/OREDisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/OREDisseminationCrosswalk.java @@ -92,7 +92,7 @@ public class OREDisseminationCrosswalk private Element disseminateItem(Context context, Item item) throws CrosswalkException, IOException, SQLException, AuthorizeException { String oaiUrl = null; - String dsUrl = configurationService.getProperty("dspace.url"); + String dsUrl = configurationService.getProperty("dspace.ui.url"); String remSource = configurationService.getProperty("oai.ore.authoritative.source"); if (remSource == null || remSource.equalsIgnoreCase("oai")) { @@ -265,7 +265,8 @@ public class OREDisseminationCrosswalk Element pmhMeta = new Element("entry",ATOM_NS); pUri = new Element("id",ATOM_NS); - String oaiId = new String("oai:" + ConfigurationManager.getProperty("dspace.hostname") + ":" + item.getHandle + String hostname = Utils.getHostName(ConfigurationManager.getProperty("dspace.ui.url")); + String oaiId = new String("oai:" + hostname + ":" + item.getHandle ()); pUri.addContent(oaiId + "#oai_dc"); pmhMeta.addContent(pUri); diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/PREMISCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/PREMISCrosswalk.java index 9f927244c9..7bb7b38b43 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/PREMISCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/PREMISCrosswalk.java @@ -219,7 +219,7 @@ public class PREMISCrosswalk // b. name of bitstream, if any // c. made-up name based on sequence ID and extension. String sid = String.valueOf(bitstream.getSequenceID()); - String baseUrl = ConfigurationManager.getProperty("dspace.url"); + String baseUrl = ConfigurationManager.getProperty("dspace.ui.url"); String handle = null; // get handle of parent Item of this bitstream, if there is one: List bn = bitstream.getBundles(); diff --git a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSDisseminator.java b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSDisseminator.java index f32990ea5b..d5e01002b3 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSDisseminator.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSDisseminator.java @@ -1400,7 +1400,7 @@ public abstract class AbstractMETSDisseminator } if (handle != null) { return configurationService - .getProperty("dspace.url") + .getProperty("dspace.ui.url") + "/bitstream/" + handle + "/" @@ -1410,7 +1410,7 @@ public abstract class AbstractMETSDisseminator } else { //no Handle assigned, so persistent(-ish) URI for bitstream is // Format: {site-base-url}/retrieve/{bitstream-internal-id} return configurationService - .getProperty("dspace.url") + .getProperty("dspace.ui.url") + "/retrieve/" + String.valueOf(bitstream.getID()); } diff --git a/dspace-api/src/main/java/org/dspace/content/service/DSpaceObjectService.java b/dspace-api/src/main/java/org/dspace/content/service/DSpaceObjectService.java index 753516a373..203d2a1787 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/DSpaceObjectService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/DSpaceObjectService.java @@ -182,7 +182,7 @@ public interface DSpaceObjectService { /** * Add metadata fields. These are appended to existing values. - * Use clearDC to remove values. The ordering of values + * Use clearMetadata to remove values. The ordering of values * passed in is maintained. *

* If metadata authority control is available, try to get authority @@ -207,7 +207,7 @@ public interface DSpaceObjectService { /** * Add metadata fields. These are appended to existing values. - * Use clearDC to remove values. The ordering of values + * Use clearMetadata to remove values. The ordering of values * passed in is maintained. * * @param context DSpace context @@ -231,7 +231,7 @@ public interface DSpaceObjectService { /** * Add metadata fields. These are appended to existing values. - * Use clearDC to remove values. The ordering of values + * Use clearMetadata to remove values. The ordering of values * passed in is maintained. * * @param context DSpace context @@ -272,7 +272,7 @@ public interface DSpaceObjectService { /** * Add a single metadata field. This is appended to existing - * values. Use clearDC to remove values. + * values. Use clearMetadata to remove values. * * @param context DSpace context * @param dso DSpaceObject @@ -292,7 +292,7 @@ public interface DSpaceObjectService { /** * Add a single metadata field. This is appended to existing - * values. Use clearDC to remove values. + * values. Use clearMetadata to remove values. * * @param context DSpace context * @param dso DSpaceObject @@ -314,10 +314,10 @@ public interface DSpaceObjectService { /** * Clear metadata values. As with getDC above, - * passing in null only matches fields where the qualifier or + * passing in null only matches fields where the qualifier orr * language is actually null.Item.ANY will * match any element, qualifier or language, including null. - * Thus, dspaceobject.clearDC(Item.ANY, Item.ANY, Item.ANY) will + * Thus, dspaceobject.clearMetadata(Item.ANY, Item.ANY, Item.ANY) will * remove all Dublin Core metadata associated with an DSpaceObject. * * @param context DSpace context @@ -370,6 +370,26 @@ public interface DSpaceObjectService { public void delete(Context context, T dso) throws SQLException, AuthorizeException, IOException; + /** + * Add a single metadata field. Whether it's appended or prepended depends on index parameter. + * Use clearMetadata to remove values. + * + * @param context DSpace context + * @param dso DSpaceObject + * @param schema the schema for the metadata field. Must match + * the name of an existing metadata schema. + * @param element the metadata element name + * @param qualifier the metadata qualifier, or null for + * unqualified + * @param lang the ISO639 language code, optionally followed by an underscore + * and the ISO3166 country code. null means the + * value has no language (for example, a date). + * @param value the value to add. + * @param authority the external authority key for this value (or null) + * @param confidence the authority confidence (default 0) + * @param index the index at which this metadata is added (0: first place, -1 for last) + * @throws SQLException if database error + */ void addAndShiftRightMetadata(Context context, T dso, String schema, String element, String qualifier, String lang, String value, String authority, int confidence, int index) throws SQLException; @@ -385,4 +405,10 @@ public interface DSpaceObjectService { * @return a org.dspace.core.Constants that represents a IndexableObject type */ public int getSupportsTypeConstant(); + + /** + * Trigger the modifiedMetadata variable in DSpaceObject + * @param dso DSpaceObject whose metadata has been modified + */ + public void setMetadataModified(T dso); } diff --git a/dspace-api/src/main/java/org/dspace/content/service/MetadataFieldService.java b/dspace-api/src/main/java/org/dspace/content/service/MetadataFieldService.java index 5bfefd1db0..fa69087dd4 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/MetadataFieldService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/MetadataFieldService.java @@ -71,6 +71,16 @@ public interface MetadataFieldService { public MetadataField findByElement(Context context, String metadataSchemaName, String element, String qualifier) throws SQLException; + /** + * Separates an mdString in schema, element and qualifier parts, separated by a given separator + * And returns it's matching metadataField if found + * @param context dspace context + * @param mdString String being separated to find corresponding mdField (ex dc.contributor) + * @param separator Separator being used to separate the mdString + * @return Corresponding MetadataField if found + */ + public MetadataField findByString(Context context, String mdString, char separator) throws SQLException; + public List findFieldsByElementNameUnqualified(Context context, String metadataSchema, String element) throws SQLException; diff --git a/dspace-api/src/main/java/org/dspace/core/Context.java b/dspace-api/src/main/java/org/dspace/core/Context.java index 1771473c62..64e86eef40 100644 --- a/dspace-api/src/main/java/org/dspace/core/Context.java +++ b/dspace-api/src/main/java/org/dspace/core/Context.java @@ -361,20 +361,18 @@ public class Context implements AutoCloseable { // If Context is no longer open/valid, just note that it has already been closed if (!isValid()) { log.info("complete() was called on a closed Context object. No changes to commit."); + return; } try { // As long as we have a valid, writeable database connection, - // rollback any changes if we are in read-only mode, - // otherwise, commit any changes made as part of the transaction - if (isReadOnly()) { - abort(); - } else { + // commit changes. Otherwise, we'll just close the DB connection (see below) + if (!isReadOnly()) { commit(); } } finally { if (dbConnection != null) { - // Free the DB connection + // Free the DB connection and invalidate the Context dbConnection.closeDBConnection(); dbConnection = null; } @@ -395,29 +393,24 @@ public class Context implements AutoCloseable { // If Context is no longer open/valid, just note that it has already been closed if (!isValid()) { log.info("commit() was called on a closed Context object. No changes to commit."); + return; } if (isReadOnly()) { throw new UnsupportedOperationException("You cannot commit a read-only context"); } - // Our DB Connection (Hibernate) will decide if an actual commit is required or not try { - // As long as we have a valid, writeable database connection, - // commit any changes made as part of the transaction - if (isValid()) { - // Dispatch events before committing changes to the database, - // as the consumers may change something too - dispatchEvents(); - } - + // Dispatch events before committing changes to the database, + // as the consumers may change something too + dispatchEvents(); } finally { if (log.isDebugEnabled()) { log.debug("Cache size on commit is " + getCacheSize()); } if (dbConnection != null) { - //Commit our changes + // Commit our changes (this closes the transaction but leaves database connection open) dbConnection.commit(); reloadContextBoundEntities(); } @@ -425,8 +418,12 @@ public class Context implements AutoCloseable { } + /** + * Dispatch any events (cached in current Context) to configured EventListeners (consumers) + * in the EventService. This should be called prior to any commit as some consumers may add + * to the current transaction. Once events are dispatched, the Context's event cache is cleared. + */ public void dispatchEvents() { - // Commit any changes made as part of the transaction Dispatcher dispatcher = null; try { @@ -462,6 +459,7 @@ public class Context implements AutoCloseable { /** * Add an event to be dispatched when this context is committed. + * NOTE: Read-only Contexts cannot add events, as they cannot modify objects. * * @param event event to be dispatched */ @@ -490,6 +488,10 @@ public class Context implements AutoCloseable { return events; } + /** + * Whether or not the context has events cached. + * @return true or false + */ public boolean hasEvents() { return !CollectionUtils.isEmpty(events); } @@ -521,22 +523,25 @@ public class Context implements AutoCloseable { // If Context is no longer open/valid, just note that it has already been closed if (!isValid()) { log.info("abort() was called on a closed Context object. No changes to abort."); + return; } try { - // Rollback ONLY if we have a database connection, and it is NOT Read Only - if (isValid() && !isReadOnly()) { + // Rollback ONLY if we have a database transaction, and it is NOT Read Only + if (!isReadOnly() && isTransactionAlive()) { dbConnection.rollback(); } } catch (SQLException se) { - log.error(se.getMessage(), se); + log.error("Error rolling back transaction during an abort()", se); } finally { try { - if (!dbConnection.isSessionAlive()) { + if (dbConnection != null) { + // Free the DB connection & invalidate the Context dbConnection.closeDBConnection(); + dbConnection = null; } } catch (Exception ex) { - log.error("Exception aborting context", ex); + log.error("Error closing the database connection", ex); } events = null; } @@ -558,7 +563,22 @@ public class Context implements AutoCloseable { */ public boolean isValid() { // Only return true if our DB connection is live - return dbConnection != null && dbConnection.isTransActionAlive(); + // NOTE: A transaction need not exist for our Context to be valid, as a Context may use multiple transactions. + return dbConnection != null && dbConnection.isSessionAlive(); + } + + /** + * Find out whether our context includes an open database transaction. + * Returns true if there is an open transaction. Returns + * false if the context is invalid (e.g. abort() or complete()) + * was called OR no current transaction exists (e.g. commit() was just called + * and no new transaction has begun) + * + * @return + */ + protected boolean isTransactionAlive() { + // Only return true if both Context is valid *and* transaction is alive + return isValid() && dbConnection.isTransActionAlive(); } /** @@ -571,21 +591,22 @@ public class Context implements AutoCloseable { return mode != null && mode == Mode.READ_ONLY; } + /** + * Add a group's UUID to the list of special groups cached in Context + * @param groupID UUID of group + */ public void setSpecialGroup(UUID groupID) { specialGroups.add(groupID); - - // System.out.println("Added " + groupID); } /** - * test if member of special group + * Test if a group is a special group * * @param groupID ID of special group to test * @return true if member */ public boolean inSpecialGroup(UUID groupID) { if (specialGroups.contains(groupID)) { - // System.out.println("Contains " + groupID); return true; } @@ -593,10 +614,9 @@ public class Context implements AutoCloseable { } /** - * Get an array of all of the special groups that current user is a member - * of. + * Get an array of all of the special groups that current user is a member of. * - * @return list of groups + * @return list of special groups * @throws SQLException if database error */ public List getSpecialGroups() throws SQLException { @@ -608,6 +628,10 @@ public class Context implements AutoCloseable { return myGroups; } + /** + * Close the context, aborting any open transactions (if any). + * @throws Throwable + */ @Override protected void finalize() throws Throwable { /* diff --git a/dspace-api/src/main/java/org/dspace/core/Email.java b/dspace-api/src/main/java/org/dspace/core/Email.java index 5e7bbe2332..803497b650 100644 --- a/dspace-api/src/main/java/org/dspace/core/Email.java +++ b/dspace-api/src/main/java/org/dspace/core/Email.java @@ -497,7 +497,7 @@ public class Email { String to = config.getProperty("mail.admin"); String subject = "DSpace test email"; String server = config.getProperty("mail.server"); - String url = config.getProperty("dspace.url"); + String url = config.getProperty("dspace.ui.url"); Email message; try { if (args.length <= 0) { diff --git a/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java b/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java index c88cd0c1cd..57d823c743 100644 --- a/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java +++ b/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java @@ -35,6 +35,23 @@ import org.springframework.orm.hibernate5.SessionFactoryUtils; /** * Hibernate implementation of the DBConnection. + *

+ * NOTE: This class does NOT represent a single Hibernate database connection. Instead, it wraps + * Hibernate's Session object to obtain access to a database connection in order to execute one or more + * transactions. + *

+ * Per DSpace's current Hibernate configuration ([dspace]/config/core-hibernate.xml), we use the one-session-per-thread + * approach (ThreadLocalSessionContext). This means that Hibernate creates a single Session per thread (request), at the + * time when getCurrentSession() is first called. + *

+ * This Session may be reused for multiple Transactions, but if commit() is called, any objects (Entities) in + * the Session become disconnected and MUST be reloaded into the Session (see reloadEntity() method below). + *

+ * If an Error occurs, the Session itself is invalidated. No further Transactions can be run on that Session. + *

+ * DSpace generally follows the "Session-per-request" transactional pattern described here: + * https://docs.jboss.org/hibernate/orm/5.0/userguide/en-US/html/ch06.html#session-per-request + * * * @author kevinvandevelde at atmire.com */ @@ -47,32 +64,61 @@ public class HibernateDBConnection implements DBConnection { private boolean batchModeEnabled = false; private boolean readOnlyEnabled = false; + /** + * Retrieves the current Session from Hibernate (per our settings, Hibernate is configured to create one Session + * per thread). If Session doesn't yet exist, it is created. A Transaction is also initialized (or reinintialized) + * in the Session if one doesn't exist, or was previously closed (e.g. if commit() was previously called) + * @return Hibernate current Session object + * @throws SQLException + */ @Override public Session getSession() throws SQLException { + // If we don't yet have a live transaction, start a new one + // NOTE: a Session cannot be used until a Transaction is started. if (!isTransActionAlive()) { sessionFactory.getCurrentSession().beginTransaction(); configureDatabaseMode(); } + // Return the current Hibernate Session object (Hibernate will create one if it doesn't yet exist) return sessionFactory.getCurrentSession(); } + /** + * Check if the connection has a currently active Transaction. A Transaction is active if it has not yet been + * either committed or rolled back. + * @return + */ @Override public boolean isTransActionAlive() { Transaction transaction = getTransaction(); return transaction != null && transaction.isActive(); } + /** + * Retrieve the current Hibernate Transaction object from our Hibernate Session. + * @return current Transaction (may be active or inactive) or null + */ protected Transaction getTransaction() { return sessionFactory.getCurrentSession().getTransaction(); } + /** + * Check if Hibernate Session is still "alive" / open. An open Session may or may not have an open Transaction + * (so isTransactionAlive() may return false even if isSessionAlive() returns true). A Session may be reused for + * multiple transactions (e.g. if commit() is called, the Session remains alive while the Transaction is closed) + * + * @return true if Session is alive, false otherwise + */ @Override public boolean isSessionAlive() { - return sessionFactory.getCurrentSession() != null && sessionFactory.getCurrentSession() - .getTransaction() != null && sessionFactory - .getCurrentSession().getTransaction().getStatus().isOneOf(TransactionStatus.ACTIVE); + return sessionFactory.getCurrentSession() != null && sessionFactory.getCurrentSession().isOpen(); } + /** + * Rollback any changes applied to the current Transaction. This also closes the Transaction. A new Transaction + * may be opened the next time getSession() is called. + * @throws SQLException + */ @Override public void rollback() throws SQLException { if (isTransActionAlive()) { @@ -80,6 +126,14 @@ public class HibernateDBConnection implements DBConnection { } } + /** + * Close our current Database connection. This also closes & unbinds the Hibernate Session from our thread. + *

+ * NOTE: Because DSpace configures Hibernate to automatically create a Session per thread, a Session may still + * exist after this method is called (as Hibernate may automatically create a new Session for the current thread). + * However, Hibernate will automatically clean up any existing Session when the thread closes. + * @throws SQLException + */ @Override public void closeDBConnection() throws SQLException { if (sessionFactory.getCurrentSession() != null && sessionFactory.getCurrentSession().isOpen()) { @@ -87,11 +141,23 @@ public class HibernateDBConnection implements DBConnection { } } + /** + * Commits any current changes cached in the Hibernate Session to the database & closes the Transaction. + * To open a new Transaction, you may call getSession(). + *

+ * WARNING: When commit() is called, while the Session is still "alive", all previously loaded objects (entities) + * become disconnected from the Session. Therefore, if you continue to use the Session, you MUST reload any needed + * objects (entities) using reloadEntity() method. + * + * @throws SQLException + */ @Override public void commit() throws SQLException { if (isTransActionAlive() && !getTransaction().getStatus().isOneOf(TransactionStatus.MARKED_ROLLBACK, TransactionStatus.ROLLING_BACK)) { + // Flush synchronizes the database with in-memory objects in Session (and frees up that memory) getSession().flush(); + // Commit those results to the database & ends the Transaction getTransaction().commit(); } } @@ -132,6 +198,16 @@ public class HibernateDBConnection implements DBConnection { return getSession().getStatistics().getEntityCount(); } + /** + * Reload an entity into the Hibernate cache. This can be called after a call to commit() to re-cache an object + * in the Hibernate Session (see commit()). Failing to reload objects into the cache may result in a Hibernate + * throwing a "LazyInitializationException" if you attempt to use an object that has been disconnected from the + * Session cache. + * @param entity The DSpace object to reload + * @param The class of the entity. The entity must implement the {@link ReloadableEntity} interface. + * @return the newly cached object. + * @throws SQLException + */ @Override @SuppressWarnings("unchecked") public E reloadEntity(final E entity) throws SQLException { @@ -167,10 +243,13 @@ public class HibernateDBConnection implements DBConnection { } /** - * Evict an entity from the hibernate cache. This is necessary when batch processing a large number of items. + * Evict an entity from the hibernate cache. + *

+ * When an entity is evicted, it frees up the memory used by that entity in the cache. This is often + * necessary when batch processing a large number of objects (to avoid out-of-memory exceptions). * - * @param entity The entity to reload - * @param The class of the enity. The entity must implement the {@link ReloadableEntity} interface. + * @param entity The entity to evict + * @param The class of the entity. The entity must implement the {@link ReloadableEntity} interface. * @throws SQLException When reloading the entity from the database fails. */ @Override diff --git a/dspace-api/src/main/java/org/dspace/core/Utils.java b/dspace-api/src/main/java/org/dspace/core/Utils.java index 32c32776f7..29ed446f1b 100644 --- a/dspace-api/src/main/java/org/dspace/core/Utils.java +++ b/dspace-api/src/main/java/org/dspace/core/Utils.java @@ -13,6 +13,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.math.BigInteger; +import java.net.URI; +import java.net.URISyntaxException; import java.rmi.dgc.VMID; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -30,7 +32,10 @@ import java.util.regex.Pattern; import com.coverity.security.Escape; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringSubstitutor; import org.apache.logging.log4j.Logger; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; /** * Utility functions for DSpace. @@ -408,4 +413,38 @@ public final class Utils { return schema + separator + element + separator + qualifier; } } + + + /** + * Retrieve the hostname from a given URI string + * @param uriString URI string + * @return hostname (without any www.) or null (if URI was invalid) + */ + public static String getHostName(String uriString) { + try { + URI uri = new URI(uriString); + String hostname = uri.getHost(); + // remove the "www." from hostname, if it exists + if (hostname != null) { + return hostname.startsWith("www.") ? hostname.substring(4) : hostname; + } + return hostname; + } catch (URISyntaxException e) { + return null; + } + } + + /** + * Replaces configuration placeholders within a String with the corresponding value + * from DSpace's Configuration Service. + *

+ * For example, given a String like "My DSpace is installed at ${dspace.dir}", this + * method will replace "${dspace.dir}" with the configured value of that property. + * @param string source string + * @return string with any placeholders replaced with configured values. + */ + public static String interpolateConfigsInString(String string) { + ConfigurationService config = DSpaceServicesFactory.getInstance().getConfigurationService(); + return StringSubstitutor.replace(string, config.getProperties()); + } } diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java index 6af1564830..94361b7cf9 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java @@ -218,7 +218,7 @@ public class SolrServiceImpl implements SearchService, IndexingService { /** * Unindex a Document in the Lucene index. - * + * * @param context the dspace context * @param searchUniqueID the search uniqueID of the document to be deleted * @throws IOException if IO error @@ -230,7 +230,7 @@ public class SolrServiceImpl implements SearchService, IndexingService { /** * Unindex a Document in the Lucene Index. - * + * * @param context the dspace context * @param searchUniqueID the search uniqueID of the document to be deleted * @throws IOException if IO error @@ -465,7 +465,7 @@ public class SolrServiceImpl implements SearchService, IndexingService { Locale.getDefault(), "internal_error")); email.addRecipient(recipient); email.addArgument(ConfigurationManager - .getProperty("dspace.url")); + .getProperty("dspace.ui.url")); email.addArgument(new Date()); String stackTrace; diff --git a/dspace-api/src/main/java/org/dspace/discovery/configuration/DiscoverySortFieldConfiguration.java b/dspace-api/src/main/java/org/dspace/discovery/configuration/DiscoverySortFieldConfiguration.java index 37e024515a..bda2e780ba 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/configuration/DiscoverySortFieldConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/discovery/configuration/DiscoverySortFieldConfiguration.java @@ -7,6 +7,7 @@ */ package org.dspace.discovery.configuration; +import org.apache.commons.lang3.builder.HashCodeBuilder; import org.springframework.beans.factory.annotation.Required; /** @@ -46,4 +47,11 @@ public class DiscoverySortFieldConfiguration { return false; } + @Override + public int hashCode() { + return new HashCodeBuilder(3, 19) + .append(this.getMetadataField()) + .append(this.getType()) + .toHashCode(); + } } diff --git a/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java index 0e1034328a..e00a9568e3 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java @@ -228,7 +228,7 @@ public class AccountServiceImpl implements AccountService { */ protected void sendEmail(Context context, String email, boolean isRegister, RegistrationData rd) throws MessagingException, IOException, SQLException { - String base = ConfigurationManager.getProperty("dspace.url"); + String base = ConfigurationManager.getProperty("dspace.ui.url"); // Note change from "key=" to "token=" String specialLink = new StringBuffer().append(base).append( diff --git a/dspace-api/src/main/java/org/dspace/eperson/EPersonConsumer.java b/dspace-api/src/main/java/org/dspace/eperson/EPersonConsumer.java index 4f094bb5c5..319dcad65c 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/EPersonConsumer.java +++ b/dspace-api/src/main/java/org/dspace/eperson/EPersonConsumer.java @@ -83,7 +83,7 @@ public class EPersonConsumer implements Consumer { adminEmail.addRecipient(notifyRecipient); adminEmail.addArgument(ConfigurationManager.getProperty("dspace.name")); - adminEmail.addArgument(ConfigurationManager.getProperty("dspace.url")); + adminEmail.addArgument(ConfigurationManager.getProperty("dspace.ui.url")); adminEmail.addArgument(eperson.getFirstName() + " " + eperson.getLastName()); // Name adminEmail.addArgument(eperson.getEmail()); adminEmail.addArgument(new Date()); diff --git a/dspace-api/src/main/java/org/dspace/handle/HandleServiceImpl.java b/dspace-api/src/main/java/org/dspace/handle/HandleServiceImpl.java index ca545e0722..4c5138cf35 100644 --- a/dspace-api/src/main/java/org/dspace/handle/HandleServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/handle/HandleServiceImpl.java @@ -72,7 +72,7 @@ public class HandleServiceImpl implements HandleService { return null; } - String url = configurationService.getProperty("dspace.url") + String url = configurationService.getProperty("dspace.ui.url") + "/handle/" + handle; if (log.isDebugEnabled()) { @@ -85,7 +85,7 @@ public class HandleServiceImpl implements HandleService { @Override public String resolveUrlToHandle(Context context, String url) throws SQLException { - String dspaceUrl = configurationService.getProperty("dspace.url") + String dspaceUrl = configurationService.getProperty("dspace.ui.url") + "/handle/"; String handleResolver = configurationService.getProperty("handle.canonical.prefix"); diff --git a/dspace-api/src/main/java/org/dspace/health/InfoCheck.java b/dspace-api/src/main/java/org/dspace/health/InfoCheck.java index ba6ba9b6c0..ddb8785d6d 100644 --- a/dspace-api/src/main/java/org/dspace/health/InfoCheck.java +++ b/dspace-api/src/main/java/org/dspace/health/InfoCheck.java @@ -38,7 +38,7 @@ public class InfoCheck extends Check { ).append("\n"); sb.append("Url: ").append( - ConfigurationManager.getProperty("dspace.url") + ConfigurationManager.getProperty("dspace.ui.url") ).append("\n"); sb.append("\n"); diff --git a/dspace-api/src/main/java/org/dspace/identifier/EZIDIdentifierProvider.java b/dspace-api/src/main/java/org/dspace/identifier/EZIDIdentifierProvider.java index b34d10167c..8defe4bc2e 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/EZIDIdentifierProvider.java +++ b/dspace-api/src/main/java/org/dspace/identifier/EZIDIdentifierProvider.java @@ -583,7 +583,7 @@ public class EZIDIdentifierProvider log.warn("{} #{} has no handle -- location not set.", contentServiceFactory.getDSpaceObjectService(dso).getTypeText(dso), dso.getID()); } else { - String url = configurationService.getProperty("dspace.url") + String url = configurationService.getProperty("dspace.ui.url") + "/handle/" + item.getHandle(); log.info("Supplying location: {}", url); mapped.put("_target", url); diff --git a/dspace-api/src/main/java/org/dspace/rdf/conversion/SimpleDSORelationsConverterPlugin.java b/dspace-api/src/main/java/org/dspace/rdf/conversion/SimpleDSORelationsConverterPlugin.java index e5475e5eb4..409a4f1518 100644 --- a/dspace-api/src/main/java/org/dspace/rdf/conversion/SimpleDSORelationsConverterPlugin.java +++ b/dspace-api/src/main/java/org/dspace/rdf/conversion/SimpleDSORelationsConverterPlugin.java @@ -473,7 +473,7 @@ public class SimpleDSORelationsConverterPlugin // we currently ignore those return null; } - String dspaceURL = configurationService.getProperty("dspace.url"); + String dspaceURL = configurationService.getProperty("dspace.ui.url"); String link = ""; try { // this currently (DSpace 4.1) works with xmlui and jspui. diff --git a/dspace-api/src/main/java/org/dspace/rdf/negotiation/Negotiator.java b/dspace-api/src/main/java/org/dspace/rdf/negotiation/Negotiator.java index dbf703e8de..c28b9ec1e6 100644 --- a/dspace-api/src/main/java/org/dspace/rdf/negotiation/Negotiator.java +++ b/dspace-api/src/main/java/org/dspace/rdf/negotiation/Negotiator.java @@ -249,7 +249,7 @@ public class Negotiator { // if html is requested we have to forward to the repositories webui. if ("html".equals(lang)) { urlBuilder.append(DSpaceServicesFactory.getInstance() - .getConfigurationService().getProperty("dspace.url")); + .getConfigurationService().getProperty("dspace.ui.url")); if (!handle.equals(DSpaceServicesFactory.getInstance() .getConfigurationService().getProperty("handle.prefix") + "/0")) { urlBuilder.append("/handle/"); diff --git a/dspace-api/src/main/java/org/dspace/scripts/DSpaceCommandLineParameter.java b/dspace-api/src/main/java/org/dspace/scripts/DSpaceCommandLineParameter.java index 0615aaace3..2862d014c9 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/DSpaceCommandLineParameter.java +++ b/dspace-api/src/main/java/org/dspace/scripts/DSpaceCommandLineParameter.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.HashCodeBuilder; /** * This class serves as a representation of a command line parameter by holding a String name and a String value @@ -95,6 +96,7 @@ public class DSpaceCommandLineParameter { * @param other The other object * @return A boolean indicating equality */ + @Override public boolean equals(Object other) { if (other == null) { return false; @@ -105,4 +107,12 @@ public class DSpaceCommandLineParameter { return StringUtils.equals(this.getName(), ((DSpaceCommandLineParameter) other).getName()) && StringUtils .equals(this.getValue(), ((DSpaceCommandLineParameter) other).getValue()); } + + @Override + public int hashCode() { + return new HashCodeBuilder(5, 17) + .append(this.getName()) + .append(this.getValue()) + .toHashCode(); + } } diff --git a/dspace-api/src/main/java/org/dspace/statistics/content/StatisticsDataVisits.java b/dspace-api/src/main/java/org/dspace/statistics/content/StatisticsDataVisits.java index 92691b99a8..7ad9e9cf88 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/content/StatisticsDataVisits.java +++ b/dspace-api/src/main/java/org/dspace/statistics/content/StatisticsDataVisits.java @@ -650,7 +650,7 @@ public class StatisticsDataVisits extends StatisticsData { } - String url = ConfigurationManager.getProperty("dspace.url") + "/bitstream/" + identifier + "/"; + String url = ConfigurationManager.getProperty("dspace.ui.url") + "/bitstream/" + identifier + "/"; // If we can put the pretty name of the bitstream on the end of the URL try { diff --git a/dspace-api/src/main/java/org/dspace/storage/bitstore/S3BitStoreService.java b/dspace-api/src/main/java/org/dspace/storage/bitstore/S3BitStoreService.java index 9d7ec14143..29f17bc442 100644 --- a/dspace-api/src/main/java/org/dspace/storage/bitstore/S3BitStoreService.java +++ b/dspace-api/src/main/java/org/dspace/storage/bitstore/S3BitStoreService.java @@ -92,7 +92,9 @@ public class S3BitStoreService implements BitStoreService { // bucket name if (StringUtils.isEmpty(bucketName)) { - bucketName = "dspace-asset-" + ConfigurationManager.getProperty("dspace.hostname"); + // get hostname of DSpace UI to use to name bucket + String hostname = Utils.getHostName(ConfigurationManager.getProperty("dspace.ui.url")); + bucketName = "dspace-asset-" + hostname; log.warn("S3 BucketName is not configured, setting default: " + bucketName); } @@ -342,8 +344,10 @@ public class S3BitStoreService implements BitStoreService { Region usEast1 = Region.getRegion(Regions.US_EAST_1); store.s3Service.setRegion(usEast1); + // get hostname of DSpace UI to use to name bucket + String hostname = Utils.getHostName(ConfigurationManager.getProperty("dspace.ui.url")); //Bucketname should be lowercase - store.bucketName = "dspace-asset-" + ConfigurationManager.getProperty("dspace.hostname") + ".s3test"; + store.bucketName = "dspace-asset-" + hostname + ".s3test"; store.s3Service.createBucket(store.bucketName); /* Broken in DSpace 6 TODO Refactor // time everything, todo, swtich to caliper diff --git a/dspace-api/src/main/java/org/dspace/submit/model/AccessConditionOption.java b/dspace-api/src/main/java/org/dspace/submit/model/AccessConditionOption.java index ca8efc09c4..398097a9ec 100644 --- a/dspace-api/src/main/java/org/dspace/submit/model/AccessConditionOption.java +++ b/dspace-api/src/main/java/org/dspace/submit/model/AccessConditionOption.java @@ -8,22 +8,59 @@ package org.dspace.submit.model; /** + * This class represents an option available in the submission upload section to + * set permission on a file. An option is defined by a name such as "open + * access", "embargo", "restricted access", etc. and some optional attributes to + * better clarify the constraints and input available to the user. For instance + * an embargo option could allow to set a start date not longer than 3 years, + * etc + * * @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it) */ public class AccessConditionOption { - + /** An unique name identifying the access contion option **/ private String name; + /** + * the name of the group that will be bound to the resource policy created if + * such option is used + */ private String groupName; + /** + * this is in alternative to the {@link #groupName}. The sub-groups listed in + * the DSpace group identified by the name here specified will be available to + * the user to personalize the access condition. They can be for instance + * University Staff, University Students, etc. so that a "restricted access" + * option can be further specified without the need to create separate access + * condition options for each group + */ private String selectGroupName; + /** + * set to true if this option requires a start date to be indicated + * for the underlying resource policy to create + */ private Boolean hasStartDate; + /** + * set to true if this option requires an end date to be indicated + * for the underlying resource policy to create + */ private Boolean hasEndDate; + /** + * It contains, if applicable, the maximum start date (i.e. when the "embargo + * expires") that can be selected. It accepts date math via joda library (such as + * +3years) + */ private String startDateLimit; + /** + * It contains, if applicable, the maximum end date (i.e. when the "lease + * expires") that can be selected. It accepts date math via joda library (such as + * +3years) + */ private String endDateLimit; public String getName() { @@ -81,6 +118,4 @@ public class AccessConditionOption { public void setSelectGroupName(String selectGroupName) { this.selectGroupName = selectGroupName; } - - } diff --git a/dspace-api/src/main/java/org/dspace/workflowbasic/BasicWorkflowServiceImpl.java b/dspace-api/src/main/java/org/dspace/workflowbasic/BasicWorkflowServiceImpl.java index 6ddd750b20..58f393804a 100644 --- a/dspace-api/src/main/java/org/dspace/workflowbasic/BasicWorkflowServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/workflowbasic/BasicWorkflowServiceImpl.java @@ -1041,7 +1041,7 @@ public class BasicWorkflowServiceImpl implements BasicWorkflowService { @Override public String getMyDSpaceLink() { - return configurationService.getProperty("dspace.url") + "/mydspace"; + return configurationService.getProperty("dspace.ui.url") + "/mydspace"; } protected void notifyOfReject(Context context, BasicWorkflowItem workflowItem, EPerson e, diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/WorkflowUtils.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/WorkflowUtils.java index 175078564f..7edafe7efe 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/WorkflowUtils.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/WorkflowUtils.java @@ -161,7 +161,7 @@ public class WorkflowUtils extends Util { email.addRecipient(recipient); email.addArgument(ConfigurationManager - .getProperty("dspace.url")); + .getProperty("dspace.ui.url")); email.addArgument(new Date()); email.addArgument(request.getSession().getId()); email.addArgument(logInfo); diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowFactoryImpl.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowFactoryImpl.java index 1689ff0bb0..ffc62dcddb 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowFactoryImpl.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowFactoryImpl.java @@ -7,29 +7,53 @@ */ package org.dspace.xmlworkflow; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.Logger; import org.dspace.content.Collection; +import org.dspace.content.service.CollectionService; +import org.dspace.core.Context; +import org.dspace.handle.service.HandleService; +import org.dspace.utils.DSpace; import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; +import org.dspace.xmlworkflow.state.Step; import org.dspace.xmlworkflow.state.Workflow; +import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Required; /** - * The workflowfactory is responsible for parsing the - * workflow xml file and is used to retrieve the workflow for - * a certain collection + * The workflowfactory is responsible for parsing the workflow xml file and is used to retrieve info about the workflow: + * - the workflow for a certain collection + * - collections mapped to a certain workflow + * - collections not mapped to any workflow + * - configured workflows and the default workflow + * - workflow action by name * * @author Bram De Schouwer (bram.deschouwer at dot com) * @author Kevin Van de Velde (kevin at atmire dot com) * @author Ben Bosman (ben at atmire dot com) * @author Mark Diggory (markd at atmire dot com) + * @author Maria Verdonck (Atmire) on 11/12/2019 */ public class XmlWorkflowFactoryImpl implements XmlWorkflowFactory { public static final String LEGACY_WORKFLOW_NAME = "defaultWorkflow"; + private Logger log = org.apache.logging.log4j.LogManager.getLogger(XmlWorkflowFactoryImpl.class); + private Map workflowMapping; + @Autowired + protected CollectionService collectionService; + + @Autowired + protected HandleService handleService; + @Override public Workflow getWorkflow(Collection collection) throws WorkflowConfigurationException { // Attempt to retrieve our workflow object @@ -50,4 +74,93 @@ public class XmlWorkflowFactoryImpl implements XmlWorkflowFactory { public void setWorkflowMapping(Map workflowMapping) { this.workflowMapping = workflowMapping; } -} \ No newline at end of file + + @Override + public Workflow getWorkflowByName(String workflowName) throws WorkflowConfigurationException { + for (Workflow workflow : workflowMapping.values()) { + if (workflow.getID().equals(workflowName)) { + return workflow; + } + } + throw new WorkflowConfigurationException( + "Error while retrieving workflow by the following name: " + workflowName); + } + + @Override + public Workflow getDefaultWorkflow() { + return this.workflowMapping.get(LEGACY_WORKFLOW_NAME); + } + + @Override + public List getAllConfiguredWorkflows() { + return new ArrayList<>(this.workflowMapping.values()); + } + + @Override + public List getCollectionHandlesMappedToWorklow(Context context, String workflowName) { + List collectionsMapped = new ArrayList<>(); + for (String handle : this.workflowMapping.keySet()) { + if (this.workflowMapping.get(handle).getID().equals(workflowName)) { + try { + Collection collection = (Collection) handleService.resolveToObject(context, handle); + if (collection != null) { + collectionsMapped.add(collection); + } + } catch (SQLException e) { + log.error("SQLException in XmlWorkflowFactoryImpl.getCollectionHandlesMappedToWorklow trying to " + + "retrieve collection with handle: " + handle, e); + } + } + } + return collectionsMapped; + } + + @Override + public List getAllNonMappedCollectionsHandles(Context context) { + List nonMappedCollections = new ArrayList<>(); + try { + for (Collection collection : this.collectionService.findAll(context)) { + if (workflowMapping.get(collection.getHandle()) == null) { + nonMappedCollections.add(collection); + } + } + } catch (SQLException e) { + log.error("SQLException in XmlWorkflowFactoryImpl.getAllNonMappedCollectionsHandles trying to " + + "retrieve all collections", e); + } + return nonMappedCollections; + } + + @Override + public boolean workflowByThisNameExists(String workflowName) { + for (Workflow workflow : this.workflowMapping.values()) { + if (workflow.getID().equals(workflowName)) { + return true; + } + } + return false; + } + + + @Override + public boolean isDefaultWorkflow(String workflowName) { + if (StringUtils.isNotBlank(workflowName)) { + Workflow defaultWorkflow = this.getDefaultWorkflow(); + if (defaultWorkflow != null && StringUtils.isNotBlank(defaultWorkflow.getID())) { + return (defaultWorkflow.getID().equals(workflowName)); + } + } + return false; + } + + @Override + public WorkflowActionConfig getActionByName(String workflowActionName) { + return new DSpace().getServiceManager().getServiceByName(workflowActionName, WorkflowActionConfig.class); + } + + @Override + public Step getStepByName(String workflowStepName) { + return new DSpace().getServiceManager().getServiceByName(workflowStepName, Step.class); + } + +} diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java index 591d5f6b29..3b7937bca1 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java @@ -1049,7 +1049,7 @@ public class XmlWorkflowServiceImpl implements XmlWorkflowService { email.addArgument(coll.getName()); email.addArgument(rejector); email.addArgument(reason); - email.addArgument(ConfigurationManager.getProperty("dspace.url") + "/mydspace"); + email.addArgument(ConfigurationManager.getProperty("dspace.ui.url") + "/mydspace"); email.send(); } catch (Exception ex) { @@ -1063,7 +1063,7 @@ public class XmlWorkflowServiceImpl implements XmlWorkflowService { @Override public String getMyDSpaceLink() { - return ConfigurationManager.getProperty("dspace.url") + "/mydspace"; + return ConfigurationManager.getProperty("dspace.ui.url") + "/mydspace"; } protected void revokeReviewerPolicies(Context context, Item item) throws SQLException, AuthorizeException { diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/factory/XmlWorkflowFactory.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/factory/XmlWorkflowFactory.java index 47626bed44..5d33843747 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/factory/XmlWorkflowFactory.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/factory/XmlWorkflowFactory.java @@ -7,23 +7,31 @@ */ package org.dspace.xmlworkflow.factory; +import java.util.List; + import org.dspace.content.Collection; +import org.dspace.core.Context; import org.dspace.xmlworkflow.WorkflowConfigurationException; +import org.dspace.xmlworkflow.state.Step; import org.dspace.xmlworkflow.state.Workflow; +import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig; /** - * The xmlworkflowfactory is responsible for parsing the - * workflow xml file and is used to retrieve the workflow for - * a certain collection + * The workflowfactory is responsible for parsing the workflow xml file and is used to retrieve info about the workflow: + * - the workflow for a certain collection + * - collections mapped to a certain workflow + * - collections not mapped to any workflow + * - configured workflows and the default workflow + * - workflow action by name * * @author Bram De Schouwer (bram.deschouwer at dot com) * @author Kevin Van de Velde (kevin at atmire dot com) * @author Ben Bosman (ben at atmire dot com) * @author Mark Diggory (markd at atmire dot com) + * @author Maria Verdonck (Atmire) on 11/12/2019 */ public interface XmlWorkflowFactory { - /** * Retrieve the workflow configuration for a single collection * @@ -32,4 +40,74 @@ public interface XmlWorkflowFactory { * @throws WorkflowConfigurationException occurs if there is a configuration error in the workflow */ public Workflow getWorkflow(Collection collection) throws WorkflowConfigurationException; + + /** + * Retrieves the workflow configuration by name + * + * @param workflowName the name for which we want our workflow + * @return the workflow configuration + * @throws WorkflowConfigurationException occurs if there is no workflow configured by that name + */ + public Workflow getWorkflowByName(String workflowName) throws WorkflowConfigurationException; + + /** + * Creates a list of all configured workflows, or returns the cache of this if it was already created + * + * @return List of all configured workflows + */ + public List getAllConfiguredWorkflows(); + + /** + * Check to see if there is a workflow configured by the given name + * + * @param workflowName Name of a possible configured workflow + * @return True if there is a workflow configured by this name, false otherwise + */ + public boolean workflowByThisNameExists(String workflowName); + + /** + * Check to see if the given workflowName is the workflow configured to be default for collections + * + * @param workflowName Name of workflow to check if default + * @return True if given workflowName is the workflow mapped to default for collections, otherwise false + */ + public boolean isDefaultWorkflow(String workflowName); + + /** + * Gets the default workflow, i.e. the workflow that is mapped to collection=default in workflow.xml + */ + public Workflow getDefaultWorkflow(); + + /** + * Return a list of collections that are mapped to the given workflow in the workflow configuration. + * * Makes use of a cache so it only retrieves the workflowName->List if it's not cached + * + * @param context Dspace context + * @param workflowName Name of workflow we want the collections of that are mapped to is + * @return List of collections mapped to the requested workflow + */ + public List getCollectionHandlesMappedToWorklow(Context context, String workflowName); + + /** + * Returns list of collections that are not mapped to any configured workflow, and thus use the default workflow + * + * @return List of collections not mapped to any workflow + */ + public List getAllNonMappedCollectionsHandles(Context context); + + /** + * Retrieves a {@link WorkflowActionConfig} object based on its name, should correspond with bean id in workflow-actions.xml + * + * @param workflowActionName Name of workflow action we want to retrieve + * @return Workflow action object corresponding to the given workflowActionName + */ + public WorkflowActionConfig getActionByName(String workflowActionName); + + /** + * Retrieves a {@link Step} object based on its name, should correspond with bean id in workflow.xml + * + * @param workflowStepName Name of workflow step we want to retrieve + * @return Workflow step object corresponding to the given workflowStepName + */ + public Step getStepByName(String workflowStepName); } diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/Action.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/Action.java index ba481be3d9..9cf202f12d 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/Action.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/Action.java @@ -36,8 +36,7 @@ import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; public abstract class Action { private WorkflowActionConfig parent; - private static String ERROR_FIELDS_ATTRIBUTE = "dspace.workflow.error_fields"; - + private static final String ERROR_FIELDS_ATTRIBUTE = "dspace.workflow.error_fields"; public abstract void activate(Context c, XmlWorkflowItem wf) throws SQLException, IOException, AuthorizeException, WorkflowException; @@ -45,6 +44,12 @@ public abstract class Action { public abstract ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request) throws SQLException, AuthorizeException, IOException, WorkflowException; + /** + * Returns a list of options that the user can select at this action which results in the next step in the workflow + * @return A list of options of this action, resulting in the next step of the workflow + */ + public abstract List getOptions(); + public WorkflowActionConfig getParent() { return parent; } diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/WorkflowActionConfig.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/WorkflowActionConfig.java index 2c1a7b1fe8..1dc61888b1 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/WorkflowActionConfig.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/WorkflowActionConfig.java @@ -7,6 +7,8 @@ */ package org.dspace.xmlworkflow.state.actions; +import java.util.List; + import org.dspace.xmlworkflow.state.Step; /** @@ -59,4 +61,12 @@ public class WorkflowActionConfig { return step; } + /** + * Returns a list of options the user has on this action, resulting in the next step of the workflow + * @return A list of options of this action, resulting in the next step of the workflow + */ + public List getOptions() { + return this.processingAction.getOptions(); + } + } diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/AcceptEditRejectAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/AcceptEditRejectAction.java index 7d330aace9..3c595b7545 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/AcceptEditRejectAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/AcceptEditRejectAction.java @@ -9,6 +9,8 @@ package org.dspace.xmlworkflow.state.actions.processingaction; import java.io.IOException; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import javax.servlet.http.HttpServletRequest; import org.dspace.authorize.AuthorizeException; @@ -31,40 +33,49 @@ import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; */ public class AcceptEditRejectAction extends ProcessingAction { - public static final int MAIN_PAGE = 0; - public static final int REJECT_PAGE = 1; + private static final String SUBMIT_APPROVE = "submit_approve"; + private static final String SUBMIT_REJECT = "submit_reject"; //TODO: rename to AcceptAndEditMetadataAction @Override - public void activate(Context c, XmlWorkflowItem wf) throws SQLException { + public void activate(Context c, XmlWorkflowItem wf) { } @Override public ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request) - throws SQLException, AuthorizeException, IOException { + throws SQLException, AuthorizeException, IOException { - if (request.getParameter("submit_approve") != null) { - return processAccept(c, wfi, step, request); + if (request.getParameter(SUBMIT_APPROVE) != null) { + return processAccept(c, wfi); } else { - if (request.getParameter("submit_reject") != null) { - return processRejectPage(c, wfi, step, request); + if (request.getParameter(SUBMIT_REJECT) != null) { + return processRejectPage(c, wfi, request); } } return new ActionResult(ActionResult.TYPE.TYPE_CANCEL); } - public ActionResult processAccept(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request) - throws SQLException, AuthorizeException { + @Override + public List getOptions() { + List options = new ArrayList<>(); + options.add(SUBMIT_APPROVE); + options.add(SUBMIT_REJECT); + options.add(ProcessingAction.SUBMIT_EDIT_METADATA); + return options; + } + + public ActionResult processAccept(Context c, XmlWorkflowItem wfi) + throws SQLException, AuthorizeException { //Delete the tasks addApprovedProvenance(c, wfi); return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE); } - public ActionResult processRejectPage(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request) - throws SQLException, AuthorizeException, IOException { + public ActionResult processRejectPage(Context c, XmlWorkflowItem wfi, HttpServletRequest request) + throws SQLException, AuthorizeException, IOException { String reason = request.getParameter("reason"); if (reason == null || 0 == reason.trim().length()) { addErrorField(request, "reason"); @@ -85,14 +96,14 @@ public class AcceptEditRejectAction extends ProcessingAction { // Get user's name + email address String usersName = XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService() - .getEPersonName(c.getCurrentUser()); + .getEPersonName(c.getCurrentUser()); String provDescription = getProvenanceStartId() + " Approved for entry into archive by " - + usersName + " on " + now + " (GMT) "; + + usersName + " on " + now + " (GMT) "; // Add to item as a DC field itemService.addMetadata(c, wfi.getItem(), MetadataSchemaEnum.DC.getName(), "description", "provenance", "en", - provDescription); + provDescription); itemService.update(c, wfi.getItem()); } } diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/FinalEditAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/FinalEditAction.java index 50686f3993..fe73bf6a5d 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/FinalEditAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/FinalEditAction.java @@ -7,8 +7,9 @@ */ package org.dspace.xmlworkflow.state.actions.processingaction; -import java.io.IOException; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import javax.servlet.http.HttpServletRequest; import org.dspace.authorize.AuthorizeException; @@ -31,21 +32,22 @@ import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; */ public class FinalEditAction extends ProcessingAction { + private static final String SUBMIT_APPROVE = "submit_approve"; @Override - public void activate(Context c, XmlWorkflowItem wf) throws SQLException { + public void activate(Context c, XmlWorkflowItem wf) { } @Override public ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request) - throws SQLException, AuthorizeException, IOException { + throws SQLException, AuthorizeException { return processMainPage(c, wfi, step, request); } public ActionResult processMainPage(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request) - throws SQLException, AuthorizeException { - if (request.getParameter("submit_approve") != null) { + throws SQLException, AuthorizeException { + if (request.getParameter(SUBMIT_APPROVE) != null) { //Delete the tasks addApprovedProvenance(c, wfi); @@ -56,20 +58,28 @@ public class FinalEditAction extends ProcessingAction { } } + @Override + public List getOptions() { + List options = new ArrayList<>(); + options.add(SUBMIT_APPROVE); + options.add(ProcessingAction.SUBMIT_EDIT_METADATA); + return options; + } + private void addApprovedProvenance(Context c, XmlWorkflowItem wfi) throws SQLException, AuthorizeException { //Add the provenance for the accept String now = DCDate.getCurrent().toString(); // Get user's name + email address String usersName = XmlWorkflowServiceFactory.getInstance().getXmlWorkflowService() - .getEPersonName(c.getCurrentUser()); + .getEPersonName(c.getCurrentUser()); String provDescription = getProvenanceStartId() + " Approved for entry into archive by " - + usersName + " on " + now + " (GMT) "; + + usersName + " on " + now + " (GMT) "; // Add to item as a DC field itemService.addMetadata(c, wfi.getItem(), MetadataSchemaEnum.DC.getName(), "description", "provenance", "en", - provDescription); + provDescription); itemService.update(c, wfi.getItem()); } diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ProcessingAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ProcessingAction.java index 1fbb02710d..98af6facf3 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ProcessingAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ProcessingAction.java @@ -34,6 +34,7 @@ public abstract class ProcessingAction extends Action { @Autowired(required = true) protected ItemService itemService; + protected static final String SUBMIT_EDIT_METADATA = "submit_edit_metadata"; @Override public boolean isAuthorized(Context context, HttpServletRequest request, XmlWorkflowItem wfi) throws SQLException { diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ReviewAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ReviewAction.java index 6ccb2c53e6..5284874572 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ReviewAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ReviewAction.java @@ -9,6 +9,8 @@ package org.dspace.xmlworkflow.state.actions.processingaction; import java.io.IOException; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -34,6 +36,8 @@ public class ReviewAction extends ProcessingAction { public static final int MAIN_PAGE = 0; public static final int REJECT_PAGE = 1; + private static final String SUBMIT_APPROVE = "submit_approve"; + private static final String SUBMIT_REJECT = "submit_reject"; @Override public void activate(Context c, XmlWorkflowItem wfItem) { @@ -43,10 +47,10 @@ public class ReviewAction extends ProcessingAction { @Override public ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request) throws SQLException, AuthorizeException, IOException { - if (request.getParameter("submit_approve") != null) { + if (request.getParameter(SUBMIT_APPROVE) != null) { return processAccept(c, wfi, step, request); } else { - if (request.getParameter("submit_reject") != null) { + if (request.getParameter(SUBMIT_REJECT) != null) { return processRejectPage(c, wfi, step, request); } } @@ -54,6 +58,14 @@ public class ReviewAction extends ProcessingAction { return new ActionResult(ActionResult.TYPE.TYPE_CANCEL); } + @Override + public List getOptions() { + List options = new ArrayList<>(); + options.add(SUBMIT_APPROVE); + options.add(SUBMIT_REJECT); + return options; + } + public ActionResult processAccept(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request) throws SQLException, AuthorizeException { //Delete the tasks diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreEvaluationAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreEvaluationAction.java index df06c6b0de..a834641111 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreEvaluationAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreEvaluationAction.java @@ -9,6 +9,7 @@ package org.dspace.xmlworkflow.state.actions.processingaction; import java.io.IOException; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -17,7 +18,6 @@ import org.dspace.content.Item; import org.dspace.content.MetadataSchemaEnum; import org.dspace.content.MetadataValue; import org.dspace.core.Context; -import org.dspace.workflow.WorkflowException; import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory; import org.dspace.xmlworkflow.service.WorkflowRequirementsService; import org.dspace.xmlworkflow.state.Step; @@ -40,14 +40,13 @@ public class ScoreEvaluationAction extends ProcessingAction { private int minimumAcceptanceScore; @Override - public void activate(Context c, XmlWorkflowItem wf) - throws SQLException, IOException, AuthorizeException, WorkflowException { + public void activate(Context c, XmlWorkflowItem wf) { } @Override public ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request) - throws SQLException, AuthorizeException, IOException, WorkflowException { + throws SQLException, AuthorizeException, IOException { boolean hasPassed = false; //Retrieve all our scores from the metadata & add em up List scores = itemService @@ -82,6 +81,11 @@ public class ScoreEvaluationAction extends ProcessingAction { } } + @Override + public List getOptions() { + return new ArrayList<>(); + } + public int getMinimumAcceptanceScore() { return minimumAcceptanceScore; } diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreReviewAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreReviewAction.java index 07780704bc..c28fe2d93e 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreReviewAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreReviewAction.java @@ -7,14 +7,14 @@ */ package org.dspace.xmlworkflow.state.actions.processingaction; -import java.io.IOException; import java.sql.SQLException; +import java.util.Arrays; +import java.util.List; import javax.servlet.http.HttpServletRequest; import org.dspace.app.util.Util; import org.dspace.authorize.AuthorizeException; import org.dspace.core.Context; -import org.dspace.workflow.WorkflowException; import org.dspace.xmlworkflow.service.WorkflowRequirementsService; import org.dspace.xmlworkflow.state.Step; import org.dspace.xmlworkflow.state.actions.ActionResult; @@ -32,20 +32,21 @@ import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; */ public class ScoreReviewAction extends ProcessingAction { + private static final String SUBMIT_SCORE = "submit_score"; + @Override - public void activate(Context c, XmlWorkflowItem wf) - throws SQLException, IOException, AuthorizeException, WorkflowException { + public void activate(Context c, XmlWorkflowItem wf) { } @Override public ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request) - throws SQLException, AuthorizeException, IOException, WorkflowException { - if (request.getParameter("submit_score") != null) { + throws SQLException, AuthorizeException { + if (request.getParameter(SUBMIT_SCORE) != null) { int score = Util.getIntParameter(request, "score"); //Add our score to the metadata itemService.addMetadata(c, wfi.getItem(), WorkflowRequirementsService.WORKFLOW_SCHEMA, "score", null, null, - String.valueOf(score)); + String.valueOf(score)); itemService.update(c, wfi.getItem()); return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE); @@ -54,4 +55,9 @@ public class ScoreReviewAction extends ProcessingAction { return new ActionResult(ActionResult.TYPE.TYPE_SUBMISSION_PAGE); } } + + @Override + public List getOptions() { + return Arrays.asList(SUBMIT_SCORE); + } } diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SelectReviewerAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SelectReviewerAction.java index e31756538f..b768b4ff8b 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SelectReviewerAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SelectReviewerAction.java @@ -7,8 +7,8 @@ */ package org.dspace.xmlworkflow.state.actions.processingaction; -import java.io.IOException; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import java.util.UUID; import javax.servlet.http.HttpServletRequest; @@ -18,7 +18,6 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.dspace.eperson.service.EPersonService; -import org.dspace.workflow.WorkflowException; import org.dspace.xmlworkflow.Role; import org.dspace.xmlworkflow.state.Step; import org.dspace.xmlworkflow.state.actions.ActionResult; @@ -39,36 +38,38 @@ import org.springframework.beans.factory.annotation.Required; */ public class SelectReviewerAction extends ProcessingAction { - public static final int MAIN_PAGE = 0; public static final int SEARCH_RESULTS_PAGE = 1; public static final int RESULTS_PER_PAGE = 5; + private static final String SUBMIT_CANCEL = "submit_cancel"; + private static final String SUBMIT_SEARCH = "submit_search"; + private static final String SUBMIT_SELECT_REVIEWER = "submit_select_reviewer_"; + private Role role; @Autowired(required = true) - protected EPersonService ePersonService; + private EPersonService ePersonService; @Autowired(required = true) - protected WorkflowItemRoleService workflowItemRoleService; + private WorkflowItemRoleService workflowItemRoleService; @Override - public void activate(Context c, XmlWorkflowItem wf) - throws SQLException, IOException, AuthorizeException, WorkflowException { + public void activate(Context c, XmlWorkflowItem wf) { } @Override public ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request) - throws SQLException, AuthorizeException, IOException, WorkflowException { - String submitButton = Util.getSubmitButton(request, "submit_cancel"); + throws SQLException, AuthorizeException { + String submitButton = Util.getSubmitButton(request, SUBMIT_CANCEL); //Check if our user has pressed cancel - if (submitButton.equals("submit_cancel")) { + if (submitButton.equals(SUBMIT_CANCEL)) { //Send us back to the submissions page return new ActionResult(ActionResult.TYPE.TYPE_CANCEL); - } else if (submitButton.equals("submit_search")) { + } else if (submitButton.equals(SUBMIT_SEARCH)) { //Perform the search String query = request.getParameter("query"); int page = Util.getIntParameter(request, "result-page"); @@ -85,7 +86,7 @@ public class SelectReviewerAction extends ProcessingAction { request.setAttribute("result-page", page); request.setAttribute("page", SEARCH_RESULTS_PAGE); return new ActionResult(ActionResult.TYPE.TYPE_PAGE, SEARCH_RESULTS_PAGE); - } else if (submitButton.startsWith("submit_select_reviewer_")) { + } else if (submitButton.startsWith(SUBMIT_SELECT_REVIEWER)) { //Retrieve the identifier of the eperson which will do the reviewing UUID reviewerId = UUID.fromString(submitButton.substring(submitButton.lastIndexOf("_") + 1)); EPerson reviewer = ePersonService.find(c, reviewerId); @@ -102,6 +103,14 @@ public class SelectReviewerAction extends ProcessingAction { return new ActionResult(ActionResult.TYPE.TYPE_ERROR); } + @Override + public List getOptions() { + List options = new ArrayList<>(); + options.add(SUBMIT_SEARCH); + options.add(SUBMIT_SELECT_REVIEWER); + return options; + } + public Role getRole() { return role; } diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SingleUserReviewAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SingleUserReviewAction.java index 215eaaf645..d115832389 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SingleUserReviewAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SingleUserReviewAction.java @@ -9,6 +9,8 @@ package org.dspace.xmlworkflow.state.actions.processingaction; import java.io.IOException; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import javax.servlet.http.HttpServletRequest; import org.dspace.app.util.Util; @@ -38,6 +40,10 @@ public class SingleUserReviewAction extends ProcessingAction { public static final int OUTCOME_REJECT = 1; + protected static final String SUBMIT_APPROVE = "submit_approve"; + protected static final String SUBMIT_REJECT = "submit_reject"; + protected static final String SUBMIT_DECLINE_TASK = "submit_decline_task"; + @Override public void activate(Context c, XmlWorkflowItem wfItem) { @@ -58,19 +64,28 @@ public class SingleUserReviewAction extends ProcessingAction { } } + @Override + public List getOptions() { + List options = new ArrayList<>(); + options.add(SUBMIT_APPROVE); + options.add(SUBMIT_REJECT); + options.add(SUBMIT_DECLINE_TASK); + return options; + } + public ActionResult processMainPage(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request) throws SQLException, AuthorizeException { - if (request.getParameter("submit_approve") != null) { + if (request.getParameter(SUBMIT_APPROVE) != null) { //Delete the tasks addApprovedProvenance(c, wfi); return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE); - } else if (request.getParameter("submit_reject") != null) { + } else if (request.getParameter(SUBMIT_REJECT) != null) { // Make sure we indicate which page we want to process request.setAttribute("page", REJECT_PAGE); // We have pressed reject item, so take the user to a page where he can reject return new ActionResult(ActionResult.TYPE.TYPE_PAGE); - } else if (request.getParameter("submit_decline_task") != null) { + } else if (request.getParameter(SUBMIT_DECLINE_TASK) != null) { return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, OUTCOME_REJECT); } else { diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/AssignAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/AssignAction.java index 5b8d9b08b8..e837a8a893 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/AssignAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/AssignAction.java @@ -8,6 +8,8 @@ package org.dspace.xmlworkflow.state.actions.userassignment; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import javax.servlet.http.HttpServletRequest; import org.dspace.core.Context; @@ -34,6 +36,11 @@ public class AssignAction extends UserSelectionAction { return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE); } + @Override + public List getOptions() { + return new ArrayList<>(); + } + public void generateTasks() { } diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/AssignOriginalSubmitterAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/AssignOriginalSubmitterAction.java index 5786515a81..01d995ccf6 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/AssignOriginalSubmitterAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/AssignOriginalSubmitterAction.java @@ -9,7 +9,9 @@ package org.dspace.xmlworkflow.state.actions.userassignment; import java.io.IOException; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import javax.mail.MessagingException; import javax.servlet.http.HttpServletRequest; @@ -112,6 +114,11 @@ public class AssignOriginalSubmitterAction extends UserSelectionAction { return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE); } + @Override + public List getOptions() { + return new ArrayList<>(); + } + /** * Create a claimed task for the user IF this user doesn't have a claimed action for this workflow item * diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/AutoAssignAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/AutoAssignAction.java index 3c6ce50b0d..3f87c26029 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/AutoAssignAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/AutoAssignAction.java @@ -9,6 +9,7 @@ package org.dspace.xmlworkflow.state.actions.userassignment; import java.io.IOException; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -106,6 +107,11 @@ public class AutoAssignAction extends UserSelectionAction { return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE); } + @Override + public List getOptions() { + return new ArrayList<>(); + } + /** * Create a claimed task for the user IF this user doesn't have a claimed action for this workflow item * diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/ClaimAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/ClaimAction.java index fb154aa7ed..78742c6553 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/ClaimAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/ClaimAction.java @@ -10,6 +10,7 @@ package org.dspace.xmlworkflow.state.actions.userassignment; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; +import java.util.List; import javax.mail.MessagingException; import javax.servlet.http.HttpServletRequest; @@ -67,6 +68,11 @@ public class ClaimAction extends UserSelectionAction { return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE); } + @Override + public List getOptions() { + return new ArrayList<>(); + } + @Override public void alertUsersOnActivation(Context c, XmlWorkflowItem wfi, RoleMembers roleMembers) throws IOException, SQLException { diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/InheritUsersAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/InheritUsersAction.java index 5e134855a7..1ffce1afdb 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/InheritUsersAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/InheritUsersAction.java @@ -8,6 +8,8 @@ package org.dspace.xmlworkflow.state.actions.userassignment; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import javax.servlet.http.HttpServletRequest; import org.dspace.core.Context; @@ -37,6 +39,11 @@ public class InheritUsersAction extends UserSelectionAction { return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE); } + @Override + public List getOptions() { + return new ArrayList<>(); + } + @Override public boolean isFinished(XmlWorkflowItem wfi) { return false; diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/NoUserSelectionAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/NoUserSelectionAction.java index 35eeaf1a0e..d23a98cedb 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/NoUserSelectionAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/userassignment/NoUserSelectionAction.java @@ -7,14 +7,12 @@ */ package org.dspace.xmlworkflow.state.actions.userassignment; -import java.io.IOException; -import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.dspace.authorize.AuthorizeException; import org.dspace.core.Context; import org.dspace.xmlworkflow.RoleMembers; -import org.dspace.xmlworkflow.WorkflowConfigurationException; import org.dspace.xmlworkflow.state.Step; import org.dspace.xmlworkflow.state.actions.ActionResult; import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; @@ -34,12 +32,11 @@ public class NoUserSelectionAction extends UserSelectionAction { } @Override - public void regenerateTasks(Context c, XmlWorkflowItem wfi, RoleMembers roleMembers) throws SQLException { + public void regenerateTasks(Context c, XmlWorkflowItem wfi, RoleMembers roleMembers) { } @Override - public boolean isValidUserSelection(Context context, XmlWorkflowItem wfi, boolean hasUI) - throws WorkflowConfigurationException, SQLException { + public boolean isValidUserSelection(Context context, XmlWorkflowItem wfi, boolean hasUI) { return true; } @@ -49,12 +46,16 @@ public class NoUserSelectionAction extends UserSelectionAction { } @Override - public void activate(Context c, XmlWorkflowItem wf) throws SQLException, IOException { + public void activate(Context c, XmlWorkflowItem wf) { } @Override - public ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request) - throws SQLException, AuthorizeException, IOException { + public ActionResult execute(Context c, XmlWorkflowItem wfi, Step step, HttpServletRequest request) { return new ActionResult(ActionResult.TYPE.TYPE_OUTCOME, ActionResult.OUTCOME_COMPLETE); } + + @Override + public List getOptions() { + return new ArrayList<>(); + } } diff --git a/dspace-api/src/test/data/dspace.cfg.more b/dspace-api/src/test/data/dspace.cfg.more deleted file mode 100644 index c23646b3d8..0000000000 --- a/dspace-api/src/test/data/dspace.cfg.more +++ /dev/null @@ -1,32 +0,0 @@ -# Configure authority control for Unit Testing (in DSpaceControlledVocabularyTest) -# (This overrides default, commented out settings in dspace.cfg) -plugin.selfnamed.org.dspace.content.authority.ChoiceAuthority = \ - org.dspace.content.authority.DCInputAuthority, \ - org.dspace.content.authority.DSpaceControlledVocabulary - -# Configure some more Plugins for PluginTest class -# NOTE: Plugins are just *interfaces*. So, here we are defining some plugins -# based on java.util.List interface and giving them names. -# (These are used by PluginTest) -plugin.named.java.util.List = \ - java.util.ArrayList = MyArrayList, \ - java.util.LinkedList = MyLinkedList, \ - java.util.AttributeList = MyAttributeList - -# Define a single Map plugin (used by PluginTest) -plugin.single.java.util.Map = java.util.HashMap - -# Define a sequence of Collection plugins (used by PluginTest) -plugin.sequence.java.util.Collection = \ - java.util.ArrayList, \ - java.util.LinkedList, \ - java.util.Stack, \ - java.util.TreeSet - -# Enable a test authority control on dc.language.iso field -choices.plugin.dc.language.iso = common_iso_languages -choices.presentation.dc.language.iso = select -authority.controlled.dc.language.iso = true - -# use the testing assetstore.dir -assetstore.dir = ${dspace.dir}/assetstore diff --git a/dspace-api/src/test/data/dspaceFolder/config/local.cfg b/dspace-api/src/test/data/dspaceFolder/config/local.cfg index faf6db71ee..3c4b4a839d 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/local.cfg +++ b/dspace-api/src/test/data/dspaceFolder/config/local.cfg @@ -25,8 +25,8 @@ # SERVER CONFIGURATION # ########################## -# Spring boot test by default mock the server on the localhost (80) -dspace.baseUrl = http://localhost +# Spring boot test: by default mock the server on the localhost (80) +dspace.server.url = http://localhost # DSpace installation directory. # This is the location where you want to install DSpace. diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml new file mode 100644 index 0000000000..b734c78937 --- /dev/null +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/identifier-service-test.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/identifier-service-test.xml index 3efda0c73b..a5ae684a57 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/spring/api/identifier-service-test.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/identifier-service-test.xml @@ -21,92 +21,18 @@ autowire="byType" scope="singleton"/> - - - - - - - - - + + - - - - - - - - - diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/workflow-actions.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/workflow-actions.xml new file mode 100644 index 0000000000..7381972961 --- /dev/null +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/workflow-actions.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/workflow.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/workflow.xml index 96ebdababd..47f22c5d88 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/spring/api/workflow.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/workflow.xml @@ -9,8 +9,8 @@ - - + + diff --git a/dspace-api/src/test/data/dspaceFolder/solr/solr.xml b/dspace-api/src/test/data/dspaceFolder/solr/solr.xml deleted file mode 100644 index 201cdf7f8a..0000000000 --- a/dspace-api/src/test/data/dspaceFolder/solr/solr.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java b/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java index 7326b7188e..e53d6a675d 100644 --- a/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java +++ b/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java @@ -15,15 +15,14 @@ import java.sql.SQLException; import java.util.Properties; import java.util.TimeZone; -import mockit.integration.junit4.JMockit; import org.apache.logging.log4j.Logger; -import org.dspace.app.util.MockUtil; import org.dspace.servicemanager.DSpaceKernelImpl; import org.dspace.servicemanager.DSpaceKernelInit; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; /** * DSpace Unit Tests need to initialize the DSpace Kernel / Service Mgr @@ -39,7 +38,7 @@ import org.junit.runner.RunWith; * @see AbstractIntegrationTest */ @Ignore -@RunWith(JMockit.class) +@RunWith(MockitoJUnitRunner.class) public class AbstractDSpaceTest { /** @@ -86,11 +85,9 @@ public class AbstractDSpaceTest { kernelImpl = DSpaceKernelInit.getKernel(null); if (!kernelImpl.isRunning()) { // NOTE: the "dspace.dir" system property MUST be specified via Maven + // For example: by using of maven-surefire-plugin or maven-failsafe-plugin kernelImpl.start(getDspaceDir()); // init the kernel } - - // Initialize mock Util class (allows Util.getSourceVersion() to work in Unit tests) - new MockUtil(); } catch (IOException ex) { log.error("Error initializing tests", ex); fail("Error initializing tests: " + ex.getMessage()); diff --git a/dspace-api/src/test/java/org/dspace/AbstractUnitTest.java b/dspace-api/src/test/java/org/dspace/AbstractUnitTest.java index cf8a50fcf6..cd3669b143 100644 --- a/dspace-api/src/test/java/org/dspace/AbstractUnitTest.java +++ b/dspace-api/src/test/java/org/dspace/AbstractUnitTest.java @@ -11,16 +11,18 @@ import static org.junit.Assert.fail; import java.sql.SQLException; +import org.apache.commons.lang3.ArrayUtils; import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.factory.AuthorizeServiceFactory; import org.dspace.authorize.service.AuthorizeService; import org.dspace.core.Context; import org.dspace.core.I18nUtil; -import org.dspace.discovery.MockIndexEventConsumer; import org.dspace.eperson.EPerson; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.EPersonService; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.storage.rdbms.DatabaseUtils; import org.junit.After; import org.junit.Before; @@ -86,9 +88,6 @@ public class AbstractUnitTest extends AbstractDSpaceTest { fail("Error initializing database: " + se.getMessage() + (se.getCause() == null ? "" : ": " + se.getCause().getMessage())); } - - // Initialize mock indexer (which does nothing, since Solr isn't running) - new MockIndexEventConsumer(); } /** @@ -127,6 +126,10 @@ public class AbstractUnitTest extends AbstractDSpaceTest { EPersonServiceFactory.getInstance().getGroupService().initDefaultGroupNames(context); context.restoreAuthSystemState(); + + // Ensure all tests run with Solr indexing disabled + disableSolrIndexing(); + } catch (AuthorizeException ex) { log.error("Error creating initial eperson or default groups", ex); fail("Error creating initial eperson or default groups in AbstractUnitTest init()"); @@ -160,7 +163,7 @@ public class AbstractUnitTest extends AbstractDSpaceTest { protected void cleanupContext(Context c) throws SQLException { // If context still valid, abort it if (c != null && c.isValid()) { - c.complete(); + c.abort(); } // Cleanup Context object by setting it to null @@ -168,4 +171,23 @@ public class AbstractUnitTest extends AbstractDSpaceTest { c = null; } } + + /** + * Utility method which ensures Solr indexing is DISABLED in all Tests. We turn this off because + * Solr is NOT used in the dspace-api test framework. Instead, Solr/Discovery indexing is + * exercised in the dspace-server Integration Tests (which use an embedded Solr). + */ + protected static void disableSolrIndexing() { + // Get our currently configured list of event consumers + ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + String[] consumers = configurationService.getArrayProperty("event.dispatcher.default.consumers"); + + // Remove "discovery" from the configured consumers (if it exists). + // This turns off Discovery/Solr indexing after any object changes. + if (ArrayUtils.contains(consumers, "discovery")) { + consumers = ArrayUtils.removeElement(consumers, "discovery"); + configurationService.setProperty("event.dispatcher.default.consumers", consumers); + } + } + } diff --git a/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java b/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java index 43b47e83b2..8ff16f5baf 100644 --- a/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java +++ b/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java @@ -15,9 +15,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.sql.SQLException; -import java.util.Arrays; import java.util.Iterator; -import java.util.List; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Source; import javax.xml.transform.TransformerException; @@ -47,7 +45,6 @@ import org.xmlunit.diff.ComparisonFormatter; import org.xmlunit.diff.DefaultComparisonFormatter; import org.xmlunit.diff.Diff; import org.xmlunit.diff.Difference; -import org.xmlunit.util.Predicate; /** * Tests of {@link StructBuilder}. @@ -314,9 +311,9 @@ public class StructBuilderIT } /** - * Reject uninteresting nodes. + * Reject uninteresting nodes. (currently commented out of tests above) */ - private static class MyNodeFilter implements Predicate { + /*private static class MyNodeFilter implements Predicate { private static final List dontCare = Arrays.asList( "description", "intro", @@ -330,5 +327,5 @@ public class StructBuilderIT String type = node.getLocalName(); return ! dontCare.contains(type); } - } + }*/ } diff --git a/dspace-api/src/test/java/org/dspace/app/util/GoogleBitstreamComparatorTest.java b/dspace-api/src/test/java/org/dspace/app/util/GoogleBitstreamComparatorTest.java index 039d634938..84e776b983 100644 --- a/dspace-api/src/test/java/org/dspace/app/util/GoogleBitstreamComparatorTest.java +++ b/dspace-api/src/test/java/org/dspace/app/util/GoogleBitstreamComparatorTest.java @@ -8,7 +8,7 @@ package org.dspace.app.util; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import java.util.ArrayList; @@ -24,12 +24,8 @@ import org.dspace.core.Context; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) public class GoogleBitstreamComparatorTest extends AbstractUnitTest { @Mock diff --git a/dspace-api/src/test/java/org/dspace/app/util/GoogleMetadataTest.java b/dspace-api/src/test/java/org/dspace/app/util/GoogleMetadataTest.java index 5bd95a51bb..e2b49ab76a 100644 --- a/dspace-api/src/test/java/org/dspace/app/util/GoogleMetadataTest.java +++ b/dspace-api/src/test/java/org/dspace/app/util/GoogleMetadataTest.java @@ -14,7 +14,9 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.sql.SQLException; +import java.util.List; +import com.google.common.base.Splitter; import org.apache.logging.log4j.Logger; import org.dspace.AbstractUnitTest; import org.dspace.authorize.AuthorizeException; @@ -85,7 +87,8 @@ public class GoogleMetadataTest extends AbstractUnitTest { log.error("SQL Error in init", ex); fail("SQL Error in init: " + ex.getMessage()); } catch (IOException e) { - e.printStackTrace(); + log.error("IO Error in init", e); + fail("IO Error in init: " + e.getMessage()); } } @@ -119,8 +122,8 @@ public class GoogleMetadataTest extends AbstractUnitTest { context.restoreAuthSystemState(); context.commit(); GoogleMetadata gm = new GoogleMetadata(this.context, it); - String[] urlSplitted = gm.getPDFURL().get(0).split("/"); - assertEquals("Pdf", urlSplitted[urlSplitted.length - 1]); + List urlSplitted = Splitter.on("/").splitToList(gm.getPDFURL().get(0)); + assertEquals("Pdf", urlSplitted.get(urlSplitted.size() - 1)); } /** @@ -154,8 +157,8 @@ public class GoogleMetadataTest extends AbstractUnitTest { context.restoreAuthSystemState(); context.commit(); GoogleMetadata gm = new GoogleMetadata(this.context, it); - String[] urlSplitted = gm.getPDFURL().get(0).split("/"); - assertEquals("size9", urlSplitted[urlSplitted.length - 1]); + List urlSplitted = Splitter.on("/").splitToList(gm.getPDFURL().get(0)); + assertEquals("size9", urlSplitted.get(urlSplitted.size() - 1)); } /** @@ -189,8 +192,8 @@ public class GoogleMetadataTest extends AbstractUnitTest { context.restoreAuthSystemState(); context.commit(); GoogleMetadata gm = new GoogleMetadata(this.context, it); - String[] urlSplitted = gm.getPDFURL().get(0).split("/"); - assertEquals("first", urlSplitted[urlSplitted.length - 1]); + List urlSplitted = Splitter.on("/").splitToList(gm.getPDFURL().get(0)); + assertEquals("first", urlSplitted.get(urlSplitted.size() - 1)); } /** @@ -225,8 +228,8 @@ public class GoogleMetadataTest extends AbstractUnitTest { context.restoreAuthSystemState(); context.commit(); GoogleMetadata gm = new GoogleMetadata(this.context, it); - String[] urlSplitted = gm.getPDFURL().get(0).split("/"); - assertEquals("primary", urlSplitted[urlSplitted.length - 1]); + List urlSplitted = Splitter.on("/").splitToList(gm.getPDFURL().get(0)); + assertEquals("primary", urlSplitted.get(urlSplitted.size() - 1)); } /** @@ -261,8 +264,8 @@ public class GoogleMetadataTest extends AbstractUnitTest { context.restoreAuthSystemState(); context.commit(); GoogleMetadata gm = new GoogleMetadata(this.context, it); - String[] urlSplitted = gm.getPDFURL().get(0).split("/"); - assertEquals("large", urlSplitted[urlSplitted.length - 1]); + List urlSplitted = Splitter.on("/").splitToList(gm.getPDFURL().get(0)); + assertEquals("large", urlSplitted.get(urlSplitted.size() - 1)); } @@ -285,7 +288,7 @@ public class GoogleMetadataTest extends AbstractUnitTest { @Test public void testGetPDFURLWithNoBitstreams() throws Exception { context.turnOffAuthorisationSystem(); - Bundle bundle = ContentServiceFactory.getInstance().getBundleService().create(context, it, "ORIGINAL"); + ContentServiceFactory.getInstance().getBundleService().create(context, it, "ORIGINAL"); context.restoreAuthSystemState(); context.commit(); @@ -319,8 +322,8 @@ public class GoogleMetadataTest extends AbstractUnitTest { context.restoreAuthSystemState(); context.commit(); GoogleMetadata gm = new GoogleMetadata(this.context, it); - String[] urlSplitted = gm.getPDFURL().get(0).split("/"); - assertEquals("small", urlSplitted[urlSplitted.length - 1]); + List urlSplitted = Splitter.on("/").splitToList(gm.getPDFURL().get(0)); + assertEquals("small", urlSplitted.get(urlSplitted.size() - 1)); } @After @@ -334,12 +337,8 @@ public class GoogleMetadataTest extends AbstractUnitTest { community = context.reloadEntity(community); ContentServiceFactory.getInstance().getCommunityService().delete(context, community); community = null; - } catch (SQLException e) { - e.printStackTrace(); - } catch (AuthorizeException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); + } catch (Exception e) { + throw new AssertionError("Error occurred in destroy()", e); } it = null; super.destroy(); diff --git a/dspace-api/src/test/java/org/dspace/app/util/MockUtil.java b/dspace-api/src/test/java/org/dspace/app/util/MockUtil.java deleted file mode 100644 index 6043f1b848..0000000000 --- a/dspace-api/src/test/java/org/dspace/app/util/MockUtil.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * 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.app.util; - -import mockit.Mock; -import mockit.MockUp; - -/** - * Mocks the "Util" class, as the getSourceVersion() method in this class - * is not usable in Unit Testing (since the final JAR is not yet created) - * - * @author Tim Donohue - */ -public class MockUtil extends MockUp { - /** - * Override/Mock the default "getSourceVersion()" method. This method - * relies on the "pom.properties" file which is generated by Maven ONLY - * when building the final dspace-api.jar. During Unit Testing, this - * "pom.properties" file does NOT exist, which causes a NPE to be thrown. - *

- * This mocked method simply returns the version as "unit-testing". - * - * @return "unit-testing" since this is only used during Unit Tests - */ - @Mock - public static String getSourceVersion() { - return "unit-testing"; - } -} diff --git a/dspace-api/src/test/java/org/dspace/authenticate/IPMatcherTest.java b/dspace-api/src/test/java/org/dspace/authenticate/IPMatcherTest.java index 3f5a08a648..511ea0da25 100644 --- a/dspace-api/src/test/java/org/dspace/authenticate/IPMatcherTest.java +++ b/dspace-api/src/test/java/org/dspace/authenticate/IPMatcherTest.java @@ -264,8 +264,8 @@ public class IPMatcherTest { assertFalse(ipMatcher.match("192.1.2.2")); } - - private ArrayList getAllIp4Except(ArrayList exceptions) { + // Commented out as this is currently not used in tests + /*private ArrayList getAllIp4Except(ArrayList exceptions) { int d1 = 0; int d2 = 0; int d3 = 0; @@ -284,7 +284,7 @@ public class IPMatcherTest { } } return ips; - } + }*/ private void verifyAllIp4Except(ArrayList exceptions, boolean asserted, IPMatcher ipMatcher) throws IPMatcherException { diff --git a/dspace-api/src/test/java/org/dspace/authorize/AuthorizeServiceTest.java b/dspace-api/src/test/java/org/dspace/authorize/AuthorizeServiceTest.java index 7cd5873e06..110700070f 100644 --- a/dspace-api/src/test/java/org/dspace/authorize/AuthorizeServiceTest.java +++ b/dspace-api/src/test/java/org/dspace/authorize/AuthorizeServiceTest.java @@ -90,8 +90,7 @@ public class AuthorizeServiceTest extends AbstractUnitTest { @Test public void testauthorizeMethodRespectSpecialGroups() { - EPerson eperson1; - EPerson eperson2; + EPerson eperson; Group group1; Community dso; @@ -99,7 +98,7 @@ public class AuthorizeServiceTest extends AbstractUnitTest { context.turnOffAuthorisationSystem(); // create an eperson and a group - eperson1 = ePersonService.create(context); + eperson = ePersonService.create(context); group1 = groupService.create(context); // A group has to have a name, otherwise there are queries that break groupService.setName(group1, "My test group 2"); @@ -111,19 +110,19 @@ public class AuthorizeServiceTest extends AbstractUnitTest { // special group to the user. Then test if the action on the DSO // is allowed for the user authorizeService.addPolicy(context, dso, Constants.ADD, group1); - context.setCurrentUser(eperson1); + context.setCurrentUser(eperson); context.setSpecialGroup(group1.getID()); context.commit(); } catch (SQLException | AuthorizeException ex) { - throw new RuntimeException(ex); + throw new AssertionError(ex); } finally { context.restoreAuthSystemState(); } try { - Assert.assertTrue(authorizeService.authorizeActionBoolean(context, eperson1, dso, Constants.ADD, true)); + Assert.assertTrue(authorizeService.authorizeActionBoolean(context, eperson, dso, Constants.ADD, true)); } catch (SQLException ex) { - throw new RuntimeException(ex); + throw new AssertionError(ex); } } } diff --git a/dspace-api/src/test/java/org/dspace/content/BitstreamFormatTest.java b/dspace-api/src/test/java/org/dspace/content/BitstreamFormatTest.java index c12234635c..958ddc0876 100644 --- a/dspace-api/src/test/java/org/dspace/content/BitstreamFormatTest.java +++ b/dspace-api/src/test/java/org/dspace/content/BitstreamFormatTest.java @@ -15,23 +15,24 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import java.sql.SQLException; import java.util.Arrays; import java.util.Collections; import java.util.List; -import mockit.NonStrictExpectations; import org.apache.logging.log4j.Logger; import org.dspace.AbstractUnitTest; import org.dspace.authorize.AuthorizeException; -import org.dspace.authorize.AuthorizeServiceImpl; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.BitstreamFormatService; -import org.dspace.core.Context; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; /** * This class tests BitstreamFormat. Due to it being tighly coupled with the @@ -60,6 +61,11 @@ public class BitstreamFormatTest extends AbstractUnitTest { protected BitstreamFormatService bitstreamFormatService = ContentServiceFactory.getInstance() .getBitstreamFormatService(); + /** + * Spy of AuthorizeService to use for tests + * (initialized / setup in @Before method) + */ + private AuthorizeService authorizeServiceSpy; /** * This method will be run before every test as per @Before. It will @@ -75,6 +81,13 @@ public class BitstreamFormatTest extends AbstractUnitTest { try { bf = bitstreamFormatService.find(context, 5); bunknown = bitstreamFormatService.findUnknown(context); + + // Initialize our spy of the autowired (global) authorizeService bean. + // This allows us to customize the bean's method return values in tests below + authorizeServiceSpy = spy(authorizeService); + // "Wire" our spy to be used by the current loaded bitstreamFormatService + // (To ensure it uses the spy instead of the real service) + ReflectionTestUtils.setField(bitstreamFormatService, "authorizeService", authorizeServiceSpy); } catch (SQLException ex) { log.error("SQL Error in init", ex); fail("SQL Error in init: " + ex.getMessage()); @@ -199,11 +212,8 @@ public class BitstreamFormatTest extends AbstractUnitTest { */ @Test public void testCreateAdmin() throws SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow full Admin perms - authorizeService.isAdmin((Context) any); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); BitstreamFormat found = bitstreamFormatService.create(context); assertThat("testCreate 0", found, notNullValue()); @@ -219,13 +229,10 @@ public class BitstreamFormatTest extends AbstractUnitTest { */ @Test(expected = AuthorizeException.class) public void testCreateNotAdmin() throws SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow full Admin perms - authorizeService.isAdmin((Context) any); - result = false; - }}; + // Disalow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(false); - BitstreamFormat found = bitstreamFormatService.create(context); + bitstreamFormatService.create(context); fail("Exception should have been thrown"); } @@ -442,11 +449,8 @@ public class BitstreamFormatTest extends AbstractUnitTest { */ @Test(expected = AuthorizeException.class) public void testUpdateNotAdmin() throws SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow full Admin perms - authorizeService.isAdmin((Context) any); - result = false; - }}; + // Disallow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(false); bitstreamFormatService.update(context, bf); fail("Exception should have been thrown"); @@ -457,11 +461,8 @@ public class BitstreamFormatTest extends AbstractUnitTest { */ @Test public void testUpdateAdmin() throws SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow full Admin perms - authorizeService.isAdmin((Context) any); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); String desc = "Test description"; String oldDescription = bf.getDescription(); @@ -478,11 +479,8 @@ public class BitstreamFormatTest extends AbstractUnitTest { */ @Test(expected = AuthorizeException.class) public void testDeleteNotAdmin() throws SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow full Admin perms - authorizeService.isAdmin((Context) any); - result = false; - }}; + // Disallow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(false); bitstreamFormatService.delete(context, bf); fail("Exception should have been thrown"); @@ -493,11 +491,8 @@ public class BitstreamFormatTest extends AbstractUnitTest { */ @Test public void testDeleteAdmin() throws SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow full Admin perms - authorizeService.isAdmin((Context) any); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); BitstreamFormat bitstreamFormat = bitstreamFormatService.create(context); int toDeleteIdentifier = bitstreamFormat.getID(); @@ -511,11 +506,8 @@ public class BitstreamFormatTest extends AbstractUnitTest { */ @Test(expected = IllegalArgumentException.class) public void testDeleteUnknown() throws SQLException, AuthorizeException { - new NonStrictExpectations(AuthorizeServiceImpl.class) {{ - // Allow full Admin perms - authorizeService.isAdmin((Context) any); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); bitstreamFormatService.delete(context, bunknown); fail("Exception should have been thrown"); diff --git a/dspace-api/src/test/java/org/dspace/content/BitstreamTest.java b/dspace-api/src/test/java/org/dspace/content/BitstreamTest.java index b390de4598..b732b90111 100644 --- a/dspace-api/src/test/java/org/dspace/content/BitstreamTest.java +++ b/dspace-api/src/test/java/org/dspace/content/BitstreamTest.java @@ -15,6 +15,11 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.spy; import java.io.File; import java.io.FileInputStream; @@ -23,9 +28,9 @@ import java.sql.SQLException; import java.util.List; import java.util.UUID; -import mockit.NonStrictExpectations; import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.BitstreamFormatService; import org.dspace.core.Constants; @@ -33,6 +38,7 @@ import org.dspace.core.Context; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; /** * Unit Tests for class Bitstream @@ -54,6 +60,12 @@ public class BitstreamTest extends AbstractDSpaceObjectTest { */ private Bitstream bs; + /** + * Spy of AuthorizeService to use for tests + * (initialized / setup in @Before method) + */ + private AuthorizeService authorizeServiceSpy; + /** * This method will be run before every test as per @Before. It will * initialize resources required for the tests. @@ -67,10 +79,18 @@ public class BitstreamTest extends AbstractDSpaceObjectTest { super.init(); try { //we have to create a new bitstream in the database + context.turnOffAuthorisationSystem(); File f = new File(testProps.get("test.bitstream").toString()); this.bs = bitstreamService.create(context, new FileInputStream(f)); this.dspaceObject = bs; - //we need to commit the changes so we don't block the table for testing + context.restoreAuthSystemState(); + + // Initialize our spy of the autowired (global) authorizeService bean. + // This allows us to customize the bean's method return values in tests below + authorizeServiceSpy = spy(authorizeService); + // "Wire" our spy to be used by the current loaded bitstreamService + // (To ensure it uses the spy instead of the real service) + ReflectionTestUtils.setField(bitstreamService, "authorizeService", authorizeServiceSpy); } catch (IOException ex) { log.error("IO Error in init", ex); fail("SQL Error in init: " + ex.getMessage()); @@ -146,11 +166,10 @@ public class BitstreamTest extends AbstractDSpaceObjectTest { */ @Test public void testRegister() throws IOException, SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - authorizeService.authorizeAction((Context) any, (Bitstream) any, - Constants.WRITE); - result = null; - }}; + // Allow general Bitstream WRITE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(any(Context.class), any(Bitstream.class), + eq(Constants.WRITE)); + int assetstore = 0; File f = new File(testProps.get("test.bitstream").toString()); Bitstream registered = bitstreamService.register(context, assetstore, f.getName()); @@ -366,13 +385,9 @@ public class BitstreamTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testUpdateNotAdmin() throws SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Bitstream WRITE perms - authorizeService.authorizeAction((Context) any, (Bitstream) any, - Constants.WRITE); - result = new AuthorizeException(); + // Disallow Bitstream WRITE permissions + doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, bs, Constants.WRITE); - }}; //TODO: we need to verify the update, how? bitstreamService.update(context, bs); } @@ -382,13 +397,8 @@ public class BitstreamTest extends AbstractDSpaceObjectTest { */ @Test public void testUpdateAdmin() throws SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Bitstream WRITE perms - authorizeService.authorizeAction((Context) any, (Bitstream) any, - Constants.WRITE); - result = null; - - }}; + // Allow Bitstream WRITE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, bs, Constants.WRITE); //TODO: we need to verify the update, how? bitstreamService.update(context, bs); @@ -399,21 +409,18 @@ public class BitstreamTest extends AbstractDSpaceObjectTest { */ @Test public void testDeleteAndExpunge() throws IOException, SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Bitstream WRITE perms - authorizeService.authorizeAction((Context) any, (Bitstream) any, - Constants.WRITE); - result = null; - authorizeService.authorizeAction((Context) any, (Bitstream) any, - Constants.DELETE); - result = null; - - }}; // Create a new bitstream, which we can delete. As ordering of these // tests is unpredictable we don't want to delete the global bitstream + context.ignoreAuthorization(); File f = new File(testProps.get("test.bitstream").toString()); Bitstream delBS = bitstreamService.create(context, new FileInputStream(f)); UUID bitstreamId = delBS.getID(); + context.restoreAuthSystemState(); + + // Allow Bitstream WRITE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, delBS, Constants.WRITE); + // Allow Bitstream DELETE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, delBS, Constants.DELETE); // Test that delete will flag the bitstream as deleted assertFalse("testIsDeleted 0", delBS.isDeleted()); @@ -431,12 +438,8 @@ public class BitstreamTest extends AbstractDSpaceObjectTest { @Test public void testRetrieveCanRead() throws IOException, SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Bitstream READ perms - authorizeService.authorizeAction((Context) any, (Bitstream) any, - Constants.READ); - result = null; - }}; + // Allow Bitstream READ permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, bs, Constants.READ); assertThat("testRetrieveCanRead 0", bitstreamService.retrieve(context, bs), notNullValue()); } @@ -447,12 +450,8 @@ public class BitstreamTest extends AbstractDSpaceObjectTest { @Test(expected = AuthorizeException.class) public void testRetrieveNoRead() throws IOException, SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Bitstream READ perms - authorizeService.authorizeAction((Context) any, (Bitstream) any, - Constants.READ); - result = new AuthorizeException(); - }}; + // Disallow Bitstream READ permissions + doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, bs, Constants.READ); assertThat("testRetrieveNoRead 0", bitstreamService.retrieve(context, bs), notNullValue()); } diff --git a/dspace-api/src/test/java/org/dspace/content/BundleTest.java b/dspace-api/src/test/java/org/dspace/content/BundleTest.java index 09e3e704eb..bd8cca912b 100644 --- a/dspace-api/src/test/java/org/dspace/content/BundleTest.java +++ b/dspace-api/src/test/java/org/dspace/content/BundleTest.java @@ -15,6 +15,11 @@ import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.spy; import java.io.File; import java.io.FileInputStream; @@ -26,16 +31,17 @@ import java.util.Iterator; import java.util.List; import java.util.UUID; -import mockit.NonStrictExpectations; import org.apache.commons.collections4.CollectionUtils; import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.ResourcePolicy; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.core.Constants; import org.dspace.core.Context; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; /** * Units tests for class Bundle @@ -56,6 +62,11 @@ public class BundleTest extends AbstractDSpaceObjectTest { private Collection collection; private Community owningCommunity; + /** + * Spy of AuthorizeService to use for tests + * (initialized / setup in @Before method) + */ + private AuthorizeService authorizeServiceSpy; /** * This method will be run before every test as per @Before. It will @@ -79,6 +90,15 @@ public class BundleTest extends AbstractDSpaceObjectTest { //we need to commit the changes so we don't block the table for testing context.restoreAuthSystemState(); + + // Initialize our spy of the autowired (global) authorizeService bean. + // This allows us to customize the bean's method return values in tests below + authorizeServiceSpy = spy(authorizeService); + // "Wire" our spy to be used by the current loaded itemService, bundleService & bitstreamService + // (To ensure it uses the spy instead of the real service) + ReflectionTestUtils.setField(itemService, "authorizeService", authorizeServiceSpy); + ReflectionTestUtils.setField(bundleService, "authorizeService", authorizeServiceSpy); + ReflectionTestUtils.setField(bitstreamService, "authorizeService", authorizeServiceSpy); } catch (SQLException | AuthorizeException ex) { log.error("SQL Error in init", ex); fail("SQL Error in init: " + ex.getMessage()); @@ -155,14 +175,9 @@ public class BundleTest extends AbstractDSpaceObjectTest { */ @Test public void testCreate() throws SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) { - { - // Allow Bundle ADD perms - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.ADD); - result = null; - } - }; + // Allow Item ADD permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.ADD); + Bundle created = bundleService.create(context, item, "testCreateBundle"); //the item created by default has no name nor type set assertThat("testCreate 0", created, notNullValue()); @@ -221,16 +236,14 @@ public class BundleTest extends AbstractDSpaceObjectTest { */ @Test public void testSetPrimaryBitstreamID() throws SQLException, AuthorizeException, IOException { - new NonStrictExpectations(authorizeService.getClass()) { - { - // Allow Bundle ADD perms - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.ADD); - authorizeService.authorizeAction((Context) any, (Bitstream) any, - Constants.WRITE); - result = null; - } - }; + // Allow Item WRITE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.WRITE); + // Allow Bundle ADD permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.ADD); + // Allow Bitstream WRITE permissions + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.WRITE)); + File f = new File(testProps.get("test.bitstream").toString()); Bitstream bs = bitstreamService.create(context, new FileInputStream(f)); bundleService.addBitstream(context, b, bs); @@ -243,16 +256,14 @@ public class BundleTest extends AbstractDSpaceObjectTest { */ @Test public void testUnsetPrimaryBitstreamID() throws IOException, SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) { - { - // Allow Bundle ADD perms - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.ADD); - authorizeService.authorizeAction((Context) any, (Bitstream) any, - Constants.WRITE); - result = null; - } - }; + // Allow Item WRITE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.WRITE); + // Allow Bundle ADD permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.ADD); + // Allow Bitstream WRITE permissions + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.WRITE)); + //set a value different than default File f = new File(testProps.get("test.bitstream").toString()); Bitstream bs = bitstreamService.create(context, new FileInputStream(f)); @@ -279,27 +290,25 @@ public class BundleTest extends AbstractDSpaceObjectTest { */ @Test public void testGetBitstreamByName() throws FileNotFoundException, SQLException, IOException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Bundle ADD perms - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.ADD); - result = null; - authorizeService.authorizeAction((Context) any, (Bitstream) any, - Constants.WRITE); - result = null; - - }}; + // Allow Bundle ADD permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.ADD); + // Allow Bitstream WRITE permissions + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.WRITE)); String name = "name"; //by default there is no bitstream assertThat("testGetHandle 0", bundleService.getBitstreamByName(b, name), nullValue()); //let's add a bitstream + context.turnOffAuthorisationSystem(); File f = new File(testProps.get("test.bitstream").toString()); Bitstream bs = bitstreamService.create(context, new FileInputStream(f)); bs.setName(context, name); bundleService.addBitstream(context, b, bs); bundleService.update(context, b); + context.restoreAuthSystemState(); + assertThat("testGetHandle 1", bundleService.getBitstreamByName(b, name), notNullValue()); assertThat("testGetHandle 2", bundleService.getBitstreamByName(b, name), equalTo(bs)); assertThat("testGetHandle 3", bundleService.getBitstreamByName(b, name).getName(), equalTo(name)); @@ -310,27 +319,25 @@ public class BundleTest extends AbstractDSpaceObjectTest { */ @Test public void testGetBitstreams() throws FileNotFoundException, SQLException, IOException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Bundle ADD perms - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.ADD); - result = null; - authorizeService.authorizeAction((Context) any, (Bitstream) any, - Constants.WRITE); - result = null; - - }}; + // Allow Bundle ADD permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.ADD); + // Allow Bitstream WRITE permissions + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.WRITE)); //default bundle has no bitstreams assertThat("testGetBitstreams 0", b.getBitstreams(), notNullValue()); assertThat("testGetBitstreams 1", b.getBitstreams().size(), equalTo(0)); //let's add a bitstream + context.turnOffAuthorisationSystem(); String name = "name"; File f = new File(testProps.get("test.bitstream").toString()); Bitstream bs = bitstreamService.create(context, new FileInputStream(f)); bs.setName(context, name); bundleService.addBitstream(context, b, bs); + context.restoreAuthSystemState(); + assertThat("testGetBitstreams 2", b.getBitstreams(), notNullValue()); assertThat("testGetBitstreams 3", b.getBitstreams().size(), equalTo(1)); assertThat("testGetBitstreams 4", b.getBitstreams().get(0).getName(), equalTo(name)); @@ -352,16 +359,11 @@ public class BundleTest extends AbstractDSpaceObjectTest { @Test(expected = AuthorizeException.class) public void testCreateBitstreamNoAuth() throws FileNotFoundException, AuthorizeException, SQLException, IOException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Bundle ADD perms - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.ADD); - result = new AuthorizeException(); - - }}; + // Disallow Bundle ADD permissions + doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, b, Constants.ADD); File f = new File(testProps.get("test.bitstream").toString()); - Bitstream bs = bitstreamService.create(context, b, new FileInputStream(f)); + bitstreamService.create(context, b, new FileInputStream(f)); fail("Exception should be thrown"); } @@ -370,16 +372,13 @@ public class BundleTest extends AbstractDSpaceObjectTest { */ @Test public void testCreateBitstreamAuth() throws FileNotFoundException, AuthorizeException, SQLException, IOException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Bundle ADD perms - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.ADD); - result = null; - authorizeService.authorizeAction((Context) any, (Bitstream) any, - Constants.WRITE); - result = null; - - }}; + // Allow Item WRITE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.WRITE); + // Allow Bundle ADD permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.ADD); + // Allow Bitstream WRITE permissions + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.WRITE)); String name = "name"; File f = new File(testProps.get("test.bitstream").toString()); @@ -395,17 +394,12 @@ public class BundleTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testRegisterBitstreamNoAuth() throws AuthorizeException, IOException, SQLException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Bundle ADD perms - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.ADD); - result = new AuthorizeException(); - - }}; + // Disallow Bundle ADD permissions + doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, b, Constants.ADD); int assetstore = 0; //default assetstore File f = new File(testProps.get("test.bitstream").toString()); - Bitstream bs = bitstreamService.register(context, b, assetstore, f.getAbsolutePath()); + bitstreamService.register(context, b, assetstore, f.getAbsolutePath()); fail("Exception should be thrown"); } @@ -414,16 +408,13 @@ public class BundleTest extends AbstractDSpaceObjectTest { */ @Test public void testRegisterBitstreamAuth() throws AuthorizeException, IOException, SQLException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Bundle ADD perms - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.ADD); - result = null; - - authorizeService.authorizeAction((Context) any, (Bitstream) any, - Constants.WRITE); - result = null; - }}; + // Allow Item WRITE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.WRITE); + // Allow Bundle ADD permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.ADD); + // Allow Bitstream WRITE permissions + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.WRITE)); int assetstore = 0; //default assetstore String name = "name bitstream"; @@ -440,13 +431,8 @@ public class BundleTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testAddBitstreamNoAuth() throws SQLException, AuthorizeException, IOException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Bundle ADD perms - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.ADD); - result = new AuthorizeException(); - - }}; + // Disallow Bundle ADD permissions + doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, b, Constants.ADD); // create a new Bitstream to add to Bundle File f = new File(testProps.get("test.bitstream").toString()); @@ -461,16 +447,13 @@ public class BundleTest extends AbstractDSpaceObjectTest { */ @Test public void testAddBitstreamAuth() throws SQLException, AuthorizeException, FileNotFoundException, IOException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Bundle ADD perms - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.ADD); - result = null; - authorizeService.authorizeAction((Context) any, (Bitstream) any, - Constants.WRITE); - result = null; - - }}; + // Allow Item WRITE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.WRITE); + // Allow Bundle ADD permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.ADD); + // Allow Bitstream WRITE permissions + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.WRITE)); File f = new File(testProps.get("test.bitstream").toString()); Bitstream bs = bitstreamService.create(context, new FileInputStream(f)); @@ -487,17 +470,15 @@ public class BundleTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testRemoveBitstreamNoAuth() throws SQLException, AuthorizeException, IOException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Bundle REMOVE perms - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.REMOVE); - result = new AuthorizeException(); - - }}; + // Disallow Bundle ADD permissions + doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, b, Constants.REMOVE); File f = new File(testProps.get("test.bitstream").toString()); + context.turnOffAuthorisationSystem(); Bitstream bs = bitstreamService.create(context, new FileInputStream(f)); bs.setName(context, "name"); + context.restoreAuthSystemState(); + bundleService.removeBitstream(context, b, bs); fail("Exception should have been thrown"); } @@ -507,28 +488,26 @@ public class BundleTest extends AbstractDSpaceObjectTest { */ @Test public void testRemoveBitstreamAuth() throws SQLException, AuthorizeException, IOException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Bundle ADD perms (to create a new Bitstream and add it) - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.ADD); - result = null; - // Allow Bundle REMOVE perms (to test remove) - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.REMOVE); - result = null; - authorizeService.authorizeAction((Context) any, (Bitstream) any, - Constants.WRITE); - result = null; - authorizeService.authorizeAction(context, (Bitstream) any, - Constants.DELETE); - result = null; - - }}; + // Allow Item WRITE permissions (to create a new bitstream) + doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.WRITE); + // Allow Bundle ADD permissions (to create a new bitstream) + doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.ADD); + // Allow Bundle REMOVE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.REMOVE); + // Allow Bitstream WRITE permissions + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.WRITE)); + // Allow Bitstream DELETE permissions + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.DELETE)); // Create a new Bitstream to test with + context.turnOffAuthorisationSystem(); File f = new File(testProps.get("test.bitstream").toString()); Bitstream bs = bitstreamService.create(context, new FileInputStream(f)); bundleService.addBitstream(context, b, bs); + context.restoreAuthSystemState(); + bundleService.removeBitstream(context, b, bs); assertThat("testRemoveBitstreamAuth 0", bundleService.getBitstreamByName(b, bs.getName()), nullValue()); } @@ -549,19 +528,10 @@ public class BundleTest extends AbstractDSpaceObjectTest { */ @Test public void testDelete() throws SQLException, AuthorizeException, IOException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Bundle ADD perms (to create a new Bitstream and add it) - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.ADD); - result = null; - // Allow Bundle REMOVE perms (to test remove) - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.REMOVE); - result = null; - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.DELETE); - result = null; - }}; + // Allow Item REMOVE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.REMOVE); + // Allow Bundle DELETE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.DELETE); UUID id = b.getID(); itemService.removeBundle(context, item, b); @@ -676,17 +646,18 @@ public class BundleTest extends AbstractDSpaceObjectTest { @Test public void testSetOrder() throws SQLException, AuthorizeException, FileNotFoundException, IOException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Bundle ADD perms - authorizeService.authorizeAction((Context) any, (Bundle) any, - Constants.ADD); - result = null; - authorizeService.authorizeAction((Context) any, (Bitstream) any, - Constants.WRITE); - result = null; - }}; + // Allow Item WRITE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.WRITE); + // Allow Bundle ADD permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.ADD); + // Allow Bundle WRITE permissions + doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.WRITE); + // Allow Bitstream WRITE permissions + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.WRITE)); // Create three Bitstreams to test ordering with. Give them different names + context.turnOffAuthorisationSystem(); File f = new File(testProps.get("test.bitstream").toString()); Bitstream bs = bitstreamService.create(context, new FileInputStream(f)); bs.setName(context, "bitstream1"); @@ -697,6 +668,7 @@ public class BundleTest extends AbstractDSpaceObjectTest { Bitstream bs3 = bitstreamService.create(context, new FileInputStream(f)); bs3.setName(context, "bitstream3"); bundleService.addBitstream(context, b, bs3); + context.restoreAuthSystemState(); // Assert Bitstreams are in the order added Bitstream[] bitstreams = b.getBitstreams().toArray(new Bitstream[b.getBitstreams().size()]); diff --git a/dspace-api/src/test/java/org/dspace/content/CollectionTest.java b/dspace-api/src/test/java/org/dspace/content/CollectionTest.java index 1e6c7f553a..0b50c53615 100644 --- a/dspace-api/src/test/java/org/dspace/content/CollectionTest.java +++ b/dspace-api/src/test/java/org/dspace/content/CollectionTest.java @@ -15,19 +15,23 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.spy; import java.io.File; import java.io.FileInputStream; -import java.io.IOException; import java.sql.SQLException; import java.util.Iterator; import java.util.List; import java.util.UUID; -import mockit.NonStrictExpectations; import org.apache.logging.log4j.Logger; -import org.dspace.app.util.AuthorizeUtil; import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.factory.AuthorizeServiceFactory; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.factory.CoreServiceFactory; @@ -37,6 +41,7 @@ import org.dspace.eperson.Group; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; /** * Unit Tests for class Collection @@ -59,6 +64,12 @@ public class CollectionTest extends AbstractDSpaceObjectTest { private Community owningCommunity; + /** + * Spy of AuthorizeService to use for tests + * (initialized / setup in @Before method) + */ + private AuthorizeService authorizeServiceSpy; + /** * This method will be run before every test as per @Before. It will * initialize resources required for the tests. @@ -78,6 +89,18 @@ public class CollectionTest extends AbstractDSpaceObjectTest { this.dspaceObject = collection; //we need to commit the changes so we don't block the table for testing context.restoreAuthSystemState(); + + // Initialize our spy of the autowired (global) authorizeService bean. + // This allows us to customize the bean's method return values in tests below + authorizeServiceSpy = spy(authorizeService); + // "Wire" our spy to be used by the current loaded object services + // (To ensure these services use the spy instead of the real service) + ReflectionTestUtils.setField(communityService, "authorizeService", authorizeServiceSpy); + ReflectionTestUtils.setField(collectionService, "authorizeService", authorizeServiceSpy); + ReflectionTestUtils.setField(itemService, "authorizeService", authorizeServiceSpy); + // Also wire into current AuthorizeServiceFactory, as that is used for some checks (e.g. AuthorizeUtil) + ReflectionTestUtils.setField(AuthorizeServiceFactory.getInstance(), "authorizeService", + authorizeServiceSpy); } catch (AuthorizeException ex) { log.error("Authorization Error in init", ex); fail("Authorization Error in init: " + ex.getMessage()); @@ -97,29 +120,22 @@ public class CollectionTest extends AbstractDSpaceObjectTest { @After @Override public void destroy() { + context.turnOffAuthorisationSystem(); + // Delete community & collection created in init() try { - if (collection != null) { - context.turnOffAuthorisationSystem(); - collectionService.update(context, collection); - communityService.update(context, owningCommunity); - collection = collectionService.find(context, collection.getID()); - if (collection != null) { - collectionService.delete(context, collection); - communityService.delete(context, communityService.find(context, owningCommunity.getID())); - } - context.restoreAuthSystemState(); - } - - } catch (SQLException ex) { - log.error("SQL Error in init", ex); - fail("SQL Error in init: " + ex.getMessage()); - } catch (AuthorizeException ex) { - log.error("Authorization Error in init", ex); - fail("Authorization Error in init: " + ex.getMessage()); - } catch (IOException ex) { - log.error("IO Error in init", ex); - fail("IO Error in init: " + ex.getMessage()); + collectionService.delete(context, collection); + } catch (Exception e) { + // ignore } + try { + communityService.delete(context, owningCommunity); + } catch (Exception e) { + // ignore + } + context.restoreAuthSystemState(); + + collection = null; + owningCommunity = null; super.destroy(); } @@ -141,12 +157,9 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testCreate() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; + // Allow Community ADD perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, owningCommunity, Constants.ADD); - }}; Collection created = collectionService.create(context, owningCommunity); assertThat("testCreate 0", created, notNullValue()); assertThat("testCreate 1", created.getName(), equalTo("")); @@ -157,12 +170,9 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testCreateWithValidHandle() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; + // Allow Community ADD perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, owningCommunity, Constants.ADD); - }}; // test creating collection with a specified handle which is NOT already in use // (this handle should not already be used by system, as it doesn't start with "1234567689" prefix) Collection created = collectionService.create(context, owningCommunity, "987654321/100"); @@ -178,18 +188,15 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test(expected = IllegalStateException.class) public void testCreateWithInvalidHandle() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; + // Allow Community ADD perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, owningCommunity, Constants.ADD); - }}; //get handle of our default created collection String inUseHandle = collection.getHandle(); // test creating collection with a specified handle which IS already in use // This should throw an exception - Collection created = collectionService.create(context, owningCommunity, inUseHandle); + collectionService.create(context, owningCommunity, inUseHandle); fail("Exception expected"); } @@ -284,7 +291,6 @@ public class CollectionTest extends AbstractDSpaceObjectTest { String itext = "introductory text"; String copy = "copyright declaration"; String sidebar = "side bar text"; - String tempItem = "3"; String provDesc = "provenance description"; String license = "license text"; @@ -330,21 +336,8 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testSetLogoAuth() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = true; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = true; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; + // Allow Collection WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.WRITE, true); File f = new File(testProps.get("test.bitstream").toString()); Bitstream logo = collectionService.setLogo(context, collection, new FileInputStream(f)); @@ -359,86 +352,15 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testSetLogoAuth2() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = true; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; + // Allow parent Community WRITE perms (test inheritance to Collection) + doNothing().when(authorizeServiceSpy).authorizeAction(context, owningCommunity, Constants.WRITE, true); File f = new File(testProps.get("test.bitstream").toString()); Bitstream logo = collectionService.setLogo(context, collection, new FileInputStream(f)); - assertThat("testSetLogoAuth2 0", collection.getLogo(), equalTo(logo)); + assertThat("testSetLogoAuth 0", collection.getLogo(), equalTo(logo)); collection.setLogo(null); - assertThat("testSetLogoAuth2 1", collection.getLogo(), nullValue()); - } - - /** - * Test of setLogo method, of class Collection. - */ - @Test - public void testSetLogoAuth3() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = true; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; - - File f = new File(testProps.get("test.bitstream").toString()); - Bitstream logo = collectionService.setLogo(context, collection, new FileInputStream(f)); - assertThat("testSetLogoAuth3 0", collection.getLogo(), equalTo(logo)); - - collection.setLogo(null); - assertThat("testSetLogoAuth3 1", collection.getLogo(), nullValue()); - } - - /** - * Test of setLogo method, of class Collection. - */ - @Test - public void testSetLogoAuth4() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; - - File f = new File(testProps.get("test.bitstream").toString()); - Bitstream logo = collectionService.setLogo(context, collection, new FileInputStream(f)); - assertThat("testSetLogoAuth4 0", collection.getLogo(), equalTo(logo)); - - collection.setLogo(null); - assertThat("testSetLogoAuth4 1", collection.getLogo(), nullValue()); + assertThat("testSetLogoAuth 1", collection.getLogo(), nullValue()); } /** @@ -446,25 +368,9 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testSetLogoNoAuth() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Disallow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = new AuthorizeException(); - }}; - File f = new File(testProps.get("test.bitstream").toString()); - Bitstream logo = collectionService.setLogo(context, collection, new FileInputStream(f)); - fail("EXception expected"); + collectionService.setLogo(context, collection, new FileInputStream(f)); + fail("Exception expected"); } /** @@ -472,11 +378,8 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testCreateWorkflowGroupAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Allow manage WorkflowsGroup perms - AuthorizeUtil.authorizeManageWorkflowsGroup((Context) any, (Collection) any); - result = null; - }}; + // Allow Collection ADMIN (to manage workflow group) + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.ADMIN); int step = 1; Group result = collectionService.createWorkflowGroup(context, collection, step); @@ -488,14 +391,8 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testCreateWorkflowGroupNoAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Disallow manage WorkflowsGroup perms - AuthorizeUtil.authorizeManageWorkflowsGroup((Context) any, (Collection) any); - result = new AuthorizeException(); - }}; - int step = 1; - Group result = collectionService.createWorkflowGroup(context, collection, step); + collectionService.createWorkflowGroup(context, collection, step); fail("Exception expected"); } @@ -551,11 +448,8 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testCreateSubmittersAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Allow manage SubmittersGroup perms - AuthorizeUtil.authorizeManageSubmittersGroup((Context) any, (Collection) any); - result = null; - }}; + // Allow Collection ADMIN (to manage submitter group) + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.ADMIN); Group result = collectionService.createSubmitters(context, collection); assertThat("testCreateSubmittersAuth 0", result, notNullValue()); @@ -566,13 +460,7 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testCreateSubmittersNoAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Disallow manage SubmittersGroup perms - AuthorizeUtil.authorizeManageSubmittersGroup((Context) any, (Collection) any); - result = new AuthorizeException(); - }}; - - Group result = collectionService.createSubmitters(context, collection); + collectionService.createSubmitters(context, collection); fail("Exception expected"); } @@ -581,11 +469,8 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testRemoveSubmittersAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Allow manage SubmittersGroup perms - AuthorizeUtil.authorizeManageSubmittersGroup((Context) any, (Collection) any); - result = null; - }}; + // Allow Collection ADMIN (to manage submitter group) + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.ADMIN); collectionService.removeSubmitters(context, collection); assertThat("testRemoveSubmittersAuth 0", collection.getSubmitters(), nullValue()); @@ -596,12 +481,6 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testRemoveSubmittersNoAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Disallow manage SubmittersGroup perms - AuthorizeUtil.authorizeManageSubmittersGroup((Context) any, (Collection) any); - result = new AuthorizeException(); - }}; - collectionService.removeSubmitters(context, collection); fail("Exception expected"); } @@ -619,11 +498,8 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testCreateAdministratorsAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Allow manage AdminGroup perms - AuthorizeUtil.authorizeManageAdminGroup((Context) any, (Collection) any); - result = null; - }}; + // Allow Collection ADMIN (to manage admin group) + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.ADMIN); Group result = collectionService.createAdministrators(context, collection); assertThat("testCreateAdministratorsAuth 0", result, notNullValue()); @@ -634,13 +510,7 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testCreateAdministratorsNoAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Disallow manage AdminGroup perms - AuthorizeUtil.authorizeManageAdminGroup((Context) any, (Collection) any); - result = new AuthorizeException(); - }}; - - Group result = collectionService.createAdministrators(context, collection); + collectionService.createAdministrators(context, collection); fail("Exception expected"); } @@ -649,17 +519,14 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testRemoveAdministratorsAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Allow manage AdminGroup perms (needed to possibly create group first) - AuthorizeUtil.authorizeManageAdminGroup((Context) any, (Collection) any); - result = null; - // Allow remove AdminGroup perms - AuthorizeUtil.authorizeRemoveAdminGroup((Context) any, (Collection) any); - result = null; - }}; + // Allow parent Community ADMIN (only Community Admins can delete a Collection Admin group) + doNothing().when(authorizeServiceSpy).authorizeAction(context, owningCommunity, Constants.ADMIN); // Ensure admin group is created first + context.turnOffAuthorisationSystem(); Group result = collectionService.createAdministrators(context, collection); + context.restoreAuthSystemState(); + assertThat("testRemoveAdministratorsAuth 0", collection.getAdministrators(), notNullValue()); assertThat("testRemoveAdministratorsAuth 1", collection.getAdministrators(), equalTo(result)); collectionService.removeAdministrators(context, collection); @@ -671,17 +538,11 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testRemoveAdministratorsNoAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Allow manage AdminGroup perms (needed to possibly create group first) - AuthorizeUtil.authorizeManageAdminGroup((Context) any, (Collection) any); - result = null; - // Disallow remove AdminGroup perms - AuthorizeUtil.authorizeRemoveAdminGroup((Context) any, (Collection) any); - result = new AuthorizeException(); - }}; - // Ensure admin group is created first + context.turnOffAuthorisationSystem(); Group result = collectionService.createAdministrators(context, collection); + context.restoreAuthSystemState(); + assertThat("testRemoveAdministratorsAuth 0", collection.getAdministrators(), notNullValue()); assertThat("testRemoveAdministratorsAuth 1", collection.getAdministrators(), equalTo(result)); collectionService.removeAdministrators(context, collection); @@ -749,11 +610,8 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testCreateTemplateItemAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Allow manage TemplateItem perms - AuthorizeUtil.authorizeManageTemplateItem((Context) any, (Collection) any); - result = null; - }}; + // Allow Collection ADMIN (to manage template item) + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.ADMIN); itemService.createTemplateItem(context, collection); assertThat("testCreateTemplateItemAuth 0", collection.getTemplateItem(), notNullValue()); @@ -764,12 +622,6 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testCreateTemplateItemNoAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Disallow manage TemplateItem perms - AuthorizeUtil.authorizeManageTemplateItem((Context) any, (Collection) any); - result = new AuthorizeException(); - }}; - itemService.createTemplateItem(context, collection); fail("Exception expected"); } @@ -779,11 +631,8 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testRemoveTemplateItemAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Allow manage TemplateItem perms - AuthorizeUtil.authorizeManageTemplateItem((Context) any, (Collection) any); - result = null; - }}; + // Allow Collection ADMIN (to manage template item) + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.ADMIN); collectionService.removeTemplateItem(context, collection); assertThat("testRemoveTemplateItemAuth 0", collection.getTemplateItem(), nullValue()); @@ -794,12 +643,6 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testRemoveTemplateItemNoAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Disallow manage TemplateItem perms - AuthorizeUtil.authorizeManageTemplateItem((Context) any, (Collection) any); - result = new AuthorizeException(); - }}; - collectionService.removeTemplateItem(context, collection); fail("Exception expected"); } @@ -809,18 +652,15 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testAddItemAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Collection ADD permissions - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.ADD); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = null; - }}; + // Allow Collection ADD perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.ADD); + // create item first + context.turnOffAuthorisationSystem(); WorkspaceItem workspaceItem = workspaceItemService.create(context, collection, false); Item item = installItemService.installItem(context, workspaceItem); + context.restoreAuthSystemState(); + collectionService.addItem(context, collection, item); boolean added = false; Iterator ii = itemService.findByCollection(context, collection); @@ -837,15 +677,15 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testAddItemNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Collection ADD permissions - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.ADD); - result = new AuthorizeException(); - }}; + // Disallow Collection ADD perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, collection, Constants.ADD); + // create item first + context.turnOffAuthorisationSystem(); WorkspaceItem workspaceItem = workspaceItemService.create(context, collection, false); Item item = installItemService.installItem(context, workspaceItem); + context.restoreAuthSystemState(); + collectionService.addItem(context, collection, item); fail("Exception expected"); } @@ -855,25 +695,24 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testRemoveItemAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Collection ADD/REMOVE permissions - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.ADD); - result = null; - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.REMOVE); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.DELETE); - result = null; - }}; + // Allow Collection REMOVE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.REMOVE); + // Allow Item DELETE perms + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Item.class), eq(Constants.DELETE)); + // Allow Item REMOVE perms + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Item.class), eq(Constants.REMOVE)); + // Allow Item WRITE perms (Needed to remove identifiers, e.g. DOI, before Item deletion) + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Item.class), eq(Constants.WRITE)); + // create & add item first + context.turnOffAuthorisationSystem(); WorkspaceItem workspaceItem = workspaceItemService.create(context, collection, false); Item item = installItemService.installItem(context, workspaceItem); collectionService.addItem(context, collection, item); + context.restoreAuthSystemState(); collectionService.removeItem(context, collection, item); boolean isthere = false; @@ -891,20 +730,16 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testRemoveItemNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Collection ADD permissions - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.ADD); - result = null; - // Disallow Collection REMOVE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.REMOVE); - result = new AuthorizeException(); - }}; + // Disallow Collection REMOVE perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy) + .authorizeAction(context, collection, Constants.REMOVE); + // create & add item first + context.turnOffAuthorisationSystem(); WorkspaceItem workspaceItem = workspaceItemService.create(context, collection, false); Item item = installItemService.installItem(context, workspaceItem); collectionService.addItem(context, collection, item); + context.restoreAuthSystemState(); collectionService.removeItem(context, collection, item); fail("Exception expected"); @@ -915,21 +750,8 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testUpdateAuth() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = true; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = true; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; + // Allow Collection WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.WRITE, true); //TODO: how to check update? collectionService.update(context, collection); @@ -940,71 +762,8 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testUpdateAuth2() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = true; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; - - //TODO: how to check update? - collectionService.update(context, collection); - } - - /** - * Test of update method, of class Collection. - */ - @Test - public void testUpdateAuth3() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = true; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; - - //TODO: how to check update? - collectionService.update(context, collection); - } - - /** - * Test of update method, of class Collection. - */ - @Test - public void testUpdateAuth4() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; + // Allow parent Community WRITE perms (test inheritance to Collection) + doNothing().when(authorizeServiceSpy).authorizeAction(context, owningCommunity, Constants.WRITE, true); //TODO: how to check update? collectionService.update(context, collection); @@ -1015,21 +774,9 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testUpdateNoAuth() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Disallow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = new AuthorizeException(); - }}; + // Disallow Collection WRITE perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy) + .authorizeAction(context, collection, Constants.WRITE, true); collectionService.update(context, collection); fail("Exception expected"); @@ -1040,21 +787,8 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testCanEditBooleanAuth() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = true; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = true; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; + // Allow Collection WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.WRITE, true); assertTrue("testCanEditBooleanAuth 0", collectionService.canEditBoolean(context, collection)); } @@ -1064,93 +798,21 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testCanEditBooleanAuth2() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = true; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; + // Allow parent Community WRITE perms (test inheritance to Collection) + doNothing().when(authorizeServiceSpy).authorizeAction(context, owningCommunity, Constants.WRITE, true); + assertTrue("testCanEditBooleanAuth2 0", collectionService.canEditBoolean(context, collection)); } - /** - * Test of canEditBoolean method, of class Collection. - */ - @Test - public void testCanEditBooleanAuth3() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = true; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; - - assertTrue("testCanEditBooleanAuth3 0", collectionService.canEditBoolean(context, collection)); - } - - /** - * Test of canEditBoolean method, of class Collection. - */ - @Test - public void testCanEditBooleanAuth4() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; - - assertTrue("testCanEditBooleanAuth4 0", collectionService.canEditBoolean(context, collection)); - } - /** * Test of canEditBoolean method, of class Collection. */ @Test public void testCanEditBooleanNoAuth() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Disallow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = new AuthorizeException(); - }}; + // Disallow Collection WRITE perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy) + .authorizeAction(context, collection, Constants.WRITE, true); assertFalse("testCanEditBooleanNoAuth 0", collectionService.canEditBoolean(context, collection)); } @@ -1159,214 +821,56 @@ public class CollectionTest extends AbstractDSpaceObjectTest { * Test of canEditBoolean method, of class Collection. */ @Test - public void testCanEditBooleanAuth_boolean() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = true; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = true; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; + public void testCanEditBooleanAuth_useInheritance() throws Exception { + // Allow Collection WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.WRITE, true); - assertTrue("testCanEditBooleanAuth_boolean 0", collectionService.canEditBoolean(context, collection, true)); + assertTrue("testCanEditBooleanAuth_useInheritance", + collectionService.canEditBoolean(context, collection, true)); } /** * Test of canEditBoolean method, of class Collection. */ @Test - public void testCanEditBooleanAuth2_boolean() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = true; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; + public void testCanEditBooleanAuth2_useInheritance() throws Exception { + // Allow parent Community WRITE perms (test inheritance to Collection) + doNothing().when(authorizeServiceSpy).authorizeAction(context, owningCommunity, Constants.WRITE, true); - assertTrue("testCanEditBooleanAuth2_boolean 0", collectionService.canEditBoolean(context, collection, true)); + assertTrue("testCanEditBooleanAuth2_useInheritance", + collectionService.canEditBoolean(context, collection, true)); } /** * Test of canEditBoolean method, of class Collection. */ @Test - public void testCanEditBooleanAuth3_boolean() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = true; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; + public void testCanEditBooleanAuth_noInheritance() throws Exception { + // Allow Collection WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.WRITE, false); - assertTrue("testCanEditBooleanAuth3_boolean 0", collectionService.canEditBoolean(context, collection, true)); + assertTrue("testCanEditBooleanAuth_noInheritance", + collectionService.canEditBoolean(context, collection, false)); } /** * Test of canEditBoolean method, of class Collection. */ @Test - public void testCanEditBooleanAuth4_boolean() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; - - assertTrue("testCanEditBooleanAuth4_boolean 0", collectionService.canEditBoolean(context, collection, true)); + public void testCanEditBooleanAuth2_noInheritance() throws Exception { + assertFalse("testCanEditBooleanAuth_noInheritance", + collectionService.canEditBoolean(context, collection, false)); } - /** - * Test of canEditBoolean method, of class Collection. - */ - @Test - public void testCanEditBooleanAuth5_boolean() throws Exception { - // Test permissions with inheritance turned *OFF* - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, false); - result = true; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, false); - result = true; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, false); - result = null; - }}; - - assertTrue("testCanEditBooleanAuth5_boolean 0", collectionService.canEditBoolean(context, collection, false)); - } - - /** - * Test of canEditBoolean method, of class Collection. - */ - @Test - public void testCanEditBooleanAuth6_boolean() throws Exception { - // Test permissions with inheritance turned *OFF* - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, false); - result = false; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, false); - result = true; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, false); - result = null; - }}; - - assertTrue("testCanEditBooleanAuth6_boolean 0", collectionService.canEditBoolean(context, collection, false)); - } - - /** - * Test of canEditBoolean method, of class Collection. - */ - @Test - public void testCanEditBooleanAuth7_boolean() throws Exception { - // Test permissions with inheritance turned *OFF* - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, false); - result = true; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, false); - result = false; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, false); - result = null; - }}; - - assertTrue("testCanEditBooleanAuth7_boolean 0", collectionService.canEditBoolean(context, collection, false)); - } - - /** - * Test of canEditBoolean method, of class Collection. - */ - @Test - public void testCanEditBooleanAuth8_boolean() throws Exception { - // Test permissions with inheritance turned *OFF* - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, false); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, false); - result = false; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, false); - result = null; - }}; - - assertTrue("testCanEditBooleanAuth8_boolean 0", collectionService.canEditBoolean(context, collection, false)); - } /** * Test of canEditBoolean method, of class Collection. */ @Test public void testCanEditBooleanNoAuth_boolean() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Disallow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = new AuthorizeException(); - }}; + // Disallow Collection WRITE perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy) + .authorizeAction(context, collection, Constants.WRITE, true); assertFalse("testCanEditBooleanNoAuth_boolean 0", collectionService.canEditBoolean(context, collection, true)); } @@ -1376,21 +880,9 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testCanEditBooleanNoAuth2_boolean() throws Exception { - // Test permissions with inheritance turned *OFF* - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, false); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, false); - result = false; - // Disallow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, false); - result = new AuthorizeException(); - }}; + // Disallow Collection WRITE perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy) + .authorizeAction(context, collection, Constants.WRITE, false); assertFalse("testCanEditBooleanNoAuth_boolean 0", collectionService.canEditBoolean(context, collection, false)); } @@ -1400,96 +892,8 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testCanEditAuth_0args() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = true; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = true; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; - - //TODO: how to check?? - collectionService.canEdit(context, collection); - } - - /** - * Test of canEditBoolean method, of class Collection. - */ - @Test - public void testCanEditAuth2_0args() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = true; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; - - //TODO: how to check?? - collectionService.canEdit(context, collection); - } - - /** - * Test of canEditBoolean method, of class Collection. - */ - @Test - public void testCanEditAuth3_0args() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = true; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; - - //TODO: how to check?? - collectionService.canEdit(context, collection); - } - - /** - * Test of canEditBoolean method, of class Collection. - */ - @Test - public void testCanEditAuth4_0args() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; + // Allow Collection WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.WRITE, true); //TODO: how to check?? collectionService.canEdit(context, collection); @@ -1500,21 +904,9 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testCanEditNoAuth_0args() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Disallow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = new AuthorizeException(); - }}; + // Disallow Collection WRITE perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy) + .authorizeAction(context, collection, Constants.WRITE, true); collectionService.canEdit(context, collection); fail("Exception expected"); @@ -1525,23 +917,10 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testCanEditAuth_boolean() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = true; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = true; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; + // Allow Collection WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.WRITE, true); - //TOO: how to check? + //TODO: how to check? collectionService.canEdit(context, collection, true); } @@ -1550,24 +929,11 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testCanEditAuth2_boolean() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = true; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; + // Allow Collection WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.WRITE, false); - //TOO: how to check? - collectionService.canEdit(context, collection, true); + //TODO: how to check? + collectionService.canEdit(context, collection, false); } /** @@ -1575,173 +941,17 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testCanEditAuth3_boolean() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = true; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; + // Allow parent Community WRITE perms (test inheritance to Collection) + doNothing().when(authorizeServiceSpy).authorizeAction(context, owningCommunity, Constants.WRITE, true); - //TOO: how to check? collectionService.canEdit(context, collection, true); } - /** - * Test of canEditBoolean method, of class Collection. - */ - @Test - public void testCanEditAuth4_boolean() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; - - //TOO: how to check? - collectionService.canEdit(context, collection, true); - } - - /** - * Test of canEditBoolean method, of class Collection. - */ - @Test - public void testCanEditAuth5_boolean() throws Exception { - // Test permissions with inheritance turned *OFF* - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, false); - result = true; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, false); - result = true; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, false); - result = null; - }}; - - //TOO: how to check? - collectionService.canEdit(context, collection, false); - } - - /** - * Test of canEditBoolean method, of class Collection. - */ - @Test - public void testCanEditAuth6_boolean() throws Exception { - // Test permissions with inheritance turned *OFF* - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, false); - result = false; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, false); - result = true; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, false); - result = null; - }}; - - //TOO: how to check? - collectionService.canEdit(context, collection, false); - } - - /** - * Test of canEditBoolean method, of class Collection. - */ - @Test - public void testCanEditAuth7_boolean() throws Exception { - // Test permissions with inheritance turned *OFF* - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, false); - result = true; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, false); - result = false; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, false); - result = null; - }}; - - //TOO: how to check? - collectionService.canEdit(context, collection, false); - } - - /** - * Test of canEditBoolean method, of class Collection. - */ - @Test - public void testCanEditAuth8_boolean() throws Exception { - // Test permissions with inheritance turned *OFF* - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, false); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, false); - result = false; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, false); - result = null; - }}; - - //TOO: how to check? - collectionService.canEdit(context, collection, false); - } - /** * Test of canEditBoolean method, of class Collection. */ @Test(expected = AuthorizeException.class) public void testCanEditNoAuth_boolean() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = false; - // Disallow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = new AuthorizeException(); - }}; - - //TOO: how to check? collectionService.canEdit(context, collection, false); fail("Exception expected"); } @@ -1751,23 +961,6 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testCanEditNoAuth2_boolean() throws Exception { - // Test permissions with inheritance turned *OFF* - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, false); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, false); - result = false; - // Disallow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, false); - result = new AuthorizeException(); - }}; - - //TOO: how to check? collectionService.canEdit(context, collection, true); fail("Exception expected"); } @@ -1777,15 +970,10 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test public void testDeleteAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class, authorizeService.getClass()) {{ - // Allow manage TemplateItem perms - AuthorizeUtil.authorizeManageTemplateItem((Context) any, (Collection) any); - result = null; - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, anyBoolean); - result = null; - }}; + // Allow Collection WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.WRITE, true); + // Allow Collection ADMIN perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.ADMIN); UUID id = collection.getID(); collectionService.delete(context, collection); @@ -1798,35 +986,6 @@ public class CollectionTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testDeleteNoAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class, authorizeService.getClass()) {{ - // Disallow manage TemplateItem perms - AuthorizeUtil.authorizeManageTemplateItem((Context) any, (Collection) any); - result = new AuthorizeException(); - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, anyBoolean); - result = null; - }}; - - collectionService.delete(context, collection); - fail("Exception expected"); - } - - /** - * Test of delete method, of class Collection. - */ - @Test(expected = AuthorizeException.class) - public void testDeleteNoAuth2() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class, authorizeService.getClass()) {{ - // Allow manage TemplateItem perms - AuthorizeUtil.authorizeManageTemplateItem((Context) any, (Collection) any); - result = null; - // Disallow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, anyBoolean); - result = new AuthorizeException(); - }}; - collectionService.delete(context, collection); fail("Exception expected"); } @@ -1863,13 +1022,13 @@ public class CollectionTest extends AbstractDSpaceObjectTest { @Test @SuppressWarnings("ObjectEqualsNull") public void testEquals() throws SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; - }}; + // create a new collection for testing + context.turnOffAuthorisationSystem(); + Collection newCollection = collectionService.create(context, owningCommunity); + context.restoreAuthSystemState(); + assertFalse("testEquals 0", collection.equals(null)); - assertFalse("testEquals 1", collection.equals(collectionService.create(context, owningCommunity))); + assertFalse("testEquals 1", collection.equals(newCollection)); assertTrue("testEquals 2", collection.equals(collection)); } diff --git a/dspace-api/src/test/java/org/dspace/content/CommunityTest.java b/dspace-api/src/test/java/org/dspace/content/CommunityTest.java index ace4bb6c14..812060d019 100644 --- a/dspace-api/src/test/java/org/dspace/content/CommunityTest.java +++ b/dspace-api/src/test/java/org/dspace/content/CommunityTest.java @@ -16,6 +16,12 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import java.io.File; import java.io.FileInputStream; @@ -23,16 +29,17 @@ import java.sql.SQLException; import java.util.List; import java.util.UUID; -import mockit.NonStrictExpectations; import org.apache.logging.log4j.Logger; -import org.dspace.app.util.AuthorizeUtil; import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.factory.AuthorizeServiceFactory; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.eperson.Group; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; /** * Unit Tests for class Community @@ -51,6 +58,12 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ private Community c; + /** + * Spy of AuthorizeService to use for tests + * (initialized / setup in @Before method) + */ + private AuthorizeService authorizeServiceSpy; + /** * This method will be run before every test as per @Before. It will * initialize resources required for the tests. @@ -70,6 +83,18 @@ public class CommunityTest extends AbstractDSpaceObjectTest { this.dspaceObject = c; //we need to commit the changes so we don't block the table for testing context.restoreAuthSystemState(); + + // Initialize our spy of the autowired (global) authorizeService bean. + // This allows us to customize the bean's method return values in tests below + authorizeServiceSpy = spy(authorizeService); + // "Wire" our spy to be used by the current loaded object services + // (To ensure both these services use the spy instead of the real service) + ReflectionTestUtils.setField(communityService, "authorizeService", authorizeServiceSpy); + ReflectionTestUtils.setField(collectionService, "authorizeService", authorizeServiceSpy); + ReflectionTestUtils.setField(itemService, "authorizeService", authorizeServiceSpy); + // Also wire into current AuthorizeServiceFactory, as that is used for some checks (e.g. AuthorizeUtil) + ReflectionTestUtils.setField(AuthorizeServiceFactory.getInstance(), "authorizeService", + authorizeServiceSpy); } catch (AuthorizeException ex) { log.error("Authorization Error in init", ex); fail("Authorization Error in init: " + ex.getMessage()); @@ -89,6 +114,15 @@ public class CommunityTest extends AbstractDSpaceObjectTest { @After @Override public void destroy() { + context.turnOffAuthorisationSystem(); + // Delete community created in init() + try { + communityService.delete(context, c); + } catch (Exception e) { + // ignore + } + context.restoreAuthSystemState(); + c = null; super.destroy(); } @@ -111,20 +145,11 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testCreateAuth() throws Exception { - //Default to Community-Admin Rights (but not full Admin rights) - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms (needed for addSubCommunity functionality) - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; - // Allow current Community ADD perms (needed to just create community) - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = true; - // Disallow full Admin perms - authorizeService.isAdmin((Context) any); - result = false; - }}; + // Below settings default to Community-Admin Rights (but not full Admin rights) + // Allow parent Community ADD perms (needed to just create community) + when(authorizeServiceSpy.authorizeActionBoolean(context, c, Constants.ADD)).thenReturn(true); + // Disallow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(false); // test that a Community Admin can create a Community with parent (Sub-Community) Community sub = communityService.create(c, context); @@ -141,16 +166,9 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testCreateAuth2() throws Exception { - //Default to Admin Rights, but NOT Community Admin Rights - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = false; - // Allow full Admin perms - authorizeService.isAdmin((Context) any); - result = true; - }}; + // Below settings default to Full Admin Rights (but not Community Admin rights) + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); //Test that a full Admin can create a Community without a parent (Top-Level Community) Community created = communityService.create(null, context); @@ -172,42 +190,12 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testCreateNoAuth() throws Exception { - //Default to NO Admin Rights, and NO Community Admin Rights - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = false; - // Disallow full Admin perms - authorizeService.isAdmin((Context) any); - result = false; - }}; + // Disallow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(false); - // test creating community with no parent (as a non-admin & non-Community Admin) + // test creating community with no parent (as a non-admin) // this should throw an exception - Community created = communityService.create(null, context); - fail("Exception expected"); - } - - /** - * Test of create method, of class Community. - */ - @Test(expected = AuthorizeException.class) - public void testCreateNoAuth2() throws Exception { - //Default to Community-Admin Rights (but not full Admin rights) - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = true; - // Disallow full Admin perms - authorizeService.isAdmin((Context) any); - result = false; - }}; - - // test creating community with no parent (as a non-admin, but with Community Admin rights) - // this should throw an exception, as only admins can create Top Level communities - Community created = communityService.create(null, context); + communityService.create(null, context); fail("Exception expected"); } @@ -216,16 +204,8 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testCreateWithValidHandle() throws Exception { - //Default to Admin Rights, but NOT Community Admin Rights - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = false; - // Allow full Admin perms - authorizeService.isAdmin((Context) any); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); // test creating community with a specified handle which is NOT already in use // (this handle should not already be used by system, as it doesn't start with "1234567689" prefix) @@ -242,23 +222,15 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test(expected = IllegalStateException.class) public void testCreateWithInvalidHandle() throws Exception { - //Default to Admin Rights, but NOT Community Admin Rights - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = false; - // Allow full Admin perms - authorizeService.isAdmin((Context) any); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); //get handle of our default created community String inUseHandle = c.getHandle(); // test creating community with a specified handle which IS already in use // This should throw an exception - Community created = communityService.create(null, context, inUseHandle); + communityService.create(null, context, inUseHandle); fail("Exception expected"); } @@ -386,21 +358,8 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testSetLogoAuth() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = true; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE); - result = true; - // Allow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = null; - }}; + // Allow current Community WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, c, Constants.WRITE); File f = new File(testProps.get("test.bitstream").toString()); Bitstream logo = communityService.setLogo(context, c, new FileInputStream(f)); @@ -410,117 +369,17 @@ public class CommunityTest extends AbstractDSpaceObjectTest { assertThat("testSetLogoAuth 1", c.getLogo(), nullValue()); } - /** - * Test of setLogo method, of class Community. - */ - @Test - public void testSetLogoAuth2() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = false; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE); - result = true; - // Allow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = null; - }}; - - File f = new File(testProps.get("test.bitstream").toString()); - Bitstream logo = communityService.setLogo(context, c, new FileInputStream(f)); - assertThat("testSetLogoAuth2 0", c.getLogo(), equalTo(logo)); - - c.setLogo(null); - assertThat("testSetLogoAuth2 1", c.getLogo(), nullValue()); - } - - /** - * Test of setLogo method, of class Community. - */ - @Test - public void testSetLogoAuth3() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = true; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE); - result = false; - // Allow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = null; - }}; - - File f = new File(testProps.get("test.bitstream").toString()); - Bitstream logo = communityService.setLogo(context, c, new FileInputStream(f)); - assertThat("testSetLogoAuth3 0", c.getLogo(), equalTo(logo)); - - c.setLogo(null); - assertThat("testSetLogoAuth3 1", c.getLogo(), nullValue()); - } - - /** - * Test of setLogo method, of class Community. - */ - @Test - public void testSetLogoAuth4() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE); - result = false; - // Allow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = null; - }}; - - File f = new File(testProps.get("test.bitstream").toString()); - Bitstream logo = communityService.setLogo(context, c, new FileInputStream(f)); - assertThat("testSetLogoAuth4 0", c.getLogo(), equalTo(logo)); - - c.setLogo(null); - assertThat("testSetLogoAuth4 1", c.getLogo(), nullValue()); - } - /** * Test of setLogo method, of class Community. */ @Test(expected = AuthorizeException.class) public void testSetLogoNoAuth() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE); - result = false; - // Disallow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = new AuthorizeException(); - }}; + // Disallow current Community WRITE perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, c, Constants.WRITE); File f = new File(testProps.get("test.bitstream").toString()); - Bitstream logo = communityService.setLogo(context, c, new FileInputStream(f)); - fail("EXception expected"); + communityService.setLogo(context, c, new FileInputStream(f)); + fail("Exception expected"); } /** @@ -528,16 +387,8 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testUpdateNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow current Community ADD perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = new AuthorizeException(); - // Disallow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = new AuthorizeException(); - }}; + // Disallow current Community WRITE perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, c, Constants.WRITE); //TODO: we need to verify the update, how? communityService.update(context, c); @@ -549,16 +400,8 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testUpdateAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow current Community ADD perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; - // Allow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = null; - }}; + // Allow current Community WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, c, Constants.WRITE); //TODO: we need to verify the update, how? communityService.update(context, c); @@ -569,11 +412,8 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testCreateAdministratorsAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Allow manage AdminGroup perms - AuthorizeUtil.authorizeManageAdminGroup((Context) any, (Community) any); - result = null; - }}; + // Allow Community ADMIN perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, c, Constants.ADMIN); Group result = communityService.createAdministrators(context, c); assertThat("testCreateAdministratorsAuth 0", c.getAdministrators(), notNullValue()); @@ -585,14 +425,7 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testCreateAdministratorsNoAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Disallow manage AdminGroup perms - AuthorizeUtil.authorizeManageAdminGroup((Context) any, (Community) any); - result = new AuthorizeException(); - - }}; - - Group result = communityService.createAdministrators(context, c); + communityService.createAdministrators(context, c); fail("Exception should have been thrown"); } @@ -602,17 +435,14 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testRemoveAdministratorsAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Allow manage AdminGroup perms (needed to possibly create group first) - AuthorizeUtil.authorizeManageAdminGroup((Context) any, (Community) any); - result = null; - // Allow remove AdminGroup perms - AuthorizeUtil.authorizeRemoveAdminGroup((Context) any, (Community) any); - result = null; - }}; + // Allow Full ADMIN perms (Community Admins cannot delete their Admin Group) + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); // Ensure admin group is created first + context.turnOffAuthorisationSystem(); Group result = communityService.createAdministrators(context, c); + context.restoreAuthSystemState(); + assertThat("testRemoveAdministratorsAuth 0", c.getAdministrators(), notNullValue()); assertThat("testRemoveAdministratorsAuth 1", c.getAdministrators(), equalTo(result)); communityService.removeAdministrators(context, c); @@ -624,17 +454,14 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testRemoveAdministratorsNoAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Allow manage AdminGroup perms (needed to possibly create group first) - AuthorizeUtil.authorizeManageAdminGroup((Context) any, (Community) any); - result = null; - // Disallow remove AdminGroup perms - AuthorizeUtil.authorizeRemoveAdminGroup((Context) any, (Community) any); - result = new AuthorizeException(); - }}; + // Allow Community ADMIN perms (Community Admins cannot delete their Admin Group) + doNothing().when(authorizeServiceSpy).authorizeAction(context, c, Constants.ADMIN); // Ensure admin group is created first + context.turnOffAuthorisationSystem(); Group result = communityService.createAdministrators(context, c); + context.restoreAuthSystemState(); + assertThat("testRemoveAdministratorsAuth 0", c.getAdministrators(), notNullValue()); assertThat("testRemoveAdministratorsAuth 1", c.getAdministrators(), equalTo(result)); communityService.removeAdministrators(context, c); @@ -655,17 +482,6 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testGetCollections() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow current Community ADD perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; - // Allow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = null; - }}; - //empty by default assertThat("testGetCollections 0", c.getCollections(), notNullValue()); assertTrue("testGetCollections 1", c.getCollections().size() == 0); @@ -697,17 +513,6 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testGetSubcommunities() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow current Community ADD perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; - // Allow *parent* Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = true; - }}; - //empty by default assertThat("testGetSubcommunities 0", c.getSubcommunities(), notNullValue()); assertTrue("testGetSubcommunities 1", c.getSubcommunities().size() == 0); @@ -739,22 +544,13 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testGetParentCommunity() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow current Community ADD perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; - // Allow *parent* Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = true; - }}; - //null by default assertThat("testGetParentCommunity 0", c.getParentCommunities().size(), equalTo(0)); - //community with parent + //community with parent + context.turnOffAuthorisationSystem(); Community son = communityService.create(c, context); + context.restoreAuthSystemState(); assertThat("testGetParentCommunity 1", son.getParentCommunities().size(), not(0)); assertThat("testGetParentCommunity 2", son.getParentCommunities().get(0), equalTo(c)); } @@ -764,23 +560,14 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testGetAllParents() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow current Community ADD perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; - // Allow *parent* Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = true; - }}; - //empty by default assertThat("testGetAllParents 0", communityService.getAllParents(context, c), notNullValue()); assertTrue("testGetAllParents 1", communityService.getAllParents(context, c).size() == 0); - //community with parent + //community with parent + context.turnOffAuthorisationSystem(); Community son = communityService.create(c, context); + context.restoreAuthSystemState(); assertThat("testGetAllParents 2", communityService.getAllParents(context, son), notNullValue()); assertTrue("testGetAllParents 3", communityService.getAllParents(context, son).size() == 1); assertThat("testGetAllParents 4", communityService.getAllParents(context, son).get(0), equalTo(c)); @@ -791,25 +578,16 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testGetAllCollections() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow current Community ADD perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; - // Allow *parent* Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = true; - }}; - //empty by default assertThat("testGetAllCollections 0", communityService.getAllCollections(context, c), notNullValue()); assertTrue("testGetAllCollections 1", communityService.getAllCollections(context, c).size() == 0); //community has a collection and a subcommunity, subcommunity has a collection + context.turnOffAuthorisationSystem(); Collection collOfC = collectionService.create(context, c); Community sub = communityService.create(c, context); Collection collOfSub = collectionService.create(context, sub); + context.restoreAuthSystemState(); assertThat("testGetAllCollections 2", communityService.getAllCollections(context, c), notNullValue()); assertTrue("testGetAllCollections 3", communityService.getAllCollections(context, c).size() == 2); assertThat("testGetAllCollections 4", communityService.getAllCollections(context, c).get(0), @@ -822,13 +600,8 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testCreateCollectionAuth() throws Exception { - // Need current Community ADD permissions in order to create a Collection - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow current Community ADD perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; - }}; + // Allow current Community ADD perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, c, Constants.ADD); Collection result = collectionService.create(context, c); assertThat("testCreateCollectionAuth 0", result, notNullValue()); @@ -841,14 +614,10 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testCreateCollectionNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow current Community ADD perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = new AuthorizeException(); - }}; + // Disallow current Community ADD perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, c, Constants.ADD); - Collection result = collectionService.create(context, c); + collectionService.create(context, c); fail("Exception expected"); } @@ -857,12 +626,8 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testAddCollectionAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow current Community ADD perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; - }}; + // Allow current Community ADD perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, c, Constants.ADD); Collection col = collectionService.create(context, c); c.addCollection(col); @@ -875,12 +640,8 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testAddCollectionNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow current Community ADD perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = new AuthorizeException(); - }}; + // Disallow current Community ADD perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, c, Constants.ADD); Collection col = collectionService.create(context, c); c.addCollection(col); @@ -892,16 +653,10 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testCreateSubcommunityAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow current Community ADD perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; - // Allow *parent* Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = true; - }}; + // Allow current Community ADD perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, c, Constants.ADD); + // Allow parent Community ADD perms + when(authorizeServiceSpy.authorizeActionBoolean(context, c, Constants.ADD)).thenReturn(true); Community result = communityService.createSubcommunity(context, c); assertThat("testCreateSubcommunityAuth 0", c.getSubcommunities(), notNullValue()); @@ -914,18 +669,10 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testCreateSubcommunityNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow current Community ADD perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = new AuthorizeException(); - // Allow *parent* Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = true; - }}; + // Disallow current Community ADD perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, c, Constants.ADD); - Community result = communityService.createSubcommunity(context, c); + communityService.createSubcommunity(context, c); fail("Exception expected"); } @@ -934,16 +681,8 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testAddSubcommunityAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow current Community ADD perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; - // Allow *parent* Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = true; - }}; + // Allow current Community ADD perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, c, Constants.ADD); // Turn off authorization temporarily to create a new top-level community context.turnOffAuthorisationSystem(); @@ -961,18 +700,11 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testAddSubcommunityNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow current Community ADD perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = new AuthorizeException(); - // Allow *parent* Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = true; - }}; - + // Turn off authorization temporarily to create a new top-level community + context.turnOffAuthorisationSystem(); Community result = communityService.create(null, context); + context.restoreAuthSystemState(); + communityService.addSubcommunity(context, c, result); fail("Exception expected"); } @@ -982,30 +714,23 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testRemoveCollectionAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass(), AuthorizeUtil.class) {{ - // Allow current Community ADD perms (to add Collection) - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; - // Allow current Community REMOVE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.REMOVE); - result = null; - // Allow Collection ManageTemplateItem perms (needed to delete Collection) - AuthorizeUtil.authorizeManageTemplateItem((Context) any, (Collection) any); - result = null; - // Allow Collection WRITE perms (needed to delete Collection) - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; - + // Turn off authorization temporarily to create a new Collection + context.turnOffAuthorisationSystem(); Collection col = collectionService.create(context, c); + context.restoreAuthSystemState(); assertThat("testRemoveCollectionAuth 0", c.getCollections(), notNullValue()); assertTrue("testRemoveCollectionAuth 1", c.getCollections().size() == 1); assertThat("testRemoveCollectionAuth 2", c.getCollections().get(0), equalTo(col)); - c.removeCollection(col); + // Allow current Community REMOVE perms (to remove Collection from Community & delete) + doNothing().when(authorizeServiceSpy).authorizeAction(context, c, Constants.REMOVE); + // Allow collection WRITE perms (needed to remove Logo before Collection can be deleted) + doNothing().when(authorizeServiceSpy).authorizeAction(context, col, Constants.WRITE,true); + // Allow current Community ADMIN perms (to remove Collection from Community & delete) + doNothing().when(authorizeServiceSpy).authorizeAction(context, col, Constants.ADMIN); + + // Note that this will *also* delete the collection (hence the extra permissions provided above) + communityService.removeCollection(context, c, col); assertThat("testRemoveCollectionAuth 3", c.getCollections(), notNullValue()); assertTrue("testRemoveCollectionAuth 4", c.getCollections().size() == 0); } @@ -1015,18 +740,13 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testRemoveCollectionNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow current Community ADD perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; - // Disallow current Community REMOVE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.REMOVE); - result = new AuthorizeException(); - }}; + // Disallow current Community REMOVE perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, c, Constants.REMOVE); + // Turn off authorization temporarily to create a new Collection + context.turnOffAuthorisationSystem(); Collection col = collectionService.create(context, c); + context.restoreAuthSystemState(); assertThat("testRemoveCollectionNoAuth 0", c.getCollections(), notNullValue()); assertTrue("testRemoveCollectionNoAuth 1", c.getCollections().size() == 1); assertThat("testRemoveCollectionNoAuth 2", c.getCollections().get(0), equalTo(col)); @@ -1040,20 +760,15 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testRemoveSubcommunityAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Community ADD perms (in order to add a new subcommunity to parent) - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD, true); - result = null; - // Allow Community REMOVE perms (needed to unmap/remove subcommunity) - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.REMOVE, true); - result = null; - // Allow Community DELETE perms (needed to actually delete subcommunity) - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.DELETE, true); - result = null; - }}; + // Allow current Community ADD perms (in order to add a new subcommunity to parent) + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Community.class), eq(Constants.ADD), eq(true)); + // Allow current Community REMOVE perms (needed to unmap/remove subcommunity) + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Community.class), eq(Constants.REMOVE), eq(true)); + // Allow current Community REMOVE perms (needed to actually delete subcommunity) + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Community.class), eq(Constants.DELETE), eq(true)); // Turn off authorization temporarily to create a new top-level community context.turnOffAuthorisationSystem(); @@ -1075,24 +790,12 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testDeleteAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow current Community ADD perms (to create content to be deleted) - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; - // Allow current Community WRITE perms (to create content to be deleted) - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = null; - // Allow current Community DELETE perms (needed to delete community) - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.DELETE); - result = null; - // Disallow *parent* Community REMOVE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.REMOVE); - result = false; - }}; + // Allow current Community WRITE perms (to create content to be deleted) + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Community.class), eq(Constants.WRITE)); + // Allow current Community DELETE perms (needed to delete community) + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Community.class), eq(Constants.DELETE)); // Turn off authorization temporarily to create a new top-level community context.turnOffAuthorisationSystem(); @@ -1111,24 +814,12 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testDeleteAuth2() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow current Community ADD perms (to create content to be deleted) - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.ADD); - result = null; - // Allow current Community WRITE perms (to create content to be deleted) - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = null; - // Allow current Community DELETE perms (needed to delete community) - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.DELETE); - result = null; - // Allow *parent* Community REMOVE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.REMOVE, true); - result = true; - }}; + // Allow current Community WRITE perms + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Community.class), eq(Constants.WRITE)); + // Allow current Community DELETE perms (needed to delete community) + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Community.class), eq(Constants.DELETE)); // Turn off authorization temporarily to create a new top-level community context.turnOffAuthorisationSystem(); @@ -1147,28 +838,27 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testDeleteHierachyAuth() throws Exception { - System.out.println("testDeleteHierarchyAuth"); - new NonStrictExpectations(authorizeService.getClass(), AuthorizeUtil.class) {{ - // Allow current Community DELETE perms (needed to delete a community) - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.DELETE, true); - result = null; - // Allow current Community REMOVE perms (needed to remove a sub-community from a community) - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.REMOVE, true); - result = null; - // Allow Collection ManageTemplateItem perms (needed to delete a collection) - AuthorizeUtil.authorizeManageTemplateItem((Context) any, (Collection) any); - result = null; - // Allow current Collection DELETE perms (needed to delete a Collection) - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.DELETE, true); - result = null; - // Allow current Item WRITE perms (needed to remove identifiers from an Item prior to deletion) - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE, true); - result = null; - }}; + // Allow current Community DELETE perms (needed to delete a community) + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Community.class), eq(Constants.DELETE), eq(true)); + // Allow current Community REMOVE perms (needed to remove a sub-community from a community) + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Community.class), eq(Constants.REMOVE), eq(true)); + // Allow current Collection DELETE perms (needed to delete a Collection) + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Collection.class), eq(Constants.DELETE), eq(true)); + // Allow current Collection ADMIN perms (needed to delete a Collection) + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Collection.class), eq(Constants.ADMIN)); + // Allow current Item WRITE perms (needed to remove identifiers from an Item prior to deletion) + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Item.class), eq(Constants.WRITE), eq(true)); + // Allow current Item DELETE perms (needed to delete Item) + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Item.class), eq(Constants.DELETE), eq(true)); + // Allow current Item REMOVE perms (needed to delete Item) + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Item.class), eq(Constants.REMOVE), eq(true)); // Create a dummy Community hierarchy to test delete with // Turn off authorization temporarily to create some test objects. @@ -1183,8 +873,8 @@ public class CommunityTest extends AbstractDSpaceObjectTest { // Create two separate items WorkspaceItem wsItem = workspaceItemService.create(context, childCol, false); Item item = installItemService.installItem(context, wsItem); - wsItem = workspaceItemService.create(context, childCol, false); - item = installItemService.installItem(context, wsItem); + wsItem = workspaceItemService.create(context, grandchildCol, false); + Item item2 = installItemService.installItem(context, wsItem); // Done creating the objects. Turn auth system back on context.restoreAuthSystemState(); @@ -1196,6 +886,7 @@ public class CommunityTest extends AbstractDSpaceObjectTest { UUID childColId = childCol.getID(); UUID grandchildColId = grandchildCol.getID(); UUID itemId = item.getID(); + UUID item2Id = item2.getID(); // Delete the parent of this entire hierarchy communityService.delete(context, parent); @@ -1213,6 +904,8 @@ public class CommunityTest extends AbstractDSpaceObjectTest { collectionService.find(context, grandchildColId), nullValue()); assertThat("Item not deleted", itemService.find(context, itemId), nullValue()); + assertThat("Item not deleted", + itemService.find(context, item2Id), nullValue()); } /** @@ -1220,16 +913,10 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testDeleteNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow current Community DELETE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.DELETE); - result = new AuthorizeException(); - // Disallow *parent* Community REMOVE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.REMOVE); - result = false; - }}; + // Disallow current Community DELETE perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Community.class), + eq(Constants.DELETE)); communityService.delete(context, c); fail("Exception expected"); @@ -1241,11 +928,8 @@ public class CommunityTest extends AbstractDSpaceObjectTest { @Test @SuppressWarnings("ObjectEqualsNull") public void testEquals() throws SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow full Admin perms (just to create top-level community) - authorizeService.isAdmin((Context) any); - result = true; - }}; + // Allow full Admin perms (just to create top-level community) + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); assertFalse("testEquals 0", c.equals(null)); assertFalse("testEquals 1", c.equals(communityService.create(null, context))); @@ -1266,117 +950,20 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testCanEditBooleanAuth() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = true; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE); - result = true; - // Allow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = null; - }}; + // Allow current Community WRITE perms + doNothing().when(authorizeServiceSpy) + .authorizeAction(context, c, Constants.WRITE); assertTrue("testCanEditBooleanAuth 0", communityService.canEditBoolean(context, c)); } - /** - * Test of canEditBoolean method, of class Community. - */ - @Test - public void testCanEditBooleanAuth2() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = false; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE); - result = true; - // Allow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = null; - }}; - - assertTrue("testCanEditBooleanAuth2 0", communityService.canEditBoolean(context, c)); - } - - /** - * Test of canEditBoolean method, of class Community. - */ - @Test - public void testCanEditBooleanAuth3() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = true; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE); - result = false; - // Allow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = null; - }}; - - assertTrue("testCanEditBooleanAuth3 0", communityService.canEditBoolean(context, c)); - } - - /** - * Test of canEditBoolean method, of class Community. - */ - @Test - public void testCanEditBooleanAuth4() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE); - result = false; - // Allow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = null; - }}; - - assertTrue("testCanEditBooleanAuth4 0", communityService.canEditBoolean(context, c)); - } - /** * Test of canEditBoolean method, of class Community. */ @Test public void testCanEditBooleanNoAuth() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE); - result = false; - // Disallow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = new AuthorizeException(); - }}; + // Disallow current Community WRITE perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, c, Constants.WRITE); assertFalse("testCanEditBooleanNoAuth 0", communityService.canEditBoolean(context, c)); } @@ -1386,93 +973,8 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test public void testCanEditAuth() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = true; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE); - result = true; - // Allow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = null; - }}; - - communityService.canEdit(context, c); - } - - /** - * Test of canEdit method, of class Community. - */ - @Test - public void testCanEditAuth1() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = false; - // Allow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE); - result = true; - // Allow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = null; - }}; - - communityService.canEdit(context, c); - } - - /** - * Test of canEdit method, of class Community. - */ - @Test - public void testCanEditAuth2() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = true; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE); - result = false; - // Allow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = null; - }}; - - communityService.canEdit(context, c); - } - - /** - * Test of canEdit method, of class Community. - */ - @Test - public void testCanEditAuth3() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE); - result = false; - // Allow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = null; - }}; + // Allow current Community WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, c, Constants.WRITE); communityService.canEdit(context, c); } @@ -1482,21 +984,8 @@ public class CommunityTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testCanEditNoAuth() throws Exception { - // Test inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow parent Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = false; - // Disallow parent Community WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE); - result = false; - // Disallow current Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE); - result = new AuthorizeException(); - }}; + // Disallow current Community WRITE perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, c,Constants.WRITE); communityService.canEdit(context, c); fail("Exception expected"); @@ -1545,7 +1034,7 @@ public class CommunityTest extends AbstractDSpaceObjectTest { assertThat("testGetParentObject 1", communityService.getParentObject(context, son), notNullValue()); assertThat("testGetParentObject 2", (Community) communityService.getParentObject(context, son), equalTo(c)); } catch (AuthorizeException ex) { - fail("Authorize exception catched"); + throw new AssertionError("AuthorizeException occurred", ex); } } diff --git a/dspace-api/src/test/java/org/dspace/content/DCDateTest.java b/dspace-api/src/test/java/org/dspace/content/DCDateTest.java index e827fb4434..8ed97bd46f 100644 --- a/dspace-api/src/test/java/org/dspace/content/DCDateTest.java +++ b/dspace-api/src/test/java/org/dspace/content/DCDateTest.java @@ -19,7 +19,6 @@ import java.util.Locale; import java.util.TimeZone; import org.apache.commons.lang3.time.DateUtils; -import org.apache.logging.log4j.Logger; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -28,11 +27,6 @@ import org.junit.Test; * @author pvillega */ public class DCDateTest { - /** - * log4j category - */ - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(DCDateTest.class); - /** * Object to use in the tests */ diff --git a/dspace-api/src/test/java/org/dspace/content/EntityServiceImplTest.java b/dspace-api/src/test/java/org/dspace/content/EntityServiceImplTest.java index 5aa9d97b79..f4563cebf0 100644 --- a/dspace-api/src/test/java/org/dspace/content/EntityServiceImplTest.java +++ b/dspace-api/src/test/java/org/dspace/content/EntityServiceImplTest.java @@ -8,16 +8,14 @@ package org.dspace.content; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; import java.util.UUID; -import org.dspace.content.dao.RelationshipTypeDAO; import org.dspace.content.service.EntityTypeService; import org.dspace.content.service.ItemService; import org.dspace.content.service.RelationshipService; @@ -27,7 +25,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class EntityServiceImplTest { @@ -60,10 +58,8 @@ public class EntityServiceImplTest { public void testfindByItemId() throws Exception { // Declare objects utilized in unit test Item item = mock(Item.class); - List relationshipList = new ArrayList<>(); Relationship relationship = mock(Relationship.class); relationship.setId(9); - relationshipList.add(relationship); // Mock the state of objects utilized in findByItemId() to meet the success criteria of an invocation when(itemService.find(any(), any())).thenReturn(item); @@ -79,10 +75,6 @@ public class EntityServiceImplTest { // Declare objects utilized in unit test Entity entity = mock(Entity.class); EntityTypeService entityTypeService = mock(EntityTypeService.class); - Item item = mock(Item.class); - List list = new ArrayList<>(); - MetadataValue metadataValue = mock(MetadataValue.class); - list.add(metadataValue); EntityType entityType = entityTypeService.findByEntityType(context, "testType"); // The returned EntityType should equal our defined entityType case @@ -151,11 +143,7 @@ public class EntityServiceImplTest { @Test public void testGetAllRelationshipTypes() throws Exception { // Declare objects utilized for this test - List list = new ArrayList<>(); - MetadataValue metadataValue = mock(MetadataValue.class); - list.add(metadataValue); Item item = mock(Item.class); - RelationshipTypeDAO relationshipTypeDAO = mock(RelationshipTypeDAO.class); Entity entity = mock(Entity.class); RelationshipType relationshipType = mock(RelationshipType.class); relationshipType.setLeftType(leftType); @@ -185,7 +173,7 @@ public class EntityServiceImplTest { RelationshipType relationshipType = mock(RelationshipType.class); // Currently this unit test will only test one case with one relationshipType - List relationshipTypeList = new LinkedList<>(); + List relationshipTypeList = new ArrayList<>(); relationshipTypeList.add(relationshipType); List metsList = new ArrayList<>(); MetadataValue metadataValue = mock(MetadataValue.class); @@ -213,7 +201,7 @@ public class EntityServiceImplTest { RelationshipType relationshipType = mock(RelationshipType.class); // Currently this unit test will only test one case with one relationshipType - List relationshipTypeList = new LinkedList<>(); + List relationshipTypeList = new ArrayList<>(); relationshipTypeList.add(relationshipType); List metsList = new ArrayList<>(); MetadataValue metadataValue = mock(MetadataValue.class); @@ -236,7 +224,7 @@ public class EntityServiceImplTest { @Test public void testGetRelationshipTypesByTypeName() throws Exception { // Declare objects utilized in unit test - List list = new LinkedList<>(); + List list = new ArrayList<>(); RelationshipType relationshipType = mock(RelationshipType.class); list.add(relationshipType); diff --git a/dspace-api/src/test/java/org/dspace/content/EntityTypeServiceImplTest.java b/dspace-api/src/test/java/org/dspace/content/EntityTypeServiceImplTest.java index 707323deb5..c54f0fc955 100644 --- a/dspace-api/src/test/java/org/dspace/content/EntityTypeServiceImplTest.java +++ b/dspace-api/src/test/java/org/dspace/content/EntityTypeServiceImplTest.java @@ -8,7 +8,7 @@ package org.dspace.content; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; @@ -23,7 +23,7 @@ import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class EntityTypeServiceImplTest { diff --git a/dspace-api/src/test/java/org/dspace/content/FormatIdentifierTest.java b/dspace-api/src/test/java/org/dspace/content/FormatIdentifierTest.java index 4e0711aac0..1e4854044e 100644 --- a/dspace-api/src/test/java/org/dspace/content/FormatIdentifierTest.java +++ b/dspace-api/src/test/java/org/dspace/content/FormatIdentifierTest.java @@ -14,7 +14,6 @@ import static org.junit.Assert.assertThat; import java.io.File; import java.io.FileInputStream; -import org.apache.logging.log4j.Logger; import org.dspace.AbstractUnitTest; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.BitstreamFormatService; @@ -30,11 +29,6 @@ import org.junit.Test; */ public class FormatIdentifierTest extends AbstractUnitTest { - /** - * log4j category - */ - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(FormatIdentifierTest.class); - protected BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); protected BitstreamFormatService bitstreamFormatService = ContentServiceFactory.getInstance() .getBitstreamFormatService(); @@ -71,8 +65,8 @@ public class FormatIdentifierTest extends AbstractUnitTest { @Test public void testGuessFormat() throws Exception { File f = new File(testProps.get("test.bitstream").toString()); - Bitstream bs = null; - BitstreamFormat result = null; + Bitstream bs; + BitstreamFormat result; BitstreamFormat pdf = bitstreamFormatService.findByShortDescription(context, "Adobe PDF"); //test null filename diff --git a/dspace-api/src/test/java/org/dspace/content/ITCommunityCollection.java b/dspace-api/src/test/java/org/dspace/content/ITCommunityCollection.java index e7f9c29794..ca197e67a9 100644 --- a/dspace-api/src/test/java/org/dspace/content/ITCommunityCollection.java +++ b/dspace-api/src/test/java/org/dspace/content/ITCommunityCollection.java @@ -19,7 +19,6 @@ import java.io.IOException; import java.sql.SQLException; import java.util.UUID; -import org.apache.logging.log4j.Logger; import org.dspace.AbstractIntegrationTest; import org.dspace.authorize.AuthorizeException; import org.dspace.content.factory.ContentServiceFactory; @@ -48,11 +47,6 @@ import org.junit.Test; * @author tdonohue */ public class ITCommunityCollection extends AbstractIntegrationTest { - /** - * log4j category - */ - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(ITCommunityCollection.class); - protected CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService(); protected ItemService itemService = ContentServiceFactory.getInstance().getItemService(); @@ -107,8 +101,8 @@ public class ITCommunityCollection extends AbstractIntegrationTest { //verify it works as expected assertThat("testCreateTree 0", parent.getParentCommunities().size(), is(0)); assertThat("testCreateTree 1", child1.getParentCommunities().get(0), equalTo(parent)); - assertThat("testCreateTree 2", (Community) collectionService.getParentObject(context, col1), equalTo(child1)); - assertThat("testCreateTree 3", (Community) collectionService.getParentObject(context, col2), equalTo(child1)); + assertThat("testCreateTree 2", collectionService.getParentObject(context, col1), equalTo(child1)); + assertThat("testCreateTree 3", collectionService.getParentObject(context, col2), equalTo(child1)); context.turnOffAuthorisationSystem(); communityService.delete(context, parent); @@ -133,8 +127,8 @@ public class ITCommunityCollection extends AbstractIntegrationTest { context.restoreAuthSystemState(); //verify it works as expected - assertThat("testCreateItems 0", (Collection) itemService.getParentObject(context, item1), equalTo(col1)); - assertThat("testCreateItems 1", (Collection) itemService.getParentObject(context, item2), equalTo(col2)); + assertThat("testCreateItems 0", itemService.getParentObject(context, item1), equalTo(col1)); + assertThat("testCreateItems 1", itemService.getParentObject(context, item2), equalTo(col2)); context.turnOffAuthorisationSystem(); communityService.delete(context, parent); @@ -158,8 +152,8 @@ public class ITCommunityCollection extends AbstractIntegrationTest { // Add same number of items to each collection for (int count = 0; count < items_per_collection; count++) { - Item item1 = installItemService.installItem(context, workspaceItemService.create(context, col1, false)); - Item item2 = installItemService.installItem(context, workspaceItemService.create(context, col2, false)); + installItemService.installItem(context, workspaceItemService.create(context, col1, false)); + installItemService.installItem(context, workspaceItemService.create(context, col2, false)); } // Finally, let's throw in a small wrench and add a mapped item @@ -229,7 +223,6 @@ public class ITCommunityCollection extends AbstractIntegrationTest { context.setCurrentUser(commAdmin); // Test deletion of single Bitstream as a Community Admin (delete just flags as deleted) - UUID bitstreamId = bitstream.getID(); bitstreamService.delete(context, bitstream); assertTrue("Community Admin unable to flag Bitstream as deleted", bitstream.isDeleted()); @@ -312,7 +305,6 @@ public class ITCommunityCollection extends AbstractIntegrationTest { context.setCurrentUser(collAdmin); // Test deletion of single Bitstream as a Collection Admin (delete just flags as deleted) - UUID bitstreamId = bitstream2.getID(); bitstreamService.delete(context, bitstream2); assertTrue("Collection Admin unable to flag Bitstream as deleted", bitstream2.isDeleted()); @@ -327,7 +319,6 @@ public class ITCommunityCollection extends AbstractIntegrationTest { // Test deletion of single Item as a Collection Admin UUID itemId = item.getID(); bundleId = bundle.getID(); - bitstreamId = bitstream.getID(); itemService.delete(context, item); assertThat("Collection Admin unable to delete sub-Item", itemService.find(context, itemId), nullValue()); diff --git a/dspace-api/src/test/java/org/dspace/content/ITMetadata.java b/dspace-api/src/test/java/org/dspace/content/ITMetadata.java index 10afd4d85f..651b712ba3 100644 --- a/dspace-api/src/test/java/org/dspace/content/ITMetadata.java +++ b/dspace-api/src/test/java/org/dspace/content/ITMetadata.java @@ -16,7 +16,6 @@ import java.io.IOException; import java.sql.SQLException; import java.util.List; -import org.apache.logging.log4j.Logger; import org.dspace.AbstractIntegrationTest; import org.dspace.authorize.AuthorizeException; import org.dspace.content.factory.ContentServiceFactory; @@ -37,11 +36,6 @@ import org.junit.Test; * @author pvillega */ public class ITMetadata extends AbstractIntegrationTest { - /** - * log4j category - */ - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(ITMetadata.class); - protected CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService(); diff --git a/dspace-api/src/test/java/org/dspace/content/InProgressSubmissionTest.java b/dspace-api/src/test/java/org/dspace/content/InProgressSubmissionTest.java index 78be99bb01..1bcac66985 100644 --- a/dspace-api/src/test/java/org/dspace/content/InProgressSubmissionTest.java +++ b/dspace-api/src/test/java/org/dspace/content/InProgressSubmissionTest.java @@ -7,7 +7,6 @@ */ package org.dspace.content; -import org.apache.logging.log4j.Logger; import org.dspace.AbstractUnitTest; import org.junit.After; import org.junit.Before; @@ -23,11 +22,6 @@ import org.junit.Test; */ public class InProgressSubmissionTest extends AbstractUnitTest { - /** - * log4j category - */ - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(InProgressSubmissionTest.class); - /** * This method will be run before every test as per @Before. It will * initialize resources required for the tests. diff --git a/dspace-api/src/test/java/org/dspace/content/InstallItemTest.java b/dspace-api/src/test/java/org/dspace/content/InstallItemTest.java index 129727bd14..62d48cbb5d 100644 --- a/dspace-api/src/test/java/org/dspace/content/InstallItemTest.java +++ b/dspace-api/src/test/java/org/dspace/content/InstallItemTest.java @@ -11,6 +11,8 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import java.io.File; import java.io.FileInputStream; @@ -21,24 +23,22 @@ import java.util.Calendar; import java.util.List; import java.util.TimeZone; -import mockit.NonStrictExpectations; import org.apache.logging.log4j.Logger; import org.dspace.AbstractUnitTest; import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; import org.dspace.content.service.InstallItemService; import org.dspace.content.service.ItemService; import org.dspace.content.service.WorkspaceItemService; -import org.dspace.core.Constants; -import org.dspace.core.Context; -import org.dspace.eperson.EPerson; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.springframework.test.util.ReflectionTestUtils; /** * Unit Tests for class InstallItem @@ -57,6 +57,12 @@ public class InstallItemTest extends AbstractUnitTest { private Collection collection; private Community owningCommunity; + /** + * Spy of AuthorizeService to use for tests + * (initialized / setup in @Before method) + */ + private AuthorizeService authorizeServiceSpy; + /** * log4j category */ @@ -78,6 +84,14 @@ public class InstallItemTest extends AbstractUnitTest { this.owningCommunity = communityService.create(null, context); this.collection = collectionService.create(context, owningCommunity); context.restoreAuthSystemState(); + + // Initialize our spy of the autowired (global) authorizeService bean. + // This allows us to customize the bean's method return values in tests below + authorizeServiceSpy = spy(authorizeService); + // "Wire" our spy to be used by the current loaded workspaceItemService and collectionService + // (To ensure it uses the spy instead of the real service) + ReflectionTestUtils.setField(workspaceItemService, "authorizeService", authorizeServiceSpy); + ReflectionTestUtils.setField(collectionService, "authorizeService", authorizeServiceSpy); } catch (SQLException | AuthorizeException ex) { log.error("SQL Error in init", ex); fail("SQL Error in init: " + ex.getMessage()); @@ -140,30 +154,22 @@ public class InstallItemTest extends AbstractUnitTest { /** * Test of installItem method (with an invalid handle), of class InstallItem. */ - @Test + @Test(expected = AuthorizeException.class) public void testInstallItem_invalidHandle() throws Exception { - //Default to Full-Admin rights - new NonStrictExpectations(authorizeService.getClass()) {{ - // Deny Community ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD); - result = false; - // Allow full Admin perms - authorizeService.isAdmin((Context) any); - result = true; - authorizeService.isAdmin((Context) any, (EPerson) any); - result = true; - }}; + // Allow full Admin rights + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); - String handle = "123456789/56789"; + // create two items for tests + context.turnOffAuthorisationSystem(); WorkspaceItem is = workspaceItemService.create(context, collection, false); WorkspaceItem is2 = workspaceItemService.create(context, collection, false); + context.restoreAuthSystemState(); //Test assigning the same Handle to two different items + String handle = "123456789/56789"; installItemService.installItem(context, is, handle); // Assigning the same handle again should throw a RuntimeException - thrown.expect(RuntimeException.class); installItemService.installItem(context, is2, handle); fail("Exception expected"); } diff --git a/dspace-api/src/test/java/org/dspace/content/ItemComparatorTest.java b/dspace-api/src/test/java/org/dspace/content/ItemComparatorTest.java index 73c8bc0eb5..54ff9ce026 100644 --- a/dspace-api/src/test/java/org/dspace/content/ItemComparatorTest.java +++ b/dspace-api/src/test/java/org/dspace/content/ItemComparatorTest.java @@ -132,8 +132,8 @@ public class ItemComparatorTest extends AbstractUnitTest { */ @Test public void testCompare() throws SQLException { - int result = 0; - ItemComparator ic = null; + int result; + ItemComparator ic; //one of the tiems has no value ic = new ItemComparator("test", "one", Item.ANY, true); @@ -246,7 +246,7 @@ public class ItemComparatorTest extends AbstractUnitTest { @SuppressWarnings( {"ObjectEqualsNull", "IncompatibleEquals"}) public void testEquals() { ItemComparator ic = new ItemComparator("test", "one", Item.ANY, true); - ItemComparator target = null; + ItemComparator target; assertFalse("testEquals 0", ic.equals(null)); assertFalse("testEquals 1", ic.equals("test one")); diff --git a/dspace-api/src/test/java/org/dspace/content/ItemTest.java b/dspace-api/src/test/java/org/dspace/content/ItemTest.java index 4287957c8e..8c3cfa5a04 100644 --- a/dspace-api/src/test/java/org/dspace/content/ItemTest.java +++ b/dspace-api/src/test/java/org/dspace/content/ItemTest.java @@ -15,6 +15,11 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import java.io.File; import java.io.FileInputStream; @@ -28,15 +33,14 @@ import java.util.Iterator; import java.util.List; import java.util.UUID; -import mockit.NonStrictExpectations; import org.apache.commons.lang3.time.DateUtils; import org.apache.logging.log4j.Logger; -import org.dspace.app.util.AuthorizeUtil; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.ResourcePolicy; +import org.dspace.authorize.factory.AuthorizeServiceFactory; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.BitstreamFormatService; -import org.dspace.content.service.CollectionService; import org.dspace.content.service.MetadataFieldService; import org.dspace.content.service.MetadataSchemaService; import org.dspace.core.Constants; @@ -46,6 +50,7 @@ import org.dspace.eperson.Group; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; /** * Unit Tests for class Item @@ -70,11 +75,15 @@ public class ItemTest extends AbstractDSpaceObjectTest { .getBitstreamFormatService(); private MetadataFieldService metadataFieldService = ContentServiceFactory.getInstance().getMetadataFieldService(); - private CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService(); - private Collection collection; private Community owningCommunity; + /** + * Spy of AuthorizeService to use for tests + * (initialized / setup in @Before method) + */ + private AuthorizeService authorizeServiceSpy; + /** * This method will be run before every test as per @Before. It will @@ -92,14 +101,24 @@ public class ItemTest extends AbstractDSpaceObjectTest { context.turnOffAuthorisationSystem(); this.owningCommunity = communityService.create(null, context); this.collection = collectionService.create(context, owningCommunity); - WorkspaceItem workspaceItem = workspaceItemService.create(context, collection, false); + WorkspaceItem workspaceItem = workspaceItemService.create(context, collection, true); this.it = installItemService.installItem(context, workspaceItem); - - it.setSubmitter(context.getCurrentUser()); - itemService.update(context, it); this.dspaceObject = it; - //we need to commit the changes so we don't block the table for testing context.restoreAuthSystemState(); + + // Initialize our spy of the autowired (global) authorizeService bean. + // This allows us to customize the bean's method return values in tests below + authorizeServiceSpy = spy(authorizeService); + // "Wire" our spy to be used by the current loaded object services + // (To ensure these services use the spy instead of the real service) + ReflectionTestUtils.setField(collectionService, "authorizeService", authorizeServiceSpy); + ReflectionTestUtils.setField(itemService, "authorizeService", authorizeServiceSpy); + ReflectionTestUtils.setField(workspaceItemService, "authorizeService", authorizeServiceSpy); + ReflectionTestUtils.setField(bundleService, "authorizeService", authorizeServiceSpy); + ReflectionTestUtils.setField(bitstreamService, "authorizeService", authorizeServiceSpy); + // Also wire into current AuthorizeServiceFactory, as that is used for some checks (e.g. AuthorizeUtil) + ReflectionTestUtils.setField(AuthorizeServiceFactory.getInstance(), "authorizeService", + authorizeServiceSpy); } catch (AuthorizeException ex) { log.error("Authorization Error in init", ex); fail("Authorization Error in init: " + ex.getMessage()); @@ -169,15 +188,9 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testCreate() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item ADD perms (needed to create an Item) - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.ADD); - result = null; - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE); - result = null; - }}; + // Allow Collection WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.ADD); + Item created = createItem(); assertThat("testCreate 0", created, notNullValue()); assertThat("testCreate 1", created.getName(), nullValue()); @@ -239,7 +252,7 @@ public class ItemTest extends AbstractDSpaceObjectTest { Iterator all = itemService.findInArchiveOrWithdrawnDiscoverableModifiedSince(context, DateUtils.addDays(it.getLastModified(),1)); assertThat("Returned list should not be null", all, notNullValue()); - boolean added = false; + boolean added = false; while (all.hasNext()) { Item tmp = all.next(); if (tmp.equals(it)) { @@ -252,7 +265,7 @@ public class ItemTest extends AbstractDSpaceObjectTest { all = itemService.findInArchiveOrWithdrawnDiscoverableModifiedSince(context, DateUtils.addDays(it.getLastModified(),-1)); assertThat("Returned list should not be null", all, notNullValue()); - added = false; + added = false; while (all.hasNext()) { Item tmp = all.next(); if (tmp.equals(it)) { @@ -268,7 +281,7 @@ public class ItemTest extends AbstractDSpaceObjectTest { all = itemService.findInArchiveOrWithdrawnDiscoverableModifiedSince(context, DateUtils.addDays(it.getLastModified(),-1)); assertThat("Returned list should not be null", all, notNullValue()); - added = false; + added = false; while (all.hasNext()) { Item tmp = all.next(); if (tmp.equals(it)) { @@ -282,7 +295,7 @@ public class ItemTest extends AbstractDSpaceObjectTest { all = itemService.findInArchiveOrWithdrawnDiscoverableModifiedSince(context, DateUtils.addDays(it.getLastModified(),-1)); assertThat("Returned list should not be null", all, notNullValue()); - added = false; + added = false; while (all.hasNext()) { Item tmp = all.next(); if (tmp.equals(it)) { @@ -291,7 +304,8 @@ public class ItemTest extends AbstractDSpaceObjectTest { } // Test 7: We should not find our item in this list assertFalse("List should not contain non-discoverable items", added); - } + } + /** * Test of findInArchiveOrWithdrawnNonDiscoverableModifiedSince method, of class Item. */ @@ -305,7 +319,7 @@ public class ItemTest extends AbstractDSpaceObjectTest { Iterator all = itemService.findInArchiveOrWithdrawnNonDiscoverableModifiedSince(context, DateUtils.addDays(it.getLastModified(),1)); assertThat("Returned list should not be null", all, notNullValue()); - boolean added = false; + boolean added = false; while (all.hasNext()) { Item tmp = all.next(); if (tmp.equals(it)) { @@ -318,7 +332,7 @@ public class ItemTest extends AbstractDSpaceObjectTest { all = itemService.findInArchiveOrWithdrawnNonDiscoverableModifiedSince(context, DateUtils.addDays(it.getLastModified(),-1)); assertThat("Returned list should not be null", all, notNullValue()); - added = false; + added = false; while (all.hasNext()) { Item tmp = all.next(); if (tmp.equals(it)) { @@ -333,7 +347,7 @@ public class ItemTest extends AbstractDSpaceObjectTest { all = itemService.findInArchiveOrWithdrawnNonDiscoverableModifiedSince(context, DateUtils.addDays(it.getLastModified(),-1)); assertThat("Returned list should not be null", all, notNullValue()); - added = false; + added = false; while (all.hasNext()) { Item tmp = all.next(); if (tmp.equals(it)) { @@ -787,13 +801,8 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testCreateBundleAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item ADD perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.ADD); - result = null; - - }}; + // Allow Item ADD perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, it, Constants.ADD); String name = "bundle"; Bundle created = bundleService.create(context, it, name); @@ -808,16 +817,7 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test(expected = SQLException.class) public void testCreateBundleNoName() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item ADD perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.ADD); - result = null; - - }}; - - String name = ""; - Bundle created = bundleService.create(context, it, name); + bundleService.create(context, it, ""); fail("Exception expected"); } @@ -825,36 +825,18 @@ public class ItemTest extends AbstractDSpaceObjectTest { * Test of createBundle method, of class Item. */ @Test(expected = SQLException.class) - public void testCreateBundleNoName2() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item ADD perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.ADD); - result = null; - - }}; - - String name = null; - Bundle created = bundleService.create(context, it, name); + public void testCreateBundleNullName() throws Exception { + bundleService.create(context, it, null); fail("Exception expected"); } - /** * Test of createBundle method, of class Item. */ @Test(expected = AuthorizeException.class) public void testCreateBundleNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Item ADD perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.ADD); - result = new AuthorizeException(); - - }}; - String name = "bundle"; - Bundle created = bundleService.create(context, it, name); + bundleService.create(context, it, name); fail("Exception expected"); } @@ -863,17 +845,13 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testAddBundleAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item ADD perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.ADD); - result = null; - - }}; + // Allow Item ADD perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, it, Constants.ADD); String name = "bundle"; Bundle created = bundleService.create(context, it, name); created.setName(context, name); + itemService.addBundle(context, it, created); assertThat("testAddBundleAuth 0", itemService.getBundles(it, name), notNullValue()); assertTrue("testAddBundleAuth 1", itemService.getBundles(it, name).size() == 1); @@ -885,19 +863,10 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testAddBundleNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Item ADD perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.ADD); - result = new AuthorizeException(); - - }}; - String name = "bundle"; Bundle created = bundleService.create(context, it, name); created.setName(context, name); - - it.addBundle(created); + itemService.addBundle(context, it, created); fail("Exception expected"); } @@ -906,23 +875,18 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testRemoveBundleAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item ADD and REMOVE perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.ADD); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.REMOVE); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.DELETE); - result = null; - }}; - + // First create a bundle for test + context.turnOffAuthorisationSystem(); String name = "bundle"; Bundle created = bundleService.create(context, it, name); created.setName(context, name); itemService.addBundle(context, it, created); + context.restoreAuthSystemState(); + + // Allow Item REMOVE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, it, Constants.REMOVE); + // Allow Bundle DELETE + doNothing().when(authorizeServiceSpy).authorizeAction(context, created, Constants.DELETE); itemService.removeBundle(context, it, created); assertThat("testRemoveBundleAuth 0", itemService.getBundles(it, name), notNullValue()); @@ -934,21 +898,13 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testRemoveBundleNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item ADD perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.ADD); - result = null; - // Disallow Item REMOVE perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.REMOVE); - result = new AuthorizeException(); - }}; - + // First create a bundle for test + context.turnOffAuthorisationSystem(); String name = "bundle"; Bundle created = bundleService.create(context, it, name); created.setName(context, name); - it.addBundle(created); + itemService.addBundle(context, it, created); + context.restoreAuthSystemState(); itemService.removeBundle(context, it, created); fail("Exception expected"); @@ -959,15 +915,15 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testCreateSingleBitstream_InputStream_StringAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item ADD perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.ADD); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE, true); - result = null; - }}; + // Allow Item ADD perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, it, Constants.ADD); + // Allow Item WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, it, Constants.WRITE, true); + // Allow Bundle ADD perms + doNothing().when(authorizeServiceSpy).authorizeAction(any(Context.class), any(Bundle.class), eq(Constants.ADD)); + // Allow Bitstream WRITE perms + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.WRITE)); String name = "new bundle"; File f = new File(testProps.get("test.bitstream").toString()); @@ -980,17 +936,9 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testCreateSingleBitstream_InputStream_StringNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Item ADD perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.ADD); - result = new AuthorizeException(); - - }}; - String name = "new bundle"; File f = new File(testProps.get("test.bitstream").toString()); - Bitstream result = itemService.createSingleBitstream(context, new FileInputStream(f), it, name); + itemService.createSingleBitstream(context, new FileInputStream(f), it, name); fail("Exception expected"); } @@ -999,16 +947,16 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testCreateSingleBitstream_InputStreamAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item ADD perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.ADD); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE, true); - result = null; + // Allow Item ADD perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, it, Constants.ADD); + // Allow Item WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, it, Constants.WRITE, true); + // Allow Bundle ADD perms + doNothing().when(authorizeServiceSpy).authorizeAction(any(Context.class), any(Bundle.class), eq(Constants.ADD)); + // Allow Bitstream WRITE perms + doNothing().when(authorizeServiceSpy) + .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.WRITE)); - }}; File f = new File(testProps.get("test.bitstream").toString()); Bitstream result = itemService.createSingleBitstream(context, new FileInputStream(f), it); @@ -1020,16 +968,8 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testCreateSingleBitstream_InputStreamNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Item ADD perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.ADD); - result = new AuthorizeException(); - - }}; - File f = new File(testProps.get("test.bitstream").toString()); - Bitstream result = itemService.createSingleBitstream(context, new FileInputStream(f), it); + itemService.createSingleBitstream(context, new FileInputStream(f), it); fail("Expected exception"); } @@ -1047,23 +987,17 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testRemoveDSpaceLicenseAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item ADD and REMOVE perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.ADD); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.REMOVE); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.DELETE); - result = null; - }}; - + // First create a bundle for test + context.turnOffAuthorisationSystem(); String name = "LICENSE"; Bundle created = bundleService.create(context, it, name); created.setName(context, name); -// it.addBundle(created); + context.restoreAuthSystemState(); + + // Allow Item REMOVE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, it, Constants.REMOVE); + // Allow Bundle DELETE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, created, Constants.DELETE); itemService.removeDSpaceLicense(context, it); assertThat("testRemoveDSpaceLicenseAuth 0", itemService.getBundles(it, name), notNullValue()); @@ -1075,20 +1009,12 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testRemoveDSpaceLicenseNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item ADD perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.ADD); - result = null; - // Disallow Item REMOVE perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.REMOVE); - result = new AuthorizeException(); - }}; - + // First create a bundle for test + context.turnOffAuthorisationSystem(); String name = "LICENSE"; Bundle created = bundleService.create(context, it, name); created.setName(context, name); + context.restoreAuthSystemState(); itemService.removeDSpaceLicense(context, it); fail("Exception expected"); @@ -1099,23 +1025,8 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testRemoveLicensesAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item ADD and REMOVE perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.ADD); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.REMOVE); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = null; - authorizeService.authorizeAction(context, (Bitstream) any, - Constants.DELETE); - result = null; - - }}; - + // First create test content + context.turnOffAuthorisationSystem(); String name = "LICENSE"; Bundle created = bundleService.create(context, it, name); created.setName(context, name); @@ -1125,7 +1036,18 @@ public class ItemTest extends AbstractDSpaceObjectTest { Bitstream result = itemService.createSingleBitstream(context, new FileInputStream(f), it, bsname); bitstreamService.setFormat(context, result, bitstreamFormatService.findByShortDescription(context, bsname)); bundleService.addBitstream(context, created, result); + context.restoreAuthSystemState(); + // Allow Item REMOVE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, it, Constants.REMOVE); + // Allow Item WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, it, Constants.WRITE); + // Allow Bundle REMOVE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, created, Constants.REMOVE); + // Allow Bundle DELETE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, created, Constants.DELETE); + // Allow Bitstream DELETE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, result, Constants.DELETE); itemService.removeLicenses(context, it); assertThat("testRemoveLicensesAuth 0", itemService.getBundles(it, name), notNullValue()); @@ -1137,17 +1059,8 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testRemoveLicensesNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) { - { - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.ADD); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.REMOVE); - result = new AuthorizeException(); - } - }; - + // First create test content + context.turnOffAuthorisationSystem(); String name = "LICENSE"; Bundle created = bundleService.create(context, it, name); created.setName(context, name); @@ -1157,6 +1070,7 @@ public class ItemTest extends AbstractDSpaceObjectTest { Bitstream result = itemService.createSingleBitstream(context, new FileInputStream(f), it, bsname); bitstreamService.setFormat(context, result, bitstreamFormatService.findByShortDescription(context, bsname)); bundleService.addBitstream(context, created, result); + context.restoreAuthSystemState(); itemService.removeLicenses(context, it); fail("Exception expected"); @@ -1167,15 +1081,9 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testUpdateAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item WRITE perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = null; + // Allow Item WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, it, Constants.WRITE); - }}; - - //TOOD: how to test? itemService.update(context, it); } @@ -1184,32 +1092,14 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testUpdateAuth2() throws Exception { - // Test permission inheritence - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Item WRITE perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = new AuthorizeException(); - // Allow parent Community WRITE and ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = true; - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = true; - // Disallow parent Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = new AuthorizeException(); - - }}; - context.turnOffAuthorisationSystem(); Collection c = createCollection(); it.setOwningCollection(c); context.restoreAuthSystemState(); - //TOOD: how to test? + // Allow parent Collection WRITE perms (to test inheritance) + doNothing().when(authorizeServiceSpy).authorizeAction(context, c, Constants.WRITE, false); + itemService.update(context, it); } @@ -1218,31 +1108,11 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testUpdateNoAuth() throws Exception { - // Test permission inheritence - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Item WRITE perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = new AuthorizeException(); - // Disallow parent Community WRITE or ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, anyBoolean); - result = false; - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, anyBoolean); - result = false; - // Disallow parent Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, anyBoolean); - result = new AuthorizeException(); - }}; - context.turnOffAuthorisationSystem(); Collection c = createCollection(); it.setOwningCollection(c); context.restoreAuthSystemState(); - //TOOD: how to test? itemService.update(context, it); } @@ -1251,18 +1121,10 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testWithdrawAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Allow Item withdraw permissions - AuthorizeUtil.authorizeWithdrawItem((Context) any, (Item) any); - result = null; - }}; - - new NonStrictExpectations(authorizeService.getClass()) {{ - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = null; - - }}; + // Allow Item WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, it, Constants.WRITE); + // Allow Collection ADMIN perms + when(authorizeServiceSpy.authorizeActionBoolean(context, collection, Constants.ADMIN)).thenReturn(true); itemService.withdraw(context, it); assertTrue("testWithdrawAuth 0", it.isWithdrawn()); @@ -1273,13 +1135,6 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testWithdrawNoAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Disallow Item withdraw permissions - AuthorizeUtil.authorizeWithdrawItem((Context) any, (Item) any); - result = new AuthorizeException(); - - }}; - itemService.withdraw(context, it); fail("Exception expected"); } @@ -1289,21 +1144,16 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testReinstateAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - AuthorizeUtil.authorizeWithdrawItem((Context) any, (Item) any); - result = null; - AuthorizeUtil.authorizeReinstateItem((Context) any, (Item) any); - result = null; - - }}; - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item withdraw and reinstate permissions - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = null; - }}; + // Allow Item WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, it, Constants.WRITE); + // Allow Collection ADD perms (needed to reinstate) + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.ADD); + // initialize item as withdrawn + context.turnOffAuthorisationSystem(); itemService.withdraw(context, it); + context.restoreAuthSystemState(); + itemService.reinstate(context, it); assertFalse("testReinstate 0", it.isWithdrawn()); } @@ -1313,21 +1163,11 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testReinstateNoAuth() throws Exception { - new NonStrictExpectations(AuthorizeUtil.class) {{ - // Allow Item withdraw permissions - AuthorizeUtil.authorizeWithdrawItem((Context) any, (Item) any); - result = null; - // Disallow Item reinstate permissions - AuthorizeUtil.authorizeReinstateItem((Context) any, (Item) any); - result = new AuthorizeException(); - }}; - new NonStrictExpectations(authorizeService.getClass()) {{ - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = null; - }}; - + // initialize item as withdrawn + context.turnOffAuthorisationSystem(); itemService.withdraw(context, it); + context.restoreAuthSystemState(); + itemService.reinstate(context, it); fail("Exception expected"); } @@ -1337,21 +1177,20 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testDeleteAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item REMOVE perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.REMOVE, true); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.DELETE, true); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = null; - }}; + // create a new item to delete + context.turnOffAuthorisationSystem(); + Item item = createItem(); + context.restoreAuthSystemState(); - UUID id = it.getID(); - itemService.delete(context, it); + // Allow Item REMOVE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.REMOVE, true); + // Allow Item DELETE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.DELETE); + // Allow Item WRITE perms (required to first delete identifiers) + doNothing().when(authorizeServiceSpy).authorizeAction(context, item, Constants.WRITE); + + UUID id = item.getID(); + itemService.delete(context, item); Item found = itemService.find(context, id); assertThat("testDeleteAuth 0", found, nullValue()); } @@ -1361,13 +1200,6 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test(expected = AuthorizeException.class) public void testDeleteNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Item REMOVE perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.REMOVE); - result = new AuthorizeException(); - }}; - itemService.delete(context, it); fail("Exception expected"); } @@ -1378,30 +1210,21 @@ public class ItemTest extends AbstractDSpaceObjectTest { @Test @SuppressWarnings("ObjectEqualsNull") public void testEquals() throws SQLException, AuthorizeException, IOException, IllegalAccessException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item ADD perms (needed to create an Item) - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.ADD); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.REMOVE); - result = null; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.DELETE); - result = null; - - }}; - assertFalse("testEquals 0", it.equals(null)); + + // create a new item to test against + context.turnOffAuthorisationSystem(); Item item = createItem(); + context.restoreAuthSystemState(); + try { assertFalse("testEquals 1", it.equals(item)); assertTrue("testEquals 2", it.equals(it)); } finally { + //delete item we created + context.turnOffAuthorisationSystem(); itemService.delete(context, item); + context.restoreAuthSystemState(); } } @@ -1435,13 +1258,6 @@ public class ItemTest extends AbstractDSpaceObjectTest { List newpolicies = new ArrayList(); ResourcePolicy pol1 = resourcePolicyService.create(context); newpolicies.add(pol1); - new NonStrictExpectations(authorizeService.getClass()) { - { - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = null; - } - }; itemService.replaceAllItemPolicies(context, it, newpolicies); List retrieved = authorizeService.getPolicies(context, it); @@ -1544,13 +1360,8 @@ public class ItemTest extends AbstractDSpaceObjectTest { context.restoreAuthSystemState(); - new NonStrictExpectations(authorizeService.getClass()) { - { - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE, true); - result = null; - } - }; + // Allow Item WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, it, Constants.WRITE, true); itemService.inheritCollectionDefaultPolicies(context, it, c); @@ -1630,24 +1441,8 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testCanEditBooleanAuth() throws Exception { - // Test Inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Item) any, - Constants.WRITE); - result = true; - // Allow parent Community WRITE and ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = true; - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = true; - // Allow parent Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; + // Allow Item WRITE perms + when(authorizeServiceSpy.authorizeActionBoolean(context, it, Constants.WRITE)).thenReturn(true); assertTrue("testCanEditBooleanAuth 0", itemService.canEdit(context, it)); } @@ -1657,24 +1452,9 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testCanEditBooleanAuth2() throws Exception { - // Test Inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Item WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Item) any, - Constants.WRITE); - result = false; - // Allow parent Community WRITE and ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = true; - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, true); - result = true; - // Allow parent Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, false); - result = null; - }}; + // Allow parent Community WRITE perms (test inheritance from community) + when(authorizeServiceSpy.authorizeActionBoolean(context, owningCommunity, Constants.WRITE, false)) + .thenReturn(true); assertTrue("testCanEditBooleanAuth2 0", itemService.canEdit(context, it)); } @@ -1684,107 +1464,24 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testCanEditBooleanAuth3() throws Exception { - // Test Inheritance of permissions for owning collection - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Item WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Item) any, - Constants.WRITE); - result = false; - // Allow parent Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, false); - result = null; - }}; - // Create a new Collection and assign it as the owner context.turnOffAuthorisationSystem(); Collection c = createCollection(); it.setOwningCollection(c); context.restoreAuthSystemState(); + // Allow parent Collection WRITE perms (test inheritance from new collection) + doNothing().when(authorizeServiceSpy).authorizeAction(context, c, Constants.WRITE, false); + // Ensure person with WRITE perms on the Collection can edit item assertTrue("testCanEditBooleanAuth3 0", itemService.canEdit(context, it)); } - /** - * Test of canEdit method, of class Item. - */ - @Test - public void testCanEditBooleanAuth4() throws Exception { - // Test Inheritance of permissions for Community Admins - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Item WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Item) any, - Constants.WRITE); - result = false; - // Allow parent Community WRITE and ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, true); - result = true; - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, false); - result = true; - // Disallow parent Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = new AuthorizeException(); - }}; - - // Ensure person with WRITE perms on the Collection can edit item - assertTrue("testCanEditBooleanAuth4 0", itemService.canEdit(context, it)); - } - - /** - * Test of canEdit method, of class Item. - */ - @Test - public void testCanEditBooleanAuth5() throws Exception { - // Test Inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Item WRITE perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = new AuthorizeException(); - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, anyBoolean); - result = null; - }}; - - collectionService.createTemplateItem(context, collection); - collectionService.update(context, collection); - assertTrue("testCanEditBooleanNoAuth5 0", itemService.canEdit(context, collection.getTemplateItem())); - } - /** * Test of canEdit method, of class Item. */ @Test public void testCanEditBooleanNoAuth() throws Exception { - // Test Inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Item WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Item) any, - Constants.WRITE); - result = false; - // Disallow parent Community WRITE and ADD perms - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.WRITE, anyBoolean); - result = false; - authorizeService.authorizeActionBoolean((Context) any, (Community) any, - Constants.ADD, anyBoolean); - result = false; - // Disallow parent Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, anyBoolean); - result = new AuthorizeException(); - }}; - - context.turnOffAuthorisationSystem(); - Collection c = createCollection(); - it.setOwningCollection(c); - context.restoreAuthSystemState(); - assertFalse("testCanEditBooleanNoAuth 0", itemService.canEdit(context, it)); } @@ -1793,17 +1490,16 @@ public class ItemTest extends AbstractDSpaceObjectTest { */ @Test public void testCanEditBooleanNoAuth2() throws Exception { + // Test that a new Item cannot be edited by default context.turnOffAuthorisationSystem(); WorkspaceItem wi = workspaceItemService.create(context, collection, true); context.restoreAuthSystemState(); - // Test Inheritance of permissions - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Item WRITE perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE, anyBoolean); - result = new AuthorizeException(); - }}; - assertFalse("testCanEditBooleanNoAuth2 0", itemService.canEdit(context, wi.getItem())); + Item item = wi.getItem(); + + // Disallow Item WRITE perms + when(authorizeServiceSpy.authorizeActionBoolean(context, item, Constants.WRITE)).thenReturn(false); + + assertFalse("testCanEditBooleanNoAuth2 0", itemService.canEdit(context, item)); } /** @@ -1880,15 +1576,11 @@ public class ItemTest extends AbstractDSpaceObjectTest { assertThat("testFindByMetadataField 0", result, notNullValue()); assertFalse("testFindByMetadataField 1", result.hasNext()); + // add new metadata to item + context.turnOffAuthorisationSystem(); itemService.addMetadata(context, it, schema, element, qualifier, Item.ANY, value); - new NonStrictExpectations(authorizeService.getClass()) { - { - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = null; - } - }; itemService.update(context, it); + context.restoreAuthSystemState(); result = itemService.findByMetadataField(context, schema, element, qualifier, value); assertThat("testFindByMetadataField 3", result, notNullValue()); @@ -1929,7 +1621,7 @@ public class ItemTest extends AbstractDSpaceObjectTest { assertThat("testGetParentObject 1", itemService.getParentObject(context, it), notNullValue()); assertThat("testGetParentObject 2", (Collection) itemService.getParentObject(context, it), equalTo(parent)); } catch (AuthorizeException ex) { - fail("Authorize exception catched"); + throw new AssertionError("Authorize Exception occurred", ex); } } @@ -1949,16 +1641,11 @@ public class ItemTest extends AbstractDSpaceObjectTest { assertThat("testFindByAuthorityValue 0", result, notNullValue()); assertFalse("testFindByAuthorityValue 1", result.hasNext()); + // add new metadata (with authority) to item + context.turnOffAuthorisationSystem(); itemService.addMetadata(context, it, schema, element, qualifier, Item.ANY, value, authority, confidence); - //Ensure that the current user can update the item - new NonStrictExpectations(authorizeService.getClass()) { - { - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = null; - } - }; itemService.update(context, it); + context.restoreAuthSystemState(); result = itemService.findByAuthorityValue(context, schema, element, qualifier, authority); assertThat("testFindByAuthorityValue 3", result, notNullValue()); diff --git a/dspace-api/src/test/java/org/dspace/content/LicenseUtilsTest.java b/dspace-api/src/test/java/org/dspace/content/LicenseUtilsTest.java index 25ed724bb9..dbba4366f0 100644 --- a/dspace-api/src/test/java/org/dspace/content/LicenseUtilsTest.java +++ b/dspace-api/src/test/java/org/dspace/content/LicenseUtilsTest.java @@ -104,11 +104,11 @@ public class LicenseUtilsTest extends AbstractUnitTest { @Test public void testGetLicenseText_5args() throws SQLException, AuthorizeException, IOException { //parameters for the test - Locale locale = null; - Collection collection = null; - Item item = null; - EPerson person = null; - Map additionalInfo = null; + Locale locale; + Collection collection; + Item item; + EPerson person; + Map additionalInfo; // We don't test attribute 4 as this is the date, and the date often differs between when the test // is executed, and when the LicenceUtils code gets the current date/time which causes the test to fail @@ -195,10 +195,10 @@ public class LicenseUtilsTest extends AbstractUnitTest { @Test public void testGetLicenseText_4args() throws SQLException, AuthorizeException, IOException { //parameters for the test - Locale locale = null; - Collection collection = null; - Item item = null; - EPerson person = null; + Locale locale; + Collection collection; + Item item; + EPerson person; String template = "Template license: %1$s %2$s %3$s %5$s %6$s"; String templateResult = "Template license: first name last name testgetlicensetext_4args@email.com "; diff --git a/dspace-api/src/test/java/org/dspace/content/MetadataFieldTest.java b/dspace-api/src/test/java/org/dspace/content/MetadataFieldTest.java index edd030a135..1cf7742830 100644 --- a/dspace-api/src/test/java/org/dspace/content/MetadataFieldTest.java +++ b/dspace-api/src/test/java/org/dspace/content/MetadataFieldTest.java @@ -13,20 +13,23 @@ import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import java.sql.SQLException; import java.util.List; -import mockit.NonStrictExpectations; import org.apache.logging.log4j.Logger; import org.dspace.AbstractUnitTest; import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.MetadataFieldService; import org.dspace.content.service.MetadataSchemaService; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; /** * Unit Tests for class MetadataFieldTest @@ -66,6 +69,12 @@ public class MetadataFieldTest extends AbstractUnitTest { .getMetadataSchemaService(); protected MetadataFieldService metadataFieldService = ContentServiceFactory.getInstance().getMetadataFieldService(); + /** + * Spy of AuthorizeService to use for tests + * (initialized / setup in @Before method) + */ + private AuthorizeService authorizeServiceSpy; + /** * This method will be run before every test as per @Before. It will * initialize resources required for the tests. @@ -90,6 +99,14 @@ public class MetadataFieldTest extends AbstractUnitTest { } this.mf.setScopeNote(scopeNote); + + // Initialize our spy of the autowired (global) authorizeService bean. + // This allows us to customize the bean's method return values in tests below + authorizeServiceSpy = spy(authorizeService); + // "Wire" our spy to be used by the current loaded object services + // (To ensure these services use the spy instead of the real service) + ReflectionTestUtils.setField(metadataFieldService, "authorizeService", authorizeServiceSpy); + ReflectionTestUtils.setField(metadataSchemaService, "authorizeService", authorizeServiceSpy); } catch (SQLException ex) { log.error("SQL Error in init", ex); fail("SQL Error in init: " + ex.getMessage()); @@ -173,11 +190,9 @@ public class MetadataFieldTest extends AbstractUnitTest { */ @Test public void testSetSchema() throws NonUniqueMetadataException, SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow full admin permissions - authorizeService.isAdmin(context); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); + MetadataSchema newSchema = metadataSchemaService.create(context, "testSetSchema", "testSetSchemaNS"); mf.setMetadataSchema(newSchema); assertThat("testSetSchemaID 0", mf.getMetadataSchema(), equalTo(newSchema)); @@ -206,11 +221,8 @@ public class MetadataFieldTest extends AbstractUnitTest { */ @Test public void testCreateAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow full admin permissions - authorizeService.isAdmin(context); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); String elem = "elem1"; String qual = "qual1"; @@ -225,12 +237,6 @@ public class MetadataFieldTest extends AbstractUnitTest { */ @Test(expected = AuthorizeException.class) public void testCreateNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow full admin permissions - authorizeService.isAdmin(context); - result = false; - }}; - String elem = "elem1"; String qual = "qual1"; metadataFieldService.create(context, dcSchema, elem, qual, null); @@ -242,11 +248,8 @@ public class MetadataFieldTest extends AbstractUnitTest { */ @Test(expected = NonUniqueMetadataException.class) public void testCreateRepeated() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow full admin permissions - authorizeService.isAdmin(context); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); String elem = element; String qual = qualifier; @@ -310,11 +313,8 @@ public class MetadataFieldTest extends AbstractUnitTest { */ @Test public void testUpdateAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow full admin permissions - authorizeService.isAdmin(context); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); String elem = "elem2"; String qual = "qual2"; @@ -330,12 +330,6 @@ public class MetadataFieldTest extends AbstractUnitTest { */ @Test(expected = AuthorizeException.class) public void testUpdateNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow full admin permissions - authorizeService.isAdmin(context); - result = false; - }}; - String elem = "elem2"; String qual = "qual2"; MetadataField m = metadataFieldService.create(context, dcSchema, elem, qual, null); @@ -348,11 +342,8 @@ public class MetadataFieldTest extends AbstractUnitTest { */ @Test(expected = NonUniqueMetadataException.class) public void testUpdateRepeated() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow full admin permissions - authorizeService.isAdmin(context); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); String elem = element; String qual = qualifier; @@ -369,11 +360,8 @@ public class MetadataFieldTest extends AbstractUnitTest { */ @Test public void testDeleteAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow full admin permissions - authorizeService.isAdmin(context); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); String elem = "elem3"; String qual = "qual3"; @@ -390,12 +378,6 @@ public class MetadataFieldTest extends AbstractUnitTest { */ @Test(expected = AuthorizeException.class) public void testDeleteNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow full admin permissions - authorizeService.isAdmin(context); - result = false; - }}; - String elem = "elem3"; String qual = "qual3"; MetadataField m = metadataFieldService.create(context, dcSchema, elem, qual, null); diff --git a/dspace-api/src/test/java/org/dspace/content/MetadataSchemaTest.java b/dspace-api/src/test/java/org/dspace/content/MetadataSchemaTest.java index 30a1a07325..dc7d899f06 100644 --- a/dspace-api/src/test/java/org/dspace/content/MetadataSchemaTest.java +++ b/dspace-api/src/test/java/org/dspace/content/MetadataSchemaTest.java @@ -14,18 +14,21 @@ import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import java.sql.SQLException; import java.util.List; -import mockit.NonStrictExpectations; import org.apache.logging.log4j.Logger; import org.dspace.AbstractUnitTest; import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.MetadataSchemaService; import org.junit.Before; import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; /** * Unit Tests for class MetadataSchema @@ -47,6 +50,12 @@ public class MetadataSchemaTest extends AbstractUnitTest { protected MetadataSchemaService metadataSchemaService = ContentServiceFactory.getInstance() .getMetadataSchemaService(); + /** + * Spy of AuthorizeService to use for tests + * (initialized / setup in @Before method) + */ + private AuthorizeService authorizeServiceSpy; + /** * This method will be run before every test as per @Before. It will * initialize resources required for the tests. @@ -60,6 +69,13 @@ public class MetadataSchemaTest extends AbstractUnitTest { super.init(); try { this.ms = metadataSchemaService.find(context, MetadataSchemaEnum.DC.getName()); + + // Initialize our spy of the autowired (global) authorizeService bean. + // This allows us to customize the bean's method return values in tests below + authorizeServiceSpy = spy(authorizeService); + // "Wire" our spy to be used by the current loaded object services + // (To ensure these services use the spy instead of the real service) + ReflectionTestUtils.setField(metadataSchemaService, "authorizeService", authorizeServiceSpy); } catch (SQLException ex) { log.error("SQL Error in init", ex); fail("SQL Error in init: " + ex.getMessage()); @@ -130,11 +146,8 @@ public class MetadataSchemaTest extends AbstractUnitTest { */ @Test public void testCreateAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow full admin permissions - authorizeService.isAdmin(context); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); String namespace = "namespace"; String name = "name"; @@ -149,12 +162,6 @@ public class MetadataSchemaTest extends AbstractUnitTest { */ @Test(expected = AuthorizeException.class) public void testCreateNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow full admin permissions - authorizeService.isAdmin(context); - result = false; - }}; - String namespace = "namespace"; String name = "name"; metadataSchemaService.create(context, name, namespace); @@ -166,11 +173,8 @@ public class MetadataSchemaTest extends AbstractUnitTest { */ @Test(expected = NonUniqueMetadataException.class) public void testCreateRepeated() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow full admin permissions - authorizeService.isAdmin(context); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); String namespace = ms.getNamespace(); String name = ms.getName(); @@ -194,11 +198,8 @@ public class MetadataSchemaTest extends AbstractUnitTest { */ @Test public void testUpdateAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow full admin permissions - authorizeService.isAdmin(context); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); String namespace = "namespace2"; String name = "name2"; @@ -215,12 +216,6 @@ public class MetadataSchemaTest extends AbstractUnitTest { */ @Test(expected = AuthorizeException.class) public void testUpdateNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow full admin permissions - authorizeService.isAdmin(context); - result = false; - }}; - metadataSchemaService.update(context, ms); fail("Exception expected"); } @@ -230,11 +225,8 @@ public class MetadataSchemaTest extends AbstractUnitTest { */ @Test(expected = NonUniqueMetadataException.class) public void testUpdateRepeated() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow full admin permissions - authorizeService.isAdmin(context); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); String namespace = ms.getNamespace(); String name = ms.getName(); @@ -251,11 +243,8 @@ public class MetadataSchemaTest extends AbstractUnitTest { */ @Test public void testDeleteAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow full admin permissions - authorizeService.isAdmin(context); - result = true; - }}; + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); String namespace = "namespace3"; String name = "name3"; @@ -272,12 +261,6 @@ public class MetadataSchemaTest extends AbstractUnitTest { */ @Test(expected = AuthorizeException.class) public void testDeleteNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow full admin permissions - authorizeService.isAdmin(context); - result = false; - }}; - String namespace = "namespace3"; String name = "name3"; MetadataSchema m = metadataSchemaService.create(context, name, namespace); diff --git a/dspace-api/src/test/java/org/dspace/content/NonUniqueMetadataExceptionTest.java b/dspace-api/src/test/java/org/dspace/content/NonUniqueMetadataExceptionTest.java index b99e44c7f2..89d4df0c68 100644 --- a/dspace-api/src/test/java/org/dspace/content/NonUniqueMetadataExceptionTest.java +++ b/dspace-api/src/test/java/org/dspace/content/NonUniqueMetadataExceptionTest.java @@ -9,8 +9,6 @@ package org.dspace.content; import static org.junit.Assert.assertTrue; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.junit.Test; /** @@ -21,12 +19,6 @@ import org.junit.Test; */ public class NonUniqueMetadataExceptionTest { - /** - * log4j category - */ - private static final Logger log = LogManager - .getLogger(NonUniqueMetadataExceptionTest.class); - /** * Dummy test to avoid initialization errors */ diff --git a/dspace-api/src/test/java/org/dspace/content/RelationshipMetadataServiceTest.java b/dspace-api/src/test/java/org/dspace/content/RelationshipMetadataServiceTest.java index 6290d010e0..df12cccc23 100644 --- a/dspace-api/src/test/java/org/dspace/content/RelationshipMetadataServiceTest.java +++ b/dspace-api/src/test/java/org/dspace/content/RelationshipMetadataServiceTest.java @@ -463,8 +463,8 @@ public class RelationshipMetadataServiceTest extends AbstractUnitTest { WorkspaceItem is = workspaceItemService.create(context, col, false); Item secondItem = installItemService.installItem(context, is); itemService.addMetadata(context, secondItem, "relationship", "type", null, null, "Publication"); - Relationship secondRelationship = relationshipService.create(context, secondItem, rightItem, - isAuthorOfPublicationRelationshipType, 0, 0); + relationshipService.create(context, secondItem, rightItem, + isAuthorOfPublicationRelationshipType, 0, 0); context.restoreAuthSystemState(); assertThat(relationshipService.findNextRightPlaceByRightItem(context, rightItem), equalTo(2)); @@ -489,8 +489,8 @@ public class RelationshipMetadataServiceTest extends AbstractUnitTest { itemService.addMetadata(context, secondAuthor, "relationship", "type", null, null, "Author"); itemService.addMetadata(context, secondAuthor, "person", "familyName", null, null, "familyName"); itemService.addMetadata(context, secondAuthor, "person", "givenName", null, null, "firstName"); - Relationship secondRelationship = relationshipService.create(context, leftItem, secondAuthor, - isAuthorOfPublicationRelationshipType, 0, 0); + relationshipService.create(context, leftItem, secondAuthor, + isAuthorOfPublicationRelationshipType, 0, 0); context.restoreAuthSystemState(); assertThat(relationshipService.findNextLeftPlaceByLeftItem(context, leftItem), equalTo(2)); diff --git a/dspace-api/src/test/java/org/dspace/content/RelationshipServiceImplTest.java b/dspace-api/src/test/java/org/dspace/content/RelationshipServiceImplTest.java index 244f58bc6d..73d80a77c0 100644 --- a/dspace-api/src/test/java/org/dspace/content/RelationshipServiceImplTest.java +++ b/dspace-api/src/test/java/org/dspace/content/RelationshipServiceImplTest.java @@ -8,12 +8,11 @@ package org.dspace.content; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; import org.dspace.authorize.service.AuthorizeService; @@ -28,7 +27,7 @@ import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class RelationshipServiceImplTest { @@ -137,7 +136,7 @@ public class RelationshipServiceImplTest { @Test public void testFindByItemAndRelationshipType() throws Exception { // Declare objects utilized in unit test - List relList = new LinkedList<>(); + List relList = new ArrayList<>(); Item item = mock(Item.class); RelationshipType testRel = new RelationshipType(); @@ -152,7 +151,7 @@ public class RelationshipServiceImplTest { @Test public void testFindByRelationshipType() throws Exception { // Declare objects utilized in unit test - List relList = new LinkedList<>(); + List relList = new ArrayList<>(); RelationshipType testRel = new RelationshipType(); // The Relationship(s) reported should match our our relList @@ -231,8 +230,6 @@ public class RelationshipServiceImplTest { public void testDelete() throws Exception { // Declare objects utilized in unit test - MetadataValue metVal = mock(MetadataValue.class); - List metsList = new ArrayList<>(); List leftTypelist = new ArrayList<>(); List rightTypelist = new ArrayList<>(); Item leftItem = mock(Item.class); @@ -246,7 +243,6 @@ public class RelationshipServiceImplTest { testRel.setRightwardType("Entitylabel"); testRel.setLeftMinCardinality(0); testRel.setRightMinCardinality(0); - metsList.add(metVal); relationship = getRelationship(leftItem, rightItem, testRel, 0,0); leftTypelist.add(relationship); rightTypelist.add(relationship); diff --git a/dspace-api/src/test/java/org/dspace/content/RelationshipTypeTest.java b/dspace-api/src/test/java/org/dspace/content/RelationshipTypeTest.java index a5a4562e96..2c57bef96e 100644 --- a/dspace-api/src/test/java/org/dspace/content/RelationshipTypeTest.java +++ b/dspace-api/src/test/java/org/dspace/content/RelationshipTypeTest.java @@ -10,15 +10,14 @@ package org.dspace.content; import static org.hamcrest.core.IsEqual.equalTo; import static org.hamcrest.core.IsNull.notNullValue; import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import java.util.Random; -import org.apache.logging.log4j.Logger; import org.dspace.content.dao.RelationshipTypeDAO; import org.dspace.core.Context; import org.junit.Before; @@ -26,13 +25,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class RelationshipTypeTest { - - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(RelationshipTypeTest.class); - @InjectMocks private RelationshipTypeServiceImpl relationshipTypeService; @@ -102,7 +98,7 @@ public class RelationshipTypeTest { @Test public void testRelationshipTypeFindAll() throws Exception { // Declare objects utilized for this test - List mockedList = new LinkedList<>(); + List mockedList = new ArrayList<>(); mockedList.add(firstRelationshipType); mockedList.add(secondRelationshipType); @@ -120,7 +116,7 @@ public class RelationshipTypeTest { @Test public void testRelationshipTypeFindByLeftOrRightwardType() throws Exception { // Declare objects utilized for this test - List mockedList = new LinkedList<>(); + List mockedList = new ArrayList<>(); mockedList.add(firstRelationshipType); // Mock DAO to return our mockedList @@ -138,7 +134,7 @@ public class RelationshipTypeTest { @Test public void testRelationshipTypefindByEntityType() throws Exception { // Declare objects utilized for this test - List mockedList = new LinkedList<>(); + List mockedList = new ArrayList<>(); mockedList.add(firstRelationshipType); // Mock DAO to return our mockedList diff --git a/dspace-api/src/test/java/org/dspace/content/SiteTest.java b/dspace-api/src/test/java/org/dspace/content/SiteTest.java index 421ce4b1bc..02e868e19b 100644 --- a/dspace-api/src/test/java/org/dspace/content/SiteTest.java +++ b/dspace-api/src/test/java/org/dspace/content/SiteTest.java @@ -58,7 +58,7 @@ public class SiteTest extends AbstractUnitTest { try { //we have to create a new community in the database context.turnOffAuthorisationSystem(); - this.s = (Site) siteService.findSite(context); + this.s = siteService.findSite(context); //we need to commit the changes so we don't block the table for testing context.restoreAuthSystemState(); } catch (SQLException ex) { @@ -120,8 +120,7 @@ public class SiteTest extends AbstractUnitTest { */ @Test public void testSiteFind() throws Exception { - int id = 0; - Site found = (Site) siteService.findSite(context); + Site found = siteService.findSite(context); assertThat("testSiteFind 0", found, notNullValue()); assertThat("testSiteFind 1", found, equalTo(s)); } @@ -141,7 +140,7 @@ public class SiteTest extends AbstractUnitTest { */ @Test public void testGetURL() { - assertThat("testGetURL 0", s.getURL(), equalTo(ConfigurationManager.getProperty("dspace.url"))); + assertThat("testGetURL 0", s.getURL(), equalTo(ConfigurationManager.getProperty("dspace.ui.url"))); } } diff --git a/dspace-api/src/test/java/org/dspace/content/ThumbnailTest.java b/dspace-api/src/test/java/org/dspace/content/ThumbnailTest.java index 93e231da1d..454def2d25 100644 --- a/dspace-api/src/test/java/org/dspace/content/ThumbnailTest.java +++ b/dspace-api/src/test/java/org/dspace/content/ThumbnailTest.java @@ -7,6 +7,7 @@ */ package org.dspace.content; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -48,11 +49,6 @@ public class ThumbnailTest extends AbstractUnitTest { */ private Bitstream orig; - /** - * Thumbnail instance for the tests, original copy - */ - private Thumbnail t; - /** * This method will be run before every test as per @Before. It will * initialize resources required for the tests. @@ -69,7 +65,9 @@ public class ThumbnailTest extends AbstractUnitTest { File f = new File(testProps.get("test.bitstream").toString()); thumb = bitstreamService.create(context, new FileInputStream(f)); orig = bitstreamService.create(context, new FileInputStream(f)); - t = new Thumbnail(thumb, orig); + Thumbnail t = new Thumbnail(thumb, orig); + assertEquals(orig, t.getOriginal()); + assertEquals(thumb, t.getThumb()); } catch (IOException ex) { log.error("IO Error in init", ex); fail("SQL Error in init: " + ex.getMessage()); @@ -89,9 +87,16 @@ public class ThumbnailTest extends AbstractUnitTest { @After @Override public void destroy() { - thumb = null; - orig = null; - t = null; + try { + context.turnOffAuthorisationSystem(); + bitstreamService.delete(context, thumb); + bitstreamService.delete(context, orig); + context.restoreAuthSystemState(); + thumb = null; + orig = null; + } catch (Exception e) { + throw new AssertionError("Error in destroy()", e); + } super.destroy(); } diff --git a/dspace-api/src/test/java/org/dspace/content/VersioningTest.java b/dspace-api/src/test/java/org/dspace/content/VersioningTest.java index f66619f615..3662e43168 100644 --- a/dspace-api/src/test/java/org/dspace/content/VersioningTest.java +++ b/dspace-api/src/test/java/org/dspace/content/VersioningTest.java @@ -25,7 +25,6 @@ import org.dspace.content.service.CommunityService; import org.dspace.content.service.InstallItemService; import org.dspace.content.service.ItemService; import org.dspace.content.service.WorkspaceItemService; -import org.dspace.core.ConfigurationManager; import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.handle.service.HandleService; import org.dspace.versioning.Version; @@ -49,7 +48,6 @@ public class VersioningTest extends AbstractUnitTest { private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(VersioningTest.class); - private String originalHandle; private Item originalItem; private Item versionedItem; private String summary = "Unit test version"; @@ -65,7 +63,7 @@ public class VersioningTest extends AbstractUnitTest { //A regex that can be used to see if a handle contains the format of handle created by the org.dspace.identifier // .VersionedHandleIdentifierProvider* - protected String versionedHandleRegex = ConfigurationManager.getProperty("handle.prefix") + "\\/[0-9]*\\.[0-9]"; + //protected String versionedHandleRegex = ConfigurationManager.getProperty("handle.prefix") + "\\/[0-9]*\\.[0-9]"; /** * This method will be run before every test as per @Before. It will @@ -86,7 +84,6 @@ public class VersioningTest extends AbstractUnitTest { WorkspaceItem is = workspaceItemService.create(context, col, false); originalItem = installItemService.installItem(context, is); - originalHandle = originalItem.getHandle(); Version version = versionService.createNewVersion(context, originalItem, summary); WorkspaceItem wsi = workspaceItemService.findByItem(context, version.getItem()); diff --git a/dspace-api/src/test/java/org/dspace/content/WorkspaceItemTest.java b/dspace-api/src/test/java/org/dspace/content/WorkspaceItemTest.java index 250ff35090..77bc170d8a 100644 --- a/dspace-api/src/test/java/org/dspace/content/WorkspaceItemTest.java +++ b/dspace-api/src/test/java/org/dspace/content/WorkspaceItemTest.java @@ -14,29 +14,32 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.spy; import java.io.IOException; import java.sql.SQLException; import java.util.List; import java.util.UUID; -import mockit.NonStrictExpectations; import org.apache.logging.log4j.Logger; import org.dspace.AbstractUnitTest; import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; import org.dspace.content.service.ItemService; import org.dspace.content.service.WorkspaceItemService; import org.dspace.core.Constants; -import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.EPersonService; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; /** * Unit Tests for class WorkspaceItem @@ -63,6 +66,12 @@ public class WorkspaceItemTest extends AbstractUnitTest { protected ItemService itemService = ContentServiceFactory.getInstance().getItemService(); protected WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService(); + /** + * Spy of AuthorizeService to use for tests + * (initialized / setup in @Before method) + */ + private AuthorizeService authorizeServiceSpy; + /** * This method will be run before every test as per @Before. It will * initialize resources required for the tests. @@ -75,13 +84,22 @@ public class WorkspaceItemTest extends AbstractUnitTest { public void init() { super.init(); try { - //we have to create a new community in the database + //we have to create a new community/collection/workspaceitem in the database context.turnOffAuthorisationSystem(); this.owningCommunity = communityService.create(null, context); this.collection = collectionService.create(context, owningCommunity); this.wi = workspaceItemService.create(context, collection, true); //we need to commit the changes so we don't block the table for testing context.restoreAuthSystemState(); + + // Initialize our spy of the autowired (global) authorizeService bean. + // This allows us to customize the bean's method return values in tests below + authorizeServiceSpy = spy(authorizeService); + // "Wire" our spy to be used by the current loaded object services + // (To ensure these services use the spy instead of the real service) + ReflectionTestUtils.setField(workspaceItemService, "authorizeService", authorizeServiceSpy); + ReflectionTestUtils.setField(collectionService, "authorizeService", authorizeServiceSpy); + ReflectionTestUtils.setField(communityService, "authorizeService", authorizeServiceSpy); } catch (AuthorizeException ex) { log.error("Authorization Error in init", ex); fail("Authorization Error in init: " + ex.getMessage()); @@ -139,15 +157,11 @@ public class WorkspaceItemTest extends AbstractUnitTest { */ @Test public void testCreateAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Collection ADD perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.ADD); - result = null; - }}; + // Allow Collection ADD perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.ADD); - boolean template = false; - WorkspaceItem created = null; + boolean template; + WorkspaceItem created; template = false; created = workspaceItemService.create(context, collection, template); @@ -167,18 +181,7 @@ public class WorkspaceItemTest extends AbstractUnitTest { */ @Test(expected = AuthorizeException.class) public void testCreateNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Collection ADD perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.ADD); - result = new AuthorizeException(); - }}; - - boolean template = false; - WorkspaceItem created = null; - - template = false; - created = workspaceItemService.create(context, collection, template); + workspaceItemService.create(context, collection, false); fail("Exception expected"); } @@ -281,18 +284,13 @@ public class WorkspaceItemTest extends AbstractUnitTest { @Test public void testUpdateAuth() throws Exception { // no need to mockup the authorization as we are the same user that have - // created the wi + // created the workspaceitem (who has full perms by default) boolean pBefore = wi.isPublishedBefore(); wi.setPublishedBefore(!pBefore); workspaceItemService.update(context, wi); - context.commit(); - // force to read the data from the database - context.uncacheEntity(wi); - // read all our test attributes objects from the fresh session - // to avoid duplicate object in session issue + + // Reload our WorkspaceItem wi = workspaceItemService.find(context, wi.getID()); - collection = wi.getCollection(); - owningCommunity = collection.getCommunities().get(0); assertTrue("testUpdate", pBefore != wi.isPublishedBefore()); } @@ -301,18 +299,29 @@ public class WorkspaceItemTest extends AbstractUnitTest { */ @Test(expected = AuthorizeException.class) public void testUpdateNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Remove Item WRITE perms - authorizeService.authorizeActionBoolean((Context) any, (Item) any, - Constants.WRITE); - result = false; - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = new AuthorizeException(); - }}; - boolean pBefore = wi.isPublishedBefore(); - wi.setPublishedBefore(!pBefore); - workspaceItemService.update(context, wi); + // Create a new Eperson to be the current user + context.turnOffAuthorisationSystem(); + EPerson eperson = ePersonService.create(context); + eperson.setEmail("jane@smith.org"); + eperson.setFirstName(context, "Jane"); + eperson.setLastName(context, "Smith"); + ePersonService.update(context, eperson); + + // Update our session to be logged in as new users + EPerson currentUser = context.getCurrentUser(); + context.setCurrentUser(eperson); + context.restoreAuthSystemState(); + + // Try and update the workspace item. A different EPerson should have no rights + try { + boolean pBefore = wi.isPublishedBefore(); + wi.setPublishedBefore(!pBefore); + workspaceItemService.update(context, wi); + } finally { + // Restore the current user + context.setCurrentUser(currentUser); + } + fail("Exception expected"); } @@ -351,12 +360,8 @@ public class WorkspaceItemTest extends AbstractUnitTest { */ @Test public void testDeleteWrapperAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Item WRITE perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = null; - }}; + // Allow Item WRITE perms + doNothing().when(authorizeServiceSpy).authorizeAction(context, wi.getItem(), Constants.WRITE); UUID itemid = wi.getItem().getID(); int id = wi.getID(); @@ -372,13 +377,9 @@ public class WorkspaceItemTest extends AbstractUnitTest { */ @Test(expected = AuthorizeException.class) public void testDeleteWrapperNoAuth() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Disallow Item WRITE perms - authorizeService.authorizeAction((Context) any, (Item) any, - Constants.WRITE); - result = new AuthorizeException(); - }}; - + // Disallow Item WRITE perms + doThrow(new AuthorizeException()).when(authorizeServiceSpy) + .authorizeAction(context, wi.getItem(), Constants.WRITE); workspaceItemService.deleteWrapper(context, wi); fail("Exception expected"); } diff --git a/dspace-api/src/test/java/org/dspace/content/comparator/NameAscendingComparatorTest.java b/dspace-api/src/test/java/org/dspace/content/comparator/NameAscendingComparatorTest.java index e03ce73ada..09e41e126e 100644 --- a/dspace-api/src/test/java/org/dspace/content/comparator/NameAscendingComparatorTest.java +++ b/dspace-api/src/test/java/org/dspace/content/comparator/NameAscendingComparatorTest.java @@ -14,7 +14,7 @@ import org.dspace.content.DSpaceObject; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class NameAscendingComparatorTest { diff --git a/dspace-api/src/test/java/org/dspace/content/packager/ITDSpaceAIP.java b/dspace-api/src/test/java/org/dspace/content/packager/ITDSpaceAIP.java index f13ac56eae..460522a6eb 100644 --- a/dspace-api/src/test/java/org/dspace/content/packager/ITDSpaceAIP.java +++ b/dspace-api/src/test/java/org/dspace/content/packager/ITDSpaceAIP.java @@ -22,9 +22,9 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; -import mockit.NonStrictExpectations; +import com.google.common.base.Splitter; import org.apache.logging.log4j.Logger; -import org.dspace.AbstractUnitTest; +import org.dspace.AbstractIntegrationTest; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.factory.AuthorizeServiceFactory; @@ -75,7 +75,7 @@ import org.junit.rules.TemporaryFolder; * * @author Tim Donohue */ -public class ITDSpaceAIP extends AbstractUnitTest { +public class ITDSpaceAIP extends AbstractIntegrationTest { /** * log4j category */ @@ -295,10 +295,7 @@ public class ITDSpaceAIP extends AbstractUnitTest { // Override default value of configured temp directory to point at our // JUnit TemporaryFolder. This ensures Crosswalk classes like RoleCrosswalk // store their temp files in a place where JUnit can clean them up automatically. - new NonStrictExpectations(configService.getClass()) {{ - configService.getProperty("upload.temp.dir"); - result = uploadTempFolder.getRoot().getAbsolutePath(); - }}; + configService.setProperty("upload.temp.dir", uploadTempFolder.getRoot().getAbsolutePath()); try { context = new Context(); @@ -322,13 +319,6 @@ public class ITDSpaceAIP extends AbstractUnitTest { */ @Test public void testRestoreCommunityHierarchy() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Full Admin permissions. Since we are working with an object - // hierarchy you really need full admin rights - authorizeService.isAdmin((Context) any); - result = true; - }}; - log.info("testRestoreCommunityHierarchy() - BEGIN"); // Locate the top level community (from our test data) @@ -378,13 +368,6 @@ public class ITDSpaceAIP extends AbstractUnitTest { */ @Test public void testRestoreRestrictedCommunity() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Full Admin permissions. Since we are working with an object - // hierarchy (Items/Bundles/Bitstreams) you need full admin rights - authorizeService.isAdmin((Context) any); - result = true; - }}; - log.info("testRestoreRestrictedCommunity() - BEGIN"); // Locate the top-level Community (as a parent) @@ -457,13 +440,6 @@ public class ITDSpaceAIP extends AbstractUnitTest { */ @Test public void testReplaceCommunityHierarchy() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Full Admin permissions. Since we are working with an object - // hierarchy you really need full admin rights - authorizeService.isAdmin((Context) any); - result = true; - }}; - log.info("testReplaceCommunityHierarchy() - BEGIN"); // Locate the top level community (from our test data) @@ -535,13 +511,6 @@ public class ITDSpaceAIP extends AbstractUnitTest { */ @Test public void testReplaceCommunityOnly() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Community WRITE perms - authorizeService.authorizeAction((Context) any, (Community) any, - Constants.WRITE, true); - result = null; - }}; - log.info("testReplaceCommunityOnly() - BEGIN"); // Locate the top level community (from our test data) @@ -575,13 +544,6 @@ public class ITDSpaceAIP extends AbstractUnitTest { */ @Test public void testRestoreCollectionHierarchy() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Full Admin permissions. Since we are working with an object - // hierarchy you really need full admin rights - authorizeService.isAdmin((Context) any); - result = true; - }}; - log.info("testRestoreCollectionHierarchy() - BEGIN"); // Locate the collection (from our test data) @@ -620,13 +582,6 @@ public class ITDSpaceAIP extends AbstractUnitTest { */ @Test public void testRestoreRestrictedCollection() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Full Admin permissions. Since we are working with an object - // hierarchy (Items/Bundles/Bitstreams) you need full admin rights - authorizeService.isAdmin((Context) any); - result = true; - }}; - log.info("testRestoreRestrictedCollection() - BEGIN"); // Locate the top-level Community (as a parent) @@ -699,13 +654,6 @@ public class ITDSpaceAIP extends AbstractUnitTest { */ @Test public void testReplaceCollectionHierarchy() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Full Admin permissions. Since we are working with an object - // hierarchy you really need full admin rights - authorizeService.isAdmin((Context) any); - result = true; - }}; - log.info("testReplaceCollectionHierarchy() - BEGIN"); // Locate the collection (from our test data) @@ -759,13 +707,6 @@ public class ITDSpaceAIP extends AbstractUnitTest { */ @Test public void testReplaceCollectionOnly() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Collection WRITE perms - authorizeService.authorizeAction((Context) any, (Collection) any, - Constants.WRITE, true); - result = null; - }}; - log.info("testReplaceCollectionOnly() - BEGIN"); // Locate the collection (from our test data) @@ -801,13 +742,6 @@ public class ITDSpaceAIP extends AbstractUnitTest { */ @Test public void testRestoreItem() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Full Admin permissions. Since we are working with an object - // hierarchy (Items/Bundles/Bitstreams) you need full admin rights - authorizeService.isAdmin((Context) any); - result = true; - }}; - log.info("testRestoreItem() - BEGIN"); // Locate the item (from our test data) @@ -870,13 +804,6 @@ public class ITDSpaceAIP extends AbstractUnitTest { */ @Test public void testRestoreRestrictedItem() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Full Admin permissions. Since we are working with an object - // hierarchy (Items/Bundles/Bitstreams) you need full admin rights - authorizeService.isAdmin((Context) any); - result = true; - }}; - log.info("testRestoreRestrictedItem() - BEGIN"); // Locate the test Collection (as a parent) @@ -948,13 +875,6 @@ public class ITDSpaceAIP extends AbstractUnitTest { */ @Test public void testRestoreItemNoPolicies() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Full Admin permissions. Since we are working with an object - // hierarchy (Items/Bundles/Bitstreams) you need full admin rights - authorizeService.isAdmin((Context) any); - result = true; - }}; - log.info("testRestoreItemNoPolicies() - BEGIN"); // Locate the test Collection (as a parent) @@ -1009,13 +929,6 @@ public class ITDSpaceAIP extends AbstractUnitTest { */ @Test public void testReplaceItem() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Full Admin permissions. Since we are working with an object - // hierarchy (Items/Bundles/Bitstreams) you need full admin rights - authorizeService.isAdmin((Context) any); - result = true; - }}; - log.info("testReplaceItem() - BEGIN"); // Locate the item (from our test data) @@ -1049,17 +962,6 @@ public class ITDSpaceAIP extends AbstractUnitTest { */ @Test public void testRestoreMappedItem() throws Exception { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Full Admin permissions. Since we are working with an object - // hierarchy (Items/Bundles/Bitstreams) you need full admin rights - authorizeService.isAdmin((Context) any); - result = true; - authorizeService.authorizeAction(context, (Collection) any, Constants.REMOVE); - result = null; - authorizeService.authorizeAction(context, (Collection) any, Constants.ADD); - result = null; - }}; - log.info("testRestoreMappedItem() - BEGIN"); // Get a reference to our test mapped Item @@ -1105,7 +1007,7 @@ public class ITDSpaceAIP extends AbstractUnitTest { * to avoid having to rewrite this code into several tests. * * @param dso DSpaceObject to create AIP(s) for - * @param pkParams any special PackageParameters to pass (if any) + * @param pkgParams any special PackageParameters to pass (if any) * @param recursive whether to recursively create AIPs or just a single AIP * @return exported root AIP file */ @@ -1145,7 +1047,7 @@ public class ITDSpaceAIP extends AbstractUnitTest { * * @param parent The DSpaceObject which will be the parent object of the newly restored object(s) * @param aipFile AIP file to start restoration from - * @param pkParams any special PackageParameters to pass (if any) + * @param pkgParams any special PackageParameters to pass (if any) * @param recursive whether to recursively restore AIPs or just a single AIP */ private void restoreFromAIP(DSpaceObject parent, File aipFile, PackageParameters pkgParams, boolean recursive) @@ -1183,7 +1085,7 @@ public class ITDSpaceAIP extends AbstractUnitTest { * * @param dso The DSpaceObject to be replaced from AIP * @param aipFile AIP file to start replacement from - * @param pkParams any special PackageParameters to pass (if any) + * @param pkgParams any special PackageParameters to pass (if any) * @param recursive whether to recursively restore AIPs or just a single AIP */ private void replaceFromAIP(DSpaceObject dso, File aipFile, PackageParameters pkgParams, boolean recursive) @@ -1293,9 +1195,9 @@ public class ITDSpaceAIP extends AbstractUnitTest { // Get the typeText & name of this object from the values String info = infoMap.get(key); - String[] values = info.split(valueseparator); - String typeText = values[0]; - String name = values[1]; + List values = Splitter.on(valueseparator).splitToList(info); + String typeText = values.get(0); + String name = values.get(1); // Also assert type and name are correct assertEquals("assertObjectsExist object " + key + " type", diff --git a/dspace-api/src/test/java/org/dspace/content/virtual/CollectedTest.java b/dspace-api/src/test/java/org/dspace/content/virtual/CollectedTest.java index 39f64cbe7d..4bf896cd08 100644 --- a/dspace-api/src/test/java/org/dspace/content/virtual/CollectedTest.java +++ b/dspace-api/src/test/java/org/dspace/content/virtual/CollectedTest.java @@ -14,6 +14,7 @@ import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.List; +import com.google.common.base.Splitter; import org.dspace.content.Item; import org.dspace.content.MetadataValue; import org.dspace.content.service.ItemService; @@ -22,7 +23,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class CollectedTest { @@ -85,18 +86,15 @@ public class CollectedTest { metadataValueList.add(metadataValue); String s = "dc.title"; list.add(s); - String[] splittedString = s.split("\\."); + List splittedString = Splitter.on('.').splitToList(s); collected.setFields(list); valueList.add("TestValue"); // Mock the state of objects utilized in getValues() to meet the success criteria of an invocation - when(itemService.getMetadata(item, splittedString.length > 0 ? splittedString[0] : - null, - splittedString.length > 1 ? splittedString[1] : - null, - splittedString.length > 2 ? splittedString[2] : - null, - Item.ANY, false)).thenReturn(metadataValueList); + when(itemService.getMetadata(item, splittedString.size() > 0 ? splittedString.get(0) : null, + splittedString.size() > 1 ? splittedString.get(1) : null, + splittedString.size() > 2 ? splittedString.get(2) : null, + Item.ANY, false)).thenReturn(metadataValueList); when(metadataValue.getValue()).thenReturn("TestValue"); // The reported value(s) should match our valueList diff --git a/dspace-api/src/test/java/org/dspace/content/virtual/ConcatenateTest.java b/dspace-api/src/test/java/org/dspace/content/virtual/ConcatenateTest.java index 2380ddbe58..52457a23d7 100644 --- a/dspace-api/src/test/java/org/dspace/content/virtual/ConcatenateTest.java +++ b/dspace-api/src/test/java/org/dspace/content/virtual/ConcatenateTest.java @@ -14,6 +14,7 @@ import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.List; +import com.google.common.base.Splitter; import org.dspace.content.Item; import org.dspace.content.MetadataValue; import org.dspace.content.service.ItemService; @@ -22,7 +23,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ConcatenateTest { @@ -107,18 +108,15 @@ public class ConcatenateTest { metadataValueList.add(metadataValue); String s = "dc.title"; list.add(s); - String[] splittedString = s.split("\\."); + List splittedString = Splitter.on(".").splitToList(s); concatenate.setFields(list); valueList.add("TestValue"); // Mock the state of objects utilized in getValues() to meet the success criteria of an invocation - when(itemService.getMetadata(item, splittedString.length > 0 ? splittedString[0] : - null, - splittedString.length > 1 ? splittedString[1] : - null, - splittedString.length > 2 ? splittedString[2] : - null, - Item.ANY, false)).thenReturn(metadataValueList); + when(itemService.getMetadata(item, splittedString.size() > 0 ? splittedString.get(0) : null, + splittedString.size() > 1 ? splittedString.get(1) : null, + splittedString.size() > 2 ? splittedString.get(2) : null, + Item.ANY, false)).thenReturn(metadataValueList); when(metadataValue.getValue()).thenReturn("TestValue"); diff --git a/dspace-api/src/test/java/org/dspace/content/virtual/EntityTypeToFilterQueryServiceTest.java b/dspace-api/src/test/java/org/dspace/content/virtual/EntityTypeToFilterQueryServiceTest.java index 8b856dd6c1..1222d65d8d 100644 --- a/dspace-api/src/test/java/org/dspace/content/virtual/EntityTypeToFilterQueryServiceTest.java +++ b/dspace-api/src/test/java/org/dspace/content/virtual/EntityTypeToFilterQueryServiceTest.java @@ -16,7 +16,7 @@ import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class EntityTypeToFilterQueryServiceTest { diff --git a/dspace-api/src/test/java/org/dspace/content/virtual/RelatedTest.java b/dspace-api/src/test/java/org/dspace/content/virtual/RelatedTest.java index d24824fbd9..cb49554d21 100644 --- a/dspace-api/src/test/java/org/dspace/content/virtual/RelatedTest.java +++ b/dspace-api/src/test/java/org/dspace/content/virtual/RelatedTest.java @@ -13,7 +13,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; import java.util.UUID; @@ -29,7 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class RelatedTest { @@ -150,8 +149,8 @@ public class RelatedTest { assertEquals("TestGetValues 1", virtualMetadataConfiguration.getValues(context, item), related.getValues(context, item)); related.setPlace(2); - // No match should return empty LinkedList - assertEquals("TestGetValues 2", new LinkedList<>(), related.getValues(context, item)); + // No match should return empty List + assertEquals("TestGetValues 2", new ArrayList<>(), related.getValues(context, item)); } diff --git a/dspace-api/src/test/java/org/dspace/content/virtual/UUIDValueTest.java b/dspace-api/src/test/java/org/dspace/content/virtual/UUIDValueTest.java index 4312131997..29aa65cd2d 100644 --- a/dspace-api/src/test/java/org/dspace/content/virtual/UUIDValueTest.java +++ b/dspace-api/src/test/java/org/dspace/content/virtual/UUIDValueTest.java @@ -11,7 +11,7 @@ import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -21,14 +21,14 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class UUIDValueTest { @InjectMocks - private UUIDValue UUIDValue; + private UUIDValue uuidValue; @Mock private Context context; @@ -36,32 +36,32 @@ public class UUIDValueTest { @Test public void testGetValues() throws Exception { // Setup objects utilized in unit test - List list = new LinkedList<>(); + List list = new ArrayList<>(); Item item = mock(Item.class); UUID uuid = UUID.randomUUID(); when(item.getID()).thenReturn(uuid); list.add(String.valueOf(uuid)); // The reported value(s) should match our defined list - assertEquals("TestGetValues 0", list, UUIDValue.getValues(context, item)); + assertEquals("TestGetValues 0", list, uuidValue.getValues(context, item)); } @Test public void testSetUseForPlace() { // Setup objects utilized in unit test - UUIDValue.setUseForPlace(true); + uuidValue.setUseForPlace(true); // The reported boolean should return true - assertEquals("TestSetUseForPlace 0", true, UUIDValue.getUseForPlace()); + assertEquals("TestSetUseForPlace 0", true, uuidValue.getUseForPlace()); } @Test public void testGetUseForPlace() { // Setup objects utilized in unit test - UUIDValue.setUseForPlace(true); + uuidValue.setUseForPlace(true); // The reported boolean should return true - assertEquals("TestGetUseForPlace 0", true, UUIDValue.getUseForPlace()); + assertEquals("TestGetUseForPlace 0", true, uuidValue.getUseForPlace()); } } diff --git a/dspace-api/src/test/java/org/dspace/content/virtual/VirtualMetadataPopulatorTest.java b/dspace-api/src/test/java/org/dspace/content/virtual/VirtualMetadataPopulatorTest.java index 207c677001..2e46e3ab98 100644 --- a/dspace-api/src/test/java/org/dspace/content/virtual/VirtualMetadataPopulatorTest.java +++ b/dspace-api/src/test/java/org/dspace/content/virtual/VirtualMetadataPopulatorTest.java @@ -18,7 +18,7 @@ import org.dspace.content.RelationshipType; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class VirtualMetadataPopulatorTest { diff --git a/dspace-api/src/test/java/org/dspace/core/ContextReadOnlyCacheTest.java b/dspace-api/src/test/java/org/dspace/core/ContextReadOnlyCacheTest.java index b948c63212..5ebab8d90b 100644 --- a/dspace-api/src/test/java/org/dspace/core/ContextReadOnlyCacheTest.java +++ b/dspace-api/src/test/java/org/dspace/core/ContextReadOnlyCacheTest.java @@ -24,7 +24,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; /** * Class to test the read-only Context cache diff --git a/dspace-api/src/test/java/org/dspace/core/ContextTest.java b/dspace-api/src/test/java/org/dspace/core/ContextTest.java index e2c5fe4d38..f5697a72dc 100644 --- a/dspace-api/src/test/java/org/dspace/core/ContextTest.java +++ b/dspace-api/src/test/java/org/dspace/core/ContextTest.java @@ -10,24 +10,31 @@ package org.dspace.core; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import java.io.IOException; import java.sql.SQLException; import java.util.List; import java.util.Locale; import java.util.UUID; -import mockit.NonStrictExpectations; import org.dspace.AbstractUnitTest; import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.EPersonService; import org.dspace.eperson.service.GroupService; +import org.junit.Before; import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; /** * Perform some basic unit tests for Context Class @@ -38,6 +45,33 @@ public class ContextTest extends AbstractUnitTest { protected EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); protected GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); + /** + * Spy of AuthorizeService to use for tests + * (initialized / setup in @Before method) + */ + private AuthorizeService authorizeServiceSpy; + + /** + * This method will be run before every test as per @Before. It will + * initialize resources required for the tests. + * + * Other methods can be annotated with @Before here or in subclasses + * but no execution order is guaranteed + */ + @Before + @Override + public void init() { + super.init(); + + // Initialize our spy of the autowired (global) authorizeService bean. + // This allows us to customize the bean's method return values in tests below + authorizeServiceSpy = spy(authorizeService); + // "Wire" our spy to be used by the current loaded object services + // (To ensure these services use the spy instead of the real service) + ReflectionTestUtils.setField(ePersonService, "authorizeService", authorizeServiceSpy); + ReflectionTestUtils.setField(groupService, "authorizeService", authorizeServiceSpy); + } + /** * Test of getDBConnection method, of class Context. */ @@ -53,12 +87,9 @@ public class ContextTest extends AbstractUnitTest { * Test of setCurrentUser method, of class Context. */ @Test - public void testSetCurrentUser() throws SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Admin permissions - needed to create a new EPerson - authorizeService.isAdmin((Context) any); - result = true; - }}; + public void testSetCurrentUser() throws SQLException, AuthorizeException, IOException { + // Allow full Admin perms + when(authorizeServiceSpy.isAdmin(context)).thenReturn(true); EPerson oldUser = context.getCurrentUser(); @@ -77,6 +108,9 @@ public class ContextTest extends AbstractUnitTest { // Restore the previous current user context.setCurrentUser(oldUser); + + // Cleanup our new user + ePersonService.delete(context, newUser); } /** @@ -104,7 +138,6 @@ public class ContextTest extends AbstractUnitTest { */ @Test public void testSetCurrentLocale() { - //Get previous value Locale oldLocale = context.getCurrentLocale(); @@ -171,6 +204,9 @@ public class ContextTest extends AbstractUnitTest { assertThat("testSetExtraLogInfo 0", context.getExtraLogInfo(), notNullValue()); assertThat("testSetExtraLogInfo 1", context.getExtraLogInfo(), equalTo(newValue)); + + //restore old value + context.setExtraLogInfo(oldValue); } /** @@ -225,28 +261,79 @@ public class ContextTest extends AbstractUnitTest { cleanupContext(instance); } + /** + * Test of commit method, of class Context. + */ + @Test + public void testCommit() throws SQLException, AuthorizeException, IOException { + // To test commit() we need a new Context object + Context instance = new Context(); + + // By default, we should have a new DB connection, so let's make sure it is there + assertThat("HibernateDBConnection should exist", instance.getDBConnection(), notNullValue()); + assertTrue("Context should be valid", instance.isValid()); + assertTrue("Transaction should be open", instance.isTransactionAlive()); + + // Allow full Admin perms (in new context) + when(authorizeServiceSpy.isAdmin(instance)).thenReturn(true); + + // Create a new EPerson (to be committed) + String createdEmail = "myfakeemail@example.com"; + EPerson newUser = ePersonService.create(instance); + newUser.setFirstName(instance, "Tim"); + newUser.setLastName(instance, "Smith"); + newUser.setEmail(createdEmail); + newUser.setCanLogIn(true); + newUser.setLanguage(instance, I18nUtil.getDefaultLocale().getLanguage()); + + // Now, call commit() + instance.commit(); + + // We expect our DB connection to still exist + assertThat("HibernateDBConnection should still be open", instance.getDBConnection(), notNullValue()); + // We expect the Context to be valid + assertTrue("Context should still be valid", instance.isValid()); + // However, the transaction should now be closed + assertFalse("DB transaction should be closed", instance.isTransactionAlive()); + + // ReloadEntity and verify changes saved + // NOTE: reloadEntity() is required, see commit() method Javadocs + newUser = instance.reloadEntity(newUser); + assertEquals("New user should be created", newUser.getEmail(), createdEmail); + + // Change the email and commit again (a Context should support multiple commit() calls) + String newEmail = "myrealemail@example.com"; + newUser.setEmail(newEmail); + instance.commit(); + + // Reload entity and new value should be there. + newUser = instance.reloadEntity(newUser); + assertEquals("New email address should be saved", newUser.getEmail(), newEmail); + + // Cleanup our new object & context + ePersonService.delete(instance, newUser); + cleanupContext(instance); + } + /** * Test of abort method, of class Context. */ @Test public void testAbort() throws SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Admin permissions - needed to create a new EPerson - authorizeService.isAdmin((Context) any); - result = true; - }}; - // To test abort() we need a new Context object Context instance = new Context(); + // Allow full Admin perms (in new context) + when(authorizeServiceSpy.isAdmin(instance)).thenReturn(true); + // Create a new EPerson (DO NOT COMMIT IT) String createdEmail = "susie@email.com"; EPerson newUser = ePersonService.create(instance); - newUser.setFirstName(context, "Susan"); - newUser.setLastName(context, "Doe"); + newUser.setFirstName(instance, "Susan"); + newUser.setLastName(instance, "Doe"); newUser.setEmail(createdEmail); newUser.setCanLogIn(true); - newUser.setLanguage(context, I18nUtil.getDefaultLocale().getLanguage()); + newUser.setLanguage(instance, I18nUtil.getDefaultLocale().getLanguage()); // Abort our context instance.abort(); @@ -268,25 +355,20 @@ public class ContextTest extends AbstractUnitTest { */ @Test public void testClose() throws SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Admin permissions - needed to create a new EPerson - authorizeService.isAdmin((Context) any); - result = true; - }}; - String createdEmail = "susie@email.com"; // To test close() we need a new Context object in a try-with-resources block try (Context instance = new Context()) { + // Allow full Admin perms (in new context) + when(authorizeServiceSpy.isAdmin(instance)).thenReturn(true); // Create a new EPerson (DO NOT COMMIT IT) EPerson newUser = ePersonService.create(instance); - newUser.setFirstName(context, "Susan"); - newUser.setLastName(context, "Doe"); + newUser.setFirstName(instance, "Susan"); + newUser.setLastName(instance, "Doe"); newUser.setEmail(createdEmail); newUser.setCanLogIn(true); - newUser.setLanguage(context, I18nUtil.getDefaultLocale().getLanguage()); - + newUser.setLanguage(instance, I18nUtil.getDefaultLocale().getLanguage()); } // Open a new context, let's make sure that EPerson isn't there @@ -410,16 +492,13 @@ public class ContextTest extends AbstractUnitTest { * Test of getSpecialGroups method, of class Context. */ @Test - public void testGetSpecialGroups() throws SQLException, AuthorizeException { - new NonStrictExpectations(authorizeService.getClass()) {{ - // Allow Admin permissions - needed to create a new Group - authorizeService.isAdmin((Context) any); - result = true; - }}; - + public void testGetSpecialGroups() throws SQLException, AuthorizeException, IOException { // To test special groups we need a new Context object Context instance = new Context(); + // Allow full Admin perms (in new context) + when(authorizeServiceSpy.isAdmin(instance)).thenReturn(true); + // Create a new group & add it as a special group Group group = groupService.create(instance); UUID groupID = group.getID(); @@ -436,7 +515,8 @@ public class ContextTest extends AbstractUnitTest { assertThat("testGetSpecialGroup 1", specialGroups.get(0), equalTo(group)); assertThat("testGetSpecialGroup 1", specialGroups.get(1), equalTo(adminGroup)); - // Cleanup our context + // Cleanup our context & group + groupService.delete(instance, group); cleanupContext(instance); } diff --git a/dspace-api/src/test/java/org/dspace/core/HibernateDBConnectionTest.java b/dspace-api/src/test/java/org/dspace/core/HibernateDBConnectionTest.java new file mode 100644 index 0000000000..093f693d56 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/core/HibernateDBConnectionTest.java @@ -0,0 +1,229 @@ +/** + * 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.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.sql.SQLException; + +import org.dspace.AbstractUnitTest; +import org.dspace.eperson.EPerson; +import org.dspace.utils.DSpace; +import org.hibernate.Session; +import org.junit.Before; +import org.junit.Test; + +/** + * Perform some basic unit tests for HibernateDBConnection + * + * @author tdonohue + */ +public class HibernateDBConnectionTest extends AbstractUnitTest { + + private HibernateDBConnection connection; + + /** + * This method will be run before every test as per @Before. It will + * initialize resources required for the tests. + * + * Other methods can be annotated with @Before here or in subclasses + * but no execution order is guaranteed + */ + @Before + @Override + public void init() { + super.init(); + // Get a DB connection to test with + connection = new DSpace().getServiceManager() + .getServiceByName(null, HibernateDBConnection.class); + } + + /** + * Test of getSession method + */ + @Test + public void testGetSession() throws SQLException { + assertNotNull("DB connection should not be null", connection); + // Connection should begin with an active transaction + assertTrue("A transaction should be open by default", connection.getTransaction().isActive()); + + // Rollback current transaction + connection.getTransaction().rollback(); + + // Transaction should be closed + assertFalse("Transaction should be closed after rollback", connection.getTransaction().isActive()); + + //Now call getSession(), saving a reference to the session + Session currentSession = connection.getSession(); + + // New transaction should be initialized + assertTrue("New transaction should be open after getSession() call", + connection.getTransaction().isActive()); + + // Call getSession again. The same Session should still be returned + assertEquals("Multiple calls to getSession should return same Session", currentSession, + connection.getSession()); + } + + /** + * Test of isTransactionAlive method + */ + @Test + public void testIsTransactionAlive() { + assertNotNull("DB connection should not be null", connection); + assertNotNull("Transaction should not be null", connection.getTransaction()); + // Connection should begin with a transaction + assertTrue("A transaction should be open by default", connection.isTransActionAlive()); + + // Rollback current transaction + connection.getTransaction().rollback(); + + // Transaction should be closed + assertFalse("Transaction should be closed after rollback", connection.isTransActionAlive()); + } + + /** + * Test of isSessionAlive method + */ + @Test + public void testIsSessionAlive() throws SQLException { + assertNotNull("DB connection should not be null", connection); + assertNotNull("Session should not be null", connection.getSession()); + assertTrue("A Session should be alive by default", connection.isSessionAlive()); + + // Rollback current transaction, closing it + connection.getTransaction().rollback(); + + // Session should still be alive even after transaction closes + assertTrue("A Session should still be alive if transaction closes", connection.isSessionAlive()); + + // NOTE: Because we configure Hibernate Session objects to be bound to a thread + // (see 'hibernate.current_session_context_class' in hibernate.cfg.xml), a Session is ALWAYS ALIVE until + // the thread closes (at which point Hibernate will clean it up automatically). + // This means that essentially isSessionAlive() will always return true, unless the connection is severed + // in some unexpected way. See also "testCloseDBConnection()" + } + + /** + * Test of closeDBConnection method + */ + @Test + public void testCloseDBConnection() throws SQLException { + // Get a reference to the current Session + Session initialSession = connection.getSession(); + + // Close the DB connection / Session + // NOTE: Because of our Hibernate configuration, Hibernate automatically creates a new Session per thread. + // Even though we "close" the connection, Hibernate will reopen a new one immediately. So, all this actually + // does is create a *new* Session + connection.closeDBConnection(); + + Session newSession = connection.getSession(); + assertNotEquals("New Session expected",initialSession, newSession); + } + + /** + * Test of commit method + */ + @Test + public void testCommit() throws SQLException { + // Ensure a transaction exists + connection.getSession(); + assertTrue("Transaction should be active", connection.getTransaction().isActive()); + + connection.commit(); + assertFalse("Commit should close transaction", connection.getTransaction().isActive()); + + // A second commit should be a no-op (no error thrown) + connection.commit(); + } + + /** + * Test of rollback method + */ + @Test + public void testRollback() throws SQLException { + // Ensure a transaction exists + connection.getSession(); + assertTrue("Transaction should be active", connection.getTransaction().isActive()); + + connection.rollback(); + assertFalse("Rollback should close transaction", connection.getTransaction().isActive()); + + // A second rollback should be a no-op (no error thrown) + connection.rollback(); + } + + /** + * Test of reloadEntity method + */ + @Test + public void testReloadEntityAfterRollback() throws SQLException { + // Get DBConnection associated with DSpace Context + HibernateDBConnection dbConnection = (HibernateDBConnection) context.getDBConnection(); + EPerson person = context.getCurrentUser(); + + assertTrue("Current user should be cached in session", dbConnection.getSession() + .contains(person)); + + dbConnection.rollback(); + assertFalse("Current user should be gone from cache", dbConnection.getSession() + .contains(person)); + + person = dbConnection.reloadEntity(person); + assertTrue("Current user should be cached back in session", dbConnection.getSession() + .contains(person)); + } + + /** + * Test of reloadEntity method + */ + @Test + public void testReloadEntityAfterCommit() throws SQLException { + // Get DBConnection associated with DSpace Context + HibernateDBConnection dbConnection = (HibernateDBConnection) context.getDBConnection(); + EPerson person = context.getCurrentUser(); + + assertTrue("Current user should be cached in session", dbConnection.getSession() + .contains(person)); + + dbConnection.commit(); + assertFalse("Current user should be gone from cache", dbConnection.getSession() + .contains(person)); + + person = dbConnection.reloadEntity(person); + assertTrue("Current user should be cached back in session", dbConnection.getSession() + .contains(person)); + } + + /** + * Test of uncacheEntity method + */ + @Test + public void testUncacheEntity() throws SQLException { + // Get DBConnection associated with DSpace Context + HibernateDBConnection dbConnection = (HibernateDBConnection) context.getDBConnection(); + EPerson person = context.getCurrentUser(); + + assertTrue("Current user should be cached in session", dbConnection.getSession() + .contains(person)); + + dbConnection.uncacheEntity(person); + assertFalse("Current user should be gone from cache", dbConnection.getSession() + .contains(person)); + + // Test ability to reload an uncached entity + person = dbConnection.reloadEntity(person); + assertTrue("Current user should be cached back in session", dbConnection.getSession() + .contains(person)); + } +} diff --git a/dspace-api/src/test/java/org/dspace/core/I18nUtilTest.java b/dspace-api/src/test/java/org/dspace/core/I18nUtilTest.java index 6f9241663e..0000b2046c 100644 --- a/dspace-api/src/test/java/org/dspace/core/I18nUtilTest.java +++ b/dspace-api/src/test/java/org/dspace/core/I18nUtilTest.java @@ -12,7 +12,6 @@ import static org.junit.Assert.assertEquals; import java.util.Locale; -import mockit.Expectations; import org.dspace.AbstractDSpaceTest; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; @@ -136,10 +135,7 @@ public class I18nUtilTest extends AbstractDSpaceTest { final ConfigurationService configService = DSpaceServicesFactory.getInstance().getConfigurationService(); // Override "default.locale" and ensure it is set to US English - new Expectations(configService.getClass()) {{ - configService.getProperty("default.locale"); - result = "en_US.UTF-8"; - }}; + configService.setProperty("default.locale", "en_US.UTF-8"); // Assert our overridden default.locale is set in I18nUtil assertEquals("Default locale", new Locale("en", "US", "UTF-8"), I18nUtil.getDefaultLocale()); diff --git a/dspace-api/src/test/java/org/dspace/core/PathsClassLoaderTest.java b/dspace-api/src/test/java/org/dspace/core/PathsClassLoaderTest.java index b09437e804..1b337a25ab 100644 --- a/dspace-api/src/test/java/org/dspace/core/PathsClassLoaderTest.java +++ b/dspace-api/src/test/java/org/dspace/core/PathsClassLoaderTest.java @@ -7,8 +7,8 @@ */ package org.dspace.core; -import static com.sun.org.apache.bcel.internal.Constants.ACC_PUBLIC; -import static org.junit.Assert.assertTrue; +import static org.apache.bcel.Const.ACC_PUBLIC; +import static org.junit.Assert.assertNotNull; import java.io.File; import java.io.FileOutputStream; @@ -16,7 +16,7 @@ import java.io.IOException; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; -import com.sun.org.apache.bcel.internal.generic.ClassGen; +import org.apache.bcel.generic.ClassGen; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -136,12 +136,12 @@ public class PathsClassLoaderTest { jarFile.getCanonicalPath()}; PathsClassLoader instance = new PathsClassLoader(parentCL, classpath); Class result = instance.findClass(className); - assertTrue("Should return a Class from file", result instanceof Class); + assertNotNull("Should return a Class from file", result); classpath[0] = jarFile.getCanonicalPath(); instance = new PathsClassLoader(parentCL, classpath); result = instance.findClass(jarClassName); - assertTrue("Should return a Class from JAR", result instanceof Class); + assertNotNull("Should return a Class from JAR", result); } } diff --git a/dspace-api/src/test/java/org/dspace/core/UtilsTest.java b/dspace-api/src/test/java/org/dspace/core/UtilsTest.java new file mode 100644 index 0000000000..915c96e209 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/core/UtilsTest.java @@ -0,0 +1,71 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.dspace.AbstractUnitTest; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.junit.Test; + +/** + * Perform some basic unit tests for Utils Class + * + * @author tdonohue + */ +public class UtilsTest extends AbstractUnitTest { + + /** + * Test of getHostName method, of class Utils + */ + @Test + public void testGetHostName() { + assertEquals("Test remove HTTP", "dspace.org", + Utils.getHostName("http://dspace.org")); + + assertEquals("Test remove HTTPS", "dspace.org", + Utils.getHostName("https://dspace.org")); + + assertEquals("Test remove trailing slash", "dspace.org", + Utils.getHostName("https://dspace.org/")); + + assertEquals("Test remove www.", "dspace.org", + Utils.getHostName("https://www.dspace.org")); + + assertEquals("Test keep other prefixes", "demo.dspace.org", + Utils.getHostName("https://demo.dspace.org")); + + // This uses a bunch of reserved URI characters + assertNull("Test invalid URI returns null", Utils.getHostName("&+,?/@=")); + } + + /** + * Test of interpolateConfigsInString method, of class Utils + */ + @Test + public void testInterpolateConfigsInString() { + ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + + // Add a new config to test with + String configName = "not.a.dspace.config.at.all"; + String configValue = "demo.dspace.org"; + configurationService.setProperty(configName, configValue); + + // Create a string where the config is represented by ${variable} + String stringWithVariable = "The config " + configName + " has a value of ${" + configName + "}!"; + String expectedValue = "The config " + configName + " has a value of " + configValue + "!"; + + assertEquals("Test config interpolation", expectedValue, + Utils.interpolateConfigsInString(stringWithVariable)); + + // remove the config we added + configurationService.setProperty(configName, null); + } +} diff --git a/dspace-api/src/test/java/org/dspace/discovery/FullTextContentStreamsTest.java b/dspace-api/src/test/java/org/dspace/discovery/FullTextContentStreamsTest.java index 50af4f6e6d..f2a759fa09 100644 --- a/dspace-api/src/test/java/org/dspace/discovery/FullTextContentStreamsTest.java +++ b/dspace-api/src/test/java/org/dspace/discovery/FullTextContentStreamsTest.java @@ -29,7 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class FullTextContentStreamsTest { diff --git a/dspace-api/src/test/java/org/dspace/discovery/MockIndexEventConsumer.java b/dspace-api/src/test/java/org/dspace/discovery/MockIndexEventConsumer.java deleted file mode 100644 index d6830a1c80..0000000000 --- a/dspace-api/src/test/java/org/dspace/discovery/MockIndexEventConsumer.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE and NOTICE files at the root of the source - * tree and available online at - * - * http://www.dspace.org/license/ - */ -package org.dspace.discovery; - -import mockit.Mock; -import mockit.MockUp; -import org.dspace.core.Context; -import org.dspace.event.Event; - -/** - * Dummy Discovery IndexEventConsumer. It essentially does nothing, - * as Discovery/Solr is not actively running during unit testing. - * - * @author tdonohue - */ -public class MockIndexEventConsumer - extends MockUp { - @Mock - public void consume(Context ctx, Event event) throws Exception { - //do nothing - Solr is not running during unit testing, so we cannot index test content in Solr - } - - @Mock - public void end(Context ctx) throws Exception { - //do nothing - Solr is not running during unit testing, so we cannot index test content in Solr - } -} diff --git a/dspace-api/src/test/java/org/dspace/external/provider/impl/MockDataProvider.java b/dspace-api/src/test/java/org/dspace/external/provider/impl/MockDataProvider.java index b8dc7b501b..e5a86f1f56 100644 --- a/dspace-api/src/test/java/org/dspace/external/provider/impl/MockDataProvider.java +++ b/dspace-api/src/test/java/org/dspace/external/provider/impl/MockDataProvider.java @@ -8,8 +8,8 @@ package org.dspace.external.provider.impl; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; @@ -28,10 +28,12 @@ public class MockDataProvider implements ExternalDataProvider { * Generic getter for the sourceIdentifier * @return the sourceIdentifier value of this MockDataProvider */ + @Override public String getSourceIdentifier() { return sourceIdentifier; } + @Override public Optional getExternalDataObject(String id) { ExternalDataObject externalDataObject = mockLookupMap.get(id); if (externalDataObject == null) { @@ -41,8 +43,9 @@ public class MockDataProvider implements ExternalDataProvider { } } + @Override public List searchExternalDataObjects(String query, int start, int limit) { - List listToReturn = new LinkedList<>(); + List listToReturn = new ArrayList<>(); for (Map.Entry entry : mockLookupMap.entrySet()) { if (StringUtils.containsIgnoreCase(entry.getKey(), query)) { listToReturn.add(entry.getValue()); @@ -72,7 +75,7 @@ public class MockDataProvider implements ExternalDataProvider { public void init() throws IOException { mockLookupMap = new HashMap<>(); - List externalDataObjectsToMake = new LinkedList<>(); + List externalDataObjectsToMake = new ArrayList<>(); externalDataObjectsToMake.add("one"); externalDataObjectsToMake.add("two"); externalDataObjectsToMake.add("three"); @@ -83,7 +86,7 @@ public class MockDataProvider implements ExternalDataProvider { externalDataObject.setId(id); externalDataObject.setValue(id); externalDataObject.setDisplayValue(id); - List list = new LinkedList<>(); + List list = new ArrayList<>(); list.add(new MetadataValueDTO("dc", "contributor", "author", null, "Donald, Smith")); externalDataObject.setMetadata(list); diff --git a/dspace-api/src/test/java/org/dspace/handle/dao/impl/HandleDAOImplTest.java b/dspace-api/src/test/java/org/dspace/handle/dao/impl/HandleDAOImplTest.java index 1560a5d04c..938dab44b0 100644 --- a/dspace-api/src/test/java/org/dspace/handle/dao/impl/HandleDAOImplTest.java +++ b/dspace-api/src/test/java/org/dspace/handle/dao/impl/HandleDAOImplTest.java @@ -128,12 +128,8 @@ public class HandleDAOImplTest extends AbstractUnitTest { owningCommunity = context.reloadEntity(owningCommunity); ContentServiceFactory.getInstance().getCommunityService().delete(context, owningCommunity); owningCommunity = null; - } catch (SQLException e) { - e.printStackTrace(); - } catch (AuthorizeException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); + } catch (Exception e) { + throw new AssertionError("Error occurred in destroy()", e); } item1 = null; item2 = null; diff --git a/dspace-api/src/test/java/org/dspace/identifier/DOIIdentifierProviderTest.java b/dspace-api/src/test/java/org/dspace/identifier/DOIIdentifierProviderTest.java index c1c7b8ee7d..d3536c629a 100644 --- a/dspace-api/src/test/java/org/dspace/identifier/DOIIdentifierProviderTest.java +++ b/dspace-api/src/test/java/org/dspace/identifier/DOIIdentifierProviderTest.java @@ -13,6 +13,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeNotNull; +import static org.mockito.Mockito.mock; import java.io.IOException; import java.sql.SQLException; @@ -36,6 +37,7 @@ import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; import org.dspace.content.service.ItemService; import org.dspace.content.service.WorkspaceItemService; +import org.dspace.identifier.doi.DOIConnector; import org.dspace.identifier.factory.IdentifierServiceFactory; import org.dspace.identifier.service.DOIService; import org.dspace.services.ConfigurationService; @@ -48,7 +50,7 @@ import org.junit.Before; import org.junit.Test; /** - * Tests for {@link DataCiteIdentifierProvider}. + * Tests for {@link DOIIdentifierProvider}. * * @author Mark H. Wood * @author Pascal-Nicolas Becker @@ -75,7 +77,7 @@ public class DOIIdentifierProviderTest private static Community community; private static Collection collection; - private static MockDOIConnector connector; + private static DOIConnector connector; private DOIIdentifierProvider provider; public DOIIdentifierProviderTest() { @@ -111,7 +113,7 @@ public class DOIIdentifierProviderTest config.setProperty(DOIIdentifierProvider.CFG_NAMESPACE_SEPARATOR, NAMESPACE_SEPARATOR); - connector = new MockDOIConnector(); + connector = mock(DOIConnector.class); provider = DSpaceServicesFactory.getInstance().getServiceManager() .getServiceByName(DOIIdentifierProvider.class.getName(), @@ -140,7 +142,6 @@ public class DOIIdentifierProviderTest public void destroy() { community = null; collection = null; - connector.reset(); connector = null; provider = null; super.destroy(); @@ -261,14 +262,13 @@ public class DOIIdentifierProviderTest @Test public void testSupports_valid_String() { - String[] validDOIs = new String[] - { - "10.5072/123abc-lkj/kljl", - PREFIX + "/" + NAMESPACE_SEPARATOR + "lkjljasd1234", - DOI.SCHEME + "10.5072/123abc-lkj/kljl", - "http://dx.doi.org/10.5072/123abc-lkj/kljl", - DOI.RESOLVER + "/10.5072/123abc-lkj/kljl" - }; + String[] validDOIs = new String[] { + "10.5072/123abc-lkj/kljl", + PREFIX + "/" + NAMESPACE_SEPARATOR + "lkjljasd1234", + DOI.SCHEME + "10.5072/123abc-lkj/kljl", + "http://dx.doi.org/10.5072/123abc-lkj/kljl", + DOI.RESOLVER + "/10.5072/123abc-lkj/kljl" + }; for (String doi : validDOIs) { assertTrue("DOI should be supported", provider.supports(doi)); @@ -277,13 +277,12 @@ public class DOIIdentifierProviderTest @Test public void testDoes_not_support_invalid_String() { - String[] invalidDOIs = new String[] - { - "11.5072/123abc-lkj/kljl", - "http://hdl.handle.net/handle/10.5072/123abc-lkj/kljl", - "", - null - }; + String[] invalidDOIs = new String[] { + "11.5072/123abc-lkj/kljl", + "http://hdl.handle.net/handle/10.5072/123abc-lkj/kljl", + "", + null + }; for (String notADoi : invalidDOIs) { assertFalse("Invalid DOIs shouldn't be supported", diff --git a/dspace-api/src/test/java/org/dspace/identifier/EZIDIdentifierProviderTest.java b/dspace-api/src/test/java/org/dspace/identifier/EZIDIdentifierProviderTest.java index 038611fe80..12b279ee1c 100644 --- a/dspace-api/src/test/java/org/dspace/identifier/EZIDIdentifierProviderTest.java +++ b/dspace-api/src/test/java/org/dspace/identifier/EZIDIdentifierProviderTest.java @@ -449,7 +449,7 @@ public class EZIDIdentifierProviderTest // Evaluate String target = (String) metadata.get("_target"); assertEquals("Generates correct _target metadatum", - config.getProperty("dspace.url") + "/handle/" + handle, + config.getProperty("dspace.ui.url") + "/handle/" + handle, target); assertTrue("Has title", metadata.containsKey("datacite.title")); assertTrue("Has publication year", metadata.containsKey("datacite.publicationyear")); diff --git a/dspace-api/src/test/java/org/dspace/identifier/MockDOIConnector.java b/dspace-api/src/test/java/org/dspace/identifier/MockDOIConnector.java deleted file mode 100644 index c3a9c84895..0000000000 --- a/dspace-api/src/test/java/org/dspace/identifier/MockDOIConnector.java +++ /dev/null @@ -1,124 +0,0 @@ -/** - * 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.identifier; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import mockit.Mock; -import mockit.MockUp; -import org.dspace.content.DSpaceObject; -import org.dspace.core.Context; -import org.dspace.identifier.doi.DOIConnector; -import org.dspace.identifier.doi.DOIIdentifierException; - -/** - * @author Pascal-Nicolas Becker (p dot becker at tu hyphen berlin dot de) - */ -public class MockDOIConnector - extends MockUp - implements org.dspace.identifier.doi.DOIConnector { - - public Map reserved; - public Map registered; - - public MockDOIConnector() { - reserved = new HashMap(); - registered = new HashMap(); - } - - public void reset() { - reserved.clear(); - registered.clear(); - } - - @Override - @Mock - public boolean isDOIReserved(Context context, String doi) - throws DOIIdentifierException { - return reserved.containsKey(doi); - } - - @Override - @Mock - public boolean isDOIRegistered(Context context, String doi) - throws DOIIdentifierException { - return registered.containsKey(doi); - } - - @Override - @Mock - public void deleteDOI(Context context, String doi) - throws DOIIdentifierException { - if (reserved.remove(doi) == null) { - throw new DOIIdentifierException("Trying to delete a DOI that was " - + "never reserved!", DOIIdentifierException.DOI_DOES_NOT_EXIST); - } - registered.remove(doi); - } - - @Override - @Mock - public void reserveDOI(Context context, DSpaceObject dso, String doi) - throws DOIIdentifierException { - UUID itemId = reserved.get(doi); - if (null != itemId) { - if (dso.getID().equals(itemId)) { - return; - } else { - throw new DOIIdentifierException("Trying to reserve a DOI that " - + "is reserved for another object.", - DOIIdentifierException.MISMATCH); - } - } - reserved.put(doi, dso.getID()); - } - - @Override - @Mock - public void registerDOI(Context context, DSpaceObject dso, String doi) - throws DOIIdentifierException { - if (!reserved.containsKey(doi)) { - throw new DOIIdentifierException("Trying to register an unreserverd " - + "DOI.", DOIIdentifierException.RESERVE_FIRST); - } - - if (!reserved.get(doi).equals(dso.getID())) { - throw new DOIIdentifierException("Trying to register a DOI that is" - + " reserved for another item.", DOIIdentifierException.MISMATCH); - } - - if (registered.containsKey(doi)) { - if (registered.get(doi).equals(dso.getID())) { - return; - } else { - throw new DOIIdentifierException("Trying to register a DOI that " - + "is registered for another item.", - DOIIdentifierException.MISMATCH); - } - } - - registered.put(doi, dso.getID()); - } - - @Override - @Mock - public void updateMetadata(Context context, DSpaceObject dso, String doi) - throws DOIIdentifierException { - if (!reserved.containsKey(doi)) { - throw new DOIIdentifierException("Trying to update a DOI that is not " - + "registered!", DOIIdentifierException.DOI_DOES_NOT_EXIST); - } - if (!reserved.get(doi).equals(dso.getID())) { - throw new DOIIdentifierException("Trying to update metadata of an " - + "unreserved DOI.", DOIIdentifierException.DOI_DOES_NOT_EXIST); - } - } - -} diff --git a/dspace-api/src/test/java/org/dspace/scripts/DSpaceCommandLineParameterTest.java b/dspace-api/src/test/java/org/dspace/scripts/DSpaceCommandLineParameterTest.java index 4e407d2e2e..e9242c6a9a 100644 --- a/dspace-api/src/test/java/org/dspace/scripts/DSpaceCommandLineParameterTest.java +++ b/dspace-api/src/test/java/org/dspace/scripts/DSpaceCommandLineParameterTest.java @@ -11,7 +11,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import org.dspace.AbstractUnitTest; @@ -121,7 +121,7 @@ public class DSpaceCommandLineParameterTest extends AbstractUnitTest { String value3 = null; DSpaceCommandLineParameter dSpaceCommandLineParameter3 = new DSpaceCommandLineParameter(key3, value3); - List dSpaceCommandLineParameterList = new LinkedList<>(); + List dSpaceCommandLineParameterList = new ArrayList<>(); dSpaceCommandLineParameterList.add(dSpaceCommandLineParameter); dSpaceCommandLineParameterList.add(dSpaceCommandLineParameter1); dSpaceCommandLineParameterList.add(dSpaceCommandLineParameter2); diff --git a/dspace-api/src/test/java/org/dspace/service/impl/ClientInfoServiceImplTest.java b/dspace-api/src/test/java/org/dspace/service/impl/ClientInfoServiceImplTest.java index 42c78944e1..e3bb4a1590 100644 --- a/dspace-api/src/test/java/org/dspace/service/impl/ClientInfoServiceImplTest.java +++ b/dspace-api/src/test/java/org/dspace/service/impl/ClientInfoServiceImplTest.java @@ -18,8 +18,6 @@ import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.statistics.util.DummyHttpServletRequest; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.runners.MockitoJUnitRunner; /** * Unit test class for the {@link ClientInfoServiceImpl} class which implements @@ -27,7 +25,6 @@ import org.mockito.runners.MockitoJUnitRunner; * * @author tom dot desair at gmail dot com */ -@RunWith(MockitoJUnitRunner.class) public class ClientInfoServiceImplTest extends AbstractDSpaceTest { private ClientInfoService clientInfoService; @@ -162,4 +159,4 @@ public class ClientInfoServiceImplTest extends AbstractDSpaceTest { assertFalse(clientInfoService.isUseProxiesEnabled()); } -} \ No newline at end of file +} diff --git a/dspace-api/src/test/java/org/dspace/statistics/FakeDatabaseReader.java b/dspace-api/src/test/java/org/dspace/statistics/FakeDatabaseReader.java deleted file mode 100644 index f40a67f54c..0000000000 --- a/dspace-api/src/test/java/org/dspace/statistics/FakeDatabaseReader.java +++ /dev/null @@ -1,118 +0,0 @@ -/** - * 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.statistics; - -import java.io.File; -import java.io.IOException; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import com.maxmind.geoip2.DatabaseReader; -import com.maxmind.geoip2.model.CityResponse; -import com.maxmind.geoip2.record.City; -import com.maxmind.geoip2.record.Continent; -import com.maxmind.geoip2.record.Country; -import com.maxmind.geoip2.record.Location; -import com.maxmind.geoip2.record.MaxMind; -import com.maxmind.geoip2.record.Postal; -import com.maxmind.geoip2.record.RepresentedCountry; -import com.maxmind.geoip2.record.Subdivision; -import com.maxmind.geoip2.record.Traits; -import mockit.Deencapsulation; -import mockit.Mock; -import mockit.MockUp; - -/** - * Mock service to mock the location Lookup Service used by the SOLR statistics - * logger. - */ -public class FakeDatabaseReader - extends MockUp { - - FakeDatabaseReader() { - } - - public FakeDatabaseReader(Object object) { - } - - public FakeDatabaseReader $init(Builder builder) { - return this; - } - - /* - @Override - public Location getLocation(String str) { - Location location = new Location(); - location.countryCode = "US"; - location.countryName = "United States"; - location.region = "NY"; - location.city = "New York"; - location.postalCode = "10036"; - location.latitude = 40.760498F; - location.longitude = -73.9933F; - location.dma_code = 501; - location.area_code = 212; - location.metro_code = 501; - - return location; - } - */ - - @Mock - public CityResponse city(InetAddress address) { - List names = new ArrayList<>(1); - - names.add("New York"); - City city = new City(names, 1, 1, new HashMap()); - - Continent continent = new Continent(); - - names.clear(); - names.add("United States"); - Country country = new Country(names, 1, 1, "US", new HashMap()); - - Location location = new Location(1, 1, 40.760498D, -73.9933D, 501, 1, "EST"); - - MaxMind maxmind = new MaxMind(); - - Postal postal = new Postal("10036", 1); - - RepresentedCountry representedCountry = new RepresentedCountry(); - - ArrayList subdivisions = new ArrayList<>(0); - - Traits traits = new Traits(); - - CityResponse response = new CityResponse(city, continent, country, - location, maxmind, postal, country, representedCountry, - subdivisions, traits); - return response; - } - - public static class Builder - extends MockUp { - - public Builder() {} - - /** - * Fake constructor. - * @param file ignored. - */ - @Mock - public void $init(File file) { - } - - @Mock - public DatabaseReader build() - throws IOException { - return Deencapsulation.newUninitializedInstance(DatabaseReader.class); - } - } -} diff --git a/dspace-api/src/test/java/org/dspace/statistics/MockSolrLoggerServiceImpl.java b/dspace-api/src/test/java/org/dspace/statistics/MockSolrLoggerServiceImpl.java index eef9878c18..cca05a12cc 100644 --- a/dspace-api/src/test/java/org/dspace/statistics/MockSolrLoggerServiceImpl.java +++ b/dspace-api/src/test/java/org/dspace/statistics/MockSolrLoggerServiceImpl.java @@ -7,9 +7,26 @@ */ package org.dspace.statistics; -import java.io.File; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; import com.maxmind.geoip2.DatabaseReader; +import com.maxmind.geoip2.model.CityResponse; +import com.maxmind.geoip2.record.City; +import com.maxmind.geoip2.record.Continent; +import com.maxmind.geoip2.record.Country; +import com.maxmind.geoip2.record.Location; +import com.maxmind.geoip2.record.MaxMind; +import com.maxmind.geoip2.record.Postal; +import com.maxmind.geoip2.record.RepresentedCountry; +import com.maxmind.geoip2.record.Traits; import org.springframework.beans.factory.InitializingBean; /** @@ -17,7 +34,7 @@ import org.springframework.beans.factory.InitializingBean; *

* NOTE: this class is overridden by one of the same name * defined in dspace-server-webapp and declared as a bean there. - * See {@code config/spring/api/Z-mock-services.xml}. Some kind of classpath + * See {@code test/data/dspaceFolder/config/spring/api/solr-services.xml}. Some kind of classpath * magic makes this work. */ public class MockSolrLoggerServiceImpl @@ -32,11 +49,31 @@ public class MockSolrLoggerServiceImpl //We don't use SOLR in the tests of this module solr = null; - new FakeDatabaseReader(); // Activate fake - new FakeDatabaseReader.Builder(); // Activate fake - File locationDb = File.createTempFile("GeoIP", ".db"); - locationDb.deleteOnExit(); - locationService = new DatabaseReader.Builder(locationDb).build(); + // Mock GeoIP's DatabaseReader + DatabaseReader reader = mock(DatabaseReader.class); + // Ensure that any tests requesting a city() get a mock/fake CityResponse + when(reader.city(any(InetAddress.class))).thenReturn(mockCityResponse()); + // Save this mock DatabaseReader to be used by SolrLoggerService + locationService = reader; } + /** + * A mock/fake GeoIP CityResponse, which will be used for *all* test statistical requests + * @return faked CityResponse + */ + private CityResponse mockCityResponse() { + List cityNames = new ArrayList(Collections.singleton("New York")); + City city = new City(cityNames, 1, 1, new HashMap()); + + List countryNames = new ArrayList(Collections.singleton("United States")); + Country country = new Country(countryNames, 1, 1, "US", new HashMap()); + + Location location = new Location(1, 1, 40.760498D, -73.9933D, 501, 1, "EST"); + + Postal postal = new Postal("10036", 1); + + return new CityResponse(city, new Continent(), country, location, new MaxMind(), postal, + country, new RepresentedCountry(), new ArrayList<>(0), + new Traits()); + } } diff --git a/dspace-api/src/test/java/org/dspace/statistics/util/DummyHttpServletRequest.java b/dspace-api/src/test/java/org/dspace/statistics/util/DummyHttpServletRequest.java index b255db3e04..61325c652c 100644 --- a/dspace-api/src/test/java/org/dspace/statistics/util/DummyHttpServletRequest.java +++ b/dspace-api/src/test/java/org/dspace/statistics/util/DummyHttpServletRequest.java @@ -11,11 +11,11 @@ package org.dspace.statistics.util; import java.io.BufferedReader; import java.io.IOException; import java.security.Principal; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; @@ -114,7 +114,7 @@ public class DummyHttpServletRequest implements HttpServletRequest { * @param headerValue The value of the header */ public void addHeader(String headerName, String headerValue) { - List values = headers.computeIfAbsent(headerName, k -> new LinkedList<>()); + List values = headers.computeIfAbsent(headerName, k -> new ArrayList<>()); values.add(headerValue); } /* (non-Javadoc) @@ -292,6 +292,7 @@ public class DummyHttpServletRequest implements HttpServletRequest { * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromUrl() */ @Override + @Deprecated public boolean isRequestedSessionIdFromUrl() { // TODO Auto-generated method stub return false; @@ -502,6 +503,7 @@ public class DummyHttpServletRequest implements HttpServletRequest { * @see javax.servlet.ServletRequest#getRealPath(java.lang.String) */ @Override + @Deprecated public String getRealPath(String arg0) { // TODO Auto-generated method stub return null; diff --git a/dspace-api/src/test/java/org/dspace/statistics/util/SpiderDetectorServiceImplTest.java b/dspace-api/src/test/java/org/dspace/statistics/util/SpiderDetectorServiceImplTest.java index 6abda2b13e..039fe31f11 100644 --- a/dspace-api/src/test/java/org/dspace/statistics/util/SpiderDetectorServiceImplTest.java +++ b/dspace-api/src/test/java/org/dspace/statistics/util/SpiderDetectorServiceImplTest.java @@ -10,25 +10,19 @@ package org.dspace.statistics.util; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import mockit.Mock; -import mockit.MockUp; import org.dspace.AbstractDSpaceTest; import org.dspace.core.factory.CoreServiceFactory; import org.dspace.service.ClientInfoService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.dspace.statistics.SolrLoggerServiceImpl; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.runners.MockitoJUnitRunner; /** * @author mwood * @author frederic at atmire.com */ -@RunWith(MockitoJUnitRunner.class) public class SpiderDetectorServiceImplTest extends AbstractDSpaceTest { private static final String NOT_A_BOT_ADDRESS = "192.168.0.1"; @@ -338,30 +332,5 @@ public class SpiderDetectorServiceImplTest extends AbstractDSpaceTest { public void cleanup() throws Exception { spiderDetectorService = null; configurationService.setProperty("usage-statistics.bots.case-insensitive", false); - ; } - - - /** - * Dummy SolrLogger for testing. - * - * @author mwood - */ - static public class MockSolrLogger - extends MockUp { - @Mock - public void $init() { - } - - @Mock - public void $clinit() { - } - - @Mock - public boolean isUseProxies() { - return false; - } - - } - } diff --git a/dspace-api/src/test/java/org/dspace/statistics/util/SpiderDetectorTest.java b/dspace-api/src/test/java/org/dspace/statistics/util/SpiderDetectorTest.java index 4797f7f1e5..63046b32b6 100644 --- a/dspace-api/src/test/java/org/dspace/statistics/util/SpiderDetectorTest.java +++ b/dspace-api/src/test/java/org/dspace/statistics/util/SpiderDetectorTest.java @@ -10,10 +10,10 @@ package org.dspace.statistics.util; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import mockit.Mock; -import mockit.MockUp; import org.dspace.AbstractDSpaceTest; -import org.dspace.statistics.SolrLoggerServiceImpl; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.junit.Before; import org.junit.Test; /** @@ -22,6 +22,15 @@ import org.junit.Test; public class SpiderDetectorTest extends AbstractDSpaceTest { private static final String NOT_A_BOT_ADDRESS = "192.168.0.1"; + @Before + public void init() { + // Get current configuration + ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + + // Ensure useProxies is set to false for all tests + configurationService.setProperty("useProxies", false); + } + /** * Test method for {@link org.dspace.statistics.util.SpiderDetector#readPatterns(java.io.File)}. */ @@ -128,25 +137,4 @@ public class SpiderDetectorTest extends AbstractDSpaceTest { SpiderDetector.isSpider(candidate, null, null, null)); } - - /** - * Dummy SolrLogger for testing. - * - * @author mwood - */ - static public class MockSolrLogger - extends MockUp { - @Mock - public void $init() { - } - - @Mock - public void $clinit() { - } - - @Mock - public boolean isUseProxies() { - return false; - } - } } diff --git a/dspace-api/src/test/java/org/dspace/util/MultiFormatDateParserTest.java b/dspace-api/src/test/java/org/dspace/util/MultiFormatDateParserTest.java index d7157b8b05..b26f36a47f 100644 --- a/dspace-api/src/test/java/org/dspace/util/MultiFormatDateParserTest.java +++ b/dspace-api/src/test/java/org/dspace/util/MultiFormatDateParserTest.java @@ -81,8 +81,8 @@ public class MultiFormatDateParserTest { {"Should parse: yyyyMM", "195701", "yyyyMM", true}, {"Should parse: yyyy", "1957", "yyyy", true}, {"Should parse: yyyy-MM-dd'T'HH:mm:ss'Z'", "1957-01-27T12:34:56Z", "yyyy-MM-dd'T'HH:mm:ss'Z'", true}, - {"Should parse: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "1957-01-27T12:34:56.789Z", "yyyy-MM-dd'T'HH:mm:ss" + - ".SSS'Z'", true}, + {"Should parse: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "1957-01-27T12:34:56.789Z", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", + true}, {"Shouldn't parse: yyyy/MM/ddHH:mm:ss", "1957/01/2720:06:20", "yyyy/MM/ddHH:mm:ss", false} }); } diff --git a/dspace-api/src/test/java/org/dspace/workflowbasic/BasicWorkflowAuthorizationIT.java b/dspace-api/src/test/java/org/dspace/workflowbasic/BasicWorkflowAuthorizationIT.java index f79ed3aaf2..7164204d25 100644 --- a/dspace-api/src/test/java/org/dspace/workflowbasic/BasicWorkflowAuthorizationIT.java +++ b/dspace-api/src/test/java/org/dspace/workflowbasic/BasicWorkflowAuthorizationIT.java @@ -271,7 +271,7 @@ public class BasicWorkflowAuthorizationIT Item item = wsi.getItem(); Bundle bundle = bundleService.create(context, item, "ORIGINAL"); File f = new File(AbstractDSpaceTest.testProps.get("test.bitstream").toString()); - Bitstream bs = bitstreamService.create(context, bundle, new FileInputStream(f)); + bitstreamService.create(context, bundle, new FileInputStream(f)); bundleService.update(context, bundle); itemService.update(context, item); workspaceItemService.update(context, wsi); @@ -289,8 +289,8 @@ public class BasicWorkflowAuthorizationIT int i = 0; // check item policies - for (int action : new int[] {Constants.READ, Constants.WRITE, Constants.ADD, Constants.REMOVE, Constants - .DELETE}) { + for (int action : new int[] {Constants.READ, Constants.WRITE, Constants.ADD, Constants.REMOVE, + Constants.DELETE}) { Assert.assertTrue("testReviewerPermissions 1-" + i++, authorizeService.authorizeActionBoolean(context, member, item, action, false)); } @@ -323,7 +323,7 @@ public class BasicWorkflowAuthorizationIT Item item = wsi.getItem(); Bundle bundle = bundleService.create(context, item, "ORIGINAL"); File f = new File(AbstractDSpaceTest.testProps.get("test.bitstream").toString()); - Bitstream bs = bitstreamService.create(context, bundle, new FileInputStream(f)); + bitstreamService.create(context, bundle, new FileInputStream(f)); bundleService.update(context, bundle); itemService.update(context, item); workspaceItemService.update(context, wsi); @@ -365,7 +365,7 @@ public class BasicWorkflowAuthorizationIT item.setSubmitter(submitter); Bundle bundle = bundleService.create(context, item, "ORIGINAL"); File f = new File(AbstractDSpaceTest.testProps.get("test.bitstream").toString()); - Bitstream bs = bitstreamService.create(context, bundle, new FileInputStream(f)); + bitstreamService.create(context, bundle, new FileInputStream(f)); bundleService.update(context, bundle); itemService.update(context, item); workspaceItemService.update(context, wsi); diff --git a/dspace-api/src/test/java/org/dspace/xmlworkflow/RoleTest.java b/dspace-api/src/test/java/org/dspace/xmlworkflow/RoleTest.java index 83c23e1e8d..262804f12b 100644 --- a/dspace-api/src/test/java/org/dspace/xmlworkflow/RoleTest.java +++ b/dspace-api/src/test/java/org/dspace/xmlworkflow/RoleTest.java @@ -31,49 +31,48 @@ public class RoleTest extends AbstractUnitTest { @Test public void defaultWorkflow_RoleReviewer() { Role role = defaultWorkflow.getRoles().get("Reviewer"); - assertEquals(role.getDescription(), - "The people responsible for this step are able to edit the metadata of incoming submissions, " + - "and then accept or reject them."); - assertEquals(role.getName(), "Reviewer"); - assertEquals(role.getScope(), Role.Scope.COLLECTION); + assertEquals("The people responsible for this step are able to edit the metadata of incoming submissions, " + + "and then accept or reject them.", role.getDescription()); + assertEquals("Reviewer", role.getName()); + assertEquals(Role.Scope.COLLECTION, role.getScope()); } @Test public void defaultWorkflow_RoleEditor() { Role role = defaultWorkflow.getRoles().get("Editor"); - assertEquals(role.getDescription(), "The people responsible for this step are able to edit the " + - "metadata of incoming submissions, and then accept or reject them."); - assertEquals(role.getName(), "Editor"); - assertEquals(role.getScope(), Role.Scope.COLLECTION); + assertEquals("The people responsible for this step are able to edit the " + + "metadata of incoming submissions, and then accept or reject them.", role.getDescription()); + assertEquals("Editor", role.getName()); + assertEquals(Role.Scope.COLLECTION, role.getScope()); } @Test public void defaultWorkflow_RoleFinalEditor() { Role role = defaultWorkflow.getRoles().get("Final Editor"); - assertEquals(role.getDescription(), "The people responsible for this step are able to edit the " + - "metadata of incoming submissions, but will not be able to reject them."); - assertEquals(role.getName(), "Final Editor"); - assertEquals(role.getScope(), Role.Scope.COLLECTION); + assertEquals("The people responsible for this step are able to edit the " + + "metadata of incoming submissions, but will not be able to reject them.", role.getDescription()); + assertEquals("Final Editor", role.getName()); + assertEquals(Role.Scope.COLLECTION, role.getScope()); } @Test public void selectSingleReviewer_RoleReviewManagers() { Role role = selectSingleReviewer.getRoles().get("ReviewManagers"); - assertEquals(role.getName(), "ReviewManagers"); - assertEquals(role.getScope(), Role.Scope.REPOSITORY); + assertEquals("ReviewManagers", role.getName()); + assertEquals(Role.Scope.REPOSITORY, role.getScope()); } @Test public void selectSingleReviewer_RoleReviewer() { Role role = selectSingleReviewer.getRoles().get("Reviewer"); - assertEquals(role.getName(), "Reviewer"); - assertEquals(role.getScope(), Role.Scope.ITEM); + assertEquals("Reviewer", role.getName()); + assertEquals(Role.Scope.ITEM, role.getScope()); } @Test public void scoreReview_RoleScoreReviewers() { Role role = scoreReview.getRoles().get("ScoreReviewers"); - assertEquals(role.getName(), "ScoreReviewers"); - assertEquals(role.getScope(), Role.Scope.COLLECTION); + assertEquals("ScoreReviewers", role.getName()); + assertEquals(Role.Scope.COLLECTION, role.getScope()); } } diff --git a/dspace-api/src/test/java/org/dspace/xmlworkflow/XmlWorkflowFactoryTest.java b/dspace-api/src/test/java/org/dspace/xmlworkflow/XmlWorkflowFactoryTest.java index ddcf764d5b..676285a2b2 100644 --- a/dspace-api/src/test/java/org/dspace/xmlworkflow/XmlWorkflowFactoryTest.java +++ b/dspace-api/src/test/java/org/dspace/xmlworkflow/XmlWorkflowFactoryTest.java @@ -25,6 +25,7 @@ import org.dspace.utils.DSpace; import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; import org.dspace.xmlworkflow.state.Workflow; +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -42,6 +43,8 @@ public class XmlWorkflowFactoryTest extends AbstractUnitTest { = new DSpace().getServiceManager().getServiceByName("xmlWorkflowFactory", XmlWorkflowFactoryImpl.class); private Community owningCommunity; + private Collection mappedCollection; + private Collection nonMappedCollection; /** * log4j category @@ -63,6 +66,9 @@ public class XmlWorkflowFactoryTest extends AbstractUnitTest { //we have to create a new community in the database context.turnOffAuthorisationSystem(); this.owningCommunity = communityService.create(null, context); + this.mappedCollection = + this.collectionService.create(context, owningCommunity, "123456789/workflow-test-1"); + this.nonMappedCollection = this.collectionService.create(context, owningCommunity, "123456789/999"); //we need to commit the changes so we don't block the table for testing context.restoreAuthSystemState(); } catch (SQLException e) { @@ -74,38 +80,46 @@ public class XmlWorkflowFactoryTest extends AbstractUnitTest { } } + /** + * This method will be run after every test as per @After. It will + * clean resources initialized by the @Before methods. + * + * Other methods can be annotated with @After here or in subclasses + * but no execution order is guaranteed + */ + @After + @Override + public void destroy() { + context.turnOffAuthorisationSystem(); + + try { + this.collectionService.delete(context, this.nonMappedCollection); + this.collectionService.delete(context, this.mappedCollection); + this.communityService.delete(context, this.owningCommunity); + } catch (Exception e) { + log.error("Error in destroy", e); + } + + context.restoreAuthSystemState(); + this.owningCommunity = null; + this.nonMappedCollection = null; + this.mappedCollection = null; + try { + super.destroy(); + } catch (Exception e) { + log.error("Error in destroy", e); + } + } + @Test public void workflowMapping_NonMappedCollection() throws WorkflowConfigurationException { - Collection collection = this.findOrCreateCollectionWithHandle("123456789/6"); - Workflow workflow = xmlWorkflowFactory.getWorkflow(collection); + Workflow workflow = xmlWorkflowFactory.getWorkflow(this.nonMappedCollection); assertEquals(workflow.getID(), "defaultWorkflow"); } @Test public void workflowMapping_MappedCollection() throws WorkflowConfigurationException { - Collection collection = this.findOrCreateCollectionWithHandle("123456789/1000000"); - Workflow workflow = xmlWorkflowFactory.getWorkflow(collection); + Workflow workflow = xmlWorkflowFactory.getWorkflow(this.mappedCollection); assertEquals(workflow.getID(), "selectSingleReviewer"); } - - private Collection findOrCreateCollectionWithHandle(String handle) { - try { - context.turnOffAuthorisationSystem(); - for (Collection collection : this.collectionService.findAll(context)) { - if (collection.getHandle().equalsIgnoreCase(handle)) { - return collection; - } - } - Collection collection = this.collectionService.create(context, owningCommunity, handle); - context.restoreAuthSystemState(); - return collection; - } catch (SQLException e) { - log.error("SQL Error in findOrCreateCollectionWithHandle", e); - fail("SQL Error in findOrCreateCollectionWithHandle: " + e.getMessage()); - } catch (AuthorizeException e) { - log.error("Authorization Error in findOrCreateCollectionWithHandle", e); - fail("Authorization Error in findOrCreateCollectionWithHandle: " + e.getMessage()); - } - return null; - } } diff --git a/dspace-api/src/test/java/org/dspace/xmlworkflow/state/StepTest.java b/dspace-api/src/test/java/org/dspace/xmlworkflow/state/StepTest.java index b14210b432..def0239e68 100644 --- a/dspace-api/src/test/java/org/dspace/xmlworkflow/state/StepTest.java +++ b/dspace-api/src/test/java/org/dspace/xmlworkflow/state/StepTest.java @@ -7,8 +7,9 @@ */ package org.dspace.xmlworkflow.state; -import static junit.framework.TestCase.assertEquals; -import static junit.framework.TestCase.assertNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import java.util.List; @@ -35,70 +36,70 @@ public class StepTest extends AbstractUnitTest { @Test public void defaultWorkflow_ReviewStep() throws WorkflowConfigurationException { Step step = defaultWorkflow.getStep("reviewstep"); - assertEquals(step.getUserSelectionMethod().getId(), "claimaction"); - assertEquals(step.getRole().getName(), "Reviewer"); + assertEquals("claimaction", step.getUserSelectionMethod().getId()); + assertEquals("Reviewer", step.getRole().getName()); List actions = step.getActions(); - assert (this.containsActionNamed(actions, "reviewaction")); - assertEquals(step.getNextStep(0).getId(), "editstep"); + assertTrue(this.containsActionNamed(actions, "reviewaction")); + assertEquals("editstep", step.getNextStep(0).getId()); } @Test public void defaultWorkflow_EditStep() throws WorkflowConfigurationException { Step step = defaultWorkflow.getStep("editstep"); - assertEquals(step.getUserSelectionMethod().getId(), "claimaction"); - assertEquals(step.getRole().getName(), "Editor"); + assertEquals("claimaction", step.getUserSelectionMethod().getId()); + assertEquals("Editor", step.getRole().getName()); List actions = step.getActions(); - assert (this.containsActionNamed(actions, "editaction")); - assertEquals(step.getNextStep(0).getId(), "finaleditstep"); + assertTrue(this.containsActionNamed(actions, "editaction")); + assertEquals("finaleditstep", step.getNextStep(0).getId()); } @Test public void defaultWorkflow_FinalEditStep() throws WorkflowConfigurationException { Step step = defaultWorkflow.getStep("finaleditstep"); - assertEquals(step.getUserSelectionMethod().getId(), "claimaction"); - assertEquals(step.getRole().getName(), "Final Editor"); + assertEquals("claimaction", step.getUserSelectionMethod().getId()); + assertEquals("Final Editor", step.getRole().getName()); List actions = step.getActions(); - assert (this.containsActionNamed(actions, "finaleditaction")); + assertTrue(this.containsActionNamed(actions, "finaleditaction")); assertNull(step.getNextStep(0)); } @Test public void selectSingleReviewer_SelectReviewerStep() throws WorkflowConfigurationException { Step step = selectSingleReviewer.getStep("selectReviewerStep"); - assertEquals(step.getUserSelectionMethod().getId(), "claimaction"); - assertEquals(step.getRole().getName(), "ReviewManagers"); + assertEquals("claimaction", step.getUserSelectionMethod().getId()); + assertEquals("ReviewManagers", step.getRole().getName()); List actions = step.getActions(); - assert (this.containsActionNamed(actions, "selectrevieweraction")); - assertEquals(step.getNextStep(0).getId(), "singleUserReviewStep"); + assertTrue(this.containsActionNamed(actions, "selectrevieweraction")); + assertEquals("singleUserReviewStep", step.getNextStep(0).getId()); } @Test public void selectSingleReviewer_SingleUserReviewStep() throws WorkflowConfigurationException { Step step = selectSingleReviewer.getStep("singleUserReviewStep"); - assertEquals(step.getUserSelectionMethod().getId(), "autoassignAction"); - assert (step.getRole().getName().equals("Reviewer")); + assertEquals("autoassignAction", step.getUserSelectionMethod().getId()); + assertEquals("Reviewer", step.getRole().getName()); List actions = step.getActions(); - assert (this.containsActionNamed(actions, "singleuserreviewaction")); - assertEquals(step.getNextStep(1).getId(), "selectReviewerStep"); + assertTrue(this.containsActionNamed(actions, "singleuserreviewaction")); + assertEquals("selectReviewerStep", step.getNextStep(1).getId()); } @Test public void scoreReview_ScoreReviewStep() throws WorkflowConfigurationException { Step step = scoreReview.getStep("scoreReviewStep"); - assertEquals(step.getUserSelectionMethod().getId(), "claimaction"); - assertEquals(step.getRole().getName(), "ScoreReviewers"); + assertEquals("claimaction", step.getUserSelectionMethod().getId()); + assertEquals("ScoreReviewers", step.getRole().getName()); List actions = step.getActions(); - assert (this.containsActionNamed(actions, "scorereviewaction")); - assertEquals(step.getNextStep(0).getId(), "evaluationStep"); - assertEquals(step.getRequiredUsers(), 2); + assertTrue(this.containsActionNamed(actions, "scorereviewaction")); + assertEquals("evaluationStep", step.getNextStep(0).getId()); + assertEquals(2, step.getRequiredUsers()); } @Test public void scoreReview_EvaluationStep() throws WorkflowConfigurationException { Step step = scoreReview.getStep("evaluationStep"); - assertEquals(step.getUserSelectionMethod().getId(), "noUserSelectionAction"); + assertEquals("noUserSelectionAction", step.getUserSelectionMethod().getId()); List actions = step.getActions(); - assert (this.containsActionNamed(actions, "evaluationaction")); + assertTrue(this.containsActionNamed(actions, "evaluationaction")); assertNull(step.getNextStep(0)); } diff --git a/dspace-api/src/test/java/org/dspace/xmlworkflow/state/WorkflowTest.java b/dspace-api/src/test/java/org/dspace/xmlworkflow/state/WorkflowTest.java index dc988696f4..85182a6440 100644 --- a/dspace-api/src/test/java/org/dspace/xmlworkflow/state/WorkflowTest.java +++ b/dspace-api/src/test/java/org/dspace/xmlworkflow/state/WorkflowTest.java @@ -7,7 +7,8 @@ */ package org.dspace.xmlworkflow.state; -import static junit.framework.TestCase.assertEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import java.util.List; @@ -31,30 +32,30 @@ public class WorkflowTest extends AbstractUnitTest { @Test public void defaultWorkflow() { - assertEquals(defaultWorkflow.getFirstStep().getId(), "reviewstep"); + assertEquals("reviewstep", defaultWorkflow.getFirstStep().getId()); List steps = defaultWorkflow.getSteps(); - assertEquals(steps.size(), 3); - assert (this.containsStepNamed(steps, "reviewstep")); - assert (this.containsStepNamed(steps, "editstep")); - assert (this.containsStepNamed(steps, "finaleditstep")); + assertEquals(3, steps.size()); + assertTrue(this.containsStepNamed(steps, "reviewstep")); + assertTrue(this.containsStepNamed(steps, "editstep")); + assertTrue(this.containsStepNamed(steps, "finaleditstep")); } @Test public void selectSingleReviewer() { - assertEquals(selectSingleReviewer.getFirstStep().getId(), "selectReviewerStep"); + assertEquals("selectReviewerStep", selectSingleReviewer.getFirstStep().getId()); List steps = selectSingleReviewer.getSteps(); - assertEquals(steps.size(), 2); - assert (this.containsStepNamed(steps, "selectReviewerStep")); - assert (this.containsStepNamed(steps, "singleUserReviewStep")); + assertEquals(2, steps.size()); + assertTrue(this.containsStepNamed(steps, "selectReviewerStep")); + assertTrue(this.containsStepNamed(steps, "singleUserReviewStep")); } @Test public void scoreReview() { - assertEquals(scoreReview.getFirstStep().getId(), "scoreReviewStep"); + assertEquals("scoreReviewStep", scoreReview.getFirstStep().getId()); List steps = scoreReview.getSteps(); - assertEquals(steps.size(), 2); - assert (this.containsStepNamed(steps, "scoreReviewStep")); - assert (this.containsStepNamed(steps, "evaluationStep")); + assertEquals(2, steps.size()); + assertTrue(this.containsStepNamed(steps, "scoreReviewStep")); + assertTrue(this.containsStepNamed(steps, "evaluationStep")); } private boolean containsStepNamed(List steps, String stepName) { diff --git a/dspace-api/src/test/resources/test-config.properties b/dspace-api/src/test/resources/test-config.properties index 273d93c968..49aaa9bb10 100644 --- a/dspace-api/src/test/resources/test-config.properties +++ b/dspace-api/src/test/resources/test-config.properties @@ -6,8 +6,8 @@ # http://www.dspace.org/license/ # # Defines the test folder where the unit tests will be run +# (Used by AbstractDSpaceTest) test.folder = ./target/testing/ -test.folder.assetstore = ./target/testing/dspace/assetstore -#Path for a test file to create bitstreams +# Path of the test bitstream (to use in BitstreamTest and elsewhere) test.bitstream = ./target/testing/dspace/assetstore/ConstitutionofIreland.pdf diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 183bf62fd7..4caec1c3b2 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -15,7 +15,7 @@ ${basedir}/.. - 3.2.11 + 3.3.0 5.87.0.RELEASE @@ -89,7 +89,7 @@ org.apache.commons commons-lang3 - + log4j log4j @@ -103,6 +103,11 @@ org.codehaus.woodstox wstx-asl + + + org.dom4j + dom4j + diff --git a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/config/DSpaceConfigurationService.java b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/config/DSpaceConfigurationService.java index f010808531..bb4cb6dc58 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/config/DSpaceConfigurationService.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/config/DSpaceConfigurationService.java @@ -7,22 +7,49 @@ */ package org.dspace.xoai.services.impl.config; -import org.dspace.core.ConfigurationManager; +import org.dspace.core.Utils; +import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.xoai.services.api.config.ConfigurationService; public class DSpaceConfigurationService implements ConfigurationService { + + private org.dspace.services.ConfigurationService configurationService = + DSpaceServicesFactory.getInstance().getConfigurationService(); + + /** + * Initialize the OAI Configuration Service + */ + public DSpaceConfigurationService() { + // Check the DSpace ConfigurationService for required OAI-PMH settings. + // If they do not exist, set sane defaults as needed. + + // Per OAI Spec, "oai.identifier.prefix" should be the hostname / domain name of the site. + // This configuration is needed by the [dspace]/config/crosswalks/oai/description.xml template, so if + // unspecified we will dynamically set it to the hostname of the "dspace.ui.url" configuration. + if (!configurationService.hasProperty("oai.identifier.prefix")) { + configurationService.setProperty("oai.identifier.prefix", + Utils.getHostName(configurationService.getProperty("dspace.ui.url"))); + } + } + + @Override public String getProperty(String key) { - return ConfigurationManager.getProperty(key); + return configurationService.getProperty(key); } @Override public String getProperty(String module, String key) { - return ConfigurationManager.getProperty(module, key); + return configurationService.getProperty(module, key); } @Override public boolean getBooleanProperty(String module, String key, boolean defaultValue) { - return ConfigurationManager.getBooleanProperty(module, key, defaultValue); + if (module == null) { + return configurationService.getBooleanProperty(key, defaultValue); + } + + // Assume "module" properties are always prefixed with the module name + return configurationService.getBooleanProperty(module + "." + key, defaultValue); } } diff --git a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/solr/DSpaceSolrServerResolver.java b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/solr/DSpaceSolrServerResolver.java index 6cdd59bd75..c544ec1659 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/solr/DSpaceSolrServerResolver.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/solr/DSpaceSolrServerResolver.java @@ -26,11 +26,12 @@ public class DSpaceSolrServerResolver implements SolrServerResolver { @Override public SolrClient getServer() throws SolrServerException { if (server == null) { + String serverUrl = configurationService.getProperty("oai.solr.url"); try { - server = new HttpSolrClient.Builder(configurationService.getProperty("oai", "solr.url")).build(); - log.debug("Solr Server Initialized"); + server = new HttpSolrClient.Builder(serverUrl).build(); + log.debug("OAI Solr Server Initialized"); } catch (Exception e) { - log.error(e.getMessage(), e); + log.error("Could not initialize OAI Solr Server at " + serverUrl , e); } } return server; diff --git a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/xoai/DSpaceRepositoryConfiguration.java b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/xoai/DSpaceRepositoryConfiguration.java index fe7fbdbfd0..2a000f43ea 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/xoai/DSpaceRepositoryConfiguration.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/xoai/DSpaceRepositoryConfiguration.java @@ -9,6 +9,7 @@ package org.dspace.xoai.services.impl.xoai; import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.sql.SQLException; import java.util.ArrayList; import java.util.Date; @@ -22,6 +23,7 @@ import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.core.Context; +import org.dspace.core.Utils; import org.dspace.xoai.exceptions.InvalidMetadataFieldException; import org.dspace.xoai.services.api.EarliestDateResolver; import org.dspace.xoai.services.api.config.ConfigurationService; @@ -132,13 +134,13 @@ public class DSpaceRepositoryConfiguration implements RepositoryConfiguration { @Override public List getDescription() { List result = new ArrayList(); - String descriptionFile = configurationService.getProperty("oai", "description.file"); + String descriptionFile = configurationService.getProperty("oai.description.file"); if (descriptionFile == null) { // Try indexed boolean stop = false; List descriptionFiles = new ArrayList(); for (int i = 0; !stop; i++) { - String tmp = configurationService.getProperty("oai", "description.file." + i); + String tmp = configurationService.getProperty("oai.description.file." + i); if (tmp == null) { stop = true; } else { @@ -150,7 +152,10 @@ public class DSpaceRepositoryConfiguration implements RepositoryConfiguration { try { File f = new File(path); if (f.exists()) { - result.add(FileUtils.readFileToString(f)); + String fileAsString = FileUtils.readFileToString(f, StandardCharsets.UTF_8); + // replace any configuration placeholders (e.g. ${variable}) in string + fileAsString = Utils.interpolateConfigsInString(fileAsString); + result.add(fileAsString); } } catch (IOException e) { log.debug(e.getMessage(), e); @@ -161,7 +166,10 @@ public class DSpaceRepositoryConfiguration implements RepositoryConfiguration { try { File f = new File(descriptionFile); if (f.exists()) { - result.add(FileUtils.readFileToString(f)); + String fileAsString = FileUtils.readFileToString(f, StandardCharsets.UTF_8); + // replace any configuration placeholders (e.g. ${variable}) in string + fileAsString = Utils.interpolateConfigsInString(fileAsString); + result.add(fileAsString); } } catch (IOException e) { log.debug(e.getMessage(), e); diff --git a/dspace-oai/src/main/java/org/dspace/xoai/solr/DSpaceSolrServer.java b/dspace-oai/src/main/java/org/dspace/xoai/solr/DSpaceSolrServer.java index 3c92ea667e..99c071c6b9 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/solr/DSpaceSolrServer.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/solr/DSpaceSolrServer.java @@ -30,12 +30,12 @@ public class DSpaceSolrServer { public static SolrClient getServer() throws SolrServerException { if (_server == null) { + String serverUrl = ConfigurationManager.getProperty("oai.solr.url"); try { - _server = new HttpSolrClient.Builder( - ConfigurationManager.getProperty("oai", "solr.url")).build(); - log.debug("Solr Server Initialized"); + _server = new HttpSolrClient.Builder(serverUrl).build(); + log.debug("OAI Solr Server Initialized"); } catch (Exception e) { - log.error(e.getMessage(), e); + log.error("Could not initialize OAI Solr Server at " + serverUrl , e); } } return _server; diff --git a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java index 5169b5523f..c2369bce23 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java @@ -176,7 +176,7 @@ public class ItemUtils { } /** - * This method will add all sub-elements to a top element, like: dc, or dcterms, ... * + * This method will add all sub-elements to a top element, like: dc, or dcterms, ... * * @param schema Element argument passed by reference that will be changed * @param val Metadatavalue that will be processed * @throws SQLException @@ -284,7 +284,7 @@ public class ItemUtils { // Repository Info Element repository = create("repository"); - repository.getField().add(createValue("url", ConfigurationManager.getProperty("dspace.baseUrl"))); + repository.getField().add(createValue("url", ConfigurationManager.getProperty("dspace.ui.url"))); repository.getField().add(createValue("name", ConfigurationManager.getProperty("dspace.name"))); repository.getField().add(createValue("mail", ConfigurationManager.getProperty("mail.admin"))); metadata.getElement().add(repository); diff --git a/dspace-rdf/src/main/java/org/dspace/rdf/providing/DataProviderServlet.java b/dspace-rdf/src/main/java/org/dspace/rdf/providing/DataProviderServlet.java index 526f138a71..007f865fb7 100644 --- a/dspace-rdf/src/main/java/org/dspace/rdf/providing/DataProviderServlet.java +++ b/dspace-rdf/src/main/java/org/dspace/rdf/providing/DataProviderServlet.java @@ -61,7 +61,7 @@ public class DataProviderServlet extends HttpServlet { log.debug("lang = " + lang + ", cType = " + cType + " and pathInfo: " + pathInfo); if (StringUtils.isEmpty(pathInfo) || StringUtils.countMatches(pathInfo, "/") < 2) { String dspaceURI = - DSpaceServicesFactory.getInstance().getConfigurationService().getProperty("dspace.url"); + DSpaceServicesFactory.getInstance().getConfigurationService().getProperty("dspace.ui.url"); this.serveNamedGraph(dspaceURI, lang, cType, response); return; } diff --git a/dspace-rest/README.md b/dspace-rest/README.md index 353b249931..07d71d66ed 100644 --- a/dspace-rest/README.md +++ b/dspace-rest/README.md @@ -1,7 +1,9 @@ -#DSpace REST API (Jersey) +#DSpace REST API (Jersey) - DEPRECATED A RESTful web services API for DSpace, built using JAX-RS1 JERSEY. +_This REST API has been deprecated and will be removed in v8. Please use the Server API (/server) webapp instead._ + ##Getting Started This REST API is integrated directly into the DSpace codebase. diff --git a/dspace-rest/src/main/java/org/dspace/rest/RestIndex.java b/dspace-rest/src/main/java/org/dspace/rest/RestIndex.java index c09e924536..26b1150229 100644 --- a/dspace-rest/src/main/java/org/dspace/rest/RestIndex.java +++ b/dspace-rest/src/main/java/org/dspace/rest/RestIndex.java @@ -59,7 +59,9 @@ public class RestIndex { // TODO Better graphics, add arguments to all methods. (limit, offset, item and so on) return "DSpace REST - index" + "" - + "

DSpace REST API

" + + + "

DSpace REST API (Deprecated)

" + + "This REST API is deprecated and will be removed in v8." + + " Please use the new Server API webapp instead.
" + "Server path: " + servletContext.getContextPath() + "

Index

" + "
    " + diff --git a/dspace-rest/src/main/webapp/WEB-INF/web.xml b/dspace-rest/src/main/webapp/WEB-INF/web.xml index 1b33aac885..34d74d9630 100644 --- a/dspace-rest/src/main/webapp/WEB-INF/web.xml +++ b/dspace-rest/src/main/webapp/WEB-INF/web.xml @@ -35,7 +35,7 @@ - DSpace REST API + DSpace REST API (Deprecated) org.glassfish.jersey.servlet.ServletContainer @@ -47,7 +47,7 @@ - DSpace REST API + DSpace REST API (Deprecated) /* @@ -59,7 +59,7 @@ - DSpace REST API + DSpace REST API (Deprecated) /* diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 49d32e25d8..c85ee73d90 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -51,7 +51,6 @@ install of DSpace, against which Tests can be run. --> maven-dependency-plugin - 2.8 ${project.build.directory}/testing @@ -95,7 +94,6 @@ org.codehaus.gmaven groovy-maven-plugin - 2.0 setproperty @@ -350,11 +348,6 @@ json-path-assert test - - org.jmockit - jmockit - test - junit junit diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java index ba2c66c734..7149996d4d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java @@ -27,7 +27,6 @@ import org.dspace.app.rest.converter.ConverterService; import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.model.BitstreamRest; import org.dspace.app.rest.model.hateoas.BitstreamResource; -import org.dspace.app.rest.projection.Projection; import org.dspace.app.rest.utils.ContextUtil; import org.dspace.app.rest.utils.MultipartFileSender; import org.dspace.app.rest.utils.Utils; @@ -243,7 +242,7 @@ public class BitstreamRestController { context.commit(); - BitstreamRest bitstreamRest = converter.toRest(context.reloadEntity(bitstream), Projection.DEFAULT); + BitstreamRest bitstreamRest = converter.toRest(context.reloadEntity(bitstream), utils.obtainProjection()); return converter.toResource(bitstreamRest); } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/CollectionItemtemplateController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/CollectionItemtemplateController.java index eef1d3eec0..8b87df6b06 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/CollectionItemtemplateController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/CollectionItemtemplateController.java @@ -17,10 +17,11 @@ import javax.ws.rs.BadRequestException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import org.dspace.app.rest.converter.ConverterService; import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.CollectionRest; -import org.dspace.app.rest.model.ItemRest; -import org.dspace.app.rest.model.hateoas.ItemResource; +import org.dspace.app.rest.model.TemplateItemRest; +import org.dspace.app.rest.model.hateoas.TemplateItemResource; import org.dspace.app.rest.repository.CollectionRestRepository; import org.dspace.app.rest.utils.ContextUtil; import org.dspace.app.rest.utils.Utils; @@ -60,6 +61,9 @@ public class CollectionItemtemplateController { @Autowired private CollectionService collectionService; + @Autowired + private ConverterService converter; + /** * This method will create an Item and add it as a template to a Collection. * @@ -108,19 +112,20 @@ public class CollectionItemtemplateController { Context context = ContextUtil.obtainContext(request); Collection collection = getCollection(context, uuid); - ItemRest inputItemRest; + TemplateItemRest inputTemplateItemRest; try { ObjectMapper mapper = new ObjectMapper(); - inputItemRest = mapper.readValue(itemBody.toString(), ItemRest.class); + inputTemplateItemRest = mapper.readValue(itemBody.toString(), TemplateItemRest.class); } catch (IOException e1) { throw new UnprocessableEntityException("Error parsing request body", e1); } - ItemRest templateItem = collectionRestRepository.createTemplateItem(context, collection, inputItemRest); + TemplateItemRest templateItem = + collectionRestRepository.createTemplateItem(context, collection, inputTemplateItemRest); context.commit(); return ControllerUtils.toResponseEntity(HttpStatus.CREATED, new HttpHeaders(), - new ItemResource(templateItem, utils)); + converter.toResource(templateItem)); } /** @@ -140,14 +145,14 @@ public class CollectionItemtemplateController { */ @PreAuthorize("hasPermission(#uuid, 'COLLECTION', 'READ')") @RequestMapping(method = RequestMethod.GET) - public ItemResource getTemplateItem(HttpServletRequest request, @PathVariable UUID uuid) + public TemplateItemResource getTemplateItem(HttpServletRequest request, @PathVariable UUID uuid) throws SQLException { Context context = ContextUtil.obtainContext(request); Collection collection = getCollection(context, uuid); - ItemRest templateItem = collectionRestRepository.getTemplateItem(collection); + TemplateItemRest templateItem = collectionRestRepository.getTemplateItem(collection); - return new ItemResource(templateItem, utils); + return converter.toResource(templateItem); } private Collection getCollection(Context context, UUID uuid) throws SQLException { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/CollectionLogoController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/CollectionLogoController.java index 76dec0555e..c0b78cdec0 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/CollectionLogoController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/CollectionLogoController.java @@ -65,7 +65,7 @@ public class CollectionLogoController { /** * This method will add a logo to the collection. * - * curl -X POST http:///api/core/collections/1c11f3f1-ba1f-4f36-908a-3f1ea9a557eb/logo' \ + * curl -X POST http:///api/core/collections/1c11f3f1-ba1f-4f36-908a-3f1ea9a557eb/logo' \ * -XPOST -H 'Content-Type: multipart/form-data' \ * -H 'Authorization: Bearer eyJhbGciOiJI...' \ * -F "file=@Downloads/test.png" @@ -73,7 +73,7 @@ public class CollectionLogoController { * Example: *
          * {@code
    -     * curl -X POST http:///api/core/collections/1c11f3f1-ba1f-4f36-908a-3f1ea9a557eb/logo' \
    +     * curl -X POST http:///api/core/collections/1c11f3f1-ba1f-4f36-908a-3f1ea9a557eb/logo' \
          *  -XPOST -H 'Content-Type: multipart/form-data' \
          *  -H 'Authorization: Bearer eyJhbGciOiJI...' \
          *  -F "file=@Downloads/test.png"
    diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/CommunityLogoController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/CommunityLogoController.java
    index 4b11320f97..78b59730c8 100644
    --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/CommunityLogoController.java
    +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/CommunityLogoController.java
    @@ -66,7 +66,7 @@ public class CommunityLogoController {
         /**
          * This method will add a logo to the community.
          *
    -     * curl -X POST http:///api/core/communities/1c11f3f1-ba1f-4f36-908a-3f1ea9a557eb/logo' \
    +     * curl -X POST http:///api/core/communities/1c11f3f1-ba1f-4f36-908a-3f1ea9a557eb/logo' \
          *  -XPOST -H 'Content-Type: multipart/form-data' \
          *  -H 'Authorization: Bearer eyJhbGciOiJI...' \
          *  -F "file=@Downloads/test.png"
    @@ -74,7 +74,7 @@ public class CommunityLogoController {
          * Example:
          * 
          * {@code
    -     * curl -X POST http:///api/core/communities/1c11f3f1-ba1f-4f36-908a-3f1ea9a557eb/logo' \
    +     * curl -X POST http:///api/core/communities/1c11f3f1-ba1f-4f36-908a-3f1ea9a557eb/logo' \
          *  -XPOST -H 'Content-Type: multipart/form-data' \
          *  -H 'Authorization: Bearer eyJhbGciOiJI...' \
          *  -F "file=@Downloads/test.png"
    diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ExternalSourcesRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ExternalSourcesRestController.java
    index 0295aae877..db016218a9 100644
    --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ExternalSourcesRestController.java
    +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ExternalSourcesRestController.java
    @@ -44,7 +44,7 @@ public class ExternalSourcesRestController {
          * This method will retrieve all the ExternalSourceEntries for the ExternalSource for the given externalSourceName
          * param
          *
    -     * curl -X GET http:///api/integration/externalsources/orcidV2/entries
    +     * curl -X GET http:///api/integration/externalsources/orcidV2/entries
          *
          * @param externalSourceName The externalSourceName that defines which ExternalDataProvider is used
          * @param query         The query used in the lookup
    @@ -74,7 +74,7 @@ public class ExternalSourcesRestController {
          * This method will retrieve one ExternalSourceEntryResource based on the ExternalSource for the given
          * externalSourceName and with the given entryId
          *
    -     * curl -X GET http:///api/integration/externalsources/orcidV2/entries/0000-0000-0000-0000
    +     * curl -X GET http:///api/integration/externalsources/orcidV2/entries/0000-0000-0000-0000
          *
          * @param externalSourceName The externalSourceName that defines which ExternalDataProvider is used
          * @param entryId       The entryId used for the lookup
    diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemAddBundleController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemAddBundleController.java
    index 0532ff2d29..12e8e057f9 100644
    --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemAddBundleController.java
    +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemAddBundleController.java
    @@ -22,7 +22,6 @@ import org.dspace.app.rest.exception.UnprocessableEntityException;
     import org.dspace.app.rest.model.BundleRest;
     import org.dspace.app.rest.model.ItemRest;
     import org.dspace.app.rest.model.hateoas.BundleResource;
    -import org.dspace.app.rest.projection.Projection;
     import org.dspace.app.rest.repository.ItemRestRepository;
     import org.dspace.app.rest.utils.ContextUtil;
     import org.dspace.app.rest.utils.Utils;
    @@ -108,7 +107,7 @@ public class ItemAddBundleController {
             }
     
             Bundle bundle = itemRestRepository.addBundleToItem(context, item, bundleRest);
    -        BundleResource bundleResource = converter.toResource(converter.toRest(bundle, Projection.DEFAULT));
    +        BundleResource bundleResource = converter.toResource(converter.toRest(bundle, utils.obtainProjection()));
             return ControllerUtils.toResponseEntity(HttpStatus.CREATED, new HttpHeaders(), bundleResource);
         }
     
    diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestController.java
    index 67aca95c10..b06360ee1d 100644
    --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestController.java
    +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestController.java
    @@ -21,7 +21,6 @@ import org.dspace.app.rest.converter.ConverterService;
     import org.dspace.app.rest.exception.DSpaceBadRequestException;
     import org.dspace.app.rest.exception.UnprocessableEntityException;
     import org.dspace.app.rest.model.CollectionRest;
    -import org.dspace.app.rest.projection.Projection;
     import org.dspace.app.rest.utils.ContextUtil;
     import org.dspace.app.rest.utils.Utils;
     import org.dspace.authorize.AuthorizeException;
    @@ -97,7 +96,7 @@ public class ItemOwningCollectionUpdateRestController {
             if (targetCollection == null) {
                 return null;
             }
    -        return converter.toRest(targetCollection, Projection.DEFAULT);
    +        return converter.toRest(targetCollection, utils.obtainProjection());
     
         }
     
    diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemtemplateRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemtemplateRestController.java
    index 0bece5ea88..fb77967b15 100644
    --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemtemplateRestController.java
    +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemtemplateRestController.java
    @@ -12,16 +12,17 @@ import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFI
     import java.io.IOException;
     import java.sql.SQLException;
     import java.util.UUID;
    -
     import javax.servlet.http.HttpServletRequest;
     
     import com.fasterxml.jackson.databind.JsonNode;
    +import org.dspace.app.rest.converter.ConverterService;
    +import org.dspace.app.rest.exception.DSpaceBadRequestException;
     import org.dspace.app.rest.exception.UnprocessableEntityException;
    -import org.dspace.app.rest.model.ItemRest;
    -import org.dspace.app.rest.model.hateoas.ItemResource;
    -import org.dspace.app.rest.projection.Projection;
    -import org.dspace.app.rest.repository.ItemRestRepository;
    +import org.dspace.app.rest.model.TemplateItemRest;
    +import org.dspace.app.rest.model.hateoas.TemplateItemResource;
    +import org.dspace.app.rest.model.wrapper.TemplateItem;
     import org.dspace.app.rest.repository.ItemTemplateItemOfLinkRepository;
    +import org.dspace.app.rest.repository.TemplateItemRestRepository;
     import org.dspace.app.rest.utils.ContextUtil;
     import org.dspace.app.rest.utils.Utils;
     import org.dspace.authorize.AuthorizeException;
    @@ -57,7 +58,10 @@ public class ItemtemplateRestController {
         private ItemService itemService;
     
         @Autowired
    -    private ItemRestRepository itemRestRepository;
    +    private TemplateItemRestRepository templateItemRestRepository;
    +
    +    @Autowired
    +    private ConverterService converter;
     
         @Autowired
         private ItemTemplateItemOfLinkRepository itemTemplateItemOfLinkRepository;
    @@ -75,24 +79,20 @@ public class ItemtemplateRestController {
          * 
    * @param request * @param uuid A UUID of a template item - * @return The template item corresponding to the UUID above + * @return The template item corresponding to the UUID above */ @PreAuthorize("hasPermission(#uuid, 'COLLECTION', 'READ')") @RequestMapping(method = RequestMethod.GET) - public ItemResource getTemplateItem(HttpServletRequest request, @PathVariable UUID uuid) { + public TemplateItemResource getTemplateItem(HttpServletRequest request, @PathVariable UUID uuid) { Context context = ContextUtil.obtainContext(request); - ItemRest templateItem = itemRestRepository.findOne(context, uuid); + TemplateItemRest templateItem = templateItemRestRepository.findOne(context, uuid); if (templateItem == null) { throw new ResourceNotFoundException("Item with id: " + uuid + " not found"); } - if (itemTemplateItemOfLinkRepository.getTemplateItemOf(request, uuid, null, Projection.DEFAULT) == null) { - throw new ResourceNotFoundException("The item with id " + uuid + " is not a template item"); - } - - return new ItemResource(templateItem, utils); + return converter.toResource(templateItem); } /** @@ -116,23 +116,23 @@ public class ItemtemplateRestController { * @param request * @param uuid The UUID of the template item to be modified * @param jsonNode The data as shown above - * @return The modified item + * @return The modified item * @throws SQLException * @throws AuthorizeException */ @PreAuthorize("hasPermission(#uuid, 'ITEM', 'WRITE')") @RequestMapping(method = RequestMethod.PATCH) - public ResponseEntity replaceTemplateItem(HttpServletRequest request, @PathVariable UUID uuid, - @RequestBody(required = true) JsonNode jsonNode) + public ResponseEntity patch(HttpServletRequest request, @PathVariable UUID uuid, + @RequestBody(required = true) JsonNode jsonNode) throws SQLException, AuthorizeException { Context context = ContextUtil.obtainContext(request); - Item item = getTemplateItem(context, uuid); - ItemRest templateItem = itemRestRepository.patchTemplateItem(item, jsonNode); + TemplateItem templateItem = getTemplateItem(context, uuid); + TemplateItemRest templateItemRest = templateItemRestRepository.patchTemplateItem(templateItem, jsonNode); context.commit(); return ControllerUtils.toResponseEntity(HttpStatus.OK, new HttpHeaders(), - new ItemResource(templateItem, utils)); + converter.toResource(templateItemRest)); } /** @@ -148,7 +148,7 @@ public class ItemtemplateRestController { *
    * @param request * @param uuid - * @return Status code 204 is returned if the deletion was successful + * @return Status code 204 is returned if the deletion was successful * @throws SQLException * @throws AuthorizeException * @throws IOException @@ -159,22 +159,27 @@ public class ItemtemplateRestController { throws SQLException, AuthorizeException, IOException { Context context = ContextUtil.obtainContext(request); - Item item = getTemplateItem(context, uuid); - itemRestRepository.removeTemplateItem(context, item); + TemplateItem item = getTemplateItem(context, uuid); + templateItemRestRepository.removeTemplateItem(context, item); context.commit(); return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT); } - private Item getTemplateItem(Context context, UUID uuid) throws SQLException { + private TemplateItem getTemplateItem(Context context, UUID uuid) throws SQLException { Item item = itemService.find(context, uuid); if (item == null) { throw new ResourceNotFoundException( "The given uuid did not resolve to an item on the server: " + uuid); } if (item.getTemplateItemOf() == null) { + throw new DSpaceBadRequestException("This given uuid does not resolve to a TemplateItem"); + } + + try { + return new TemplateItem(item); + } catch (IllegalArgumentException e) { throw new UnprocessableEntityException("The item with id " + uuid + " is not a template item"); } - return item; } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MappedCollectionRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MappedCollectionRestController.java index 3b993d582b..4681fea1a3 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MappedCollectionRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MappedCollectionRestController.java @@ -60,14 +60,14 @@ public class MappedCollectionRestController { * This method will add an Item to a Collection. The Collection object is encapsulated in the request due to the * text/uri-list consumer and the Item UUID comes from the path in the URL * - * curl -X POST http:///api/core/item/{uuid}/mappedCollections + * curl -X POST http:///api/core/item/{uuid}/mappedCollections * -H "Content-Type:text/uri-list" * --data $'https://{url}/rest/api/core/collections/{uuid}' * * Example: *
          * {@code
    -     * curl -X POST http:///api/core/item/{uuid}/mappedCollections
    +     * curl -X POST http:///api/core/item/{uuid}/mappedCollections
          *  -H "Content-Type:text/uri-list"
          *  --data $'https://{url}/rest/api/core/collections/506a7e54-8d7c-4d5b-8636-d5f6411483de'
          * }
    @@ -116,12 +116,12 @@ public class MappedCollectionRestController {
          * This method will delete a Collection to Item relation. It will remove an Item with UUID given in the request
          * URL from the Collection with UUID given in the request URL.
          *
    -     * curl -X DELETE http:///api/core/item/{uuid}/mappedCollections/{collectionUuid}
    +     * curl -X DELETE http:///api/core/item/{uuid}/mappedCollections/{collectionUuid}
          *
          * Example:
          * 
          * {@code
    -     * curl -X DELETE http:///api/core/item/{uuid}/mappedCollections/{collectionUuid}
    +     * curl -X DELETE http:///api/core/item/{uuid}/mappedCollections/{collectionUuid}
          * }
          * 
    * diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java index 4113469e7a..ec36721fc0 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java @@ -648,8 +648,7 @@ public class RestResourceController implements InitializingBean { @RequestMapping(method = RequestMethod.PATCH, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_DIGIT) public ResponseEntity patch(HttpServletRequest request, @PathVariable String apiCategory, @PathVariable String model, @PathVariable Integer id, - @RequestBody(required = true) JsonNode jsonNode) - throws HttpRequestMethodNotSupportedException { + @RequestBody(required = true) JsonNode jsonNode) { return patchInternal(request, apiCategory, model, id, jsonNode); } @@ -671,8 +670,7 @@ public class RestResourceController implements InitializingBean { public ResponseEntity patch(HttpServletRequest request, @PathVariable String apiCategory, @PathVariable String model, @PathVariable(name = "uuid") UUID id, - @RequestBody(required = true) JsonNode jsonNode) - throws HttpRequestMethodNotSupportedException { + @RequestBody(required = true) JsonNode jsonNode) { return patchInternal(request, apiCategory, model, id, jsonNode); } @@ -690,8 +688,7 @@ public class RestResourceController implements InitializingBean { public ResponseEntity patchInternal(HttpServletRequest request, String apiCategory, String model, ID id, - JsonNode jsonNode) - throws HttpRequestMethodNotSupportedException { + JsonNode jsonNode) { checkModelPluralForm(apiCategory, model); DSpaceRestRepository repository = utils.getResourceRepository(apiCategory, model); RestAddressableModel modelObject = null; @@ -1058,12 +1055,12 @@ public class RestResourceController implements InitializingBean { /** * Execute a PUT request for an entity with id of type UUID; * - * curl -X PUT http:///api/{apiCategory}/{model}/{uuid} + * curl -X PUT http:///api/{apiCategory}/{model}/{uuid} * * Example: *
          * {@code
    -     *      curl -X PUT http:///api/core/collection/8b632938-77c2-487c-81f0-e804f63e68e6
    +     *      curl -X PUT http:///api/core/collection/8b632938-77c2-487c-81f0-e804f63e68e6
          * }
          * 
    * diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/WorkflowDefinitionCollectionsLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/WorkflowDefinitionCollectionsLinkRepository.java new file mode 100644 index 0000000000..9496e32738 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/WorkflowDefinitionCollectionsLinkRepository.java @@ -0,0 +1,82 @@ +/** + * 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.app.rest; + +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; + +import org.dspace.app.rest.converter.ConverterService; +import org.dspace.app.rest.model.CollectionRest; +import org.dspace.app.rest.model.WorkflowDefinitionRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.app.rest.repository.AbstractDSpaceRestRepository; +import org.dspace.app.rest.repository.LinkRestRepository; +import org.dspace.app.rest.utils.Utils; +import org.dspace.content.Collection; +import org.dspace.core.Context; +import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +/** + * Link repository for "collections" subresource of an individual workflow definition. + * + * @author Maria Verdonck (Atmire) on 11/12/2019 + */ +@Component(WorkflowDefinitionRest.CATEGORY + "." + WorkflowDefinitionRest.NAME + "." + + WorkflowDefinitionRest.COLLECTIONS_MAPPED_TO) +public class WorkflowDefinitionCollectionsLinkRepository extends AbstractDSpaceRestRepository + implements LinkRestRepository { + + @Autowired + protected XmlWorkflowFactory xmlWorkflowFactory; + + @Autowired + protected ConverterService converter; + + @Autowired + protected Utils utils; + + /** + * GET endpoint that returns the list of collections that make an explicit use of the workflow-definition. + * If a collection doesn't specify the workflow-definition to be used, the default mapping applies, + * but this collection is not included in the list returned by this method. + * + * @param request The request object + * @param workflowName Name of workflow we want the collections of that are mapped to is + * @return List of collections mapped to the requested workflow + */ + @PreAuthorize("hasAuthority('AUTHENTICATED')") + public Page getCollections(@Nullable HttpServletRequest request, + String workflowName, + @Nullable Pageable optionalPageable, + Projection projection) { + if (xmlWorkflowFactory.workflowByThisNameExists(workflowName)) { + Context context = obtainContext(); + List collectionsMappedToWorkflow = new ArrayList<>(); + if (xmlWorkflowFactory.isDefaultWorkflow(workflowName)) { + collectionsMappedToWorkflow.addAll(xmlWorkflowFactory.getAllNonMappedCollectionsHandles(context)); + } + collectionsMappedToWorkflow.addAll(xmlWorkflowFactory.getCollectionHandlesMappedToWorklow(context, + workflowName)); + Pageable pageable = optionalPageable != null ? optionalPageable : new PageRequest(0, 20); + return converter.toRestPage(utils.getPage(collectionsMappedToWorkflow, pageable), + projection); + } else { + throw new ResourceNotFoundException("No workflow with name " + workflowName + " is configured"); + } + } + +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/WorkflowDefinitionStepsLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/WorkflowDefinitionStepsLinkRepository.java new file mode 100644 index 0000000000..fe05a4c1d0 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/WorkflowDefinitionStepsLinkRepository.java @@ -0,0 +1,63 @@ +/** + * 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.app.rest; + +import java.util.List; +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; + +import org.dspace.app.rest.model.WorkflowDefinitionRest; +import org.dspace.app.rest.model.WorkflowStepRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.app.rest.repository.AbstractDSpaceRestRepository; +import org.dspace.app.rest.repository.LinkRestRepository; +import org.dspace.xmlworkflow.WorkflowConfigurationException; +import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; +import org.dspace.xmlworkflow.state.Step; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +/** + * Link repository for "steps" subresource of an individual workflow definition. + * + * @author Maria Verdonck (Atmire) on 24/02/2020 + */ +@Component(WorkflowDefinitionRest.CATEGORY + "." + WorkflowDefinitionRest.NAME + "." + + WorkflowDefinitionRest.STEPS) +public class WorkflowDefinitionStepsLinkRepository extends AbstractDSpaceRestRepository + implements LinkRestRepository { + + @Autowired + protected XmlWorkflowFactory xmlWorkflowFactory; + + /** + * GET endpoint that returns the list of steps of a workflow-definition. + * + * @param request The request object + * @param workflowName Name of workflow we want the steps from + * @return List of steps of the requested workflow + */ + @PreAuthorize("hasAuthority('AUTHENTICATED')") + public Page getSteps(@Nullable HttpServletRequest request, + String workflowName, + @Nullable Pageable optionalPageable, + Projection projection) { + try { + List steps = xmlWorkflowFactory.getWorkflowByName(workflowName).getSteps(); + Pageable pageable = optionalPageable != null ? optionalPageable : new PageRequest(0, 20); + return converter.toRestPage(utils.getPage(steps, pageable), projection); + } catch (WorkflowConfigurationException e) { + throw new ResourceNotFoundException("No workflow with name " + workflowName + " is configured"); + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/WorkflowStepActionsLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/WorkflowStepActionsLinkRepository.java new file mode 100644 index 0000000000..b11dd929d5 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/WorkflowStepActionsLinkRepository.java @@ -0,0 +1,57 @@ +/** + * 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.app.rest; + +import java.util.List; +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; + +import org.dspace.app.rest.model.WorkflowActionRest; +import org.dspace.app.rest.model.WorkflowStepRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.app.rest.repository.AbstractDSpaceRestRepository; +import org.dspace.app.rest.repository.LinkRestRepository; +import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; +import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +/** + * Link repository for "actions" subresource of an individual workflow step. + * + * @author Maria Verdonck (Atmire) on 24/02/2020 + */ +@Component(WorkflowStepRest.CATEGORY + "." + WorkflowStepRest.NAME + "." + + WorkflowStepRest.ACTIONS) +public class WorkflowStepActionsLinkRepository extends AbstractDSpaceRestRepository + implements LinkRestRepository { + + @Autowired + protected XmlWorkflowFactory xmlWorkflowFactory; + + /** + * GET endpoint that returns the list of actions of a workflow step. + * + * @param request The request object + * @param workflowStepName Name of workflow step we want the actions from + * @return List of actions of the requested workflow step + */ + @PreAuthorize("hasAuthority('AUTHENTICATED')") + public Page getActions(@Nullable HttpServletRequest request, + String workflowStepName, + @Nullable Pageable optionalPageable, + Projection projection) { + List actions = xmlWorkflowFactory.getStepByName(workflowStepName).getActions(); + Pageable pageable = optionalPageable != null ? optionalPageable : new PageRequest(0, 20); + return converter.toRestPage(utils.getPage(actions, pageable), projection); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ConverterService.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ConverterService.java index 9e4f6c00f1..295634599b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ConverterService.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ConverterService.java @@ -32,6 +32,7 @@ import org.springframework.core.type.filter.AssignableTypeFilter; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.hateoas.Link; import org.springframework.hateoas.Resource; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; @@ -155,9 +156,30 @@ public class ConverterService { * @throws ClassCastException if the resource type is not compatible with the inferred return type. */ public T toResource(RestModel restObject) { + return toResource(restObject, new Link[] {}); + } + + /** + * Converts the given rest object to a {@link HALResource} object. + *

    + * If the rest object is a {@link RestAddressableModel}, the projection returned by + * {@link RestAddressableModel#getProjection()} will be used to determine which optional + * embeds and links will be added, and {@link Projection#transformResource(HALResource)} + * will be automatically called before returning the final, fully converted resource. + *

    + * In all cases, the {@link HalLinkService} will be used immediately after the resource is constructed, + * to ensure all {@link HalLinkFactory}s have had a chance to add links as needed. + *

    + * + * @param restObject the input rest object. + * @param oldLinks The old links fo the Resource Object + * @param the return type, a subclass of {@link HALResource}. + * @return the fully converted resource, with all automatic links and embeds applied. + */ + public T toResource(RestModel restObject, Link... oldLinks) { T halResource = getResource(restObject); if (restObject instanceof RestAddressableModel) { - utils.embedOrLinkClassLevelRels(halResource); + utils.embedOrLinkClassLevelRels(halResource, oldLinks); halLinkService.addLinks(halResource); Projection projection = ((RestAddressableModel) restObject).getProjection(); return projection.transformResource(halResource); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ResourcePolicyConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ResourcePolicyConverter.java index 1e7834d7ed..ab8694874c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ResourcePolicyConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ResourcePolicyConverter.java @@ -26,12 +26,6 @@ public class ResourcePolicyConverter implements DSpaceConverter { + + @Autowired + ConverterService converter; + + @Autowired + private ItemService itemService; + + @Override + public TemplateItemRest convert(TemplateItem templateItem, Projection projection) { + TemplateItemRest templateItemRest = new TemplateItemRest(); + templateItemRest.setProjection(projection); + if (templateItem.getID() != null) { + templateItemRest.setId(templateItem.getID()); + templateItemRest.setUuid(templateItem.getID()); + } + + templateItemRest.setLastModified(templateItem.getLastModified()); + Collection templateItemOf = templateItem.getTemplateItemOf(); + if (templateItemOf != null) { + templateItemRest.setTemplateItemOf(converter.toRest(templateItemOf, projection)); + } + + List fullList = + itemService.getMetadata(templateItem.getItem(), Item.ANY, Item.ANY, Item.ANY, Item.ANY, true); + MetadataValueList metadataValues = new MetadataValueList(fullList); + templateItemRest.setMetadata(converter.toRest(metadataValues, projection)); + + return templateItemRest; + } + + @Override + public Class getModelClass() { + return TemplateItem.class; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/WorkflowActionConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/WorkflowActionConverter.java new file mode 100644 index 0000000000..ee6479433e --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/WorkflowActionConverter.java @@ -0,0 +1,36 @@ +/** + * 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.app.rest.converter; + +import org.dspace.app.rest.model.WorkflowActionRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig; +import org.springframework.stereotype.Component; + +/** + * Converter to translate {@link WorkflowActionConfig} to a {@link WorkflowActionRest} object + * + * @author Maria Verdonck (Atmire) on 06/01/2020 + */ +@Component +public class WorkflowActionConverter implements DSpaceConverter { + + @Override + public WorkflowActionRest convert(WorkflowActionConfig modelObject, Projection projection) { + WorkflowActionRest restModel = new WorkflowActionRest(); + restModel.setProjection(projection); + restModel.setId(modelObject.getId()); + restModel.setOptions(modelObject.getOptions()); + return restModel; + } + + @Override + public Class getModelClass() { + return WorkflowActionConfig.class; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/WorkflowDefinitionConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/WorkflowDefinitionConverter.java new file mode 100644 index 0000000000..04af851e8b --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/WorkflowDefinitionConverter.java @@ -0,0 +1,50 @@ +/** + * 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.app.rest.converter; + +import java.util.stream.Collectors; + +import org.dspace.app.rest.model.WorkflowDefinitionRest; +import org.dspace.app.rest.model.WorkflowStepRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; +import org.dspace.xmlworkflow.state.Workflow; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Converter to translate Workflow to a Workflow Definition + * + * @author Maria Verdonck (Atmire) on 11/12/2019 + */ +@Component +public class WorkflowDefinitionConverter implements DSpaceConverter { + + @Autowired + protected XmlWorkflowFactory xmlWorkflowFactory; + + @Autowired + ConverterService converter; + + @Override + public WorkflowDefinitionRest convert(Workflow modelObject, Projection projection) { + WorkflowDefinitionRest restModel = new WorkflowDefinitionRest(); + restModel.setName(modelObject.getID()); + restModel.setIsDefault(xmlWorkflowFactory.isDefaultWorkflow(modelObject.getID())); + restModel.setProjection(projection); + restModel.setSteps(modelObject.getSteps().stream() + .map(x -> (WorkflowStepRest) converter.toRest(x, projection)) + .collect(Collectors.toList())); + return restModel; + } + + @Override + public Class getModelClass() { + return Workflow.class; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/WorkflowStepConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/WorkflowStepConverter.java new file mode 100644 index 0000000000..e1809f2cc4 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/WorkflowStepConverter.java @@ -0,0 +1,45 @@ +/** + * 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.app.rest.converter; + +import java.util.stream.Collectors; + +import org.dspace.app.rest.model.WorkflowActionRest; +import org.dspace.app.rest.model.WorkflowStepRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.xmlworkflow.state.Step; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Converter to translate {@link Step} to a {@link WorkflowStepRest} object + * + * @author Maria Verdonck (Atmire) on 10/01/2020 + */ +@Component +public class WorkflowStepConverter implements DSpaceConverter { + + @Autowired + ConverterService converter; + + @Override + public WorkflowStepRest convert(Step modelObject, Projection projection) { + WorkflowStepRest restModel = new WorkflowStepRest(); + restModel.setProjection(projection); + restModel.setId(modelObject.getId()); + restModel.setWorkflowactions(modelObject.getActions().stream() + .map(x -> (WorkflowActionRest) converter.toRest(x, projection)) + .collect(Collectors.toList())); + return restModel; + } + + @Override + public Class getModelClass() { + return Step.class; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java index a0b033d8c6..e0b3a86d18 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java @@ -92,34 +92,37 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH HttpStatus.UNPROCESSABLE_ENTITY.value()); } - @ExceptionHandler( {MissingParameterException.class, QueryMethodParameterConversionException.class}) + @ExceptionHandler(QueryMethodParameterConversionException.class) protected void ParameterConversionException(HttpServletRequest request, HttpServletResponse response, Exception ex) throws IOException { - - //422 is not defined in HttpServletResponse. Its meaning is "Unprocessable Entity". - //Using the value from HttpStatus. - //Since this is a handled exception case, the stack trace will not be returned. + // we want the 400 status for missing parameters, see https://jira.lyrasis.org/browse/DS-4428 sendErrorResponse(request, response, null, ex.getMessage(), - HttpStatus.UNPROCESSABLE_ENTITY.value()); + HttpStatus.BAD_REQUEST.value()); + } + + @ExceptionHandler(MissingParameterException.class) + protected void MissingParameterException(HttpServletRequest request, HttpServletResponse response, Exception ex) + throws IOException { + // we want the 400 status for missing parameters, see https://jira.lyrasis.org/browse/DS-4428 + sendErrorResponse(request, response, null, + ex.getMessage(), + HttpStatus.BAD_REQUEST.value()); } @Override protected ResponseEntity handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { - // we want the 422 status for missing parameter as it seems to be the common behavior for REST application, see - // https://stackoverflow.com/questions/3050518/what-http-status-response-code-should-i-use-if-the-request-is-missing-a-required - return super.handleMissingServletRequestParameter(ex, headers, HttpStatus.UNPROCESSABLE_ENTITY, request); + // we want the 400 status for missing parameters, see https://jira.lyrasis.org/browse/DS-4428 + return super.handleMissingServletRequestParameter(ex, headers, HttpStatus.BAD_REQUEST, request); } @Override protected ResponseEntity handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { - // we want the 422 status for type mismatch on parameters as it seems to be the common behavior for REST - // application, see - // https://stackoverflow.com/questions/3050518/what-http-status-response-code-should-i-use-if-the-request-is-missing-a-required - return super.handleTypeMismatch(ex, headers, HttpStatus.UNPROCESSABLE_ENTITY, request); + // we want the 400 status for missing parameters, see https://jira.lyrasis.org/browse/DS-4428 + return super.handleTypeMismatch(ex, headers, HttpStatus.BAD_REQUEST, request); } @ExceptionHandler(Exception.class) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/process/ProcessResourceHalLinkFactory.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/process/ProcessResourceHalLinkFactory.java index 4bbf77ff97..8325080861 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/process/ProcessResourceHalLinkFactory.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/process/ProcessResourceHalLinkFactory.java @@ -28,7 +28,7 @@ public class ProcessResourceHalLinkFactory extends HalLinkFactory list) throws Exception { - String dspaceRestUrl = configurationService.getProperty("dspace.restUrl"); + String dspaceRestUrl = configurationService.getProperty("dspace.server.url"); list.add( buildLink("script", dspaceRestUrl + "/api/system/scripts/" + halResource.getContent().getScriptName())); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CollectionRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CollectionRest.java index b577de7327..ac248f435b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CollectionRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CollectionRest.java @@ -26,6 +26,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; @LinkRest( name = CollectionRest.MAPPED_ITEMS, method = "getMappedItems" + ), + @LinkRest( + name = CollectionRest.PARENT_COMMUNITY, + method = "getParentCommunity" ) }) public class CollectionRest extends DSpaceObjectRest { @@ -37,6 +41,7 @@ public class CollectionRest extends DSpaceObjectRest { public static final String LICENSE = "license"; public static final String LOGO = "logo"; public static final String MAPPED_ITEMS = "mappedItems"; + public static final String PARENT_COMMUNITY = "parentCommunity"; @Override public String getCategory() { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CommunityRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CommunityRest.java index c5097662c7..ee80c9633b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CommunityRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CommunityRest.java @@ -26,6 +26,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; @LinkRest( name = CommunityRest.SUBCOMMUNITIES, method = "getSubcommunities" + ), + @LinkRest( + name = CommunityRest.PARENT_COMMUNITY, + method = "getParentCommunity" ) }) public class CommunityRest extends DSpaceObjectRest { @@ -36,6 +40,8 @@ public class CommunityRest extends DSpaceObjectRest { public static final String COLLECTIONS = "collections"; public static final String LOGO = "logo"; public static final String SUBCOMMUNITIES = "subcommunities"; + public static final String PARENT_COMMUNITY = "parentCommunity"; + @Override public String getCategory() { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataRest.java index 23474e793e..d1367c8fea 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataRest.java @@ -14,6 +14,7 @@ import java.util.TreeMap; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; +import org.apache.commons.lang3.builder.HashCodeBuilder; /** * Rest representation of a map of metadata keys to ordered lists of values. @@ -66,4 +67,11 @@ public class MetadataRest { public boolean equals(Object object) { return object instanceof MetadataRest && ((MetadataRest) object).getMap().equals(map); } -} \ No newline at end of file + + @Override + public int hashCode() { + return new HashCodeBuilder(7, 37) + .append(this.getMap()) + .toHashCode(); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResourcePolicyRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResourcePolicyRest.java index 606bd8c273..656d9049fa 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResourcePolicyRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResourcePolicyRest.java @@ -8,7 +8,6 @@ package org.dspace.app.rest.model; import java.util.Date; -import java.util.UUID; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; @@ -34,12 +33,6 @@ public class ResourcePolicyRest extends BaseObjectRest { private String description; - @JsonInclude(Include.NON_NULL) - private UUID groupUUID; - - @JsonInclude(Include.NON_NULL) - private UUID epersonUUID; - @JsonIgnore private EPersonRest eperson; @@ -55,14 +48,6 @@ public class ResourcePolicyRest extends BaseObjectRest { private Date endDate; - public UUID getGroupUUID() { - return groupUUID; - } - - public void setGroupUUID(UUID groupUuid) { - this.groupUUID = groupUuid; - } - public Date getEndDate() { return endDate; } @@ -111,14 +96,6 @@ public class ResourcePolicyRest extends BaseObjectRest { this.description = description; } - public UUID getEpersonUUID() { - return epersonUUID; - } - - public void setEpersonUUID(UUID epersonUUID) { - this.epersonUUID = epersonUUID; - } - public EPersonRest getEperson() { return eperson; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionVisibilityRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionVisibilityRest.java index 40f71a97f4..d7a35b7760 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionVisibilityRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionVisibilityRest.java @@ -10,6 +10,8 @@ package org.dspace.app.rest.model; import java.util.Objects; +import org.apache.commons.lang3.builder.HashCodeBuilder; + /** * The SubmissionVisibility REST Resource. It is not addressable directly, only * used as inline object in the SubmissionPanel resource and SubmissionForm's fields @@ -49,4 +51,12 @@ public class SubmissionVisibilityRest { } return super.equals(obj); } + + @Override + public int hashCode() { + return new HashCodeBuilder(5, 31) + .append(this.getMain()) + .append(this.getOther()) + .toHashCode(); + } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/TemplateItemRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/TemplateItemRest.java new file mode 100644 index 0000000000..cc6b11d12a --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/TemplateItemRest.java @@ -0,0 +1,83 @@ +/** + * 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.app.rest.model; + +import java.util.Date; +import java.util.UUID; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.dspace.app.rest.RestResourceController; + +/** + * The TemplateItem REST Resource + */ +public class TemplateItemRest extends BaseObjectRest { + private UUID uuid; + + public static final String NAME = "itemtemplate"; + public static final String CATEGORY = RestAddressableModel.CORE; + @JsonIgnore + private CollectionRest templateItemOf; + MetadataRest metadata = new MetadataRest(); + private Date lastModified = new Date(); + + public Date getLastModified() { + return lastModified; + } + + public void setLastModified(Date lastModified) { + this.lastModified = lastModified; + } + + public CollectionRest getTemplateItemOf() { + return templateItemOf; + } + + public void setTemplateItemOf(CollectionRest templateItemOf) { + this.templateItemOf = templateItemOf; + } + + public void setMetadata(MetadataRest metadata) { + this.metadata = metadata; + } + + public MetadataRest getMetadata() { + return this.metadata; + } + + @Override + public String getCategory() { + return CATEGORY; + } + + @Override + public Class getController() { + return RestResourceController.class; + } + + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public String getType() { + return NAME; + } + + @Override + public UUID getId() { + return uuid; + } + + public UUID getUuid() { + return uuid; + } + + public void setUuid(UUID uuid) { + this.uuid = uuid; + } + +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/UploadBitstreamAccessConditionDTO.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/UploadBitstreamAccessConditionDTO.java new file mode 100644 index 0000000000..120cb6a9d2 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/UploadBitstreamAccessConditionDTO.java @@ -0,0 +1,101 @@ +/** + * 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.app.rest.model; + +import java.util.Date; +import java.util.UUID; + +import org.dspace.app.rest.model.step.UploadBitstreamRest; + +/** + * The UploadAccessConditionDTO is a partial representation of the DSpace + * {@link ResourcePolicyRest} as used in the patch payload for the upload + * submission section (see {@link UploadBitstreamRest}. The main reason for this + * class is to have a DTO to use serialize/deserialize the REST model, that + * include reference to the GroupRest and EPersonRest object, in the upload + * section data in a simpler way where such reference are just UUID. Indeed, due + * to the fact that the RestModel class are serialized according to the HAL + * format and the reference are only exposed in the _links section of the + * RestResource it was not possible to use the {@link ResourcePolicyRest} class + * directly in the upload section + * + * @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it) + */ +public class UploadBitstreamAccessConditionDTO { + + private Integer id; + + private UUID groupUUID; + + private UUID epersonUUID; + + private String name; + + private String description; + + private Date startDate; + + private Date endDate; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public UUID getGroupUUID() { + return groupUUID; + } + + public void setGroupUUID(UUID groupUUID) { + this.groupUUID = groupUUID; + } + + public UUID getEpersonUUID() { + return epersonUUID; + } + + public void setEpersonUUID(UUID epersonUUID) { + this.epersonUUID = epersonUUID; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Date getStartDate() { + return startDate; + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + public Date getEndDate() { + return endDate; + } + + public void setEndDate(Date endDate) { + this.endDate = endDate; + } + +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowActionRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowActionRest.java new file mode 100644 index 0000000000..e998df6bc2 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowActionRest.java @@ -0,0 +1,59 @@ +/** + * 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.app.rest.model; + +import java.util.List; + +import org.dspace.app.rest.RestResourceController; + +/** + * The rest resource used for workflow actions + * + * @author Maria Verdonck (Atmire) on 06/01/2020 + */ +public class WorkflowActionRest extends BaseObjectRest { + + public static final String CATEGORY = "config"; + public static final String NAME = "workflowaction"; + public static final String NAME_PLURAL = "workflowactions"; + + private List options; + + @Override + public String getCategory() { + return CATEGORY; + } + + @Override + public Class getController() { + return RestResourceController.class; + } + + @Override + public String getType() { + return NAME; + } + + /** + * Generic getter for the options + * + * @return the options value of this WorkflowActionRest + */ + public List getOptions() { + return options; + } + + /** + * Generic setter for the options + * + * @param options The options to be set on this WorkflowActionRest + */ + public void setOptions(List options) { + this.options = options; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowDefinitionRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowDefinitionRest.java new file mode 100644 index 0000000000..7c2de7071b --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowDefinitionRest.java @@ -0,0 +1,88 @@ +/** + * 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.app.rest.model; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.dspace.app.rest.RestResourceController; + +/** + * The rest resource used for workflow definitions + * + * @author Maria Verdonck (Atmire) on 11/12/2019 + */ +@LinksRest(links = { + @LinkRest( + name = WorkflowDefinitionRest.COLLECTIONS_MAPPED_TO, + method = "getCollections" + ), + @LinkRest( + name = WorkflowDefinitionRest.STEPS, + method = "getSteps" + ) +}) +public class WorkflowDefinitionRest extends BaseObjectRest { + + public static final String CATEGORY = "config"; + public static final String NAME = "workflowdefinition"; + public static final String NAME_PLURAL = "workflowdefinitions"; + + public static final String COLLECTIONS_MAPPED_TO = "collections"; + public static final String STEPS = "steps"; + + private String name; + private boolean isDefault; + private List steps; + + @Override + public String getCategory() { + return CATEGORY; + } + + @Override + public Class getController() { + return RestResourceController.class; + } + + @Override + public String getType() { + return NAME; + } + + @Override + @JsonIgnore + public String getId() { + return name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean getIsDefault() { + return isDefault; + } + + public void setIsDefault(boolean isDefault) { + this.isDefault = isDefault; + } + + @JsonIgnore + public List getSteps() { + return steps; + } + + public void setSteps(List steps) { + this.steps = steps; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowStepRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowStepRest.java new file mode 100644 index 0000000000..648cffbca8 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowStepRest.java @@ -0,0 +1,59 @@ +/** + * 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.app.rest.model; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.dspace.app.rest.RestResourceController; + +/** + * The rest resource used for workflow steps + * + * @author Maria Verdonck (Atmire) on 10/01/2020 + */ +@LinksRest(links = { + @LinkRest( + name = WorkflowStepRest.ACTIONS, + method = "getActions" + ), +}) +public class WorkflowStepRest extends BaseObjectRest { + + public static final String CATEGORY = "config"; + public static final String NAME = "workflowstep"; + public static final String NAME_PLURAL = "workflowsteps"; + + public static final String ACTIONS = "workflowactions"; + + private List workflowactions; + + @Override + public String getCategory() { + return CATEGORY; + } + + @Override + public Class getController() { + return RestResourceController.class; + } + + @Override + public String getType() { + return NAME; + } + + @JsonIgnore + public List getWorkflowactions() { + return workflowactions; + } + + public void setWorkflowactions(List actions) { + this.workflowactions = actions; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/TemplateItemResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/TemplateItemResource.java new file mode 100644 index 0000000000..d9a5db4f0b --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/TemplateItemResource.java @@ -0,0 +1,25 @@ +/** + * 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.app.rest.model.hateoas; + +import org.dspace.app.rest.model.TemplateItemRest; +import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource; +import org.dspace.app.rest.utils.Utils; + +/** + * TemplateItem Rest HAL Resource. The HAL Resource wraps the REST Resource + * adding support for the links and embedded resources + * + */ +@RelNameDSpaceResource(TemplateItemRest.NAME) +public class TemplateItemResource extends DSpaceResource { + + public TemplateItemResource(TemplateItemRest data, Utils utils) { + super(data, utils); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/WorkflowActionResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/WorkflowActionResource.java new file mode 100644 index 0000000000..373c6a35ac --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/WorkflowActionResource.java @@ -0,0 +1,25 @@ +/** + * 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.app.rest.model.hateoas; + +import org.dspace.app.rest.model.WorkflowActionRest; +import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource; +import org.dspace.app.rest.utils.Utils; + +/** + * {@link WorkflowActionRest} HAL Resource. The HAL Resource wraps the REST Resource + * adding support for the links and embedded resources + * + * @author Maria Verdonck (Atmire) on 06/01/2020 + */ +@RelNameDSpaceResource(WorkflowActionRest.NAME) +public class WorkflowActionResource extends DSpaceResource { + public WorkflowActionResource(WorkflowActionRest data, Utils utils) { + super(data, utils); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/WorkflowDefinitionResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/WorkflowDefinitionResource.java new file mode 100644 index 0000000000..9247749e9d --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/WorkflowDefinitionResource.java @@ -0,0 +1,24 @@ +/** + * 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.app.rest.model.hateoas; + +import org.dspace.app.rest.model.WorkflowDefinitionRest; +import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource; +import org.dspace.app.rest.utils.Utils; + +/** + * WorkflowDefinition Rest HAL Resource. The HAL Resource wraps the REST Resource + * adding support for the links and embedded resources + * @author Maria Verdonck (Atmire) on 11/12/2019 + */ +@RelNameDSpaceResource(WorkflowDefinitionRest.NAME) +public class WorkflowDefinitionResource extends DSpaceResource { + public WorkflowDefinitionResource(WorkflowDefinitionRest data, Utils utils) { + super(data, utils); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/WorkflowStepResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/WorkflowStepResource.java new file mode 100644 index 0000000000..6128c984a0 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/WorkflowStepResource.java @@ -0,0 +1,25 @@ +/** + * 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.app.rest.model.hateoas; + +import org.dspace.app.rest.model.WorkflowStepRest; +import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource; +import org.dspace.app.rest.utils.Utils; + +/** + * {@link WorkflowStepRest} HAL Resource. The HAL Resource wraps the REST Resource + * adding support for the links and embedded resources + * + * @author Maria Verdonck (Atmire) on 10/01/2020 + */ +@RelNameDSpaceResource(WorkflowStepRest.NAME) +public class WorkflowStepResource extends DSpaceResource { + public WorkflowStepResource(WorkflowStepRest data, Utils utils) { + super(data, utils); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/UploadBitstreamRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/UploadBitstreamRest.java index 80e36fdb59..7024e5cdb1 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/UploadBitstreamRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/UploadBitstreamRest.java @@ -8,6 +8,7 @@ package org.dspace.app.rest.model.step; import java.util.ArrayList; + import java.util.HashMap; import java.util.List; import java.util.Map; @@ -16,13 +17,19 @@ import java.util.UUID; import org.dspace.app.rest.model.BitstreamFormatRest; import org.dspace.app.rest.model.CheckSumRest; import org.dspace.app.rest.model.MetadataValueRest; -import org.dspace.app.rest.model.ResourcePolicyRest; +import org.dspace.app.rest.model.UploadBitstreamAccessConditionDTO; +/** + * This Java Bean is used to represent a single bitstream with all its metadata + * and access conditions ({@link UploadBitstreamAccessConditionDTO}) inside an + * upload submission section ({@link DataUpload} + * + */ public class UploadBitstreamRest extends UploadStatusResponse { private UUID uuid; private Map> metadata = new HashMap<>(); - private List accessConditions; + private List accessConditions; private BitstreamFormatRest format; private Long sizeBytes; private CheckSumRest checkSum; @@ -68,14 +75,14 @@ public class UploadBitstreamRest extends UploadStatusResponse { this.metadata = metadata; } - public List getAccessConditions() { + public List getAccessConditions() { if (accessConditions == null) { accessConditions = new ArrayList<>(); } return accessConditions; } - public void setAccessConditions(List accessConditions) { + public void setAccessConditions(List accessConditions) { this.accessConditions = accessConditions; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/TemplateItem.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/TemplateItem.java new file mode 100644 index 0000000000..ca0b1f1353 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/TemplateItem.java @@ -0,0 +1,52 @@ +/** + * 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.app.rest.model.wrapper; + +import java.util.Date; +import java.util.List; +import java.util.UUID; + +import org.dspace.content.Collection; +import org.dspace.content.Item; +import org.dspace.content.MetadataValue; + +/** + * This class represents a Collection's Item Template. It acts as a wrapper for an {@link Item} object to differentiate + * between actual Items and TemplateItems + */ +public class TemplateItem { + private Item item; + + public TemplateItem(Item item) { + if (item.getTemplateItemOf() == null) { + throw new IllegalArgumentException("Cannot create a TemplateItem from an item that isn't a template item"); + } + + this.item = item; + } + + public Item getItem() { + return this.item; + } + + public List getMetadata() { + return item.getMetadata(); + } + + public UUID getID() { + return item.getID(); + } + + public Date getLastModified() { + return item.getLastModified(); + } + + public Collection getTemplateItemOf() { + return item.getTemplateItemOf(); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/AbstractProjection.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/AbstractProjection.java index 12a492191d..bbf38fd97f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/AbstractProjection.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/AbstractProjection.java @@ -8,8 +8,10 @@ package org.dspace.app.rest.projection; import org.dspace.app.rest.model.LinkRest; +import org.dspace.app.rest.model.RestAddressableModel; import org.dspace.app.rest.model.RestModel; import org.dspace.app.rest.model.hateoas.HALResource; +import org.springframework.hateoas.Link; /** * Abstract base class for projections. @@ -34,7 +36,8 @@ public abstract class AbstractProjection implements Projection { } @Override - public boolean allowEmbedding(HALResource halResource, LinkRest linkRest) { + public boolean allowEmbedding(HALResource halResource, LinkRest linkRest, + Link... oldLinks) { return false; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/CompositeProjection.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/CompositeProjection.java new file mode 100644 index 0000000000..d9a28cec04 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/CompositeProjection.java @@ -0,0 +1,84 @@ +/** + * 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.app.rest.projection; + +import java.util.List; + +import org.dspace.app.rest.model.LinkRest; +import org.dspace.app.rest.model.RestAddressableModel; +import org.dspace.app.rest.model.RestModel; +import org.dspace.app.rest.model.hateoas.HALResource; +import org.springframework.hateoas.Link; + +/** + * A projection that combines the behavior of multiple projections. + * + * Model, rest, and resource transformations will be performed in the order of the projections given in + * the constructor. Embedding will be allowed if any of the given projections allow them. Linking will + * be allowed if all of the given projections allow them. + */ +public class CompositeProjection implements Projection { + + public final static String NAME = "composite"; + + private final List projections; + + public CompositeProjection(List projections) { + this.projections = projections; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public T transformModel(T modelObject) { + for (Projection projection : projections) { + modelObject = projection.transformModel(modelObject); + } + return modelObject; + } + + @Override + public T transformRest(T restObject) { + for (Projection projection : projections) { + restObject = projection.transformRest(restObject); + } + return restObject; + } + + @Override + public T transformResource(T halResource) { + for (Projection projection : projections) { + halResource = projection.transformResource(halResource); + } + return halResource; + } + + @Override + public boolean allowEmbedding(HALResource halResource, LinkRest linkRest, + Link... oldLinks) { + for (Projection projection : projections) { + if (projection.allowEmbedding(halResource, linkRest, oldLinks)) { + return true; + } + } + return false; + } + + @Override + public boolean allowLinking(HALResource halResource, LinkRest linkRest) { + for (Projection projection : projections) { + if (!projection.allowLinking(halResource, linkRest)) { + return false; + } + } + return true; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/EmbedRelsProjection.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/EmbedRelsProjection.java new file mode 100644 index 0000000000..1db0c6a74d --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/EmbedRelsProjection.java @@ -0,0 +1,65 @@ +/** + * 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.app.rest.projection; + +import java.util.Set; + +import org.dspace.app.rest.model.LinkRest; +import org.dspace.app.rest.model.RestAddressableModel; +import org.dspace.app.rest.model.hateoas.HALResource; +import org.springframework.hateoas.Link; + +/** + * Projection that allows a given set of rels to be embedded. + * A Rel refers to a Link Relation, this is an Embedded Object of the HalResource and the HalResource contains + * a link to this + */ +public class EmbedRelsProjection extends AbstractProjection { + + public final static String NAME = "embedrels"; + + private final Set embedRels; + + public EmbedRelsProjection(Set embedRels) { + this.embedRels = embedRels; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean allowEmbedding(HALResource halResource, LinkRest linkRest, + Link... oldLinks) { + // If level 0, and the name is present, the link can be embedded (e.g. the logo on a collection page) + if (halResource.getContent().getEmbedLevel() == 0 && embedRels.contains(linkRest.name())) { + return true; + } + + StringBuilder fullName = new StringBuilder(); + for (Link oldLink : oldLinks) { + fullName.append(oldLink.getRel()).append("/"); + } + fullName.append(linkRest.name()); + // If the full name matches, the link can be embedded (e.g. mappedItems/owningCollection on a collection page) + if (embedRels.contains(fullName.toString())) { + return true; + } + + fullName.append("/"); + // If the full name starts with the allowed embed, but the embed goes deeper, the link can be embedded + // (e.g. making sure mappedItems/owningCollection also embeds mappedItems on a collection page) + for (String embedRel : embedRels) { + if (embedRel.startsWith(fullName.toString())) { + return true; + } + } + return false; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/FullProjection.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/FullProjection.java index 9c5a4a9f9e..99719c8ac3 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/FullProjection.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/FullProjection.java @@ -8,7 +8,10 @@ package org.dspace.app.rest.projection; import org.dspace.app.rest.model.LinkRest; +import org.dspace.app.rest.model.RestAddressableModel; import org.dspace.app.rest.model.hateoas.HALResource; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.springframework.hateoas.Link; import org.springframework.stereotype.Component; /** @@ -18,14 +21,17 @@ import org.springframework.stereotype.Component; public class FullProjection extends AbstractProjection { public final static String NAME = "full"; + private final int maxEmbed = DSpaceServicesFactory.getInstance().getConfigurationService() + .getIntProperty("rest.projections.full.max", 2); public String getName() { return NAME; } @Override - public boolean allowEmbedding(HALResource halResource, LinkRest linkRest) { - return true; + public boolean allowEmbedding(HALResource halResource, LinkRest linkRest, + Link... oldLinks) { + return halResource.getContent().getEmbedLevel() < maxEmbed; } @Override diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/Projection.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/Projection.java index d9e0b10261..cb16093464 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/Projection.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/Projection.java @@ -10,9 +10,12 @@ package org.dspace.app.rest.projection; import javax.persistence.Entity; import org.dspace.app.rest.model.LinkRest; +import org.dspace.app.rest.model.RestAddressableModel; import org.dspace.app.rest.model.RestModel; import org.dspace.app.rest.model.hateoas.HALResource; import org.dspace.app.rest.repository.DSpaceRestRepository; +import org.dspace.app.rest.utils.Utils; +import org.springframework.hateoas.Link; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RestController; @@ -44,7 +47,7 @@ import org.springframework.web.bind.annotation.RestController; *
  • After it is converted to a {@link RestModel}, the projection may modify it * via {@link #transformRest(RestModel)}.
  • *
  • During conversion to a {@link HALResource}, the projection may opt in to certain annotation-discovered - * HAL embeds and links via {@link #allowEmbedding(HALResource, LinkRest)} + * HAL embeds and links via {@link #allowEmbedding(HALResource, LinkRest, Link...)} * and {@link #allowLinking(HALResource, LinkRest)}
  • *
  • After conversion to a {@link HALResource}, the projection may modify it * via {@link #transformResource(HALResource)}.
  • @@ -52,8 +55,7 @@ import org.springframework.web.bind.annotation.RestController; * *

    How a projection is chosen

    * - * When a REST request is made, the projection argument, if present, is used to look up the projection to use, - * by name. If no argument is present, {@link DefaultProjection} will be used. + * See {@link Utils#obtainProjection()}. */ public interface Projection { @@ -115,16 +117,18 @@ public interface Projection { * * @param halResource the resource from which the embed may or may not be made. * @param linkRest the LinkRest annotation through which the related resource was discovered on the rest object. + * @param oldLinks The previously traversed links * @return true if allowed, false otherwise. */ - boolean allowEmbedding(HALResource halResource, LinkRest linkRest); + boolean allowEmbedding(HALResource halResource, LinkRest linkRest, + Link... oldLinks); /** * Tells whether this projection permits the linking of a particular linkable subresource. * * This gives the projection an opportunity to opt in to to certain links, by returning {@code true}. * - * Note: If {@link #allowEmbedding(HALResource, LinkRest)} returns {@code true} for a given subresource, + * Note: If {@link #allowEmbedding(HALResource, LinkRest, Link...)} returns {@code true} for a given subresource, * it will be automatically linked regardless of what this method returns. * * @param halResource the resource from which the link may or may not be made. diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/SpecificLevelProjection.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/SpecificLevelProjection.java new file mode 100644 index 0000000000..7ef603b23c --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/projection/SpecificLevelProjection.java @@ -0,0 +1,69 @@ +/** + * 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.app.rest.projection; + +import org.apache.commons.lang3.StringUtils; +import org.dspace.app.rest.exception.MissingParameterException; +import org.dspace.app.rest.model.LinkRest; +import org.dspace.app.rest.model.RestAddressableModel; +import org.dspace.app.rest.model.hateoas.HALResource; +import org.dspace.services.RequestService; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.Link; + +/** + * This Projection will allow us to specify how many levels deep we're going to embed resources onto the requested + * HalResource. + * The projection is used by using the name combined with the embedLevelDepth parameter to specify how deep the embeds + * have to go. There is an upperlimit in place for this, which is specified on the bean through the maxEmbed property + */ +public class SpecificLevelProjection extends AbstractProjection { + + @Autowired + private RequestService requestService; + + public final static String NAME = "level"; + + private int maxEmbed = DSpaceServicesFactory.getInstance().getConfigurationService() + .getIntProperty("rest.projections.full.max", 2); + + public int getMaxEmbed() { + return maxEmbed; + } + + public void setMaxEmbed(int maxEmbed) { + this.maxEmbed = maxEmbed; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean allowEmbedding(HALResource halResource, LinkRest linkRest, + Link... oldLinks) { + String embedLevelDepthString = requestService.getCurrentRequest().getHttpServletRequest() + .getParameter("embedLevelDepth"); + if (StringUtils.isBlank(embedLevelDepthString)) { + throw new MissingParameterException("The embedLevelDepth parameter needs to be specified" + + " for this Projection"); + } + Integer embedLevelDepth = Integer.parseInt(embedLevelDepthString); + if (embedLevelDepth > maxEmbed) { + throw new IllegalArgumentException("The embedLevelDepth may not exceed the configured max: " + maxEmbed); + } + return halResource.getContent().getEmbedLevel() < embedLevelDepth; + } + + @Override + public boolean allowLinking(HALResource halResource, LinkRest linkRest) { + return true; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BitstreamFormatRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BitstreamFormatRestRepository.java index 4c68a02bed..7cea8ee7fa 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BitstreamFormatRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BitstreamFormatRestRepository.java @@ -18,7 +18,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.BitstreamFormatRest; -import org.dspace.app.rest.projection.Projection; import org.dspace.authorize.AuthorizeException; import org.dspace.content.BitstreamFormat; import org.dspace.content.service.BitstreamFormatService; @@ -89,7 +88,7 @@ public class BitstreamFormatRestRepository extends DSpaceRestRepository() { }); + super(dsoService); this.bs = dsoService; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BundleRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BundleRestRepository.java index 7d8e7f56ca..d26ceeb2bf 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BundleRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BundleRestRepository.java @@ -23,8 +23,6 @@ import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.BitstreamRest; import org.dspace.app.rest.model.BundleRest; import org.dspace.app.rest.model.patch.Patch; -import org.dspace.app.rest.projection.Projection; -import org.dspace.app.rest.repository.patch.BundlePatch; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.Bitstream; @@ -71,8 +69,8 @@ public class BundleRestRepository extends DSpaceObjectRestRepository() {}); + super(dsoService); } @Override @@ -135,13 +134,13 @@ public class CollectionRestRepository extends DSpaceObjectRestRepository findAuthorizedByCommunity( - @Parameter(value = "uuid", required = true) UUID communityUuid, Pageable pageable) { + @Parameter(value = "uuid", required = true) UUID communityUuid, Pageable pageable) { try { Context context = obtainContext(); Community com = communityService.find(context, communityUuid); if (com == null) { throw new ResourceNotFoundException( - CommunityRest.CATEGORY + "." + CommunityRest.NAME + " with id: " + communityUuid + CommunityRest.CATEGORY + "." + CommunityRest.NAME + " with id: " + communityUuid + " not found"); } List collections = cs.findAuthorized(context, com, Constants.ADD); @@ -211,14 +210,14 @@ public class CollectionRestRepository extends DSpaceObjectRestRepository() {}); + super(dsoService); this.cs = dsoService; } @@ -104,7 +102,7 @@ public class CommunityRestRepository extends DSpaceObjectRestRepository { final DSpaceObjectService dsoService; - final DSpaceObjectPatch dsoPatch; + @Autowired + ResourcePatch resourcePatch; @Autowired MetadataConverter metadataConverter; - DSpaceObjectRestRepository(DSpaceObjectService dsoService, - DSpaceObjectPatch dsoPatch) { + DSpaceObjectRestRepository(DSpaceObjectService dsoService) { this.dsoService = dsoService; - this.dsoPatch = dsoPatch; } /** @@ -63,23 +61,7 @@ public abstract class DSpaceObjectRestRepository new ResourceNotFoundException( "Couldn't find an ExternalSource for source: " + externalSourceName + " and ID: " + entryId)); - return converter.toRest(dataObject, Projection.DEFAULT); + return converter.toRest(dataObject, utils.obtainProjection()); } /** @@ -84,7 +83,7 @@ public class ExternalSourceRestRepository extends DSpaceRestRepository() {}); + super(dsoService); this.gs = dsoService; } @@ -75,7 +73,7 @@ public class GroupRestRepository extends DSpaceObjectRestRepository> configs = OAIHarvester.getAvailableMetadataFormats(); return harvestedCollectionConverter.fromModel(harvestedCollection, collection, configs, - Projection.DEFAULT); + utils.obtainProjection()); } else { throw new UnprocessableEntityException( "Incorrect harvest settings in request. The following errors were found: " + errors.toString() diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemRestRepository.java index 06071ed645..c6643496ae 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemRestRepository.java @@ -21,7 +21,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; -import org.dspace.app.rest.converter.JsonPatchConverter; import org.dspace.app.rest.converter.MetadataConverter; import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException; @@ -29,9 +28,7 @@ import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.BundleRest; import org.dspace.app.rest.model.ItemRest; import org.dspace.app.rest.model.patch.Patch; -import org.dspace.app.rest.projection.Projection; import org.dspace.app.rest.repository.handler.service.UriListHandlerService; -import org.dspace.app.rest.repository.patch.ItemPatch; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bundle; import org.dspace.content.Collection; @@ -97,9 +94,8 @@ public class ItemRestRepository extends DSpaceObjectRestRepository it = itemService.findAll(context, pageable.getPageSize(), - Math.toIntExact(pageable.getOffset())); + Math.toIntExact(pageable.getOffset())); List items = new ArrayList<>(); while (it.hasNext()) { items.add(it.next()); @@ -141,32 +140,6 @@ public class ItemRestRepository extends DSpaceObjectRestRepository getDomainClass() { return ItemRest.class; @@ -184,15 +157,15 @@ public class ItemRestRepository extends DSpaceObjectRestRepository parseVirtualMetadataTypes(String[] copyVirtual) { List types = new ArrayList<>(); - for (String typeString: copyVirtual) { + for (String typeString : copyVirtual) { if (!StringUtils.isNumeric(typeString)) { throw new DSpaceBadRequestException("parameter " + REQUESTPARAMETER_COPYVIRTUALMETADATA + " should only contain a single value '" + COPYVIRTUAL_ALL[0] + "', '" + COPYVIRTUAL_CONFIGURED[0] @@ -267,8 +240,9 @@ public class ItemRestRepository extends DSpaceObjectRestRepository 0) { throw new DSpaceBadRequestException("The bundle name already exists in the item"); } @@ -374,51 +348,12 @@ public class ItemRestRepository extends DSpaceObjectRestRepository stringList) throws AuthorizeException, SQLException, RepositoryMethodNotImplementedException { HttpServletRequest req = getRequestService().getCurrentRequest().getHttpServletRequest(); Item item = uriListHandlerService.handle(context, req, stringList, Item.class); - return converter.toRest(item, Projection.DEFAULT); + return converter.toRest(item, utils.obtainProjection()); } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataFieldRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataFieldRestRepository.java index 2135f141b5..1ca8ecc1dd 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataFieldRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataFieldRestRepository.java @@ -24,7 +24,6 @@ import org.dspace.app.rest.SearchRestMethod; import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.MetadataFieldRest; -import org.dspace.app.rest.projection.Projection; import org.dspace.authorize.AuthorizeException; import org.dspace.content.MetadataField; import org.dspace.content.MetadataSchema; @@ -147,7 +146,7 @@ public class MetadataFieldRestRepository extends DSpaceRestRepository resourcePatch; @Override @PreAuthorize("hasPermission(#id, 'resourcepolicy', 'READ')") @@ -103,21 +98,18 @@ public class ResourcePolicyRestRepository extends DSpaceRestRepository findByResource(@Parameter(value = "uuid", required = true) UUID resourceUuid, - @Parameter(value = "action", required = false) String action, Pageable pageable) { - + @Parameter(value = "action", required = false) String action, + Pageable pageable) { List resourcePolisies = null; int total = 0; try { @@ -125,13 +117,13 @@ public class ResourcePolicyRestRepository extends DSpaceRestRepository findByEPerson(@Parameter(value = "uuid", required = true) UUID epersonUuid, - @Parameter(value = "resource", required = false) UUID resourceUuid, Pageable pageable) { - + @Parameter(value = "resource", required = false) UUID resourceUuid, + Pageable pageable) { List resourcePolisies = null; int total = 0; try { @@ -168,14 +156,14 @@ public class ResourcePolicyRestRepository extends DSpaceRestRepository findByGroup(@Parameter(value = "uuid", required = true) UUID groupUuid, - @Parameter(value = "resource", required = false) UUID resourceUuid, Pageable pageable) { - + @Parameter(value = "resource", required = false) UUID resourceUuid, + Pageable pageable) { List resourcePolisies = null; int total = 0; try { @@ -215,13 +199,13 @@ public class ResourcePolicyRestRepository extends DSpaceRestRepository() {}); + super(dsoService); this.sitesv = dsoService; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/TemplateItemRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/TemplateItemRestRepository.java new file mode 100644 index 0000000000..2e1436d4fb --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/TemplateItemRestRepository.java @@ -0,0 +1,120 @@ +/** + * 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.app.rest.repository; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.UUID; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.dspace.app.rest.converter.JsonPatchConverter; +import org.dspace.app.rest.model.TemplateItemRest; +import org.dspace.app.rest.model.patch.Patch; +import org.dspace.app.rest.model.wrapper.TemplateItem; +import org.dspace.app.rest.repository.patch.ResourcePatch; +import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Collection; +import org.dspace.content.Item; +import org.dspace.content.service.CollectionService; +import org.dspace.content.service.ItemService; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.stereotype.Component; + +/** + * This is the repository class that is responsible for handling {@link TemplateItemRest} objects + */ +@Component(TemplateItemRest.CATEGORY + "." + TemplateItemRest.NAME) +public class TemplateItemRestRepository extends DSpaceRestRepository { + + @Autowired + private ItemService itemService; + + @Autowired + private ItemRestRepository itemRestRepository; + + @Autowired + private CollectionService collectionService; + + @Autowired + ResourcePatch resourcePatch; + + @Override + public TemplateItemRest findOne(Context context, UUID uuid) { + Item item = null; + try { + item = itemService.find(context, uuid); + } catch (SQLException e) { + throw new RuntimeException(e.getMessage(), e); + } + if (item == null) { + return null; + } + + try { + return converter.toRest(new TemplateItem(item), utils.obtainProjection()); + } catch (IllegalArgumentException e) { + throw new ResourceNotFoundException("The item with id " + item.getID() + " is not a template item"); + } + } + + @Override + public Page findAll(Context context, Pageable pageable) { + return null; + } + + @Override + public Class getDomainClass() { + return TemplateItemRest.class; + } + + /** + * Modify a template Item which is a template Item + * + * @param templateItem The Item to be modified + * @param jsonNode The patch to be applied + * @return The Item as it is after applying the patch + * @throws SQLException + * @throws AuthorizeException + */ + public TemplateItemRest patchTemplateItem(TemplateItem templateItem, JsonNode jsonNode) + throws SQLException, AuthorizeException { + ObjectMapper mapper = new ObjectMapper(); + JsonPatchConverter patchConverter = new JsonPatchConverter(mapper); + Patch patch = patchConverter.convert(jsonNode); + + Item item = templateItem.getItem(); + resourcePatch.patch(obtainContext(), item, patch.getOperations()); + itemService.update(obtainContext(), item); + return findById(templateItem.getID()).orElse(null); + } + + /** + * Remove an Item which is a template for a Collection. + * + * Note: The caller is responsible for checking that this item is in fact a template item. + * + * @param context + * @param templateItem The item to be removed + * @throws SQLException + * @throws IOException + * @throws AuthorizeException + */ + public void removeTemplateItem(Context context, TemplateItem templateItem) + throws SQLException, IOException, AuthorizeException { + + Collection collection = templateItem.getItem().getTemplateItemOf(); + collectionService.removeTemplateItem(context, collection); + collectionService.update(context, collection); + } + +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowActionRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowActionRestRepository.java new file mode 100644 index 0000000000..75c2e5c140 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowActionRestRepository.java @@ -0,0 +1,55 @@ +/** + * 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.app.rest.repository; + +import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException; +import org.dspace.app.rest.model.WorkflowActionRest; +import org.dspace.core.Context; +import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; +import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +/** + * This is the rest repository responsible for managing {@link WorkflowActionRest} objects + * + * @author Maria Verdonck (Atmire) on 06/01/2020 + */ +@Component(WorkflowActionRest.CATEGORY + "." + WorkflowActionRest.NAME) +public class WorkflowActionRestRepository extends DSpaceRestRepository { + + @Autowired + protected XmlWorkflowFactory xmlWorkflowFactory; + + @Override + @PreAuthorize("hasAuthority('AUTHENTICATED')") + public WorkflowActionRest findOne(Context context, String workflowActionName) { + WorkflowActionConfig actionConfig = this.xmlWorkflowFactory.getActionByName(workflowActionName); + if (actionConfig != null) { + return converter.toRest(actionConfig, utils.obtainProjection()); + } else { + throw new ResourceNotFoundException("No workflow action with name " + workflowActionName + + " is configured"); + } + } + + @Override + @PreAuthorize("hasAuthority('AUTHENTICATED')") + public Page findAll(Context context, Pageable pageable) { + throw new RepositoryMethodNotImplementedException(WorkflowActionRest.NAME, "findAll"); + } + + @Override + public Class getDomainClass() { + return WorkflowActionRest.class; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowDefinitionRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowDefinitionRestRepository.java new file mode 100644 index 0000000000..6837168521 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowDefinitionRestRepository.java @@ -0,0 +1,95 @@ +/** + * 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.app.rest.repository; + +import java.sql.SQLException; +import java.util.List; +import java.util.UUID; + +import org.dspace.app.rest.Parameter; +import org.dspace.app.rest.SearchRestMethod; +import org.dspace.app.rest.model.WorkflowDefinitionRest; +import org.dspace.content.Collection; +import org.dspace.content.service.CollectionService; +import org.dspace.core.Context; +import org.dspace.xmlworkflow.WorkflowConfigurationException; +import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; +import org.dspace.xmlworkflow.state.Workflow; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +/** + * This is the rest repository responsible for managing WorkflowDefinition Rest objects + * + * @author Maria Verdonck (Atmire) on 11/12/2019 + */ +@Component(WorkflowDefinitionRest.CATEGORY + "." + WorkflowDefinitionRest.NAME) +public class WorkflowDefinitionRestRepository extends DSpaceRestRepository { + + @Autowired + protected XmlWorkflowFactory xmlWorkflowFactory; + + @Autowired + private CollectionService collectionService; + + @Override + @PreAuthorize("hasAuthority('AUTHENTICATED')") + public WorkflowDefinitionRest findOne(Context context, String workflowName) { + if (xmlWorkflowFactory.workflowByThisNameExists(workflowName)) { + try { + return converter.toRest(xmlWorkflowFactory.getWorkflowByName(workflowName), utils.obtainProjection()); + } catch (WorkflowConfigurationException e) { + // Should never occur, since xmlWorkflowFactory.getWorkflowByName only throws a + // WorkflowConfigurationException if no workflow by that name is configured (tested earlier) + throw new ResourceNotFoundException("No workflow with name " + workflowName + " is configured"); + } + } else { + throw new ResourceNotFoundException("No workflow with name " + workflowName + " is configured"); + } + } + + @Override + @PreAuthorize("hasAuthority('AUTHENTICATED')") + public Page findAll(Context context, Pageable pageable) { + List workflows = xmlWorkflowFactory.getAllConfiguredWorkflows(); + return converter.toRestPage(utils.getPage(workflows, pageable), utils.obtainProjection()); + } + + /** + * GET endpoint that returns the workflow definition that applies to a specific collection eventually fallback + * to the default configuration. + * + * @param collectionId Uuid of the collection + * @return the workflow definition for this collection + */ + @SearchRestMethod(name = "findByCollection") + @PreAuthorize("hasAuthority('AUTHENTICATED')") + public WorkflowDefinitionRest findByCollection(@Parameter(value = "uuid") UUID collectionId) throws SQLException { + Context context = obtainContext(); + Collection collectionFromUuid = collectionService.find(context, collectionId); + if (collectionFromUuid != null) { + try { + return converter.toRest(xmlWorkflowFactory.getWorkflow(collectionFromUuid), utils.obtainProjection()); + } catch (WorkflowConfigurationException e) { + throw new ResourceNotFoundException("No workflow for this collection fault and " + + "no defaultWorkflow found"); + } + } else { + throw new ResourceNotFoundException("Collection with id " + collectionId + " not found"); + } + } + + @Override + public Class getDomainClass() { + return WorkflowDefinitionRest.class; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java index 99f6c5d38a..2c9eb832c7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java @@ -24,7 +24,6 @@ import org.dspace.app.rest.model.ErrorRest; import org.dspace.app.rest.model.WorkflowItemRest; import org.dspace.app.rest.model.patch.Operation; import org.dspace.app.rest.model.patch.Patch; -import org.dspace.app.rest.projection.Projection; import org.dspace.app.rest.submit.AbstractRestProcessingStep; import org.dspace.app.rest.submit.SubmissionService; import org.dspace.app.rest.submit.UploadableStep; @@ -157,7 +156,7 @@ public class WorkflowItemRestRepository extends DSpaceRestRepository { + + @Autowired + protected XmlWorkflowFactory xmlWorkflowFactory; + + @Override + @PreAuthorize("hasAuthority('AUTHENTICATED')") + public WorkflowStepRest findOne(Context context, String workflowStepName) { + Step step = this.xmlWorkflowFactory.getStepByName(workflowStepName); + if (step != null) { + return converter.toRest(step, utils.obtainProjection()); + } else { + throw new ResourceNotFoundException("No workflow step with name " + workflowStepName + + " is configured"); + } + } + + @Override + @PreAuthorize("hasAuthority('AUTHENTICATED')") + public Page findAll(Context context, Pageable pageable) { + throw new RepositoryMethodNotImplementedException(WorkflowStepRest.NAME, "findAll"); + } + + @Override + public Class getDomainClass() { + return WorkflowStepRest.class; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java index 4ba223e5d2..a77a92aced 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java @@ -31,7 +31,6 @@ import org.dspace.app.rest.model.ErrorRest; import org.dspace.app.rest.model.WorkspaceItemRest; import org.dspace.app.rest.model.patch.Operation; import org.dspace.app.rest.model.patch.Patch; -import org.dspace.app.rest.projection.Projection; import org.dspace.app.rest.repository.handler.service.UriListHandlerService; import org.dspace.app.rest.submit.AbstractRestProcessingStep; import org.dspace.app.rest.submit.SubmissionService; @@ -167,7 +166,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository { - - /** - * Handles the patch operations. Patch implementations are provided by subclasses. - * The default methods throw an UnprocessableEntityException. - * - * @param restModel the rest resource to patch - * @param operations list of patch operations - * @throws UnprocessableEntityException - * @throws DSpaceBadRequestException - */ - public R patch(R restModel, List operations) { - - // Note: the list of possible operations is taken from JsonPatchConverter class. Does not implement - // test https://tools.ietf.org/html/rfc6902#section-4.6 - ops: for (Operation op : operations) { - switch (op.getOp()) { - case "add": - restModel = add(restModel, op); - continue ops; - case "replace": - restModel = replace(restModel, op); - continue ops; - case "remove": - restModel = remove(restModel, op); - continue ops; - case "copy": - restModel = copy(restModel, op); - continue ops; - case "move": - restModel = move(restModel, op); - continue ops; - default: - // JsonPatchConverter should have thrown error before this point. - throw new DSpaceBadRequestException("Missing or illegal patch operation: " + op.getOp()); - } - } - - return restModel; - - } - // The default patch methods throw an error when no sub-class implementation is provided. - - protected R add(R restModel, Operation operation) - throws UnprocessableEntityException, DSpaceBadRequestException { - throw new UnprocessableEntityException( - "The add operation is not supported." - ); - } - - protected R replace(R restModel, Operation operation) - throws UnprocessableEntityException, DSpaceBadRequestException { - throw new UnprocessableEntityException( - "The replace operation is not supported." - ); - } - - protected R remove(R restModel, Operation operation) - - throws UnprocessableEntityException, DSpaceBadRequestException { - throw new UnprocessableEntityException( - "The remove operation is not supported." - ); - } - - protected R copy(R restModel, Operation operation) - throws UnprocessableEntityException, DSpaceBadRequestException { - throw new UnprocessableEntityException( - "The copy operation is not supported." - ); - } - - protected R move(R restModel, Operation operation) - throws UnprocessableEntityException, DSpaceBadRequestException { - throw new UnprocessableEntityException( - "The move operation is not supported." - ); - } - -} \ No newline at end of file diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/BundlePatch.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/BundlePatch.java deleted file mode 100644 index f5e1007510..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/BundlePatch.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * 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.app.rest.repository.patch; - -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.exception.UnprocessableEntityException; -import org.dspace.app.rest.model.BundleRest; -import org.dspace.app.rest.model.patch.Operation; -import org.dspace.app.rest.repository.patch.factories.BundleOperationFactory; -import org.dspace.app.rest.repository.patch.factories.impl.ResourcePatchOperation; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * Provides PATCH operations for bundle updates. - */ -@Component -public class BundlePatch extends DSpaceObjectPatch { - - @Autowired - BundleOperationFactory patchFactory; - - /** - * Performs the move operation. - * @param restModel the rest representation of the bundle - * @param operation the move operation - * @throws UnprocessableEntityException - * @throws DSpaceBadRequestException - */ - protected BundleRest move(BundleRest restModel, Operation operation) { - ResourcePatchOperation patchOperation = patchFactory.getMoveOperation(); - return patchOperation.perform(restModel, operation); - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/DSpaceObjectPatch.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/DSpaceObjectPatch.java deleted file mode 100644 index e0e520496f..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/DSpaceObjectPatch.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * 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.app.rest.repository.patch; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.flipkart.zjsonpatch.JsonPatch; -import org.dspace.app.rest.converter.JsonPatchConverter; -import org.dspace.app.rest.model.DSpaceObjectRest; -import org.dspace.app.rest.model.MetadataRest; -import org.dspace.app.rest.model.patch.Operation; -import org.dspace.app.rest.model.patch.Patch; - -/** - * Base class for DSpaceObject-based PATCH operations, providing common functionality. - * - * @param the type of DSpaceObjectRest object the class is applicable to. - */ -public abstract class DSpaceObjectPatch extends AbstractResourcePatch { - - private static final String METADATA_PATH = "/metadata"; - - private ObjectMapper objectMapper = new ObjectMapper(); - - private JsonPatchConverter jsonPatchConverter = new JsonPatchConverter(objectMapper); - - /** - * Applies the given patch operations to the given DSpaceObjectRest instance. - * - * This extends the default implementation by first applying metadata-based patch operations, - * then applying any others. - * - * @param dsoRest the instance to apply the changes to. - * @param operations the list of patch operations. - * @return the modified DSpaceObectRest instance. - */ - @Override - public R patch(R dsoRest, List operations) { - List metadataOperations = new ArrayList<>(); - List otherOperations = new ArrayList<>(); - - for (Operation operation : operations) { - String path = operation.getPath(); - if (path.equals(METADATA_PATH) || path.startsWith(METADATA_PATH + "/")) { - metadataOperations.add(operation); - } else { - otherOperations.add(operation); - } - } - - if (!metadataOperations.isEmpty()) { - dsoRest.setMetadata(applyMetadataPatch( - jsonPatchConverter.convert(new Patch(metadataOperations)), - dsoRest.getMetadata())); - } - - return super.patch(dsoRest, otherOperations); - } - - private MetadataRest applyMetadataPatch(JsonNode patch, MetadataRest metadataRest) { - try { - ObjectNode objectNode = objectMapper.createObjectNode(); - JsonNode metadataNode = objectMapper.valueToTree(metadataRest); - objectNode.replace("metadata", metadataNode); - JsonPatch.applyInPlace(patch, objectNode); - return objectMapper.treeToValue(objectNode.get("metadata"), MetadataRest.class); - } catch (IOException e) { - throw new IllegalArgumentException(e); - } - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/EPersonPatch.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/EPersonPatch.java deleted file mode 100644 index 36abde2fcc..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/EPersonPatch.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * 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.app.rest.repository.patch; - -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.exception.UnprocessableEntityException; -import org.dspace.app.rest.model.EPersonRest; -import org.dspace.app.rest.model.patch.Operation; -import org.dspace.app.rest.repository.patch.factories.EPersonOperationFactory; -import org.dspace.app.rest.repository.patch.factories.impl.ResourcePatchOperation; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * Provides patch operations for eperson updates. - * - * @author Michael Spalti - */ -@Component -public class EPersonPatch extends DSpaceObjectPatch { - - @Autowired - EPersonOperationFactory patchFactory; - - /** - * Performs the replace operation. - * @param eperson the eperson rest representation - * @param operation the replace operation - * @throws UnprocessableEntityException - * @throws DSpaceBadRequestException - */ - protected EPersonRest replace(EPersonRest eperson, Operation operation) { - ResourcePatchOperation patchOperation = - patchFactory.getReplaceOperationForPath(operation.getPath()); - - return patchOperation.perform(eperson, operation); - - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/ItemPatch.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/ItemPatch.java deleted file mode 100644 index fa652e9616..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/ItemPatch.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * 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.app.rest.repository.patch; - -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.exception.UnprocessableEntityException; -import org.dspace.app.rest.model.ItemRest; -import org.dspace.app.rest.model.patch.Operation; -import org.dspace.app.rest.repository.patch.factories.ItemOperationFactory; -import org.dspace.app.rest.repository.patch.factories.impl.ResourcePatchOperation; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * Provides PATCH operations for item updates. - * - * @author Michael Spalti - */ -@Component -public class ItemPatch extends DSpaceObjectPatch { - - @Autowired - ItemOperationFactory patchFactory; - - /** - * Performs the replace operation. - * @param item the rest representation of the item - * @param operation the replace operation - * @throws UnprocessableEntityException - * @throws DSpaceBadRequestException - */ - protected ItemRest replace(ItemRest item, Operation operation) { - - ResourcePatchOperation patchOperation = - patchFactory.getReplaceOperationForPath(operation.getPath()); - - return patchOperation.perform(item, operation); - - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/ResourcePatch.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/ResourcePatch.java new file mode 100644 index 0000000000..7838cc7526 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/ResourcePatch.java @@ -0,0 +1,67 @@ +/** + * 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.app.rest.repository.patch; + +import java.sql.SQLException; +import java.util.List; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.exception.UnprocessableEntityException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.repository.patch.operation.PatchOperation; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * The base class for resource PATCH operations. + */ +@Component +public class ResourcePatch { + + @Autowired + private List patchOperations; + + /** + * Handles the patch operations. Patch implementations are provided by subclasses. + * The default methods throw an UnprocessableEntityException. + * + * @param context Context of patch operation + * @param dso the dso resource to patch + * @param operations list of patch operations + * @throws UnprocessableEntityException + * @throws DSpaceBadRequestException + */ + public void patch(Context context, M dso, List operations) throws SQLException { + for (Operation operation: operations) { + performPatchOperation(context, dso, operation); + } + } + + /** + * Checks with all possible patch operations whether they support this operation + * (based on instanceof dso and operation.path) + * @param context Context of patch operation + * @param object the resource to patch + * @param operation the patch operation + * @throws DSpaceBadRequestException + */ + protected void performPatchOperation(Context context, M object, Operation operation) + throws DSpaceBadRequestException, SQLException { + for (PatchOperation patchOperation: patchOperations) { + if (patchOperation.supports(object, operation)) { + patchOperation.perform(context,object, operation); + return; + } + } + throw new DSpaceBadRequestException( + "This operation is not supported." + ); + } + +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/BundleOperationFactory.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/BundleOperationFactory.java deleted file mode 100644 index a712f3a224..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/BundleOperationFactory.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories; - -import org.dspace.app.rest.model.BundleRest; -import org.dspace.app.rest.repository.patch.factories.impl.BundleMoveOperation; -import org.dspace.app.rest.repository.patch.factories.impl.ResourcePatchOperation; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * Provides factory methods for obtaining instances of bundle patch operations. - */ -@Component -public class BundleOperationFactory { - - @Autowired - BundleMoveOperation bundleMoveOperation; - - /** - * Returns the patch instance for the move operation - */ - public ResourcePatchOperation getMoveOperation() { - return bundleMoveOperation; - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/EPersonOperationFactory.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/EPersonOperationFactory.java deleted file mode 100644 index d556634188..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/EPersonOperationFactory.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories; - -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.EPersonRest; -import org.dspace.app.rest.repository.patch.factories.impl.EPersonCertificateReplaceOperation; -import org.dspace.app.rest.repository.patch.factories.impl.EPersonEmailReplaceOperation; -import org.dspace.app.rest.repository.patch.factories.impl.EPersonLoginReplaceOperation; -import org.dspace.app.rest.repository.patch.factories.impl.EPersonNetidReplaceOperation; -import org.dspace.app.rest.repository.patch.factories.impl.EPersonPasswordReplaceOperation; -import org.dspace.app.rest.repository.patch.factories.impl.ResourcePatchOperation; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * Provides factory methods for obtaining instances of eperson patch operations. - * - * @author Michael Spalti - */ -@Component -public class EPersonOperationFactory { - - @Autowired - EPersonPasswordReplaceOperation passwordReplaceOperation; - - @Autowired - EPersonLoginReplaceOperation loginReplaceOperation; - - @Autowired - EPersonCertificateReplaceOperation certificateReplaceOperation; - - @Autowired - EPersonNetidReplaceOperation netIdReplaceOperation; - - @Autowired - EPersonEmailReplaceOperation emailReplaceOperation; - - public static final String OPERATION_PASSWORD_CHANGE = "/password"; - public static final String OPERATION_CAN_LOGIN = "/canLogin"; - public static final String OPERATION_REQUIRE_CERTIFICATE = "/certificate"; - public static final String OPERATION_SET_NETID = "/netid"; - public static final String OPERATION_SET_EMAIL = "/email"; - /** - * Returns the patch instance for the replace operation (based on the operation path). - * - * @param path the operation path - * @return the patch operation implementation - * @throws DSpaceBadRequestException - */ - public ResourcePatchOperation getReplaceOperationForPath(String path) { - - switch (path) { - case OPERATION_PASSWORD_CHANGE: - return passwordReplaceOperation; - case OPERATION_CAN_LOGIN: - return loginReplaceOperation; - case OPERATION_REQUIRE_CERTIFICATE: - return certificateReplaceOperation; - case OPERATION_SET_NETID: - return netIdReplaceOperation; - case OPERATION_SET_EMAIL: - return emailReplaceOperation; - default: - throw new DSpaceBadRequestException("Missing patch operation for: " + path); - } - } - -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/ItemOperationFactory.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/ItemOperationFactory.java deleted file mode 100644 index 8a37ced711..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/ItemOperationFactory.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories; - -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.ItemRest; -import org.dspace.app.rest.repository.patch.factories.impl.ItemDiscoverableReplaceOperation; -import org.dspace.app.rest.repository.patch.factories.impl.ItemWithdrawReplaceOperation; -import org.dspace.app.rest.repository.patch.factories.impl.ResourcePatchOperation; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * Provides factory methods for obtaining instances of item patch operations. - * - * @author Michael Spalti - */ -@Component -public class ItemOperationFactory { - - @Autowired - ItemDiscoverableReplaceOperation itemDiscoverableReplaceOperation; - - @Autowired - ItemWithdrawReplaceOperation itemWithdrawReplaceOperation; - - private static final String OPERATION_PATH_WITHDRAW = "/withdrawn"; - private static final String OPERATION_PATH_DISCOVERABLE = "/discoverable"; - - /** - * Returns the patch instance for the replace operation (based on the operation path). - * - * @param path the operation path - * @return the patch operation implementation - * @throws DSpaceBadRequestException - */ - public ResourcePatchOperation getReplaceOperationForPath(String path) { - - switch (path) { - case OPERATION_PATH_DISCOVERABLE: - return itemDiscoverableReplaceOperation; - case OPERATION_PATH_WITHDRAW: - return itemWithdrawReplaceOperation; - default: - throw new DSpaceBadRequestException("Missing patch operation for: " + path); - } - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/ResourcePolicyOperationFactory.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/ResourcePolicyOperationFactory.java deleted file mode 100644 index 5622726539..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/ResourcePolicyOperationFactory.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories; - -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.ResourcePolicyRest; -import org.dspace.app.rest.repository.patch.factories.impl.ResourcePatchOperation; -import org.dspace.app.rest.repository.patch.factories.impl.ResourcePolicyDescriptionOperations; -import org.dspace.app.rest.repository.patch.factories.impl.ResourcePolicyEndDateOperations; -import org.dspace.app.rest.repository.patch.factories.impl.ResourcePolicyNameOperations; -import org.dspace.app.rest.repository.patch.factories.impl.ResourcePolicyStartDateOperations; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * Provides factory methods for obtaining instances of ResourcePolicy patch operations. - * - * @author Andrea Bollini (andrea.bollini at 4science.it) - */ -@Component -public class ResourcePolicyOperationFactory { - - @Autowired - ResourcePolicyStartDateOperations resourcePolicyStartDateOperations; - - @Autowired - ResourcePolicyEndDateOperations resourcePolicyEndDateOperations; - - @Autowired - ResourcePolicyNameOperations resourcePolicyNameOperations; - - @Autowired - ResourcePolicyDescriptionOperations resourcePolicyDescriptionOperations; - - private static final String OPERATION_PATH_STARTDATE = "/startDate"; - private static final String OPERATION_PATH_ENDDATE = "/endDate"; - private static final String OPERATION_PATH_DESCRIPTION = "/description"; - private static final String OPERATION_PATH_NAME = "/name"; - - /** - * Returns the patch instance for the operation (based on the operation path). - * - * @param path the operation path - * @return the patch operation implementation - * @throws DSpaceBadRequestException - */ - public ResourcePatchOperation getOperationForPath(String path) { - - switch (path) { - case OPERATION_PATH_STARTDATE: - return resourcePolicyStartDateOperations; - case OPERATION_PATH_ENDDATE: - return resourcePolicyEndDateOperations; - case OPERATION_PATH_DESCRIPTION: - return resourcePolicyDescriptionOperations; - case OPERATION_PATH_NAME: - return resourcePolicyNameOperations; - default: - throw new DSpaceBadRequestException("Missing patch operation for: " + path); - } - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/EPersonCertificateReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/EPersonCertificateReplaceOperation.java deleted file mode 100644 index 66e8b117f7..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/EPersonCertificateReplaceOperation.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories.impl; - -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.EPersonRest; -import org.dspace.app.rest.model.patch.Operation; -import org.springframework.stereotype.Component; - -/** - * Implementation for EPerson requires certificate patches. - * - * Example: - * curl -X PATCH http://${dspace.url}/api/epersons/eperson/<:id-eperson> -H " - * Content-Type: application/json" -d '[{ "op": "replace", "path": " - * /certificate", "value": true|false]' - * - * - * @author Michael Spalti - */ -@Component -public class EPersonCertificateReplaceOperation extends ReplacePatchOperation - implements ResourcePatchOperation { - - @Override - public EPersonRest replace(EPersonRest eperson, Operation operation) { - - Boolean requireCert = getBooleanOperationValue(operation.getValue()); - eperson.setRequireCertificate(requireCert); - return eperson; - - } - - @Override - void checkModelForExistingValue(EPersonRest resource, Operation operation) { - // TODO: many (all?) boolean values on the rest model should never be null. - // So perhaps the error to throw in this case is different...IllegalStateException? - // Or perhaps do nothing (no check is required). - if ((Object) resource.isRequireCertificate() == null) { - throw new DSpaceBadRequestException("Attempting to replace a non-existent value."); - } - } - - @Override - protected Class getArrayClassForEvaluation() { - return Boolean[].class; - } - - @Override - protected Class getClassForEvaluation() { - return Boolean.class; - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/EPersonEmailReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/EPersonEmailReplaceOperation.java deleted file mode 100644 index 9907361e4e..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/EPersonEmailReplaceOperation.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories.impl; - -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.EPersonRest; -import org.dspace.app.rest.model.patch.Operation; -import org.springframework.stereotype.Component; - -/** - * Implementation for EPerson password patches. - * - * Example: - * curl -X PATCH http://${dspace.url}/api/epersons/eperson/<:id-eperson> -H " - * Content-Type: application/json" -d '[{ "op": "replace", "path": " - * /email", "value": "new@email"]' - * - * - * @author Michael Spalti - */ -@Component -public class EPersonEmailReplaceOperation extends ReplacePatchOperation - implements ResourcePatchOperation { - @Override - EPersonRest replace(EPersonRest eperson, Operation operation) { - - eperson.setEmail((String) operation.getValue()); - return eperson; - } - - @Override - void checkModelForExistingValue(EPersonRest resource, Operation operation) { - if (resource.getEmail() == null) { - throw new DSpaceBadRequestException("Attempting to replace a non-existent value."); - } - } - - @Override - protected Class getArrayClassForEvaluation() { - - return String[].class; - } - - @Override - protected Class getClassForEvaluation() { - - return String.class; - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/EPersonLoginReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/EPersonLoginReplaceOperation.java deleted file mode 100644 index 7806bdd87e..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/EPersonLoginReplaceOperation.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories.impl; - -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.EPersonRest; -import org.dspace.app.rest.model.patch.Operation; -import org.springframework.stereotype.Component; - -/** - * Implementation for EPerson canLogin patches. - * - * Example: - * curl -X PATCH http://${dspace.url}/api/epersons/eperson/<:id-eperson> -H " - * Content-Type: application/json" -d '[{ "op": "replace", "path": " - * /canLogin", "value": true|false]' - * - * - * @author Michael Spalti - */ -@Component -public class EPersonLoginReplaceOperation extends ReplacePatchOperation - implements ResourcePatchOperation { - - @Override - public EPersonRest replace(EPersonRest eperson, Operation operation) { - - Boolean canLogin = getBooleanOperationValue(operation.getValue()); - eperson.setCanLogIn(canLogin); - return eperson; - } - - @Override - void checkModelForExistingValue(EPersonRest resource, Operation operation) { - if ((Object) resource.isCanLogIn() == null) { - throw new DSpaceBadRequestException("Attempting to replace a non-existent value."); - } - } - - @Override - protected Class getArrayClassForEvaluation() { - return Boolean[].class; - } - - @Override - protected Class getClassForEvaluation() { - return Boolean.class; - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/EPersonNetidReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/EPersonNetidReplaceOperation.java deleted file mode 100644 index cb7ebba2c3..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/EPersonNetidReplaceOperation.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories.impl; - -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.EPersonRest; -import org.dspace.app.rest.model.patch.Operation; -import org.springframework.stereotype.Component; - -/** - * Implementation for EPerson netid patches. - * - * Example: - * curl -X PATCH http://${dspace.url}/api/epersons/eperson/<:id-eperson> -H " - * Content-Type: application/json" -d '[{ "op": "replace", "path": " - * /netid", "value": "newNetId"]' - * - * - * @author Michael Spalti - */ -@Component -public class EPersonNetidReplaceOperation extends ReplacePatchOperation - implements ResourcePatchOperation { - - @Override - EPersonRest replace(EPersonRest eperson, Operation operation) { - - eperson.setNetid((String) operation.getValue()); - return eperson; - } - - - @Override - void checkModelForExistingValue(EPersonRest resource, Operation operation) { - if (resource.getNetid() == null) { - throw new DSpaceBadRequestException("Attempting to replace a non-existent value."); - } - } - - @Override - protected Class getArrayClassForEvaluation() { - return String[].class; - } - - @Override - protected Class getClassForEvaluation() { - return String.class; - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/EPersonPasswordReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/EPersonPasswordReplaceOperation.java deleted file mode 100644 index 61b6c2b00e..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/EPersonPasswordReplaceOperation.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories.impl; - -import org.dspace.app.rest.model.EPersonRest; -import org.dspace.app.rest.model.patch.Operation; -import org.springframework.stereotype.Component; - -/** - * Implementation for EPerson password patches. - * - * Example: - * curl -X PATCH http://${dspace.url}/api/epersons/eperson/<:id-eperson> -H " - * Content-Type: application/json" -d '[{ "op": "replace", "path": " - * /password", "value": "newpassword"]' - * - * - * @author Michael Spalti - */ -@Component -public class EPersonPasswordReplaceOperation extends ReplacePatchOperation { - - @Override - EPersonRest replace(EPersonRest eperson, Operation operation) { - - eperson.setPassword((String) operation.getValue()); - return eperson; - } - - @Override - void checkModelForExistingValue(EPersonRest resource, Operation operation) { - /* - * FIXME: the password field in eperson rest model is always null because - * the value is not set in the rest converter. - * We would normally throw an exception here since replace - * operations are not allowed on non-existent values, but that - * would prevent the password update from ever taking place. - */ - } - - @Override - protected Class getArrayClassForEvaluation() { - - return String[].class; - } - - @Override - protected Class getClassForEvaluation() { - - return String.class; - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ItemDiscoverableReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ItemDiscoverableReplaceOperation.java deleted file mode 100644 index 29e5e4cd6f..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ItemDiscoverableReplaceOperation.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories.impl; - -import org.apache.log4j.Logger; -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.ItemRest; -import org.dspace.app.rest.model.patch.Operation; -import org.springframework.stereotype.Component; - -/** - * This is the implementation for Item resource patches. - * - * Example: - * curl -X PATCH http://${dspace.url}/api/item/<:id-item> -H " - * Content-Type: application/json" -d '[{ "op": "replace", "path": " - * /discoverable", "value": true|false]' - * - * - * @author Michael Spalti - */ -@Component -public class ItemDiscoverableReplaceOperation extends ReplacePatchOperation { - - private static final Logger log = Logger.getLogger(ItemDiscoverableReplaceOperation.class); - - @Override - public ItemRest replace(ItemRest item, Operation operation) { - - item.setDiscoverable(getBooleanOperationValue(operation.getValue())); - return item; - - } - - @Override - void checkModelForExistingValue(ItemRest resource, Operation operation) { - if ((Object) resource.getDiscoverable() == null) { - throw new DSpaceBadRequestException("Attempting to replace a non-existent value."); - } - } - - protected Class getArrayClassForEvaluation() { - return Boolean[].class; - } - - protected Class getClassForEvaluation() { - return Boolean.class; - } - -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ItemWithdrawReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ItemWithdrawReplaceOperation.java deleted file mode 100644 index 1eda27aedd..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ItemWithdrawReplaceOperation.java +++ /dev/null @@ -1,80 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories.impl; - -import org.apache.log4j.Logger; -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.exception.UnprocessableEntityException; -import org.dspace.app.rest.model.ItemRest; -import org.dspace.app.rest.model.patch.Operation; -import org.springframework.stereotype.Component; - -/** - * This is the implementation for Item resource patches. - *

    - * Example: - * curl -X PATCH http://${dspace.url}/api/item/<:id-item> -H " - * Content-Type: application/json" -d '[{ "op": "replace", "path": " - * /withdrawn", "value": true|false]' - * - * - * @author Michael Spalti - */ -@Component -public class ItemWithdrawReplaceOperation extends ReplacePatchOperation { - - private static final Logger log = Logger.getLogger(ItemWithdrawReplaceOperation.class); - - @Override - public ItemRest replace(ItemRest item, Operation operation) { - - Boolean withdraw = getBooleanOperationValue(operation.getValue()); - - // This is a request to withdraw the item. - if (withdraw) { - // The item is currently not withdrawn and also not archived. Is this a possible situation? - if (!item.getWithdrawn() && !item.getInArchive()) { - throw new UnprocessableEntityException("Cannot withdraw item when it is not in archive."); - } - // Item is already withdrawn. No-op, 200 response. - // (The operation is not idempotent since it results in a provenance note in the record.) - if (item.getWithdrawn()) { - return item; - } - item.setWithdrawn(true); - return item; - - } else { - // No need to reinstate item if it has not previously been not withdrawn. - // No-op, 200 response. (The operation is not idempotent since it results - // in a provenance note in the record.) - if (!item.getWithdrawn()) { - return item; - } - item.setWithdrawn(false); - return item; - } - - } - - @Override - void checkModelForExistingValue(ItemRest resource, Operation operation) { - if ((Object) resource.getWithdrawn() == null) { - throw new DSpaceBadRequestException("Attempting to replace a non-existent value."); - } - } - - protected Class getArrayClassForEvaluation() { - return Boolean[].class; - } - - protected Class getClassForEvaluation() { - return Boolean.class; - } - -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/MovePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/MovePatchOperation.java deleted file mode 100644 index bc56faec2c..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/MovePatchOperation.java +++ /dev/null @@ -1,106 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories.impl; - -import org.apache.commons.lang3.StringUtils; -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.exception.UnprocessableEntityException; -import org.dspace.app.rest.model.RestModel; -import org.dspace.app.rest.model.patch.MoveOperation; -import org.dspace.app.rest.model.patch.Operation; - -/** - * Base class for move patch operations. - */ -public abstract class MovePatchOperation extends PatchOperation { - - /** - * The index to move the object from - */ - protected int from; - - /** - * The index to move the object to - */ - protected int to; - - /** - * Implements the patch operation for move operations. - * Before performing the move operation this method checks - * if the arguments provided are valid - * - * @param resource the rest model. - * @param operation the move patch operation. - * @return the updated rest model. - * @throws DSpaceBadRequestException - * @throws UnprocessableEntityException - */ - @Override - public R perform(R resource, Operation operation) { - checkMoveOperation(operation); - return move(resource, operation); - } - - /** - * Executes the move patch operation. - * - * @param resource the rest model. - * @param operation the move patch operation. - * @return the updated rest model. - * @throws DSpaceBadRequestException - * @throws UnprocessableEntityException - */ - abstract R move(R resource, Operation operation); - - /** - * This method checks if the operation contains any invalid arguments. Invalid arguments include: - * - from and path point to the same index - * - either of the indexes are negative - * @param operation the move patch operation. - */ - private void checkMoveOperation(Operation operation) { - if (!(operation instanceof MoveOperation)) { - throw new DSpaceBadRequestException( - "Expected a MoveOperation, but received " + operation.getClass().getName() + " instead." - ); - } - from = getLocationFromPath(((MoveOperation)operation).getFrom()); - to = getLocationFromPath(operation.getPath()); - if (from == to) { - throw new DSpaceBadRequestException( - "The \"from\" location must be different from the \"to\" location." - ); - } - if (from < 0) { - throw new DSpaceBadRequestException("A negative \"from\" location was provided: " + from); - } - if (to < 0) { - throw new DSpaceBadRequestException("A negative \"to\" location was provided: " + to); - } - } - - /** - * Fetches and returns the index from a path argument - * @param path the provided path (e.g. "/_links/bitstreams/1/href") - */ - protected int getLocationFromPath(String path) { - String[] parts = StringUtils.split(path, "/"); - String locationStr; - if (parts.length > 1) { - if (StringUtils.equals(parts[parts.length - 1], "href")) { - locationStr = parts[parts.length - 2]; - } else { - locationStr = parts[parts.length - 1]; - } - } else { - locationStr = parts[0]; - } - return Integer.parseInt(locationStr); - } -} - diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/PatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/PatchOperation.java deleted file mode 100644 index 0c28f01b4e..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/PatchOperation.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories.impl; - -import org.apache.commons.lang3.BooleanUtils; -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.RestModel; -import org.dspace.app.rest.model.patch.Operation; - -/** - * Base class for all resource patch operations. - * - * @author Michael Spalti - */ -public abstract class PatchOperation - implements ResourcePatchOperation { - - /** - * Updates the rest model by applying the patch operation. - * - * @param resource the rest model. - * @param operation the patch operation. - * @return the updated rest model. - * @throws DSpaceBadRequestException - */ - public abstract R perform(R resource, Operation operation); - - /** - * Throws PatchBadRequestException for missing operation value. - * - * @param value the value to test - */ - void checkOperationValue(Object value) { - if (value == null) { - throw new DSpaceBadRequestException("No value provided for the operation."); - } - } - - /** - * Allows clients to use either a boolean or a string representation of boolean value. - * - * @param value the operation value - * @return the original or derived boolean value - * @throws DSpaceBadRequestException - */ - Boolean getBooleanOperationValue(Object value) { - Boolean bool; - - if (value instanceof String) { - bool = BooleanUtils.toBooleanObject((String) value); - if (bool == null) { - // make sure the string was converted to boolean. - throw new DSpaceBadRequestException("Boolean value not provided."); - } - } else { - bool = (Boolean) value; - } - return bool; - } - - /** - * This method should return the typed array to be used in the - * LateObjectEvaluator evaluation of json arrays. - * - * @return - */ - protected abstract Class getArrayClassForEvaluation(); - - /** - * This method should return the object type to be used in the - * LateObjectEvaluator evaluation of json objects. - * - * @return - */ - protected abstract Class getClassForEvaluation(); - -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ReplacePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ReplacePatchOperation.java deleted file mode 100644 index 3205c7019f..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ReplacePatchOperation.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories.impl; - -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.exception.UnprocessableEntityException; -import org.dspace.app.rest.model.RestModel; -import org.dspace.app.rest.model.patch.Operation; - -/** - * Base class for replace patch operations. - * - * @author Michael Spalti - */ -public abstract class ReplacePatchOperation - extends PatchOperation { - - /** - * Implements the patch operation for replace operations. - * Before performing the replace operation this method checks - * for a non-null operation value and a non-null value on the rest model - * (replace operations should only be applied to an existing value). - * - * @param resource the rest model. - * @param operation the replace patch operation. - * @return the updated rest model. - * @throws DSpaceBadRequestException - * @throws UnprocessableEntityException - */ - @Override - public R perform(R resource, Operation operation) { - - checkOperationValue(operation.getValue()); - checkModelForExistingValue(resource, operation); - return replace(resource, operation); - - } - - /** - * Executes the replace patch operation. - * - * @param resource the rest model. - * @param operation the replace patch operation. - * @return the updated rest model. - * @throws DSpaceBadRequestException - * @throws UnprocessableEntityException - */ - abstract R replace(R resource, Operation operation); - - /** - * Replace operations are not allowed on non-existent values. - * Null values may exist in the RestModel for certain fields - * (usually non-boolean). This method should be implemented - * to assure that the replace operation acts only on an existing value. - * - * @param resource the rest model. - * @throws DSpaceBadRequestException - */ - abstract void checkModelForExistingValue(R resource, Operation operation); - - -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ResourcePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ResourcePatchOperation.java deleted file mode 100644 index 9409328fba..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ResourcePatchOperation.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories.impl; - -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.RestModel; -import org.dspace.app.rest.model.patch.Operation; - -/** - * The patch interface used by repository classes. - * @param - */ -public interface ResourcePatchOperation { - - R perform(R resource, Operation operation) - throws DSpaceBadRequestException; - -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ResourcePolicyDescriptionOperations.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ResourcePolicyDescriptionOperations.java deleted file mode 100644 index 11ce5e0e45..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ResourcePolicyDescriptionOperations.java +++ /dev/null @@ -1,107 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories.impl; - -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.ResourcePolicyRest; -import org.dspace.app.rest.model.patch.Operation; -import org.springframework.stereotype.Component; - -/** - * Implementation for ResourcePolicy name patches. - * - * Example: - * curl -X PATCH http://${dspace.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " - * Content-Type: application/json" -d '[{ "op": "replace", "path": " - * /description", "value": "my description"]' - * - * - * @author Andrea Bollini (andrea.bollini at 4science.it) - */ -@Component -public class ResourcePolicyDescriptionOperations implements ResourcePatchOperation { - - @Override - public ResourcePolicyRest perform(ResourcePolicyRest resource, Operation operation) - throws DSpaceBadRequestException { - switch (operation.getOp()) { - case "replace": - checkOperationValue(operation.getValue()); - checkModelForExistingValue(resource, operation); - return replace(resource, operation); - case "add": - checkOperationValue(operation.getValue()); - checkModelForNotExistingValue(resource, operation); - return add(resource, operation); - case "remove": - checkModelForExistingValue(resource, operation); - return delete(resource, operation); - default: - throw new DSpaceBadRequestException("Unsupported operation " + operation.getOp()); - } - } - - public ResourcePolicyRest replace(ResourcePolicyRest resourcePolicy, Operation operation) { - String newDescription = (String) operation.getValue(); - resourcePolicy.setDescription(newDescription); - return resourcePolicy; - } - - public ResourcePolicyRest add(ResourcePolicyRest resourcePolicy, Operation operation) { - String description = (String) operation.getValue(); - resourcePolicy.setDescription(description); - return resourcePolicy; - } - - public ResourcePolicyRest delete(ResourcePolicyRest resourcePolicy, Operation operation) { - resourcePolicy.setDescription(null); - return resourcePolicy; - } - - /** - * Throws PatchBadRequestException for missing operation value. - * - * @param value - * the value to test - */ - void checkOperationValue(Object value) { - if (value == null || value.equals("")) { - throw new DSpaceBadRequestException("No value provided for the operation."); - } - } - - /** - * Throws PatchBadRequestException for missing value in the /description path. - * - * @param resource - * the resource to update - * @param operation - * the operation to apply - * - */ - void checkModelForExistingValue(ResourcePolicyRest resource, Operation operation) { - if (resource.getDescription() == null) { - throw new DSpaceBadRequestException("Attempting to " + operation.getOp() + " a non-existent value."); - } - } - - /** - * Throws PatchBadRequestException if a value is already set in the /description path. - * - * @param resource - * the resource to update - * @param operation - * the operation to apply - * - */ - void checkModelForNotExistingValue(ResourcePolicyRest resource, Operation operation) { - if (resource.getDescription() != null) { - throw new DSpaceBadRequestException("Attempting to add a value to an already existing path."); - } - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ResourcePolicyEndDateOperations.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ResourcePolicyEndDateOperations.java deleted file mode 100644 index 64c75a6114..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ResourcePolicyEndDateOperations.java +++ /dev/null @@ -1,147 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories.impl; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; - -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.ResourcePolicyRest; -import org.dspace.app.rest.model.patch.Operation; -import org.springframework.stereotype.Component; - -/** - * Implementation for ResourcePolicy endDate patches. - * - * Example: - * curl -X PATCH http://${dspace.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " - * Content-Type: application/json" -d '[{ "op": "replace", "path": " - * /endDate", "value": "YYYY-MM-DD"]' - * - * - * @author Andrea Bollini (andrea.bollini at 4science.it) - */ -@Component -public class ResourcePolicyEndDateOperations implements ResourcePatchOperation { - - final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); - - @Override - public ResourcePolicyRest perform(ResourcePolicyRest resource, Operation operation) - throws DSpaceBadRequestException { - switch (operation.getOp()) { - case "replace": - checkOperationValue(operation.getValue()); - checkModelForExistingValue(resource, operation); - checkModelForConsistentValue(resource, operation); - return replace(resource, operation); - case "add": - checkOperationValue(operation.getValue()); - checkModelForNotExistingValue(resource, operation); - checkModelForConsistentValue(resource, operation); - return add(resource, operation); - case "remove": - checkModelForExistingValue(resource, operation); - return delete(resource, operation); - default: - throw new DSpaceBadRequestException("Unsupported operation " + operation.getOp()); - } - } - - ResourcePolicyRest add(ResourcePolicyRest resourcePolicy, Operation operation) { - String dateS = (String) operation.getValue(); - try { - Date date = simpleDateFormat.parse(dateS); - resourcePolicy.setEndDate(date); - } catch (ParseException e) { - throw new DSpaceBadRequestException("Invalid endDate value", e); - } - return resourcePolicy; - } - - ResourcePolicyRest delete(ResourcePolicyRest resourcePolicy, Operation operation) { - resourcePolicy.setEndDate(null); - return resourcePolicy; - } - - ResourcePolicyRest replace(ResourcePolicyRest resourcePolicy, Operation operation) { - String dateS = (String) operation.getValue(); - try { - Date date = simpleDateFormat.parse(dateS); - resourcePolicy.setEndDate(date); - } catch (ParseException e) { - throw new DSpaceBadRequestException("Invalid endDate value", e); - } - return resourcePolicy; - } - - /** - * Throws PatchBadRequestException for missing operation value. - * - * @param value - * the value to test - */ - void checkOperationValue(Object value) { - if (value == null) { - throw new DSpaceBadRequestException("No value provided for the operation."); - } - } - - /** - * Throws PatchBadRequestException for missing value in the /endDate path. - * - * @param resource - * the resource to update - * @param operation - * the operation to apply - * - */ - void checkModelForExistingValue(ResourcePolicyRest resource, Operation operation) { - if (resource.getEndDate() == null) { - throw new DSpaceBadRequestException("Attempting to " + operation.getOp() + " a non-existent value."); - } - } - - /** - * Throws PatchBadRequestException if a value is already set in the /endDate path. - * - * @param resource - * the resource to update - * @param operation - * the operation to apply - * - */ - void checkModelForNotExistingValue(ResourcePolicyRest resource, Operation operation) { - if (resource.getEndDate() != null) { - throw new DSpaceBadRequestException("Attempting to add a value to an already existing path."); - } - } - - /** - * Throws PatchBadRequestException if the value for endDate is not consistent with the startDate value, if present - * (smaller than). - * - * @param resource - * the resource to update - * @param operation - * the operation to apply - * - */ - void checkModelForConsistentValue(ResourcePolicyRest resource, Operation operation) { - String dateS = (String) operation.getValue(); - try { - Date date = simpleDateFormat.parse(dateS); - if (resource.getEndDate() != null && resource.getStartDate().after(date)) { - throw new DSpaceBadRequestException("Attempting to set an invalid endDate smaller than the startDate."); - } - } catch (ParseException e) { - throw new DSpaceBadRequestException("Invalid endDate value", e); - } - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ResourcePolicyNameOperations.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ResourcePolicyNameOperations.java deleted file mode 100644 index f78607e180..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ResourcePolicyNameOperations.java +++ /dev/null @@ -1,107 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories.impl; - -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.ResourcePolicyRest; -import org.dspace.app.rest.model.patch.Operation; -import org.springframework.stereotype.Component; - -/** - * Implementation for ResourcePolicy name patches. - * - * Example: - * curl -X PATCH http://${dspace.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " - * Content-Type: application/json" -d '[{ "op": "replace", "path": " - * /name", "value": "New Name"]' - * - * - * @author Andrea Bollini (andrea.bollini at 4science.it) - */ -@Component -public class ResourcePolicyNameOperations implements ResourcePatchOperation { - - @Override - public ResourcePolicyRest perform(ResourcePolicyRest resource, Operation operation) - throws DSpaceBadRequestException { - switch (operation.getOp()) { - case "replace": - checkOperationValue(operation.getValue()); - checkModelForExistingValue(resource, operation); - return replace(resource, operation); - case "add": - checkOperationValue(operation.getValue()); - checkModelForNotExistingValue(resource, operation); - return add(resource, operation); - case "remove": - checkModelForExistingValue(resource, operation); - return delete(resource, operation); - default: - throw new DSpaceBadRequestException("Unsupported operation " + operation.getOp()); - } - } - - public ResourcePolicyRest replace(ResourcePolicyRest resourcePolicy, Operation operation) { - String newName = (String) operation.getValue(); - resourcePolicy.setName(newName); - return resourcePolicy; - } - - public ResourcePolicyRest add(ResourcePolicyRest resourcePolicy, Operation operation) { - String name = (String) operation.getValue(); - resourcePolicy.setName(name); - return resourcePolicy; - } - - public ResourcePolicyRest delete(ResourcePolicyRest resourcePolicy, Operation operation) { - resourcePolicy.setName(null); - return resourcePolicy; - } - - /** - * Throws PatchBadRequestException for missing operation value. - * - * @param value - * the value to test - */ - void checkOperationValue(Object value) { - if (value == null || value.equals("")) { - throw new DSpaceBadRequestException("No value provided for the operation."); - } - } - - /** - * Throws PatchBadRequestException for missing value in the /name path. - * - * @param resource - * the resource to update - * @param operation - * the operation to apply - * - */ - void checkModelForExistingValue(ResourcePolicyRest resource, Operation operation) { - if (resource.getName() == null) { - throw new DSpaceBadRequestException("Attempting to " + operation.getOp() + " a non-existent value."); - } - } - - /** - * Throws PatchBadRequestException if a value is already set in the /name path. - * - * @param resource - * the resource to update - * @param operation - * the operation to apply - * - */ - void checkModelForNotExistingValue(ResourcePolicyRest resource, Operation operation) { - if (resource.getName() != null) { - throw new DSpaceBadRequestException("Attempting to add a value to an already existing path."); - } - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ResourcePolicyStartDateOperations.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ResourcePolicyStartDateOperations.java deleted file mode 100644 index 67f6c95413..0000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/ResourcePolicyStartDateOperations.java +++ /dev/null @@ -1,161 +0,0 @@ -/** - * 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.app.rest.repository.patch.factories.impl; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; - -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.ResourcePolicyRest; -import org.dspace.app.rest.model.patch.Operation; -import org.springframework.stereotype.Component; - -/** - * Implementation for ResourcePolicy startDate patches. - * - * Examples: - * - * - * curl -X PATCH http://${dspace.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " - * Content-Type: application/json" -d '[{ "op": "replace", "path": " - * /startDate", "value": "YYYY-MM-DD"]' - * - * - * - * curl -X PATCH http://${dspace.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " - * Content-Type: application/json" -d '[{ "op": "add", "path": " - * /startDate", "value": "YYYY-MM-DD"]' - * - * - * - * curl -X PATCH http://${dspace.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " - * Content-Type: application/json" -d '[{ "op": "delete", "path": " - * /startDate"]' - * - * - * @author Andrea Bollini (andrea.bollini at 4science.it) - */ -@Component -public class ResourcePolicyStartDateOperations implements ResourcePatchOperation { - - final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); - - @Override - public ResourcePolicyRest perform(ResourcePolicyRest resource, Operation operation) - throws DSpaceBadRequestException { - switch (operation.getOp()) { - case "replace": - checkOperationValue(operation.getValue()); - checkModelForExistingValue(resource, operation); - checkModelForConsistentValue(resource, operation); - return replace(resource, operation); - case "add": - checkOperationValue(operation.getValue()); - checkModelForNotExistingValue(resource, operation); - checkModelForConsistentValue(resource, operation); - return add(resource, operation); - case "remove": - checkModelForExistingValue(resource, operation); - return delete(resource, operation); - default: - throw new DSpaceBadRequestException("Unsupported operation " + operation.getOp()); - } - } - - ResourcePolicyRest add(ResourcePolicyRest resourcePolicy, Operation operation) { - String dateS = (String) operation.getValue(); - try { - Date date = simpleDateFormat.parse(dateS); - resourcePolicy.setStartDate(date); - } catch (ParseException e) { - throw new DSpaceBadRequestException("Invalid startDate value", e); - } - return resourcePolicy; - } - - ResourcePolicyRest delete(ResourcePolicyRest resourcePolicy, Operation operation) { - resourcePolicy.setStartDate(null); - return resourcePolicy; - } - - ResourcePolicyRest replace(ResourcePolicyRest resourcePolicy, Operation operation) { - String dateS = (String) operation.getValue(); - try { - Date date = simpleDateFormat.parse(dateS); - resourcePolicy.setStartDate(date); - } catch (ParseException e) { - throw new DSpaceBadRequestException("Invalid startDate value", e); - } - return resourcePolicy; - } - - /** - * Throws PatchBadRequestException for missing operation value. - * - * @param value - * the value to test - */ - void checkOperationValue(Object value) { - if (value == null) { - throw new DSpaceBadRequestException("No value provided for the operation."); - } - } - - /** - * Throws PatchBadRequestException for missing value in the /startDate path. - * - * @param resource - * the resource to update - * @param operation - * the operation to apply - * - */ - void checkModelForExistingValue(ResourcePolicyRest resource, Operation operation) { - if (resource.getStartDate() == null) { - throw new DSpaceBadRequestException("Attempting to " + operation.getOp() + " a non-existent value."); - } - } - - /** - * Throws PatchBadRequestException if a value is already set in the /startDate path. - * - * @param resource - * the resource to update - * @param operation - * the operation to apply - * - */ - void checkModelForNotExistingValue(ResourcePolicyRest resource, Operation operation) { - if (resource.getStartDate() != null) { - throw new DSpaceBadRequestException("Attempting to add a value to an already existing path."); - } - } - - /** - * Throws PatchBadRequestException if the value for startDate is not consistent with the endDate value, if present - * (greater than). - * - * @param resource - * the resource to update - * @param operation - * the operation to apply - * - */ - void checkModelForConsistentValue(ResourcePolicyRest resource, Operation operation) { - String dateS = (String) operation.getValue(); - try { - Date date = simpleDateFormat.parse(dateS); - if (resource.getEndDate() != null && resource.getEndDate().before(date)) { - throw new DSpaceBadRequestException("Attempting to set an invalid startDate greater than the endDate."); - } - } catch (ParseException e) { - throw new DSpaceBadRequestException("Invalid startDate value", e); - } - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/BundleMoveOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/BundleMoveOperation.java similarity index 70% rename from dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/BundleMoveOperation.java rename to dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/BundleMoveOperation.java index 09ee1b3c51..fcf012dcec 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/factories/impl/BundleMoveOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/BundleMoveOperation.java @@ -5,19 +5,17 @@ * * http://www.dspace.org/license/ */ -package org.dspace.app.rest.repository.patch.factories.impl; +package org.dspace.app.rest.repository.patch.operation; import java.sql.SQLException; import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.BundleRest; +import org.dspace.app.rest.model.patch.MoveOperation; import org.dspace.app.rest.model.patch.Operation; -import org.dspace.app.rest.utils.ContextUtil; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bundle; import org.dspace.content.service.BundleService; import org.dspace.core.Context; -import org.dspace.services.RequestService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -26,33 +24,37 @@ import org.springframework.stereotype.Component; * This operation moves bitstreams within a bundle from one index to another. * * Example: - * curl -X PATCH http://${dspace.url}/api/bundles/<:id-bundle> -H " + * curl -X PATCH http://${dspace.server.url}/api/bundles/<:id-bundle> -H " * Content-Type: application/json" -d '[{ "op": "move", "path": " * /_links/bitstreams/1/href", "from": "/_links/bitstreams/0/href"]' * */ @Component -public class BundleMoveOperation extends MovePatchOperation { +public class BundleMoveOperation extends PatchOperation { @Autowired BundleService bundleService; @Autowired - RequestService requestService; + DSpaceObjectMetadataPatchUtils dspaceObjectMetadataPatchUtils; + + private static final String OPERATION_PATH_BUNDLE_MOVE = "/_links/bitstreams/"; /** * Executes the move patch operation. * - * @param resource the rest model. + * @param bundle the bundle in which we want to move files around. * @param operation the move patch operation. * @return the updated rest model. * @throws DSpaceBadRequestException */ @Override - public BundleRest move(BundleRest resource, Operation operation) { - Context context = ContextUtil.obtainContext(requestService.getCurrentRequest().getServletRequest()); + public Bundle perform(Context context, Bundle bundle, Operation operation) { try { - Bundle bundle = bundleService.findByIdOrLegacyId(context, resource.getId()); + MoveOperation moveOperation = (MoveOperation) operation; + final int from = Integer.parseInt(dspaceObjectMetadataPatchUtils.getIndexFromPath(moveOperation.getFrom())); + final int to = Integer.parseInt(dspaceObjectMetadataPatchUtils.getIndexFromPath(moveOperation.getPath())); + int totalAmount = bundle.getBitstreams().size(); if (totalAmount < 1) { @@ -78,29 +80,13 @@ public class BundleMoveOperation extends MovePatchOperation throw new DSpaceBadRequestException(e.getMessage(), e); } - return resource; + return bundle; } - /** - * This method should return the typed array to be used in the - * LateObjectEvaluator evaluation of json arrays. - * - * @return - */ @Override - protected Class getArrayClassForEvaluation() { - return Integer[].class; - } - - /** - * This method should return the object type to be used in the - * LateObjectEvaluator evaluation of json objects. - * - * @return - */ - @Override - protected Class getClassForEvaluation() { - return Integer.class; + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof Bundle && operation.getOp().trim().equalsIgnoreCase(OPERATION_MOVE) + && operation.getPath().trim().startsWith(OPERATION_PATH_BUNDLE_MOVE)); } /** diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataAddOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataAddOperation.java new file mode 100644 index 0000000000..25662634bd --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataAddOperation.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.app.rest.repository.patch.operation; + +import java.sql.SQLException; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.MetadataValueRest; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.content.DSpaceObject; +import org.dspace.content.MetadataField; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.DSpaceObjectService; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + Class for PATCH ADD operations on Dspace Objects' metadata + * Usage: (can be done on other dso than Item also): + * - ADD metadata (with schema.identifier.qualifier) value of a dso (here: Item) to end of list of md + * + * curl -X PATCH http://${dspace.server.url}/api/core/items/<:id-item> -H " + * Content-Type: application/json" -d '[{ "op": "add", "path": " + * /metadata/schema.identifier.qualifier(/0|-)}", "value": "metadataValue"]' + * + * @author Maria Verdonck (Atmire) on 18/11/2019 + */ +@Component +public class DSpaceObjectMetadataAddOperation extends PatchOperation { + + @Autowired + DSpaceObjectMetadataPatchUtils metadataPatchUtils; + + @Override + public R perform(Context context, R resource, Operation operation) throws SQLException { + DSpaceObjectService dsoService = ContentServiceFactory.getInstance().getDSpaceObjectService(resource); + MetadataValueRest metadataValueToAdd = metadataPatchUtils.extractMetadataValueFromOperation(operation); + MetadataField metadataField = metadataPatchUtils.getMetadataField(context, operation); + String indexInPath = metadataPatchUtils.getIndexFromPath(operation.getPath()); + + add(context, resource, dsoService, metadataField, metadataValueToAdd, indexInPath); + return resource; + } + + /** + * Adds metadata to the dso (appending if index is 0 or left out, prepending if -) + * + * @param context context patch is being performed in + * @param dso dso being patched + * @param dsoService service doing the patch in db + * @param metadataField md field being patched + * @param metadataValue value of md element + * @param index determines whether we're prepending (-) or appending (0) md value + */ + private void add(Context context, DSpaceObject dso, DSpaceObjectService dsoService, MetadataField metadataField, + MetadataValueRest metadataValue, String index) { + metadataPatchUtils.checkMetadataFieldNotNull(metadataField); + int indexInt = 0; + if (index != null && index.equals("-")) { + indexInt = -1; + } + try { + dsoService.addAndShiftRightMetadata(context, dso, metadataField.getMetadataSchema().getName(), + metadataField.getElement(), metadataField.getQualifier(), metadataValue.getLanguage(), + metadataValue.getValue(), metadataValue.getAuthority(), metadataValue.getConfidence(), indexInt); + } catch (SQLException e) { + throw new DSpaceBadRequestException("SQLException in DspaceObjectMetadataAddOperation.add trying to add " + + "metadata to dso.", e); + } + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return ((operation.getPath().startsWith(metadataPatchUtils.OPERATION_METADATA_PATH) + || operation.getPath().equals(metadataPatchUtils.OPERATION_METADATA_PATH)) + && operation.getOp().trim().equalsIgnoreCase(OPERATION_ADD) + && objectToMatch instanceof DSpaceObject); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataCopyOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataCopyOperation.java new file mode 100644 index 0000000000..e9de6e5354 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataCopyOperation.java @@ -0,0 +1,102 @@ +/** + * 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.app.rest.repository.patch.operation; + +import java.sql.SQLException; +import java.util.List; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.exception.UnprocessableEntityException; +import org.dspace.app.rest.model.MetadataValueRest; +import org.dspace.app.rest.model.patch.CopyOperation; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.content.DSpaceObject; +import org.dspace.content.Item; +import org.dspace.content.MetadataField; +import org.dspace.content.MetadataValue; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.DSpaceObjectService; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Class for PATCH COPY operations on Dspace Objects' metadata + * Usage: (can be done on other dso than Item also): + * - COPY metadata (with schema.identifier.qualifier) value of a dso (here: Item) from given index to end of list of md + * + * curl -X PATCH http://${dspace.server.url}/api/core/items/<:id-item> -H " + * Content-Type: application/json" -d '[{ "op": "copy", + * "from": "/metadata/schema.identifier.qualifier/indexToCopyFrom" + * "path": "/metadata/schema.identifier.qualifier/-"}]' + * + * + * @author Maria Verdonck (Atmire) on 18/11/2019 + */ +@Component +public class DSpaceObjectMetadataCopyOperation extends PatchOperation { + + @Autowired + DSpaceObjectMetadataPatchUtils metadataPatchUtils; + + @Override + public R perform(Context context, R resource, Operation operation) throws SQLException { + DSpaceObjectService dsoService = ContentServiceFactory.getInstance().getDSpaceObjectService(resource); + MetadataField metadataField = metadataPatchUtils.getMetadataField(context, operation); + String[] partsFromCopy = ((CopyOperation) operation).getFrom().split("/"); + String indexToCopyFrom = (partsFromCopy.length > 3) ? partsFromCopy[3] : null; + + copy(context, resource, dsoService, metadataField, indexToCopyFrom); + return resource; + } + + /** + * Copies metadata of the dso from indexFrom to new index at end of md + * + * @param context context patch is being performed in + * @param dso dso being patched + * @param dsoService service doing the patch in db + * @param metadataField md field being patched + * @param indexToCopyFrom index we're copying metadata from + */ + private void copy(Context context, DSpaceObject dso, DSpaceObjectService dsoService, MetadataField metadataField, + String indexToCopyFrom) { + metadataPatchUtils.checkMetadataFieldNotNull(metadataField); + List metadataValues = dsoService.getMetadata(dso, metadataField.getMetadataSchema().getName(), + metadataField.getElement(), metadataField.getQualifier(), Item.ANY); + try { + int indexToCopyFromInt = Integer.parseInt(indexToCopyFrom); + if (indexToCopyFromInt >= 0 && metadataValues.size() > indexToCopyFromInt + && metadataValues.get(indexToCopyFromInt) != null) { + MetadataValue metadataValueToCopy = metadataValues.get(indexToCopyFromInt); + MetadataValueRest metadataValueRestToCopy + = metadataPatchUtils.convertMdValueToRest(metadataValueToCopy); + // Add metadata value to end of md list + dsoService.addAndShiftRightMetadata(context, dso, metadataField.getMetadataSchema().getName(), + metadataField.getElement(), metadataField.getQualifier(), metadataValueRestToCopy.getLanguage(), + metadataValueRestToCopy.getValue(), metadataValueRestToCopy.getAuthority(), + metadataValueRestToCopy.getConfidence(), -1); + } else { + throw new UnprocessableEntityException("There is no metadata of this type at that index"); + } + } catch (NumberFormatException e) { + throw new IllegalArgumentException("This index (" + indexToCopyFrom + ") is not valid number.", e); + } catch (SQLException e) { + throw new DSpaceBadRequestException("SQLException in DspaceObjectMetadataCopyOperation.copy trying to " + + "add metadata to dso.", e); + } + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return ((operation.getPath().startsWith(metadataPatchUtils.OPERATION_METADATA_PATH) + || operation.getPath().equals(metadataPatchUtils.OPERATION_METADATA_PATH)) + && operation.getOp().trim().equalsIgnoreCase(OPERATION_COPY) + && objectToMatch instanceof DSpaceObject); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataMoveOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataMoveOperation.java new file mode 100644 index 0000000000..8ed07b5aca --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataMoveOperation.java @@ -0,0 +1,84 @@ +/** + * 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.app.rest.repository.patch.operation; + +import java.sql.SQLException; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.MoveOperation; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.content.DSpaceObject; +import org.dspace.content.MetadataField; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.DSpaceObjectService; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Class for PATCH MOVE operations on Dspace Objects' metadata + * Usage: (can be done on other dso than Item also): + * - MOVE metadata (with schema.identifier.qualifier) value of a dso (here: Item) + * from given index in from to given index in path + * + * curl -X PATCH http://${dspace.server.url}/api/core/items/<:id-item> -H " + * Content-Type: application/json" -d '[{ "op": "move", + * "from": "/metadata/schema.identifier.qualifier/indexToCopyFrom" + * "path": "/metadata/schema.identifier.qualifier/indexToCopyTo"}]' + * + * + * @author Maria Verdonck (Atmire) on 18/11/2019 + */ +@Component +public class DSpaceObjectMetadataMoveOperation extends PatchOperation { + + @Autowired + DSpaceObjectMetadataPatchUtils metadataPatchUtils; + + @Override + public R perform(Context context, R resource, Operation operation) throws SQLException { + DSpaceObjectService dsoService = ContentServiceFactory.getInstance().getDSpaceObjectService(resource); + MetadataField metadataField = metadataPatchUtils.getMetadataField(context, operation); + String indexInPath = metadataPatchUtils.getIndexFromPath(operation.getPath()); + String indexToMoveFrom = metadataPatchUtils.getIndexFromPath(((MoveOperation) operation).getFrom()); + + move(context, resource, dsoService, metadataField, indexInPath, indexToMoveFrom); + return resource; + } + + /** + * Moves metadata of the dso from indexFrom to indexTo + * + * @param context context patch is being performed in + * @param dso dso being patched + * @param dsoService service doing the patch in db + * @param metadataField md field being patched + * @param indexFrom index we're moving metadata from + * @param indexTo index we're moving metadata to + */ + private void move(Context context, DSpaceObject dso, + DSpaceObjectService dsoService, MetadataField metadataField, String indexFrom, String indexTo) { + metadataPatchUtils.checkMetadataFieldNotNull(metadataField); + try { + dsoService.moveMetadata(context, dso, metadataField.getMetadataSchema().getName(), + metadataField.getElement(), metadataField.getQualifier(), Integer.parseInt(indexFrom), + Integer.parseInt(indexTo)); + } catch (SQLException e) { + throw new DSpaceBadRequestException("SQLException in DspaceObjectMetadataMoveOperation.move trying to " + + "move metadata in dso.", e); + } + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return ((operation.getPath().startsWith(metadataPatchUtils.OPERATION_METADATA_PATH) + || operation.getPath().equals(metadataPatchUtils.OPERATION_METADATA_PATH)) + && operation.getOp().trim().equalsIgnoreCase(OPERATION_MOVE) + && objectToMatch instanceof DSpaceObject); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataPatchUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataPatchUtils.java new file mode 100644 index 0000000000..bc4c2a6fa7 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataPatchUtils.java @@ -0,0 +1,160 @@ +/** + * 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.app.rest.repository.patch.operation; + +import java.io.IOException; +import java.sql.SQLException; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.StringUtils; +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.MetadataValueRest; +import org.dspace.app.rest.model.patch.JsonValueEvaluator; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.content.MetadataField; +import org.dspace.content.MetadataValue; +import org.dspace.content.service.MetadataFieldService; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Util class for shared methods between the Metadata Operations + * @author Maria Verdonck (Atmire) on 18/11/2019 + */ +@Component +public final class DSpaceObjectMetadataPatchUtils { + + private ObjectMapper objectMapper = new ObjectMapper(); + + @Autowired + private MetadataFieldService metadataFieldService; + + /** + * Path in json body of patch that uses these metadata operations + */ + protected static final String OPERATION_METADATA_PATH = "/metadata"; + + private DSpaceObjectMetadataPatchUtils() { + } + + /** + * Extract metadataValue from Operation by parsing the json and mapping it to a MetadataValueRest + * @param operation Operation whose value is begin parsed + * @return MetadataValueRest extracted from json in operation value + */ + protected MetadataValueRest extractMetadataValueFromOperation(Operation operation) { + MetadataValueRest metadataValue = null; + try { + if (operation.getValue() != null) { + if (operation.getValue() instanceof JsonValueEvaluator) { + JsonNode valueNode = ((JsonValueEvaluator) operation.getValue()).getValueNode(); + if (valueNode.isArray()) { + metadataValue = objectMapper.treeToValue(valueNode.get(0), MetadataValueRest.class); + } else { + metadataValue = objectMapper.treeToValue(valueNode, MetadataValueRest.class); + } + } + if (operation.getValue() instanceof String) { + String valueString = (String) operation.getValue(); + metadataValue = new MetadataValueRest(); + metadataValue.setValue(valueString); + } + } + } catch (IOException e) { + throw new DSpaceBadRequestException("IOException in " + + "DspaceObjectMetadataOperation.extractMetadataValueFromOperation trying to map json from " + + "operation.value to MetadataValue class.", e); + } + if (metadataValue == null) { + throw new DSpaceBadRequestException("Could not extract MetadataValue Object from Operation"); + } + return metadataValue; + } + + /** + * Extracts the mdField String (schema.element.qualifier) from the operation and returns it + * @param operation The patch operation + * @return The mdField (schema.element.qualifier) patch is being performed on + */ + protected String extractMdFieldStringFromOperation(Operation operation) { + String mdElement = StringUtils.substringBetween(operation.getPath(), OPERATION_METADATA_PATH + "/", "/"); + if (mdElement == null) { + mdElement = StringUtils.substringAfter(operation.getPath(), OPERATION_METADATA_PATH + "/"); + if (mdElement == null) { + throw new DSpaceBadRequestException("No metadata field string found in path: " + operation.getPath()); + } + } + return mdElement; + } + + /** + * Converts a metadataValue (database entity) to a REST equivalent of it + * @param md Original metadataValue + * @return The REST equivalent + */ + protected MetadataValueRest convertMdValueToRest(MetadataValue md) { + MetadataValueRest dto = new MetadataValueRest(); + dto.setAuthority(md.getAuthority()); + dto.setConfidence(md.getConfidence()); + dto.setLanguage(md.getLanguage()); + dto.setPlace(md.getPlace()); + dto.setValue(md.getValue()); + return dto; + } + + /** + * Extracts which property of the metadata is being changed in the replace patch operation + * @param partsOfPath Parts of the path of the operation, separated with / + * @return The property that is begin replaced of the metadata + */ + protected String extractPropertyOfMdFromPath(String[] partsOfPath) { + return (partsOfPath.length > 4) ? partsOfPath[4] : null; + } + + /** + * Extracts the new value of the metadata from the operation for the replace patch operation + * @param operation The patch operation + * @return The new value of the metadata being replaced in the patch operation + */ + protected String extractNewValueOfMd(Operation operation) { + if (operation.getValue() instanceof String) { + return (String) operation.getValue(); + } + return null; + } + + /** + * Retrieves metadataField based on the metadata element found in the operation + * @param context Context the retrieve metadataField from service with string + * @param operation Operation of the patch + * @return The metadataField corresponding to the md element string of the operation + */ + protected MetadataField getMetadataField(Context context, Operation operation) throws SQLException { + String mdElement = this.extractMdFieldStringFromOperation(operation); + return metadataFieldService.findByString(context, mdElement, '.'); + } + + /** + * Retrieved the index from the path of the patch operation, if one can be found + * @param path The string from the operation + * @return The index in the path if there is one (path ex: /metadata/dc.title/1 (1 being the index)) + */ + protected String getIndexFromPath(String path) { + String[] partsOfPath = path.split("/"); + // Index of md being patched + return (partsOfPath.length > 3) ? partsOfPath[3] : null; + } + + protected void checkMetadataFieldNotNull(MetadataField metadataField) { + if (metadataField == null) { + throw new DSpaceBadRequestException("There was no metadataField found in path of operation"); + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataRemoveOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataRemoveOperation.java new file mode 100644 index 0000000000..4cf93fcf81 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataRemoveOperation.java @@ -0,0 +1,107 @@ +/** + * 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.app.rest.repository.patch.operation; + +import java.sql.SQLException; +import java.util.Arrays; +import java.util.List; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.exception.UnprocessableEntityException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.content.DSpaceObject; +import org.dspace.content.Item; +import org.dspace.content.MetadataField; +import org.dspace.content.MetadataValue; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.DSpaceObjectService; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Class for PATCH REMOVE operations on Dspace Objects' metadata + * Usage: (can be done on other dso than Item also): + * - REMOVE metadata (with schema.identifier.qualifier) value of a dso (here: Item) + * > Without index: removes all md values of that schema.identifier.qualifier type + * > With index: removes only that select md value + * + * curl -X PATCH http://${dspace.server.url}/api/core/items/<:id-item> -H " + * Content-Type: application/json" -d '[{ "op": "remove", + * "path": "/metadata/schema.identifier.qualifier(/indexOfSpecificMdToRemove)"}]' + * + * + * @author Maria Verdonck (Atmire) on 18/11/2019 + */ +@Component +public class DSpaceObjectMetadataRemoveOperation extends PatchOperation { + + @Autowired + DSpaceObjectMetadataPatchUtils metadataPatchUtils; + + @Override + public R perform(Context context, R resource, Operation operation) throws SQLException { + DSpaceObjectService dsoService = ContentServiceFactory.getInstance().getDSpaceObjectService(resource); + String indexInPath = metadataPatchUtils.getIndexFromPath(operation.getPath()); + MetadataField metadataField = metadataPatchUtils.getMetadataField(context, operation); + + remove(context, resource, dsoService, metadataField, indexInPath); + return resource; + } + + /** + * Removes a metadata from the dso at a given index (or all of that type if no index was given) + * + * @param context context patch is being performed in + * @param dso dso being patched + * @param dsoService service doing the patch in db + * @param metadataField md field being patched + * @param index index at where we want to delete metadata + */ + private void remove(Context context, DSpaceObject dso, DSpaceObjectService dsoService, MetadataField metadataField, + String index) { + metadataPatchUtils.checkMetadataFieldNotNull(metadataField); + try { + if (index == null) { + // remove all metadata of this type + dsoService.clearMetadata(context, dso, metadataField.getMetadataSchema().getName(), + metadataField.getElement(), metadataField.getQualifier(), Item.ANY); + } else { + // remove metadata at index + List metadataValues = dsoService.getMetadata(dso, + metadataField.getMetadataSchema().getName(), metadataField.getElement(), + metadataField.getQualifier(), Item.ANY); + int indexInt = Integer.parseInt(index); + if (indexInt >= 0 && metadataValues.size() > indexInt + && metadataValues.get(indexInt) != null) { + // remove that metadata + dsoService.removeMetadataValues(context, dso, + Arrays.asList(metadataValues.get(indexInt))); + } else { + throw new UnprocessableEntityException("UnprocessableEntityException - There is no metadata of " + + "this type at that index"); + } + } + } catch (NumberFormatException e) { + throw new IllegalArgumentException("This index (" + index + ") is not valid number.", e); + } catch (ArrayIndexOutOfBoundsException e) { + throw new UnprocessableEntityException("There is no metadata of this type at that index"); + } catch (SQLException e) { + throw new DSpaceBadRequestException("SQLException in DspaceObjectMetadataRemoveOperation.remove " + + "trying to remove metadata from dso.", e); + } + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return ((operation.getPath().startsWith(metadataPatchUtils.OPERATION_METADATA_PATH) + || operation.getPath().equals(metadataPatchUtils.OPERATION_METADATA_PATH)) + && operation.getOp().trim().equalsIgnoreCase(OPERATION_REMOVE) + && objectToMatch instanceof DSpaceObject); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java new file mode 100644 index 0000000000..64764db972 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java @@ -0,0 +1,222 @@ +/** + * 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.app.rest.repository.patch.operation; + +import java.sql.SQLException; +import java.util.List; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.exception.UnprocessableEntityException; +import org.dspace.app.rest.model.MetadataValueRest; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.content.DSpaceObject; +import org.dspace.content.Item; +import org.dspace.content.MetadataField; +import org.dspace.content.MetadataValue; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.DSpaceObjectService; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + Class for PATCH REPLACE operations on Dspace Objects' metadata + * Usage: (can be done on other dso than Item also): + * - REPLACE metadata (with schema.identifier.qualifier) value of a dso (here: Item) + * from existing value to new given value + * + * curl -X PATCH http://${dspace.server.url}/api/core/items/<:id-item> -H " + * Content-Type: application/json" -d '[{ "op": "replace", "path": " + * /metadata/schema.identifier.qualifier}", "value": "newMetadataValue"]' + * + * @author Maria Verdonck (Atmire) on 18/11/2019 + */ +@Component +public class DSpaceObjectMetadataReplaceOperation extends PatchOperation { + + @Autowired + DSpaceObjectMetadataPatchUtils metadataPatchUtils; + + @Override + public R perform(Context context, R resource, Operation operation) throws SQLException { + DSpaceObjectService dsoService = ContentServiceFactory.getInstance().getDSpaceObjectService(resource); + MetadataField metadataField = metadataPatchUtils.getMetadataField(context, operation); + String[] partsOfPath = operation.getPath().split("/"); + // Index of md being patched + String indexInPath = (partsOfPath.length > 3) ? partsOfPath[3] : null; + MetadataValueRest metadataValueToReplace = metadataPatchUtils.extractMetadataValueFromOperation(operation); + // Property of md being altered + String propertyOfMd = metadataPatchUtils.extractPropertyOfMdFromPath(partsOfPath); + String newValueMdAttribute = metadataPatchUtils.extractNewValueOfMd(operation); + + replace(context, resource, dsoService, metadataField, metadataValueToReplace, indexInPath, propertyOfMd, + newValueMdAttribute); + return resource; + } + + /** + * Replaces metadata in the dso; 4 cases: + * * - If we replace everything: clears all metadata + * * - If we replace for a single field: clearMetadata on the field & add the new ones + * * - A single existing metadata value: + * * Retrieve the metadatavalue object & make alterations directly on this object + * * - A single existing metadata property: + * * Retrieve the metadatavalue object & make alterations directly on this object + * @param context context patch is being performed in + * @param dso dso being patched + * @param dsoService service doing the patch in db + * @param metadataField possible md field being patched (if null all md gets cleared) + * @param metadataValue value of md element + * @param index possible index of md being replaced + * @param propertyOfMd possible property of md being replaced + * @param valueMdProperty possible new value of property of md being replaced + */ + private void replace(Context context, DSpaceObject dso, DSpaceObjectService dsoService, MetadataField metadataField, + MetadataValueRest metadataValue, String index, String propertyOfMd, String valueMdProperty) { + // replace entire set of metadata + if (metadataField == null) { + this.replaceAllMetadata(context, dso, dsoService); + return; + } + + // replace all metadata for existing key + if (index == null) { + this.replaceMetadataFieldMetadata(context, dso, dsoService, metadataField, metadataValue); + return; + } + // replace single existing metadata value + if (propertyOfMd == null) { + this.replaceSingleMetadataValue(dso, dsoService, metadataField, metadataValue, index); + return; + } + // replace single property of exiting metadata value + this.replaceSinglePropertyOfMdValue(dso, dsoService, metadataField, index, propertyOfMd, valueMdProperty); + } + + /** + * Clears all metadata of dso + * @param context context patch is being performed in + * @param dso dso being patched + * @param dsoService service doing the patch in db + */ + private void replaceAllMetadata(Context context, DSpaceObject dso, DSpaceObjectService dsoService) { + try { + dsoService.clearMetadata(context, dso, Item.ANY, Item.ANY, Item.ANY, Item.ANY); + } catch (SQLException e) { + throw new DSpaceBadRequestException("SQLException in DspaceObjectMetadataOperation.replace trying to " + + "remove and replace metadata from dso.", e); + } + } + + /** + * Replaces all metadata for an existing single mdField with new value(s) + * @param context context patch is being performed in + * @param dso dso being patched + * @param dsoService service doing the patch in db + * @param metadataField md field being patched + * @param metadataValue value of md element + */ + private void replaceMetadataFieldMetadata(Context context, DSpaceObject dso, DSpaceObjectService dsoService, + MetadataField metadataField, MetadataValueRest metadataValue) { + try { + dsoService.clearMetadata(context, dso, metadataField.getMetadataSchema().getName(), + metadataField.getElement(), metadataField.getQualifier(), Item.ANY); + dsoService.addAndShiftRightMetadata(context, dso, metadataField.getMetadataSchema().getName(), + metadataField.getElement(), metadataField.getQualifier(), metadataValue.getLanguage(), + metadataValue.getValue(), metadataValue.getAuthority(), metadataValue.getConfidence(), -1); + } catch (SQLException e) { + throw new DSpaceBadRequestException("SQLException in DspaceObjectMetadataOperation.replace trying to " + + "remove and replace metadata from dso.", e); + } + } + + /** + * Replaces metadata value of a single metadataValue object + * Retrieve the metadatavalue object & make alerations directly on this object + * @param dso dso being patched + * @param dsoService service doing the patch in db + * @param metadataField md field being patched + * @param metadataValue new value of md element + * @param index index of md being replaced + */ + // replace single existing metadata value + private void replaceSingleMetadataValue(DSpaceObject dso, DSpaceObjectService dsoService, + MetadataField metadataField, MetadataValueRest metadataValue, + String index) { + try { + List metadataValues = dsoService.getMetadata(dso, + metadataField.getMetadataSchema().getName(), metadataField.getElement(), + metadataField.getQualifier(), Item.ANY); + int indexInt = Integer.parseInt(index); + if (indexInt >= 0 && metadataValues.size() > indexInt + && metadataValues.get(indexInt) != null) { + // Alter this existing md + MetadataValue existingMdv = metadataValues.get(indexInt); + existingMdv.setAuthority(metadataValue.getAuthority()); + existingMdv.setConfidence(metadataValue.getConfidence()); + existingMdv.setLanguage(metadataValue.getLanguage()); + existingMdv.setValue(metadataValue.getValue()); + dsoService.setMetadataModified(dso); + } else { + throw new UnprocessableEntityException("There is no metadata of this type at that index"); + } + } catch (NumberFormatException e) { + throw new IllegalArgumentException("This index (" + index + ") is not valid number.", e); + } + } + + /** + * Replaces single property of a specific mdValue object + * @param dso dso being patched + * @param dsoService service doing the patch in db + * @param metadataField md field being patched + * @param index index of md being replaced + * @param propertyOfMd property of md being replaced + * @param valueMdProperty new value of property of md being replaced + */ + private void replaceSinglePropertyOfMdValue(DSpaceObject dso, DSpaceObjectService dsoService, + MetadataField metadataField, + String index, String propertyOfMd, String valueMdProperty) { + try { + List metadataValues = dsoService.getMetadata(dso, + metadataField.getMetadataSchema().getName(), metadataField.getElement(), + metadataField.getQualifier(), Item.ANY); + int indexInt = Integer.parseInt(index); + if (indexInt >= 0 && metadataValues.size() > indexInt && metadataValues.get(indexInt) != null) { + // Alter only asked propertyOfMd + MetadataValue existingMdv = metadataValues.get(indexInt); + if (propertyOfMd.equals("authority")) { + existingMdv.setAuthority(valueMdProperty); + } + if (propertyOfMd.equals("confidence")) { + existingMdv.setConfidence(Integer.valueOf(valueMdProperty)); + } + if (propertyOfMd.equals("language")) { + existingMdv.setLanguage(valueMdProperty); + } + if (propertyOfMd.equals("value")) { + existingMdv.setValue(valueMdProperty); + } + dsoService.setMetadataModified(dso); + } else { + throw new UnprocessableEntityException("There is no metadata of this type at that index"); + } + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Not all numbers are valid numbers. " + + "(Index and confidence should be nr)", e); + } + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return ((operation.getPath().startsWith(metadataPatchUtils.OPERATION_METADATA_PATH) + || operation.getPath().equals(metadataPatchUtils.OPERATION_METADATA_PATH)) + && operation.getOp().trim().equalsIgnoreCase(OPERATION_REPLACE) + && objectToMatch instanceof DSpaceObject); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonCertificateReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonCertificateReplaceOperation.java new file mode 100644 index 0000000000..1b79e99b2c --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonCertificateReplaceOperation.java @@ -0,0 +1,51 @@ +/** + * 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.app.rest.repository.patch.operation; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.core.Context; +import org.dspace.eperson.EPerson; +import org.springframework.stereotype.Component; + +/** + * Implementation for EPerson requires certificate patches. + * + * Example: + * curl -X PATCH http://${dspace.server.url}/api/epersons/eperson/<:id-eperson> -H " + * Content-Type: application/json" -d '[{ "op": "replace", "path": " + * /certificate", "value": true|false]' + * + */ +@Component +public class EPersonCertificateReplaceOperation extends PatchOperation { + + /** + * Path in json body of patch that uses this operation + */ + private static final String OPERATION_PATH_CERTIFICATE = "/certificate"; + + @Override + public R perform(Context context, R object, Operation operation) { + checkOperationValue(operation.getValue()); + Boolean requireCert = getBooleanOperationValue(operation.getValue()); + if (supports(object, operation)) { + EPerson eperson = (EPerson) object; + eperson.setRequireCertificate(requireCert); + return object; + } else { + throw new DSpaceBadRequestException("EPersonCertificateReplaceOperation does not support this operation."); + } + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof EPerson && operation.getOp().trim().equalsIgnoreCase(OPERATION_REPLACE) + && operation.getPath().trim().equalsIgnoreCase(OPERATION_PATH_CERTIFICATE)); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonEmailReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonEmailReplaceOperation.java new file mode 100644 index 0000000000..2bc155afa5 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonEmailReplaceOperation.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.app.rest.repository.patch.operation; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.core.Context; +import org.dspace.eperson.EPerson; +import org.springframework.stereotype.Component; + +/** + * Implementation for EPerson password patches. + * + * Example: + * curl -X PATCH http://${dspace.server.url}/api/epersons/eperson/<:id-eperson> -H " + * Content-Type: application/json" -d '[{ "op": "replace", "path": " + * /email", "value": "new@email"]' + * + */ +@Component +public class EPersonEmailReplaceOperation extends PatchOperation { + + /** + * Path in json body of patch that uses this operation + */ + private static final String OPERATION_PATH_EMAIL = "/email"; + + @Override + public R perform(Context context, R object, Operation operation) { + checkOperationValue(operation.getValue()); + if (supports(object, operation)) { + EPerson eperson = (EPerson) object; + checkModelForExistingValue(eperson); + eperson.setEmail((String) operation.getValue()); + return object; + } else { + throw new DSpaceBadRequestException("EPersonEmailReplaceOperation does not support this operation"); + } + } + + /** + * Checks whether the email of Eperson has an existing value to replace + * @param ePerson Object on which patch is being done + */ + private void checkModelForExistingValue(EPerson ePerson) { + if (ePerson.getEmail() == null) { + throw new DSpaceBadRequestException("Attempting to replace a non-existent value (e-mail)."); + } + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof EPerson && operation.getOp().trim().equalsIgnoreCase(OPERATION_REPLACE) + && operation.getPath().trim().equalsIgnoreCase(OPERATION_PATH_EMAIL)); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonLoginReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonLoginReplaceOperation.java new file mode 100644 index 0000000000..a28580186c --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonLoginReplaceOperation.java @@ -0,0 +1,51 @@ +/** + * 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.app.rest.repository.patch.operation; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.core.Context; +import org.dspace.eperson.EPerson; +import org.springframework.stereotype.Component; + +/** + * Implementation for EPerson canLogin patches. + * + * Example: + * curl -X PATCH http://${dspace.server.url}/api/epersons/eperson/<:id-eperson> -H " + * Content-Type: application/json" -d '[{ "op": "replace", "path": " + * /canLogin", "value": true|false]' + * + */ +@Component +public class EPersonLoginReplaceOperation extends PatchOperation { + + /** + * Path in json body of patch that uses this operation + */ + private static final String OPERATION_PATH_CANLOGIN = "/canLogin"; + + @Override + public R perform(Context context, R object, Operation operation) { + checkOperationValue(operation.getValue()); + Boolean canLogin = getBooleanOperationValue(operation.getValue()); + if (supports(object, operation)) { + EPerson eperson = (EPerson) object; + eperson.setCanLogIn(canLogin); + return object; + } else { + throw new DSpaceBadRequestException("EPersonLoginReplaceOperation does not support this operation"); + } + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof EPerson && operation.getOp().trim().equalsIgnoreCase(OPERATION_REPLACE) + && operation.getPath().trim().equalsIgnoreCase(OPERATION_PATH_CANLOGIN)); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonNetidReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonNetidReplaceOperation.java new file mode 100644 index 0000000000..f014eaeb1f --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonNetidReplaceOperation.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.app.rest.repository.patch.operation; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.core.Context; +import org.dspace.eperson.EPerson; +import org.springframework.stereotype.Component; + +/** + * Implementation for EPerson netid patches. + * + * Example: + * curl -X PATCH http://${dspace.server.url}/api/epersons/eperson/<:id-eperson> -H " + * Content-Type: application/json" -d '[{ "op": "replace", "path": " + * /netid", "value": "newNetId"]' + * + */ +@Component +public class EPersonNetidReplaceOperation extends PatchOperation { + + /** + * Path in json body of patch that uses this operation + */ + private static final String OPERATION_PATH_NETID = "/netid"; + + @Override + public R perform(Context context, R object, Operation operation) { + checkOperationValue(operation.getValue()); + if (supports(object, operation)) { + EPerson eperson = (EPerson) object; + checkModelForExistingValue(eperson); + eperson.setNetid((String) operation.getValue()); + return object; + } else { + throw new DSpaceBadRequestException("EPersonNetidReplaceOperation does not support this operation"); + } + } + + /** + * Checks whether the netID of Eperson has an existing value to replace + * @param ePerson Object on which patch is being done + */ + private void checkModelForExistingValue(EPerson ePerson) { + if (ePerson.getNetid() == null) { + throw new DSpaceBadRequestException("Attempting to replace a non-existent value (netID)."); + } + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof EPerson && operation.getOp().trim().equalsIgnoreCase(OPERATION_REPLACE) + && operation.getPath().trim().equalsIgnoreCase(OPERATION_PATH_NETID)); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java new file mode 100644 index 0000000000..00b30e24f1 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java @@ -0,0 +1,66 @@ +/** + * 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.app.rest.repository.patch.operation; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.core.Context; +import org.dspace.eperson.EPerson; +import org.dspace.eperson.factory.EPersonServiceFactory; +import org.dspace.eperson.service.EPersonService; +import org.springframework.stereotype.Component; + +/** + * Implementation for EPerson password patches. + * + * Example: + * curl -X PATCH http://${dspace.server.url}/api/epersons/eperson/<:id-eperson> -H " + * Content-Type: application/json" -d '[{ "op": "replace", "path": " + * /password", "value": "newpassword"]' + * + */ +@Component +public class EPersonPasswordReplaceOperation extends PatchOperation { + + /** + * Path in json body of patch that uses this operation + */ + public static final String OPERATION_PASSWORD_CHANGE = "/password"; + protected EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); + + @Override + public R perform(Context context, R object, Operation operation) { + checkOperationValue(operation.getValue()); + if (supports(object, operation)) { + EPerson eperson = (EPerson) object; + checkModelForExistingValue(eperson); + ePersonService.setPassword(eperson, (String) operation.getValue()); + return object; + } else { + throw new DSpaceBadRequestException("EPersonPasswordReplaceOperation does not support this operation"); + } + } + + /** + * Checks whether the ePerson has a password via the ePersonService to checking if it has a non null password hash + * throws a DSpaceBadRequestException if not pw hash was present + * @param ePerson Object on which patch is being performed + */ + private void checkModelForExistingValue(EPerson ePerson) { + if (ePersonService.getPasswordHash(ePerson) == null + || ePersonService.getPasswordHash(ePerson).getHash() == null) { + throw new DSpaceBadRequestException("Attempting to replace a non-existent value (netID)."); + } + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof EPerson && operation.getOp().trim().equalsIgnoreCase(OPERATION_REPLACE) + && operation.getPath().trim().equalsIgnoreCase(OPERATION_PASSWORD_CHANGE)); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/ItemDiscoverableReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/ItemDiscoverableReplaceOperation.java new file mode 100644 index 0000000000..df17d4e92d --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/ItemDiscoverableReplaceOperation.java @@ -0,0 +1,56 @@ +/** + * 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.app.rest.repository.patch.operation; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.exception.UnprocessableEntityException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.content.Item; +import org.dspace.core.Context; +import org.springframework.stereotype.Component; + +/** + * This is the implementation for Item 'discoverable' patches. + * + * Example: + * curl -X PATCH http://${dspace.server.url}/api/core/items/<:id-item> -H " + * Content-Type: application/json" -d '[{ "op": "replace", "path": " + * /discoverable", "value": true|false]' + * + */ +@Component +public class ItemDiscoverableReplaceOperation extends PatchOperation { + + /** + * Path in json body of patch that uses this operation + */ + private static final String OPERATION_PATH_DISCOVERABLE = "/discoverable"; + + @Override + public R perform(Context context, R object, Operation operation) { + checkOperationValue(operation.getValue()); + Boolean discoverable = getBooleanOperationValue(operation.getValue()); + if (supports(object, operation)) { + Item item = (Item) object; + if (discoverable && item.getTemplateItemOf() != null) { + throw new UnprocessableEntityException("A template item cannot be discoverable."); + } + item.setDiscoverable(discoverable); + return object; + } else { + throw new DSpaceBadRequestException("ItemDiscoverableReplaceOperation does not support this operation"); + } + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof Item && operation.getOp().trim().equalsIgnoreCase(OPERATION_REPLACE) + && operation.getPath().trim().equalsIgnoreCase(OPERATION_PATH_DISCOVERABLE)); + } + +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/ItemWithdrawReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/ItemWithdrawReplaceOperation.java new file mode 100644 index 0000000000..42a47ee7e9 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/ItemWithdrawReplaceOperation.java @@ -0,0 +1,94 @@ +/** + * 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.app.rest.repository.patch.operation; + +import java.sql.SQLException; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.exception.RESTAuthorizationException; +import org.dspace.app.rest.exception.UnprocessableEntityException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Item; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.ItemService; +import org.dspace.core.Context; +import org.springframework.stereotype.Component; + +/** + * This is the implementation for Item 'withdrawn' patches. + *

    + * Example: + * curl -X PATCH http://${dspace.server.url}/api/core/items/<:id-item> -H " + * Content-Type: application/json" -d '[{ "op": "replace", "path": " + * /withdrawn", "value": true|false]' + * + */ +@Component +public class ItemWithdrawReplaceOperation extends PatchOperation { + + /** + * Path in json body of patch that uses this operation + */ + private static final String OPERATION_PATH_WITHDRAW = "/withdrawn"; + private static final ItemService itemService = ContentServiceFactory.getInstance().getItemService(); + + @Override + public R perform(Context context, R object, Operation operation) { + checkOperationValue(operation.getValue()); + + boolean withdraw = getBooleanOperationValue(operation.getValue()); + + if (supports(object, operation)) { + Item item = (Item) object; + // This is a request to withdraw the item. + try { + if (withdraw) { + if (item.getTemplateItemOf() != null) { + throw new UnprocessableEntityException("A template item cannot be withdrawn."); + } + + // The item is currently not withdrawn and also not archived. Is this a possible situation? + if (!item.isWithdrawn() && !item.isArchived()) { + throw new UnprocessableEntityException("Cannot withdraw item when it is not in archive."); + } + // Item is already withdrawn. No-op, 200 response. + // (The operation is not idempotent since it results in a provenance note in the record.) + if (item.isWithdrawn()) { + return object; + } + itemService.withdraw(context, item); + return object; + + } else { + // No need to reinstate item if it has not previously been not withdrawn. + // No-op, 200 response. (The operation is not idempotent since it results + // in a provenance note in the record.) + if (!item.isWithdrawn()) { + return object; + } + itemService.reinstate(context, item); + return object; + } + } catch (AuthorizeException e) { + throw new RESTAuthorizationException("Unauthorized user for item withdrawal/reinstation"); + } catch (SQLException e) { + throw new DSpaceBadRequestException("SQL exception during item withdrawal/reinstation"); + } + } else { + throw new DSpaceBadRequestException("ItemWithdrawReplaceOperation does not support this operation"); + } + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof Item && operation.getOp().trim().equalsIgnoreCase(OPERATION_REPLACE) + && operation.getPath().trim().equalsIgnoreCase(OPERATION_PATH_WITHDRAW)); + } + +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/PatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/PatchOperation.java new file mode 100644 index 0000000000..fa82aef374 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/PatchOperation.java @@ -0,0 +1,86 @@ +/** + * 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.app.rest.repository.patch.operation; + +import java.sql.SQLException; + +import org.apache.commons.lang3.BooleanUtils; +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.core.Context; + +/** + * Base class for all resource patch operations. + */ +public abstract class PatchOperation { + + // All PATCH operations's string (in operation.getOp()) + protected static final String OPERATION_REPLACE = "replace"; + protected static final String OPERATION_ADD = "add"; + protected static final String OPERATION_COPY = "copy"; + protected static final String OPERATION_MOVE = "move"; + protected static final String OPERATION_REMOVE = "remove"; + + /** + * Updates the rest model by applying the patch operation. + * + * @param context context of patch operation + * @param resource the dso. + * @param operation the patch operation. + * @return the patched dso + * @throws DSpaceBadRequestException + */ + public abstract M perform(Context context, M resource, Operation operation) throws SQLException; + + /** + * Throws PatchBadRequestException for missing operation value. + * + * @param value + * the value to test + */ + public void checkOperationValue(Object value) { + if (value == null) { + throw new DSpaceBadRequestException("No value provided for the operation."); + } + if (value instanceof String && (((String) value).trim().isBlank())) { + throw new DSpaceBadRequestException("Value can't be empty or just spaces."); + } + } + + /** + * Allows clients to use either a boolean or a string representation of boolean value. + * + * @param value the operation value + * @return the original or derived boolean value + * @throws DSpaceBadRequestException + */ + Boolean getBooleanOperationValue(Object value) { + Boolean bool; + + if (value instanceof String) { + bool = BooleanUtils.toBooleanObject((String) value); + if (bool == null) { + // make sure the string was converted to boolean. + throw new DSpaceBadRequestException("Boolean value not provided."); + } + } else { + bool = (Boolean) value; + } + return bool; + } + + /** + * Determines whether or not this Patch Operation can do this patch (Object of operation and path gets checked) + * @param objectToMatch Object whose class must be instance of type object + * for which this PatchOperation was created + * @param operation Operation of the patch, should match this type of Patch Operation + * @return True if this PatchOperation class can do the patch for this given dso type and Path + */ + public abstract boolean supports(Object objectToMatch, Operation operation); + +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyDescriptionAddOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyDescriptionAddOperation.java new file mode 100644 index 0000000000..e6ca562623 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyDescriptionAddOperation.java @@ -0,0 +1,78 @@ +/** + * 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.app.rest.repository.patch.operation.resourcePolicy; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.repository.patch.operation.PatchOperation; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Implementation for ResourcePolicy description ADD patch. + * + * Example: + * + * curl -X PATCH http://${dspace.server.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " + * Content-Type: application/json" -d '[{ "op": "add", "path": " + * /description", "value": "YYYY-MM-DD"]' + * + * + * @author Maria Verdonck (Atmire) on 14/02/2020 + * @author Andrea Bollini (andrea.bollini at 4science.it) + */ +@Component +public class ResourcePolicyDescriptionAddOperation extends PatchOperation { + + @Autowired + ResourcePolicyUtils resourcePolicyUtils; + + @Override + public R perform(Context context, R resource, Operation operation) { + checkOperationValue(operation.getValue()); + if (this.supports(resource, operation)) { + ResourcePolicy resourcePolicy = (ResourcePolicy) resource; + this.checkResourcePolicyForNonExistingDescriptionValue(resourcePolicy); + this.add(resourcePolicy, operation); + return resource; + } else { + throw new DSpaceBadRequestException(this.getClass() + " does not support this operation"); + } + } + + /** + * Performs the actual add description of resourcePolicy operation + * + * @param resourcePolicy resourcePolicy being patched + * @param operation patch operation + */ + private void add(ResourcePolicy resourcePolicy, Operation operation) { + String description = (String) operation.getValue(); + resourcePolicy.setRpDescription(description); + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof ResourcePolicy && operation.getOp().trim().equalsIgnoreCase(OPERATION_ADD) + && operation.getPath().trim().equalsIgnoreCase(resourcePolicyUtils.OPERATION_PATH_DESCRIPTION)); + } + + /** + * Throws PatchBadRequestException if a value is already set in the /description path. + * + * @param resource the resource to update + + */ + void checkResourcePolicyForNonExistingDescriptionValue(ResourcePolicy resource) { + if (resource.getRpDescription() != null) { + throw new DSpaceBadRequestException("Attempting to add a value to an already existing path."); + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyDescriptionRemoveOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyDescriptionRemoveOperation.java new file mode 100644 index 0000000000..a085f2b273 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyDescriptionRemoveOperation.java @@ -0,0 +1,63 @@ +/** + * 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.app.rest.repository.patch.operation.resourcePolicy; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.repository.patch.operation.PatchOperation; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Implementation for ResourcePolicy description DELETE patch. + * + * Example: + * + * + * curl -X PATCH http://${dspace.server.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " + * Content-Type: application/json" -d '[{ "op": "remove", "path": " + * /description"]' + * + * + * @author Maria Verdonck (Atmire) on 14/02/2020 + * @author Andrea Bollini (andrea.bollini at 4science.it) + */ +@Component +public class ResourcePolicyDescriptionRemoveOperation extends PatchOperation { + + @Autowired + ResourcePolicyUtils resourcePolicyUtils; + + @Override + public R perform(Context context, R resource, Operation operation) { + if (this.supports(resource, operation)) { + ResourcePolicy resourcePolicy = (ResourcePolicy) resource; + resourcePolicyUtils.checkResourcePolicyForExistingDescriptionValue(resourcePolicy, operation); + this.delete(resourcePolicy); + return resource; + } else { + throw new DSpaceBadRequestException(this.getClass() + " does not support this operation"); + } + } + + /** + * Performs the actual delete description of resourcePolicy operation + * @param resourcePolicy resourcePolicy being patched + */ + private void delete(ResourcePolicy resourcePolicy) { + resourcePolicy.setRpDescription(null); + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof ResourcePolicy && operation.getOp().trim().equalsIgnoreCase(OPERATION_REMOVE) + && operation.getPath().trim().equalsIgnoreCase(resourcePolicyUtils.OPERATION_PATH_DESCRIPTION)); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyDescriptionReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyDescriptionReplaceOperation.java new file mode 100644 index 0000000000..c3acacdb32 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyDescriptionReplaceOperation.java @@ -0,0 +1,65 @@ +/** + * 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.app.rest.repository.patch.operation.resourcePolicy; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.repository.patch.operation.PatchOperation; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Implementation for ResourcePolicy description REPLACE patch. + * + * Example: + * + * curl -X PATCH http://${dspace.server.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " + * Content-Type: application/json" -d '[{ "op": "replace", "path": " + * /description", "value": "my description"]' + * + * + * @author Maria Verdonck (Atmire) on 14/02/2020 + * @author Andrea Bollini (andrea.bollini at 4science.it) + */ +@Component +public class ResourcePolicyDescriptionReplaceOperation extends PatchOperation { + + @Autowired + ResourcePolicyUtils resourcePolicyUtils; + + @Override + public R perform(Context context, R resource, Operation operation) { + checkOperationValue(operation.getValue()); + if (this.supports(resource, operation)) { + ResourcePolicy resourcePolicy = (ResourcePolicy) resource; + resourcePolicyUtils.checkResourcePolicyForExistingDescriptionValue(resourcePolicy, operation); + this.replace(resourcePolicy, operation); + return resource; + } else { + throw new DSpaceBadRequestException(this.getClass() + " does not support this operation"); + } + } + + /** + * Performs the actual replace description of resourcePolicy operation + * @param resourcePolicy resourcePolicy being patched + * @param operation patch operation + */ + private void replace(ResourcePolicy resourcePolicy, Operation operation) { + String newDescription = (String) operation.getValue(); + resourcePolicy.setRpDescription(newDescription); + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof ResourcePolicy && operation.getOp().trim().equalsIgnoreCase(OPERATION_REPLACE) + && operation.getPath().trim().equalsIgnoreCase(resourcePolicyUtils.OPERATION_PATH_DESCRIPTION)); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyEndDateAddOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyEndDateAddOperation.java new file mode 100644 index 0000000000..54572614c9 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyEndDateAddOperation.java @@ -0,0 +1,75 @@ +/** + * 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.app.rest.repository.patch.operation.resourcePolicy; + +import java.text.ParseException; +import java.util.Date; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.repository.patch.operation.PatchOperation; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Implementation for ResourcePolicy startDate ADD patch. + * + * Example: + * + * + * curl -X PATCH http://${dspace.server.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " + * Content-Type: application/json" -d '[{ "op": "add", "path": " + * /endDate", "value": "YYYY-MM-DD"]' + * + * + * @author Maria Verdonck (Atmire) on 14/02/2020 + * @author Andrea Bollini (andrea.bollini at 4science.it) + */ +@Component +public class ResourcePolicyEndDateAddOperation extends PatchOperation { + + @Autowired + ResourcePolicyUtils resourcePolicyUtils; + + @Override + public R perform(Context context, R resource, Operation operation) { + checkOperationValue(operation.getValue()); + if (this.supports(resource, operation)) { + ResourcePolicy resourcePolicy = (ResourcePolicy) resource; + resourcePolicyUtils.checkResourcePolicyForExistingEndDateValue(resourcePolicy, operation); + resourcePolicyUtils.checkResourcePolicyForConsistentEndDateValue(resourcePolicy, operation); + this.add(resourcePolicy, operation); + return resource; + } else { + throw new DSpaceBadRequestException(this.getClass() + " does not support this operation"); + } + } + + /** + * Performs the actual add endDate of resourcePolicy operation + * @param resourcePolicy resourcePolicy being patched + * @param operation patch operation + */ + private void add(ResourcePolicy resourcePolicy, Operation operation) { + String dateS = (String) operation.getValue(); + try { + Date date = resourcePolicyUtils.simpleDateFormat.parse(dateS); + resourcePolicy.setEndDate(date); + } catch (ParseException e) { + throw new DSpaceBadRequestException("Invalid endDate value", e); + } + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof ResourcePolicy && operation.getOp().trim().equalsIgnoreCase(OPERATION_ADD) + && operation.getPath().trim().equalsIgnoreCase(resourcePolicyUtils.OPERATION_PATH_ENDDATE)); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyEndDateRemoveOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyEndDateRemoveOperation.java new file mode 100644 index 0000000000..c7b6a43a45 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyEndDateRemoveOperation.java @@ -0,0 +1,63 @@ +/** + * 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.app.rest.repository.patch.operation.resourcePolicy; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.repository.patch.operation.PatchOperation; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Implementation for ResourcePolicy endDate DELETE patch. + * + * Example: + * + * + * curl -X PATCH http://${dspace.server.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " + * Content-Type: application/json" -d '[{ "op": "remove", "path": " + * /endDate"]' + * + * + * @author Maria Verdonck (Atmire) on 14/02/2020 + * @author Andrea Bollini (andrea.bollini at 4science.it) + */ +@Component +public class ResourcePolicyEndDateRemoveOperation extends PatchOperation { + + @Autowired + ResourcePolicyUtils resourcePolicyUtils; + + @Override + public R perform(Context context, R resource, Operation operation) { + if (this.supports(resource, operation)) { + ResourcePolicy resourcePolicy = (ResourcePolicy) resource; + resourcePolicyUtils.checkResourcePolicyForExistingEndDateValue(resourcePolicy, operation); + this.delete(resourcePolicy); + return resource; + } else { + throw new DSpaceBadRequestException(this.getClass() + " does not support this operation"); + } + } + + /** + * Performs the actual delete endDate of resourcePolicy operation + * @param resourcePolicy resourcePolicy being patched + */ + private void delete(ResourcePolicy resourcePolicy) { + resourcePolicy.setEndDate(null); + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof ResourcePolicy && operation.getOp().trim().equalsIgnoreCase(OPERATION_REMOVE) + && operation.getPath().trim().equalsIgnoreCase(resourcePolicyUtils.OPERATION_PATH_ENDDATE)); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyEndDateReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyEndDateReplaceOperation.java new file mode 100644 index 0000000000..628301e213 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyEndDateReplaceOperation.java @@ -0,0 +1,75 @@ +/** + * 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.app.rest.repository.patch.operation.resourcePolicy; + +import java.text.ParseException; +import java.util.Date; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.repository.patch.operation.PatchOperation; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.core.Context; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Implementation for ResourcePolicy endDate REPLACE patch. + * + * Example: + * + * curl -X PATCH http://${dspace.server.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " + * Content-Type: application/json" -d '[{ "op": "replace", "path": " + * /endDate", "value": "YYYY-MM-DD"]' + * + * + * @author Maria Verdonck (Atmire) on 14/02/2020 + * @author Andrea Bollini (andrea.bollini at 4science.it) + */ +@Component +public class ResourcePolicyEndDateReplaceOperation extends PatchOperation { + + @Autowired + ResourcePolicyUtils resourcePolicyUtils; + + @Override + public R perform(Context context, R resource, Operation operation) { + checkOperationValue(operation.getValue()); + if (this.supports(resource, operation)) { + ResourcePolicy resourcePolicy = (ResourcePolicy) resource; + resourcePolicyUtils.checkResourcePolicyForExistingEndDateValue(resourcePolicy, operation); + resourcePolicyUtils.checkResourcePolicyForConsistentEndDateValue(resourcePolicy, operation); + this.replace(resourcePolicy, operation); + return resource; + } else { + throw new DSpaceBadRequestException(this.getClass() + " does not support this operation"); + } + } + + /** + * Performs the actual replace endDate of resourcePolicy operation + * @param resourcePolicy resourcePolicy being patched + * @param operation patch operation + */ + private void replace(ResourcePolicy resourcePolicy, Operation operation) { + String dateS = (String) operation.getValue(); + try { + Date date = resourcePolicyUtils.simpleDateFormat.parse(dateS); + resourcePolicy.setEndDate(date); + } catch (ParseException e) { + throw new DSpaceBadRequestException("Invalid endDate value", e); + } + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof ResourcePolicy && operation.getOp().trim().equalsIgnoreCase(OPERATION_REPLACE) + && operation.getPath().trim().equalsIgnoreCase(resourcePolicyUtils.OPERATION_PATH_ENDDATE)); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyNameAddOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyNameAddOperation.java new file mode 100644 index 0000000000..e19b50e837 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyNameAddOperation.java @@ -0,0 +1,76 @@ +/** + * 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.app.rest.repository.patch.operation.resourcePolicy; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.repository.patch.operation.PatchOperation; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Implementation for ResourcePolicy name ADD patch. + * + * Example: + * curl -X PATCH http://${dspace.server.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " + * Content-Type: application/json" -d '[{ "op": "add", "path": " + * /name", "value": "New Name"]' + * + * + * @author Maria Verdonck (Atmire) on 14/02/2020 + * @author Andrea Bollini (andrea.bollini at 4science.it) + */ +@Component +public class ResourcePolicyNameAddOperation extends PatchOperation { + + @Autowired + ResourcePolicyUtils resourcePolicyUtils; + + @Override + public R perform(Context context, R resource, Operation operation) { + checkOperationValue(operation.getValue()); + if (this.supports(resource, operation)) { + ResourcePolicy resourcePolicy = (ResourcePolicy) resource; + this.checkModelForNotExistingValue(resourcePolicy); + this.add(resourcePolicy, operation); + return resource; + } else { + throw new DSpaceBadRequestException(this.getClass() + " does not support this operation"); + } + } + + /** + * Performs the actual add name of resourcePolicy operation + * + * @param resourcePolicy resourcePolicy being patched + * @param operation patch operation + */ + public void add(ResourcePolicy resourcePolicy, Operation operation) { + String name = (String) operation.getValue(); + resourcePolicy.setRpName(name); + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof ResourcePolicy && operation.getOp().trim().equalsIgnoreCase(OPERATION_ADD) + && operation.getPath().trim().equalsIgnoreCase(resourcePolicyUtils.OPERATION_PATH_NAME)); + } + + /** + * Throws PatchBadRequestException if a value is already set in the /name path. + * + * @param resource the resource to update + */ + private void checkModelForNotExistingValue(ResourcePolicy resource) { + if (resource.getRpName() != null) { + throw new DSpaceBadRequestException("Attempting to add a value to an already existing path."); + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyNameRemoveOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyNameRemoveOperation.java new file mode 100644 index 0000000000..a71b7304f2 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyNameRemoveOperation.java @@ -0,0 +1,62 @@ +/** + * 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.app.rest.repository.patch.operation.resourcePolicy; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.repository.patch.operation.PatchOperation; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Implementation for ResourcePolicy name DELETE patch. + * + * Example: + * + * curl -X PATCH http://${dspace.server.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " + * Content-Type: application/json" -d '[{ "op": "remove", "path": " + * /name"]' + * + * + * @author Maria Verdonck (Atmire) on 14/02/2020 + * @author Andrea Bollini (andrea.bollini at 4science.it) + */ +@Component +public class ResourcePolicyNameRemoveOperation extends PatchOperation { + + @Autowired + ResourcePolicyUtils resourcePolicyUtils; + + @Override + public R perform(Context context, R resource, Operation operation) { + if (this.supports(resource, operation)) { + ResourcePolicy resourcePolicy = (ResourcePolicy) resource; + resourcePolicyUtils.checkResourcePolicyForExistingNameValue(resourcePolicy, operation); + this.delete(resourcePolicy); + return resource; + } else { + throw new DSpaceBadRequestException(this.getClass() + " does not support this operation"); + } + } + + /** + * Performs the actual delete name of resourcePolicy operation + * @param resourcePolicy resourcePolicy being patched + */ + private void delete(ResourcePolicy resourcePolicy) { + resourcePolicy.setRpName(null); + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof ResourcePolicy && operation.getOp().trim().equalsIgnoreCase(OPERATION_REMOVE) + && operation.getPath().trim().equalsIgnoreCase(resourcePolicyUtils.OPERATION_PATH_NAME)); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyNameReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyNameReplaceOperation.java new file mode 100644 index 0000000000..b692455e35 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyNameReplaceOperation.java @@ -0,0 +1,65 @@ +/** + * 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.app.rest.repository.patch.operation.resourcePolicy; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.repository.patch.operation.PatchOperation; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Implementation for ResourcePolicy name REPLACE patch. + * + * Example: + * Example: + * curl -X PATCH http://${dspace.server.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " + * Content-Type: application/json" -d '[{ "op": "replace", "path": " + * /name", "value": "New Name"]' + * + * + * @author Maria Verdonck (Atmire) on 14/02/2020 + * @author Andrea Bollini (andrea.bollini at 4science.it) + */ +@Component +public class ResourcePolicyNameReplaceOperation extends PatchOperation { + + @Autowired + ResourcePolicyUtils resourcePolicyUtils; + + @Override + public R perform(Context context, R resource, Operation operation) { + checkOperationValue(operation.getValue()); + if (this.supports(resource, operation)) { + ResourcePolicy resourcePolicy = (ResourcePolicy) resource; + resourcePolicyUtils.checkResourcePolicyForExistingNameValue(resourcePolicy, operation); + this.replace(resourcePolicy, operation); + return resource; + } else { + throw new DSpaceBadRequestException(this.getClass() + " does not support this operation"); + } + } + + /** + * Performs the actual replace name of resourcePolicy operation + * @param resourcePolicy resourcePolicy being patched + * @param operation patch operation + */ + private void replace(ResourcePolicy resourcePolicy, Operation operation) { + String newName = (String) operation.getValue(); + resourcePolicy.setRpName(newName); + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof ResourcePolicy && operation.getOp().trim().equalsIgnoreCase(OPERATION_REPLACE) + && operation.getPath().trim().equalsIgnoreCase(resourcePolicyUtils.OPERATION_PATH_NAME)); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyStartDateAddOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyStartDateAddOperation.java new file mode 100644 index 0000000000..f8f74b6586 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyStartDateAddOperation.java @@ -0,0 +1,86 @@ +/** + * 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.app.rest.repository.patch.operation.resourcePolicy; + +import java.text.ParseException; +import java.util.Date; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.repository.patch.operation.PatchOperation; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Implementation for ResourcePolicy startDate ADD patch. + * + * Example: + * + * curl -X PATCH http://${dspace.server.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " + * Content-Type: application/json" -d '[{ "op": "add", "path": " + * /startDate", "value": "YYYY-MM-DD"]' + * + * + * @author Maria Verdonck (Atmire) on 14/02/2020 + * @author Andrea Bollini (andrea.bollini at 4science.it) + */ +@Component +public class ResourcePolicyStartDateAddOperation extends PatchOperation { + + @Autowired + ResourcePolicyUtils resourcePolicyUtils; + + @Override + public R perform(Context context, R resource, Operation operation) { + checkOperationValue(operation.getValue()); + if (this.supports(resource, operation)) { + ResourcePolicy resourcePolicy = (ResourcePolicy) resource; + this.checkResourcePolicyForNotExistingStartDateValue(resourcePolicy); + resourcePolicyUtils.checkResourcePolicyForConsistentStartDateValue(resourcePolicy, operation); + this.add(resourcePolicy, operation); + return resource; + } else { + throw new DSpaceBadRequestException(this.getClass() + " does not support this operation"); + } + } + + /** + * Performs the actual add startDate of resourcePolicy operation + * + * @param resourcePolicy resourcePolicy being patched + * @param operation patch operation + */ + private void add(ResourcePolicy resourcePolicy, Operation operation) { + String dateS = (String) operation.getValue(); + try { + Date date = resourcePolicyUtils.simpleDateFormat.parse(dateS); + resourcePolicy.setStartDate(date); + } catch (ParseException e) { + throw new DSpaceBadRequestException("Invalid startDate value", e); + } + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof ResourcePolicy && operation.getOp().trim().equalsIgnoreCase(OPERATION_ADD) + && operation.getPath().trim().equalsIgnoreCase(resourcePolicyUtils.OPERATION_PATH_STARTDATE)); + } + + /** + * Throws PatchBadRequestException if a value is already set in the /startDate path. + * + * @param resource the resource to update + */ + void checkResourcePolicyForNotExistingStartDateValue(ResourcePolicy resource) { + if (resource.getStartDate() != null) { + throw new DSpaceBadRequestException("Attempting to add a value to an already existing path."); + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyStartDateRemoveOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyStartDateRemoveOperation.java new file mode 100644 index 0000000000..5d79b01997 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyStartDateRemoveOperation.java @@ -0,0 +1,63 @@ +/** + * 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.app.rest.repository.patch.operation.resourcePolicy; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.repository.patch.operation.PatchOperation; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Implementation for ResourcePolicy startDate DELETE patch. + * + * Example: + * + * + * curl -X PATCH http://${dspace.server.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " + * Content-Type: application/json" -d '[{ "op": "remove", "path": " + * /startDate"]' + * + * + * @author Maria Verdonck (Atmire) on 14/02/2020 + * @author Andrea Bollini (andrea.bollini at 4science.it) + */ +@Component +public class ResourcePolicyStartDateRemoveOperation extends PatchOperation { + + @Autowired + ResourcePolicyUtils resourcePolicyUtils; + + @Override + public R perform(Context context, R resource, Operation operation) { + if (this.supports(resource, operation)) { + ResourcePolicy resourcePolicy = (ResourcePolicy) resource; + resourcePolicyUtils.checkResourcePolicyForExistingStartDateValue(resourcePolicy, operation); + this.delete(resourcePolicy); + return resource; + } else { + throw new DSpaceBadRequestException(this.getClass() + " does not support this operation"); + } + } + + /** + * Performs the actual delete startDate of resourcePolicy operation + * @param resourcePolicy resourcePolicy being patched + */ + private void delete(ResourcePolicy resourcePolicy) { + resourcePolicy.setStartDate(null); + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof ResourcePolicy && operation.getOp().trim().equalsIgnoreCase(OPERATION_REMOVE) + && operation.getPath().trim().equalsIgnoreCase(resourcePolicyUtils.OPERATION_PATH_STARTDATE)); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyStartDateReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyStartDateReplaceOperation.java new file mode 100644 index 0000000000..a6812f6581 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyStartDateReplaceOperation.java @@ -0,0 +1,74 @@ +/** + * 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.app.rest.repository.patch.operation.resourcePolicy; + +import java.text.ParseException; +import java.util.Date; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.repository.patch.operation.PatchOperation; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Implementation for ResourcePolicy startDate REPLACE patch. + * + * Example: + * + * curl -X PATCH http://${dspace.server.url}/api/authz/resourcepolicies/<:id-resourcepolicy> -H " + * Content-Type: application/json" -d '[{ "op": "replace", "path": " + * /startDate", "value": "YYYY-MM-DD"]' + * + * + * @author Maria Verdonck (Atmire) on 14/02/2020 + * @author Andrea Bollini (andrea.bollini at 4science.it) + */ +@Component +public class ResourcePolicyStartDateReplaceOperation extends PatchOperation { + + @Autowired + ResourcePolicyUtils resourcePolicyUtils; + + @Override + public R perform(Context context, R resource, Operation operation) { + checkOperationValue(operation.getValue()); + if (this.supports(resource, operation)) { + ResourcePolicy resourcePolicy = (ResourcePolicy) resource; + resourcePolicyUtils.checkResourcePolicyForExistingStartDateValue(resourcePolicy, operation); + resourcePolicyUtils.checkResourcePolicyForConsistentStartDateValue(resourcePolicy, operation); + this.replace(resourcePolicy, operation); + return resource; + } else { + throw new DSpaceBadRequestException(this.getClass() + " does not support this operation"); + } + } + + /** + * Performs the actual replace startDate of resourcePolicy operation + * @param resourcePolicy resourcePolicy being patched + * @param operation patch operation + */ + private void replace(ResourcePolicy resourcePolicy, Operation operation) { + String dateS = (String) operation.getValue(); + try { + Date date = resourcePolicyUtils.simpleDateFormat.parse(dateS); + resourcePolicy.setStartDate(date); + } catch (ParseException e) { + throw new DSpaceBadRequestException("Invalid startDate value", e); + } + } + + @Override + public boolean supports(Object objectToMatch, Operation operation) { + return (objectToMatch instanceof ResourcePolicy && operation.getOp().trim().equalsIgnoreCase(OPERATION_REPLACE) + && operation.getPath().trim().equalsIgnoreCase(resourcePolicyUtils.OPERATION_PATH_STARTDATE)); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyUtils.java new file mode 100644 index 0000000000..5902841669 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyUtils.java @@ -0,0 +1,144 @@ +/** + * 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.app.rest.repository.patch.operation.resourcePolicy; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.authorize.ResourcePolicy; +import org.springframework.stereotype.Component; + +/** + * Util class for shared methods between the ResourcePolicy patches. + * + * @author Maria Verdonck (Atmire) on 14/02/2020 + * @author Andrea Bollini (andrea.bollini at 4science.it) + */ +@Component +public class ResourcePolicyUtils { + + public static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); + + /** + * Paths in json body of patched that use these resourcePolicy operations + */ + public static final String OPERATION_PATH_STARTDATE = "/startDate"; + public static final String OPERATION_PATH_ENDDATE = "/endDate"; + public static final String OPERATION_PATH_DESCRIPTION = "/description"; + public static final String OPERATION_PATH_NAME = "/name"; + + /** + * Throws PatchBadRequestException for missing value in the /startDate path. + * + * @param resource + * the resource to update + * @param operation + * the operation to apply + * + */ + public void checkResourcePolicyForExistingStartDateValue(ResourcePolicy resource, Operation operation) { + if (resource.getStartDate() == null) { + throw new DSpaceBadRequestException("Attempting to " + operation.getOp() + + " a non-existent start date value."); + } + } + + /** + * Throws PatchBadRequestException for missing value in the /endDate path. + * + * @param resource + * the resource to update + * @param operation + * the operation to apply + * + */ + public void checkResourcePolicyForExistingEndDateValue(ResourcePolicy resource, Operation operation) { + if (resource.getEndDate() == null) { + throw new DSpaceBadRequestException("Attempting to " + operation.getOp() + + " a non-existent end date value."); + } + } + + /** + * Throws PatchBadRequestException for missing value in the /startDate path. + * + * @param resource + * the resource to update + * @param operation + * the operation to apply + * + */ + public void checkResourcePolicyForExistingNameValue(ResourcePolicy resource, Operation operation) { + if (resource.getRpName() == null) { + throw new DSpaceBadRequestException("Attempting to " + operation.getOp() + " a non-existent name value."); + } + } + + /** + * Throws PatchBadRequestException for missing value in the /description path. + * + * @param resource + * the resource to update + * @param operation + * the operation to apply + * + */ + public void checkResourcePolicyForExistingDescriptionValue(ResourcePolicy resource, Operation operation) { + if (resource.getRpDescription() == null) { + throw new DSpaceBadRequestException("Attempting to " + operation.getOp() + + " a non-existent description value."); + } + } + + /** + * Throws PatchBadRequestException if the value for startDate is not consistent with the endDate value, if present + * (greater than). + * + * @param resource + * the resource to update + * @param operation + * the operation to apply + * + */ + public void checkResourcePolicyForConsistentStartDateValue(ResourcePolicy resource, Operation operation) { + String dateS = (String) operation.getValue(); + try { + Date date = simpleDateFormat.parse(dateS); + if (resource.getEndDate() != null && resource.getEndDate().before(date)) { + throw new DSpaceBadRequestException("Attempting to set an invalid startDate greater than the endDate."); + } + } catch (ParseException e) { + throw new DSpaceBadRequestException("Invalid startDate value", e); + } + } + + /** + * Throws PatchBadRequestException if the value for endDate is not consistent with the startDate value, if present + * (smaller than). + * + * @param resource + * the resource to update + * @param operation + * the operation to apply + * + */ + public void checkResourcePolicyForConsistentEndDateValue(ResourcePolicy resource, Operation operation) { + String dateS = (String) operation.getValue(); + try { + Date date = simpleDateFormat.parse(dateS); + if (resource.getEndDate() != null && resource.getStartDate().after(date)) { + throw new DSpaceBadRequestException("Attempting to set an invalid endDate smaller than the startDate."); + } + } catch (ParseException e) { + throw new DSpaceBadRequestException("Invalid endDate value", e); + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPlugin.java index 77b9e44721..ac005ad29c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPlugin.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPlugin.java @@ -14,7 +14,7 @@ import java.util.UUID; import org.dspace.app.rest.model.patch.Operation; import org.dspace.app.rest.model.patch.Patch; -import org.dspace.app.rest.repository.patch.factories.EPersonOperationFactory; +import org.dspace.app.rest.repository.patch.operation.EPersonPasswordReplaceOperation; import org.dspace.app.rest.utils.ContextUtil; import org.dspace.authorize.service.AuthorizeService; import org.dspace.core.Constants; @@ -105,7 +105,7 @@ public class EPersonRestPermissionEvaluatorPlugin extends RestObjectPermissionEv * update their own password. */ for (Operation op: operations) { - if (!op.getPath().contentEquals(EPersonOperationFactory.OPERATION_PASSWORD_CHANGE)) { + if (!op.getPath().contentEquals(EPersonPasswordReplaceOperation.OPERATION_PASSWORD_CHANGE)) { return false; } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java index 59ae72f432..d8a86fa41a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java @@ -8,6 +8,7 @@ package org.dspace.app.rest.submit; import java.io.IOException; + import java.sql.SQLException; import java.util.ArrayList; import java.util.List; @@ -23,8 +24,9 @@ import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.BitstreamRest; import org.dspace.app.rest.model.CheckSumRest; import org.dspace.app.rest.model.MetadataValueRest; -import org.dspace.app.rest.model.ResourcePolicyRest; +import org.dspace.app.rest.model.UploadBitstreamAccessConditionDTO; import org.dspace.app.rest.model.WorkspaceItemRest; +import org.dspace.app.rest.model.step.DataUpload; import org.dspace.app.rest.model.step.UploadBitstreamRest; import org.dspace.app.rest.projection.Projection; import org.dspace.app.rest.utils.ContextUtil; @@ -77,10 +79,12 @@ public class SubmissionService { private RequestService requestService; @Autowired private ConverterService converter; + @Autowired + private org.dspace.app.rest.utils.Utils utils; /** - * Create a workspaceitem using the information in the reqest - * + * Create a workspaceitem using the information in the request + * * @param context * the dspace context * @param request @@ -133,7 +137,17 @@ public class SubmissionService { } } - +/** + * Build the rest representation of a bitstream as used in the upload section + * ({@link DataUpload}. It contains all its metadata and the list of applied + * access conditions (@link {@link UploadBitstreamAccessConditionDTO} + * + * @param configurationService the DSpace ConfigurationService + * @param source the bitstream to translate in its rest submission + * representation + * @return + * @throws SQLException + */ public UploadBitstreamRest buildUploadBitstream(ConfigurationService configurationService, Bitstream source) throws SQLException { UploadBitstreamRest data = new UploadBitstreamRest(); @@ -162,14 +176,14 @@ public class SubmissionService { } } - + Projection projection = utils.obtainProjection(); HttpServletRequest request = requestService.getCurrentRequest().getHttpServletRequest(); - data.setFormat(converter.toRest(source.getFormat(ContextUtil.obtainContext(request)), Projection.DEFAULT)); + data.setFormat(converter.toRest(source.getFormat(ContextUtil.obtainContext(request)), projection)); for (ResourcePolicy rp : source.getResourcePolicies()) { if (ResourcePolicy.TYPE_CUSTOM.equals(rp.getRpType())) { - ResourcePolicyRest resourcePolicyRest = converter.toRest(rp, Projection.DEFAULT); - data.getAccessConditions().add(resourcePolicyRest); + UploadBitstreamAccessConditionDTO uploadAccessCondition = createAccessConditionFromResourcePolicy(rp); + data.getAccessConditions().add(uploadAccessCondition); } } @@ -179,14 +193,14 @@ public class SubmissionService { checksum.setValue(source.getChecksum()); data.setCheckSum(checksum); data.setSizeBytes(source.getSizeBytes()); - data.setUrl(configurationService.getProperty("dspace.url") + "/api/" + BitstreamRest.CATEGORY + "/" + English - .plural(BitstreamRest.NAME) + "/" + source.getID() + "/content"); + data.setUrl(configurationService.getProperty("dspace.server.url") + "/api/" + BitstreamRest.CATEGORY + "/" + + English.plural(BitstreamRest.NAME) + "/" + source.getID() + "/content"); return data; } /** - * Create a workflowitem using the information in the reqest - * + * Create a workflowitem using the information in the request + * * @param context * the dspace context * @param requestUriListString @@ -219,7 +233,7 @@ public class SubmissionService { if (wsi == null) { throw new UnprocessableEntityException("Workspace item is not found"); } - WorkspaceItemRest wsiRest = converter.toRest(wsi, Projection.DEFAULT); + WorkspaceItemRest wsiRest = converter.toRest(wsi, utils.obtainProjection()); if (!wsiRest.getErrors().isEmpty()) { throw new UnprocessableEntityException( "Start workflow failed due to validation error on workspaceitem"); @@ -235,6 +249,23 @@ public class SubmissionService { return wi; } + private UploadBitstreamAccessConditionDTO createAccessConditionFromResourcePolicy(ResourcePolicy rp) { + UploadBitstreamAccessConditionDTO accessCondition = new UploadBitstreamAccessConditionDTO(); + + accessCondition.setId(rp.getID()); + accessCondition.setName(rp.getRpName()); + accessCondition.setDescription(rp.getRpDescription()); + accessCondition.setStartDate(rp.getStartDate()); + accessCondition.setEndDate(rp.getEndDate()); + if (rp.getGroup() != null) { + accessCondition.setGroupUUID(rp.getGroup().getID()); + } + if (rp.getEPerson() != null) { + accessCondition.setEpersonUUID(rp.getEPerson().getID()); + } + return accessCondition; + } + public void saveWorkflowItem(Context context, XmlWorkflowItem source) throws SQLException, AuthorizeException { workflowItemService.update(context, source); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyAddPatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyAddPatchOperation.java similarity index 83% rename from dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyAddPatchOperation.java rename to dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyAddPatchOperation.java index 7367ab2bd1..05e7c5052a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyAddPatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyAddPatchOperation.java @@ -8,10 +8,11 @@ package org.dspace.app.rest.submit.factory.impl; import java.util.ArrayList; + import java.util.Date; import java.util.List; -import org.dspace.app.rest.model.ResourcePolicyRest; +import org.dspace.app.rest.model.UploadBitstreamAccessConditionDTO; import org.dspace.app.rest.model.patch.LateObjectEvaluator; import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.AuthorizeService; @@ -35,7 +36,7 @@ import org.springframework.beans.factory.annotation.Autowired; * * @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it) */ -public class ResourcePolicyAddPatchOperation extends AddPatchOperation { +public class BitstreamResourcePolicyAddPatchOperation extends AddPatchOperation { @Autowired BitstreamService bitstreamService; @@ -48,6 +49,7 @@ public class ResourcePolicyAddPatchOperation extends AddPatchOperation newAccessConditions = new ArrayList(); + List newAccessConditions = + new ArrayList(); if (split.length == 3) { authorizeService.removePoliciesActionFilter(context, b, Constants.READ); newAccessConditions = evaluateArrayObject((LateObjectEvaluator) value); @@ -74,7 +77,7 @@ public class ResourcePolicyAddPatchOperation extends AddPatchOperation getArrayClassForEvaluation() { - return ResourcePolicyRest[].class; + protected Class getArrayClassForEvaluation() { + return UploadBitstreamAccessConditionDTO[].class; } @Override - protected Class getClassForEvaluation() { - return ResourcePolicyRest.class; + protected Class getClassForEvaluation() { + return UploadBitstreamAccessConditionDTO.class; } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyRemovePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyRemovePatchOperation.java similarity index 86% rename from dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyRemovePatchOperation.java rename to dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyRemovePatchOperation.java index 18b15dfba0..fcf96578a1 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyRemovePatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyRemovePatchOperation.java @@ -9,7 +9,7 @@ package org.dspace.app.rest.submit.factory.impl; import java.util.List; -import org.dspace.app.rest.model.ResourcePolicyRest; +import org.dspace.app.rest.model.UploadBitstreamAccessConditionDTO; import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.ResourcePolicyService; import org.dspace.content.Bitstream; @@ -28,7 +28,8 @@ import org.springframework.beans.factory.annotation.Autowired; * * @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it) */ -public class ResourcePolicyRemovePatchOperation extends RemovePatchOperation { +public class BitstreamResourcePolicyRemovePatchOperation + extends RemovePatchOperation { @Autowired ItemService itemService; @@ -83,12 +84,12 @@ public class ResourcePolicyRemovePatchOperation extends RemovePatchOperation getArrayClassForEvaluation() { - return ResourcePolicyRest[].class; + protected Class getArrayClassForEvaluation() { + return UploadBitstreamAccessConditionDTO[].class; } @Override - protected Class getClassForEvaluation() { - return ResourcePolicyRest.class; + protected Class getClassForEvaluation() { + return UploadBitstreamAccessConditionDTO.class; } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyReplacePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyReplacePatchOperation.java similarity index 88% rename from dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyReplacePatchOperation.java rename to dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyReplacePatchOperation.java index c03ea98271..7f0a23dbb9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ResourcePolicyReplacePatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyReplacePatchOperation.java @@ -9,6 +9,7 @@ package org.dspace.app.rest.submit.factory.impl; import java.util.Date; import java.util.List; +import java.util.UUID; import org.dspace.app.rest.model.ResourcePolicyRest; import org.dspace.app.rest.model.patch.LateObjectEvaluator; @@ -35,7 +36,7 @@ import org.springframework.beans.factory.annotation.Autowired; * * @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it) */ -public class ResourcePolicyReplacePatchOperation extends ReplacePatchOperation { +public class BitstreamResourcePolicyReplacePatchOperation extends ReplacePatchOperation { @Autowired BitstreamService bitstreamService; @@ -89,12 +90,14 @@ public class ResourcePolicyReplacePatchOperation extends ReplacePatchOperation/<:metadata>/-" * * Example: - * curl -X PATCH http://${dspace.url}/api/submission/workspaceitems/<:id-workspaceitem> -H " + * curl -X PATCH http://${dspace.server.url}/api/submission/workspaceitems/<:id-workspaceitem> -H " * Content-Type: application/json" -d '[{ "op": "add", "path": " * /sections/traditionalpageone/dc.title/-", "value": {"value": "Add new * title"}}]' @@ -38,7 +38,7 @@ import org.springframework.util.Assert; * "/sections/<:name-of-the-form>/<:metadata>/<:idx-zero-based>" * * Example: - * curl -X PATCH http://${dspace.url}/api/submission/workspaceitems/<:id-workspaceitem> -H " + * curl -X PATCH http://${dspace.server.url}/api/submission/workspaceitems/<:id-workspaceitem> -H " * Content-Type: application/json" -d '[{ "op": "add", "path": " * /sections/traditionalpageone/dc.title/1", "value": {"value": "Add new * title"}}]' @@ -48,7 +48,7 @@ import org.springframework.util.Assert; * "/sections/<:name-of-the-form>/<:metadata>" * * Example: - * curl -X PATCH http://${dspace.url}/api/submission/workspaceitems/<:id-workspaceitem> -H " + * curl -X PATCH http://${dspace.server.url}/api/submission/workspaceitems/<:id-workspaceitem> -H " * Content-Type: application/json" -d '[{ "op": "add", "path": " * /sections/traditionalpageone/dc.title", "value": [{"value": "Add new first * title"}, {"value": "Add new second title"}]}]' diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueMovePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueMovePatchOperation.java index 82afc8e233..f0e3c662f8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueMovePatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueMovePatchOperation.java @@ -21,7 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired; * instance to put the 3rd author as 1st author you need to run: * * - * curl -X PATCH http://${dspace.url}/api/submission/workspaceitems/<:id-workspaceitem> -H " + * curl -X PATCH http://${dspace.server.url}/api/submission/workspaceitems/<:id-workspaceitem> -H " * Content-Type: application/json" -d '[{ "op": "move", "from": " * /sections/traditionalpageone/dc.contributor.author/2", "path": " * /sections/traditionalpageone/dc.contributor.author/0"}]' diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueRemovePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueRemovePatchOperation.java index 3bce1ab985..20d6b335da 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueRemovePatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueRemovePatchOperation.java @@ -21,7 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired; * "/sections/<:name-of-the-form>/<:metadata>/<:idx-zero-based>" * * Example: - * curl -X PATCH http://${dspace.url}/api/submission/workspaceitems/<:id-workspaceitem> -H " + * curl -X PATCH http://${dspace.server.url}/api/submission/workspaceitems/<:id-workspaceitem> -H " * Content-Type: application/json" -d '[{ "op": "remove", "path": " * /sections/traditionalpageone/dc.title/1"}]' * @@ -30,7 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired; * "/sections/<:name-of-the-form>/<:metadata>" * * Example: - * curl -X PATCH http://${dspace.url}/api/submission/workspaceitems/<:id-workspaceitem> -H " + * curl -X PATCH http://${dspace.server.url}/api/submission/workspaceitems/<:id-workspaceitem> -H " * Content-Type: application/json" -d '[{ "op": "remove", "path": " * /sections/traditionalpageone/dc.title"}]' * diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueReplacePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueReplacePatchOperation.java index 96ae7c0f06..500f473fa9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueReplacePatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueReplacePatchOperation.java @@ -28,7 +28,7 @@ import org.springframework.util.Assert; * must return an error. * * Example: - * curl -X PATCH http://${dspace.url}/api/submission/workspaceitems/<:id-workspaceitem> -H " + * curl -X PATCH http://${dspace.server.url}/api/submission/workspaceitems/<:id-workspaceitem> -H " * Content-Type: application/json" -d '[{ "op": "replace", "path": " * /sections/traditionalpageone/dc.title/0", "value": {"value": "Add new * title", "language": "en"}}]' @@ -37,7 +37,7 @@ import org.springframework.util.Assert; * It is also possible to change only a single attribute of the {@link MetadataValueRest} (except the "place"). * * Example: - * curl -X PATCH http://${dspace.url}/api/submission/workspaceitems/<:id-workspaceitem> -H " + * curl -X PATCH http://${dspace.server.url}/api/submission/workspaceitems/<:id-workspaceitem> -H " * Content-Type: application/json" -d '[{ "op": "replace", "path": " * /sections/traditionalpageone/dc.title/0/language", "value": "it"}]' * diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/LicenseStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/LicenseStep.java index d158066920..7ed0ffd746 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/LicenseStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/LicenseStep.java @@ -42,7 +42,7 @@ public class LicenseStep extends org.dspace.submit.step.LicenseStep implements A String acceptanceDate = bitstreamService.getMetadata(bitstream, DCTERMS_RIGHTSDATE); result.setAcceptanceDate(acceptanceDate); result.setUrl( - configurationService.getProperty("dspace.url") + "/api/" + BitstreamRest.CATEGORY + "/" + English + configurationService.getProperty("dspace.server.url") + "/api/" + BitstreamRest.CATEGORY + "/" + English .plural(BitstreamRest.NAME) + "/" + bitstream.getID() + "/content"); result.setGranted(true); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/Utils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/Utils.java index e73bb1707e..18392fc5a7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/Utils.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/Utils.java @@ -27,6 +27,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -37,6 +38,7 @@ import java.util.Set; import java.util.TreeSet; import java.util.UUID; import javax.annotation.Nullable; +import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; @@ -56,7 +58,9 @@ import org.dspace.app.rest.model.RestModel; import org.dspace.app.rest.model.hateoas.DSpaceResource; import org.dspace.app.rest.model.hateoas.EmbeddedPage; import org.dspace.app.rest.model.hateoas.HALResource; +import org.dspace.app.rest.projection.CompositeProjection; import org.dspace.app.rest.projection.DefaultProjection; +import org.dspace.app.rest.projection.EmbedRelsProjection; import org.dspace.app.rest.projection.Projection; import org.dspace.app.rest.repository.DSpaceRestRepository; import org.dspace.app.rest.repository.LinkRestRepository; @@ -98,7 +102,7 @@ public class Utils { /** * The maximum number of embed levels to allow. */ - private static final int EMBED_MAX_LEVELS = 2; + private static final int EMBED_MAX_LEVELS = 10; @Autowired ApplicationContext applicationContext; @@ -439,28 +443,87 @@ public class Utils { } /** - * Gets the projection requested by the current servlet request, or {@link DefaultProjection} if none + * Gets the effective projection requested by the current servlet request, or {@link DefaultProjection} if none * is specified. + *

    + * Any number of individual {@code Projections} that are spring-registered {@link Component}s may be specified + * by name via the {@code projection} parameter. If multiple projections are specified, they will be wrapped in a + * {@link CompositeProjection} and applied in order as described there. + *

    + * In addition, any number of embeds may be specified by rel name via the {@code embed} parameter. + * When provided, these act as a whitelist of embeds that may be included in the response, as described + * and implemented by {@link EmbedRelsProjection}. + *

    * * @return the requested or default projection, never {@code null}. * @throws IllegalArgumentException if the request specifies an unknown projection name. */ public Projection obtainProjection() { - String projectionName = requestService.getCurrentRequest().getServletRequest().getParameter("projection"); - return converter.getProjection(projectionName); + ServletRequest servletRequest = requestService.getCurrentRequest().getServletRequest(); + List projectionNames = getValues(servletRequest, "projection"); + Set embedRels = new HashSet<>(getValues(servletRequest, "embed")); + + List projections = new ArrayList<>(); + + for (String projectionName : projectionNames) { + projections.add(converter.getProjection(projectionName)); + } + + if (!embedRels.isEmpty()) { + projections.add(new EmbedRelsProjection(embedRels)); + } + + if (projections.isEmpty()) { + return Projection.DEFAULT; + } else if (projections.size() == 1) { + return projections.get(0); + } else { + return new CompositeProjection(projections); + } + } + + /** + * Gets zero or more values for the given servlet request parameter. + *

    + * This convenience method reads multiple values that have been specified as request parameter in multiple ways: + * via * {@code ?paramName=value1¶mName=value2}, via {@code ?paramName=value1,value2}, + * or a combination. + *

    + * It provides the values in the order they were given in the request, and automatically de-dupes them. + *

    + * + * @param servletRequest the servlet request. + * @param parameterName the parameter name. + * @return the ordered, de-duped values, possibly empty, never {@code null}. + */ + private List getValues(ServletRequest servletRequest, String parameterName) { + String[] rawValues = servletRequest.getParameterValues(parameterName); + List values = new ArrayList<>(); + if (rawValues != null) { + for (String rawValue : rawValues) { + for (String value : rawValue.split(",")) { + String trimmedValue = value.trim(); + if (trimmedValue.length() > 0 && !values.contains(trimmedValue)) { + values.add(trimmedValue); + } + } + } + } + return values; } /** * Adds embeds or links for all class-level LinkRel annotations for which embeds or links are allowed. * * @param halResource the resource. + * @param oldLinks previously traversed links */ - public void embedOrLinkClassLevelRels(HALResource halResource) { + public void embedOrLinkClassLevelRels(HALResource halResource, Link... oldLinks) { Projection projection = halResource.getContent().getProjection(); getLinkRests(halResource.getContent().getClass()).stream().forEach((linkRest) -> { Link link = linkToSubResource(halResource.getContent(), linkRest.name()); - if (projection.allowEmbedding(halResource, linkRest)) { - embedRelFromRepository(halResource, linkRest.name(), link, linkRest); + if (projection.allowEmbedding(halResource, linkRest, oldLinks)) { + embedRelFromRepository(halResource, linkRest.name(), link, linkRest, oldLinks); halResource.add(link); // unconditionally link if embedding was allowed } else if (projection.allowLinking(halResource, linkRest)) { halResource.add(link); @@ -499,6 +562,32 @@ public class Utils { */ void embedRelFromRepository(HALResource resource, String rel, Link link, LinkRest linkRest) { + embedRelFromRepository(resource, rel, link, linkRest, new Link[] {}); + } + + /** + * Embeds a rel whose value comes from a {@link LinkRestRepository}, if the maximum embed level has not + * been exceeded yet. + *

    + * The embed will be skipped if 1) the link repository reports that it is not embeddable or 2) the returned + * value is null and the LinkRest annotation has embedOptional = true. + *

    + * Implementation note: The caller is responsible for ensuring that the projection allows the embed + * before calling this method. + *

    + * + * @param resource the resource from which the embed will be made. + * @param rel the name of the rel. + * @param link the link. + * @param linkRest the LinkRest annotation (must have method defined). + * @param oldLinks The previously traversed links + * @throws RepositoryNotFoundException if the link repository could not be found. + * @throws IllegalArgumentException if the method specified by the LinkRest could not be found in the + * link repository. + * @throws RuntimeException if any other problem occurs when trying to invoke the method. + */ + void embedRelFromRepository(HALResource resource, + String rel, Link link, LinkRest linkRest, Link... oldLinks) { if (resource.getContent().getEmbedLevel() == EMBED_MAX_LEVELS) { return; } @@ -510,7 +599,7 @@ public class Utils { Object contentId = getContentIdForLinkMethod(resource.getContent(), method); try { Object linkedObject = method.invoke(linkRepository, null, contentId, null, projection); - resource.embedResource(rel, wrapForEmbedding(resource, linkedObject, link)); + resource.embedResource(rel, wrapForEmbedding(resource, linkedObject, link, oldLinks)); } catch (InvocationTargetException e) { if (e.getTargetException() instanceof RuntimeException) { throw (RuntimeException) e.getTargetException(); @@ -615,17 +704,37 @@ public class Utils { */ private Object wrapForEmbedding(HALResource resource, Object linkedObject, Link link) { + return wrapForEmbedding(resource, linkedObject, link, new Link[] {}); + } + + /** + * Wraps the given linked object (retrieved from a link repository or link method on the rest item) + * in an object that is appropriate for embedding, if needed. Does not perform the actual embed; the + * caller is responsible for that. + * + * @param resource the resource from which the embed will be made. + * @param linkedObject the linked object. + * @param link the link, which is used if the linked object is a list or page, to determine the self link + * and embed property name to use for the subresource. + * @param oldLinks The previously traversed links + * @return the wrapped object, which will have an "embed level" one greater than the given parent resource. + */ + private Object wrapForEmbedding(HALResource resource, + Object linkedObject, Link link, Link... oldLinks) { int childEmbedLevel = resource.getContent().getEmbedLevel() + 1; + //Add the latest link to the list + Link[] newList = Arrays.copyOf(oldLinks, oldLinks.length + 1); + newList[oldLinks.length] = link; if (linkedObject instanceof RestAddressableModel) { RestAddressableModel restObject = (RestAddressableModel) linkedObject; restObject.setEmbedLevel(childEmbedLevel); - return converter.toResource(restObject); + return converter.toResource(restObject, newList); } else if (linkedObject instanceof Page) { // The first page has already been constructed by a link repository and we only need to wrap it Page page = (Page) linkedObject; return new EmbeddedPage(link.getHref(), page.map((restObject) -> { restObject.setEmbedLevel(childEmbedLevel); - return converter.toResource(restObject); + return converter.toResource(restObject, newList); }), null, link.getRel()); } else if (linkedObject instanceof List) { // The full list has been retrieved and we need to provide the first page for embedding @@ -637,7 +746,7 @@ public class Utils { return new EmbeddedPage(link.getHref(), page.map((restObject) -> { restObject.setEmbedLevel(childEmbedLevel); - return converter.toResource(restObject); + return converter.toResource(restObject, newList); }), list, link.getRel()); } else { diff --git a/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml b/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml index ed3e185839..61459f11d6 100644 --- a/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml +++ b/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml @@ -50,7 +50,7 @@ + class="org.dspace.app.rest.submit.factory.impl.BitstreamResourcePolicyAddPatchOperation"/> @@ -75,7 +75,7 @@ + class="org.dspace.app.rest.submit.factory.impl.BitstreamResourcePolicyRemovePatchOperation"/> @@ -96,7 +96,7 @@ + class="org.dspace.app.rest.submit.factory.impl.BitstreamResourcePolicyReplacePatchOperation"/> + + diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/oai/OAIpmhIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/oai/OAIpmhIT.java index fff2e6ce20..08bb69543a 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/oai/OAIpmhIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/oai/OAIpmhIT.java @@ -9,8 +9,8 @@ package org.dspace.app.oai; import static org.hamcrest.Matchers.startsWith; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rdf/RdfIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rdf/RdfIT.java index 9cc731ad9c..681b2ced81 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rdf/RdfIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rdf/RdfIT.java @@ -139,8 +139,8 @@ public class RdfIT extends AbstractWebClientIntegrationTest { ResponseEntity response = getResponseAsString(REDIRECTION_PATH + "/" + communityHandle); // Expect a 303 (See Other) response code, redirecting us to the HTTP URI of the Community assertThat(response.getStatusCode(), equalTo(HttpStatus.SEE_OTHER)); - // Expect location of redirection to be [dspace.url]/handle/[community_handle] + // Expect location of redirection to be [dspace.ui.url]/handle/[community_handle] assertThat(response.getHeaders().getLocation(), equalTo( - URI.create(configurationService.getProperty("dspace.url") + "/handle/" + communityHandle + "/"))); + URI.create(configurationService.getProperty("dspace.ui.url") + "/handle/" + communityHandle + "/"))); } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamFormatRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamFormatRestRepositoryIT.java index c2eda5bb64..862e2046c0 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamFormatRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamFormatRestRepositoryIT.java @@ -29,6 +29,7 @@ import org.dspace.app.rest.builder.BitstreamFormatBuilder; import org.dspace.app.rest.builder.EPersonBuilder; import org.dspace.app.rest.converter.ConverterService; import org.dspace.app.rest.matcher.BitstreamFormatMatcher; +import org.dspace.app.rest.matcher.HalMatcher; import org.dspace.app.rest.model.BitstreamFormatRest; import org.dspace.app.rest.projection.Projection; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; @@ -134,6 +135,7 @@ public class BitstreamFormatRestRepositoryIT extends AbstractControllerIntegrati public void createAdminAccess() throws Exception { ObjectMapper mapper = new ObjectMapper(); BitstreamFormatRest bitstreamFormatRest = this.createRandomMockBitstreamRest(false); + //Create bitstream format String token = getAuthToken(admin.getEmail(), password); @@ -142,10 +144,10 @@ public class BitstreamFormatRestRepositoryIT extends AbstractControllerIntegrati try { MvcResult mvcResult = getClient(token).perform(post("/api/core/bitstreamformats/") - .content(mapper.writeValueAsBytes( - bitstreamFormatRest)) + .content(mapper.writeValueAsBytes(bitstreamFormatRest)) .contentType(contentType)) .andExpect(status().isCreated()) + .andExpect(jsonPath("$", HalMatcher.matchNoEmbeds())) .andReturn(); String content = mvcResult.getResponse().getContentAsString(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java index 060946e3ea..2578ae01e2 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java @@ -57,6 +57,7 @@ import org.apache.solr.client.solrj.SolrServerException; import org.dspace.app.rest.builder.BitstreamBuilder; import org.dspace.app.rest.builder.CollectionBuilder; import org.dspace.app.rest.builder.CommunityBuilder; +import org.dspace.app.rest.builder.EPersonBuilder; import org.dspace.app.rest.builder.GroupBuilder; import org.dspace.app.rest.builder.ItemBuilder; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; @@ -70,9 +71,9 @@ import org.dspace.content.service.BitstreamFormatService; import org.dspace.content.service.BitstreamService; import org.dspace.core.Constants; import org.dspace.disseminate.CitationDocumentServiceImpl; +import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.services.ConfigurationService; -import org.dspace.statistics.FakeDatabaseReader; import org.dspace.statistics.ObjectCount; import org.dspace.statistics.SolrLoggerServiceImpl; import org.dspace.statistics.factory.StatisticsServiceFactory; @@ -119,13 +120,6 @@ public class BitstreamRestControllerIT extends AbstractControllerIntegrationTest StatisticsServiceFactory.getInstance().getSolrLoggerService().removeIndex("*:*"); } - @BeforeClass - public static void mockGeoIP() - throws IOException { - // Set up a mock GeoIP implementation so we don't need to d/l a database. - new FakeDatabaseReader.Builder(); // Activate fake - } - @Before @Override public void setUp() throws Exception { @@ -349,6 +343,184 @@ public class BitstreamRestControllerIT extends AbstractControllerIntegrationTest } } + @Test + public void embargoedBitstreamForbiddenTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Collection col1 = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection 1") + .build(); + + // a public item with an embargoed bitstream + String bitstreamContent = "Embargoed!"; + + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald") + .build(); + + bitstream = BitstreamBuilder + .createBitstream(context, publicItem1, is) + .withName("Test Embargoed Bitstream") + .withDescription("This bitstream is embargoed") + .withMimeType("text/plain") + .withEmbargoPeriod("3 months") + .build(); + } + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andExpect(status().isForbidden()); + + getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andExpect(status().isUnauthorized()); + + checkNumberOfStatsRecords(bitstream, 0); + } + + @Test + public void expiredEmbargoedBitstreamTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Collection col1 = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection 1") + .build(); + + String bitstreamContent = "Embargoed!"; + + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Public item 1") + .withIssueDate("2015-10-17") + .withAuthor("Smith, Donald") + .build(); + + Bitstream bitstream = BitstreamBuilder + .createBitstream(context, publicItem1, is) + .withName("Test Embargoed Bitstream") + .withDescription("This bitstream is embargoed") + .withMimeType("text/plain") + .withEmbargoPeriod("-3 months") + .build(); + } + context.restoreAuthSystemState(); + + // all are allowed access to item with embargoed expired + + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andExpect(status().isOk()); + + getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andExpect(status().isOk()); + + checkNumberOfStatsRecords(bitstream, 2); + } + + @Test + public void embargoedBitstreamAccessGrantByAdminsTest() throws Exception { + + context.turnOffAuthorisationSystem(); + + EPerson adminParentCommunity = EPersonBuilder.createEPerson(context) + .withEmail("adminCommunity@mail.com") + .withPassword("qwerty02") + .build(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .withAdminGroup(adminParentCommunity) + .build(); + + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + + EPerson adminChild2 = EPersonBuilder.createEPerson(context) + .withEmail("adminChil2@mail.com") + .withPassword("qwerty05") + .build(); + Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community 2") + .withAdminGroup(adminChild2) + .build(); + + EPerson adminCollection1 = EPersonBuilder.createEPerson(context) + .withEmail("adminCollection1@mail.com") + .withPassword("qwerty03") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .withAdminGroup(adminCollection1) + .build(); + + EPerson adminCollection2 = EPersonBuilder.createEPerson(context) + .withEmail("adminCol2@mail.com") + .withPassword("qwerty01") + .build(); + Collection col2 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 2") + .withAdminGroup(adminCollection2) + .build(); + + + String bitstreamContent = "Embargoed!"; + Bitstream bitstream = null; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + + Item item = ItemBuilder.createItem(context, col1) + .withTitle("Test") + .withIssueDate("2018-10-18") + .withAuthor("Smith, Donald") + .withSubject("ExtraEntry") + .build(); + + bitstream = BitstreamBuilder.createBitstream(context, item, is) + .withName("Bitstream") + .withDescription("Description") + .withMimeType("text/plain") + .withEmbargoPeriod("2 week") + .build(); + } + context.restoreAuthSystemState(); + + // parent community's admin user is allowed access to embargoed item + String tokenAdminParentCommunity = getAuthToken(adminParentCommunity.getEmail(), "qwerty02"); + getClient(tokenAdminParentCommunity).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andExpect(status().isOk()); + + // collection1's admin user is allowed access to embargoed item + String tokenAdminCollection1 = getAuthToken(adminCollection1.getEmail(), "qwerty03"); + getClient(tokenAdminCollection1).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andExpect(status().isOk()); + + checkNumberOfStatsRecords(bitstream, 2); + + // admin of second collection is NOT allowed access to embargoed item of first collection + String tokenAdminCollection2 = getAuthToken(adminCollection2.getEmail(), "qwerty01"); + getClient(tokenAdminCollection2).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andExpect(status().isForbidden()); + + // admin of child2 community is NOT allowed access to embargoed item of first collection + String tokenAdminChild2 = getAuthToken(adminChild2.getEmail(), "qwerty05"); + getClient(tokenAdminCollection2).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andExpect(status().isForbidden()); + + checkNumberOfStatsRecords(bitstream, 2); + } + @Test public void testPrivateBitstream() throws Exception { context.turnOffAuthorisationSystem(); @@ -396,6 +568,153 @@ public class BitstreamRestControllerIT extends AbstractControllerIntegrationTest } } + @Test + public void restrictedGroupBitstreamForbiddenTest() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson eperson2 = EPersonBuilder.createEPerson(context) + .withEmail("eperson2@mail.com") + .withPassword("qwerty02") + .build(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Collection col1 = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection 1") + .build(); + + Group restrictedGroup = GroupBuilder.createGroup(context) + .withName("Restricted Group") + .addMember(eperson) + .build(); + + String bitstreamContent = "Private!"; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + + Item item = ItemBuilder.createItem(context, col1) + .withTitle("item 1") + .withIssueDate("2013-01-17") + .withAuthor("Doe, John") + .build(); + bitstream = BitstreamBuilder + .createBitstream(context, item, is) + .withName("Test Embargoed Bitstream") + .withDescription("This bitstream is embargoed") + .withMimeType("text/plain") + .withReaderGroup(restrictedGroup) + .build(); + } + context.restoreAuthSystemState(); + // download the bitstream + // eperson that belong to restricted group is allowed access to the item + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andExpect(status().isOk()); + + checkNumberOfStatsRecords(bitstream, 1); + + String tokenEPerson2 = getAuthToken(eperson2.getEmail(), "qwerty02"); + getClient(tokenEPerson2).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andExpect(status().isForbidden()); + + // Anonymous users CANNOT access/download Bitstreams that are restricted + getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andExpect(status().isUnauthorized()); + + checkNumberOfStatsRecords(bitstream, 1); + } + + @Test + public void restrictedGroupBitstreamAccessGrantByAdminsTest() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson adminParentCommunity = EPersonBuilder.createEPerson(context) + .withEmail("adminCommunity@mail.com") + .withPassword("qwerty00") + .build(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .withAdminGroup(adminParentCommunity) + .build(); + + EPerson adminChild1 = EPersonBuilder.createEPerson(context) + .withEmail("adminChild1@mail.com") + .withPassword("qwerty05") + .build(); + Community child1 = CommunityBuilder.createCommunity(context) + .withName("Sub Community") + .withAdminGroup(adminChild1) + .build(); + + EPerson adminCol1 = EPersonBuilder.createEPerson(context) + .withEmail("admin1@mail.com") + .withPassword("qwerty01") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection 1") + .withAdminGroup(adminCol1) + .build(); + + EPerson adminCol2 = EPersonBuilder.createEPerson(context) + .withEmail("admin2@mail.com") + .withPassword("qwerty02") + .build(); + Collection col2 = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection 2") + .withAdminGroup(adminCol2) + .build(); + + Group restrictedGroup = GroupBuilder.createGroup(context) + .withName("Restricted Group") + .addMember(eperson) + .build(); + + String bitstreamContent = "Private!"; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + + Item item = ItemBuilder.createItem(context, col1) + .withTitle("item") + .withIssueDate("2018-10-17") + .withAuthor("Doe, John") + .build(); + bitstream = BitstreamBuilder + .createBitstream(context, item, is) + .withName("Test Embargoed Bitstream") + .withDescription("This bitstream is embargoed") + .withMimeType("text/plain") + .withReaderGroup(restrictedGroup) + .build(); + } + context.restoreAuthSystemState(); + // download the bitstream + // parent community's admin user is allowed access to the item belong restricted group + String tokenAdminParentCommuity = getAuthToken(adminParentCommunity.getEmail(), "qwerty00"); + getClient(tokenAdminParentCommuity).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andExpect(status().isOk()); + + // collection1's admin user is allowed access to the item belong restricted group + String tokenAdminCol1 = getAuthToken(adminCol1.getEmail(), "qwerty01"); + getClient(tokenAdminCol1).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andExpect(status().isOk()); + + checkNumberOfStatsRecords(bitstream, 2); + + // collection2's admin user is NOT allowed access to the item belong collection1 + String tokenAdminCol2 = getAuthToken(adminCol2.getEmail(), "qwerty02"); + getClient(tokenAdminCol2).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andExpect(status().isForbidden()); + + // child1's admin user is NOT allowed access to the item belong collection1 + String tokenAdminChild1 = getAuthToken(adminChild1.getEmail(), "qwerty05"); + getClient(tokenAdminCol2).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andExpect(status().isForbidden()); + + checkNumberOfStatsRecords(bitstream, 2); + } + // Verify number of hits/views of Bitstream is as expected private void checkNumberOfStatsRecords(Bitstream bitstream, int expectedNumberOfStatsRecords) throws SolrServerException, IOException { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java index 39a543a5d0..2fffc74f05 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java @@ -28,6 +28,7 @@ import org.dspace.app.rest.builder.CommunityBuilder; import org.dspace.app.rest.builder.EPersonBuilder; import org.dspace.app.rest.converter.ConverterService; import org.dspace.app.rest.matcher.CollectionMatcher; +import org.dspace.app.rest.matcher.CommunityMatcher; import org.dspace.app.rest.matcher.HalMatcher; import org.dspace.app.rest.matcher.MetadataMatcher; import org.dspace.app.rest.matcher.PageMatcher; @@ -84,8 +85,10 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$._embedded.collections", Matchers.containsInAnyOrder( - CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle()), - CollectionMatcher.matchCollectionEntry(col2.getName(), col2.getID(), col2.getHandle()) + CollectionMatcher.matchCollectionEntryFullProjection(col1.getName(), col1.getID(), + col1.getHandle()), + CollectionMatcher.matchCollectionEntryFullProjection(col2.getName(), col2.getID(), + col2.getHandle()) ))); } @@ -218,11 +221,13 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$._embedded.collections", Matchers.contains( - CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle()) + CollectionMatcher.matchCollectionEntryFullProjection(col1.getName(), col1.getID(), + col1.getHandle()) ))) .andExpect(jsonPath("$._embedded.collections", Matchers.not( Matchers.contains( - CollectionMatcher.matchCollectionEntry(col2.getName(), col2.getID(), col2.getHandle()) + CollectionMatcher.matchCollectionEntryFullProjection(col2.getName(), col2.getID(), + col2.getHandle()) ) ))); @@ -233,11 +238,13 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$._embedded.collections", Matchers.contains( - CollectionMatcher.matchCollectionEntry(col2.getName(), col2.getID(), col2.getHandle()) + CollectionMatcher.matchCollectionEntryFullProjection(col2.getName(), col2.getID(), + col2.getHandle()) ))) .andExpect(jsonPath("$._embedded.collections", Matchers.not( Matchers.contains( - CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle()) + CollectionMatcher.matchCollectionEntryFullProjection(col1.getName(), col1.getID(), + col1.getHandle()) ) ))); } @@ -404,11 +411,13 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", is( - CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle()) + CollectionMatcher.matchCollectionEntryFullProjection(col1.getName(), col1.getID(), + col1.getHandle()) ))) .andExpect(jsonPath("$", Matchers.not( is( - CollectionMatcher.matchCollectionEntry(col2.getName(), col2.getID(), col2.getHandle()) + CollectionMatcher.matchCollectionEntryFullProjection(col2.getName(), col2.getID(), + col2.getHandle()) ))) ) ; @@ -489,7 +498,7 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes @Test public void findAuthorizedByCommunityWithoutUUIDTest() throws Exception { getClient().perform(get("/api/core/collections/search/findAuthorizedByCommunity")) - .andExpect(status().isUnprocessableEntity()); + .andExpect(status().isBadRequest()); } @Test @@ -550,11 +559,13 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", is( - CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle()) + CollectionMatcher.matchCollectionEntryFullProjection(col1.getName(), col1.getID(), + col1.getHandle()) ))) .andExpect(jsonPath("$", Matchers.not( is( - CollectionMatcher.matchCollectionEntry(col2.getName(), col2.getID(), col2.getHandle()) + CollectionMatcher.matchCollectionEntryFullProjection(col2.getName(), col2.getID(), + col2.getHandle()) )))); } @@ -578,7 +589,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( - CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle()) + CollectionMatcher.matchCollectionEntryFullProjection(col1.getName(), col1.getID(), + col1.getHandle()) ))) .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/collections"))) ; @@ -605,7 +617,7 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( - CollectionMatcher.matchCollectionEntry("Electronic theses and dissertations", + CollectionMatcher.matchCollectionEntryFullProjection("Electronic theses and dissertations", col1.getID(), col1.getHandle()) ))) .andExpect(jsonPath("$._links.self.href", @@ -647,10 +659,11 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( - CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle()) + CollectionMatcher.matchCollectionEntryFullProjection(col1.getName(), col1.getID(), + col1.getHandle()) ))) .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("/api/core/collections"))) ; + Matchers.containsString("/api/core/collections"))); getClient(token).perform(delete("/api/core/collections/" + col1.getID().toString())) .andExpect(status().isNoContent()) ; @@ -691,10 +704,11 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( - CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle()) + CollectionMatcher.matchCollectionEntryFullProjection(col1.getName(), col1.getID(), + col1.getHandle()) ))) .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("/api/core/collections"))) ; + Matchers.containsString("/api/core/collections"))); getClient().perform(delete("/api/core/collections/" + col1.getID().toString())) .andExpect(status().isUnauthorized()) ; @@ -734,9 +748,11 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes getClient(authToken).perform(post("/api/core/collections") .content(mapper.writeValueAsBytes(collectionRest)) .param("parent", parentCommunity.getID().toString()) - .contentType(contentType)) + .contentType(contentType) + .param("projection", "full")) .andExpect(status().isCreated()) .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", CollectionMatcher.matchFullEmbeds())) .andExpect(jsonPath("$", Matchers.allOf( hasJsonPath("$.id", not(empty())), hasJsonPath("$.uuid", not(empty())), @@ -756,6 +772,13 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes "Title Text") ))))); + getClient(authToken).perform(post("/api/core/collections") + .content(mapper.writeValueAsBytes(collectionRest)) + .param("parent", parentCommunity.getID().toString()) + .contentType(contentType)) + .andExpect(status().isCreated()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", HalMatcher.matchNoEmbeds())); } @Test @@ -903,10 +926,11 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( - CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle()) + CollectionMatcher.matchCollectionEntryFullProjection(col1.getName(), col1.getID(), + col1.getHandle()) ))) .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("/api/core/collections"))) ; + Matchers.containsString("/api/core/collections"))); getClient(token).perform(delete("/api/core/collections/" + col1.getID().toString())) .andExpect(status().isNoContent()) ; @@ -939,7 +963,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( - CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle()) + CollectionMatcher.matchCollectionEntryFullProjection(col1.getName(), col1.getID(), + col1.getHandle()) ))) .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/collections"))) ; @@ -969,7 +994,7 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( - CollectionMatcher.matchCollectionEntry("Electronic theses and dissertations", + CollectionMatcher.matchCollectionEntryFullProjection("Electronic theses and dissertations", col1.getID(), col1.getHandle()) ))) .andExpect(jsonPath("$._links.self.href", @@ -1110,9 +1135,140 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$._embedded.collections", Matchers.containsInAnyOrder( - CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle()), - CollectionMatcher.matchCollectionEntry(col2.getName(), col2.getID(), col2.getHandle()) + CollectionMatcher.matchCollectionEntryFullProjection(col1.getName(), col1.getID(), + col1.getHandle()), + CollectionMatcher.matchCollectionEntryFullProjection(col2.getName(), col2.getID(), + col2.getHandle()) ))) - .andExpect(jsonPath("$.page", PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 2))); + .andExpect(jsonPath("$.page", PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, + 1, 2))); + } + + @Test + public void projectonLevelTest() throws Exception { + //We turn off the authorization system in order to create the structure as defined below + context.turnOffAuthorisationSystem(); + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and one collection. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Community child1child = CommunityBuilder.createSubCommunity(context, child1) + .withName("Sub Community Two") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .withLogo("TestingContentForLogo") + .build(); + Collection col2 = CollectionBuilder.createCollection(context, child1child).withName("Collection 2").build(); + + getClient().perform(get("/api/core/collections/" + col1.getID()) + .param("projection", "level") + .param("embedLevelDepth", "1")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", CollectionMatcher.matchCollectionEntry(col1.getName(), + col1.getID(), + col1.getHandle()))) + // .exists() makes sure that the embed is there, but it could be empty + .andExpect(jsonPath("$._embedded.mappedItems").exists()) + // .isEmpty() makes sure that the embed is there, but that there's no actual data + .andExpect(jsonPath("$._embedded.mappedItems._embedded.mappedItems").isEmpty()) + .andExpect(jsonPath("$._embedded.parentCommunity", + CommunityMatcher.matchCommunityEntry(child1.getName(), + child1.getID(), + child1.getHandle()))) + // .doesNotExist() makes sure that this section is not embedded, it's not there at all + .andExpect(jsonPath("$._embedded.parentCommunity._embedded.subcommunities").doesNotExist()) + .andExpect(jsonPath("$._embedded.logo", Matchers.not(Matchers.empty()))) + // .doesNotExist() makes sure that this section is not embedded, it's not there at all + .andExpect(jsonPath("$._embedded.logo._embedded.format").doesNotExist()); + + getClient().perform(get("/api/core/collections/" + col1.getID()) + .param("projection", "level") + .param("embedLevelDepth", "3")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", CollectionMatcher.matchCollectionEntry(col1.getName(), + col1.getID(), + col1.getHandle()))) + // .exists() makes sure that the embed is there, but it could be empty + .andExpect(jsonPath("$._embedded.mappedItems").exists()) + // .isEmpty() makes sure that the embed is there, but that there's no actual data + .andExpect(jsonPath("$._embedded.mappedItems._embedded.mappedItems").isEmpty()) + .andExpect(jsonPath("$._embedded.parentCommunity", + CommunityMatcher.matchCommunityEntry(child1.getName(), + child1.getID(), + child1.getHandle()))) + // .exists() makes sure that the embed is there, but it could be empty + .andExpect(jsonPath("$._embedded.parentCommunity._embedded.subcommunities").exists()) + .andExpect(jsonPath("$._embedded.parentCommunity._embedded.subcommunities._embedded.subcommunities", + Matchers.contains(CommunityMatcher.matchCommunityEntry(child1child.getID(), + child1child.getHandle()) + ))) + .andExpect(jsonPath("$._embedded.parentCommunity._embedded.subcommunities" + + "._embedded.subcommunities[0]._embedded.collections._embedded.collections", + Matchers.contains(CollectionMatcher.matchCollectionEntry(col2.getName(), + col2.getID(), + col2.getHandle()) + ))) + // .doesNotExist() makes sure that this section is not embedded, it's not there at all + .andExpect(jsonPath("$._embedded.parentCommunity._embedded.subcommunities" + + "._embedded.subcommunities[0]._embedded.collections._embedded" + + ".collections[0]._embedded.logo").doesNotExist()) + .andExpect(jsonPath("$._embedded.logo", Matchers.not(Matchers.empty()))) + // .exists() makes sure that the embed is there, but it could be empty + .andExpect(jsonPath("$._embedded.logo._embedded.format").exists()); + } + + @Test + public void projectonLevelEmbedLevelDepthHigherThanEmbedMaxBadRequestTest() throws Exception { + //We turn off the authorization system in order to create the structure as defined below + context.turnOffAuthorisationSystem(); + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and one collection. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Community child1child = CommunityBuilder.createSubCommunity(context, child1) + .withName("Sub Community Two") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .withLogo("TestingContentForLogo") + .build(); + + getClient().perform(get("/api/core/collections/" + col1.getID()) + .param("projection", "level") + .param("embedLevelDepth", "100")) + .andExpect(status().isBadRequest()); + } + @Test + public void projectonLevelEmbedLevelDepthNotPresentBadRequestTest() throws Exception { + //We turn off the authorization system in order to create the structure as defined below + context.turnOffAuthorisationSystem(); + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and one collection. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Community child1child = CommunityBuilder.createSubCommunity(context, child1) + .withName("Sub Community Two") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .withLogo("TestingContentForLogo") + .build(); + + getClient().perform(get("/api/core/collections/" + col1.getID()) + .param("projection", "level")) + .andExpect(status().isBadRequest()); } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityCollectionItemParentIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityCollectionItemParentIT.java new file mode 100644 index 0000000000..d85cf34d6a --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityCollectionItemParentIT.java @@ -0,0 +1,374 @@ +/** + * 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.app.rest; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.sql.SQLException; +import java.util.UUID; + +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.matcher.CollectionMatcher; +import org.dspace.app.rest.matcher.CommunityMatcher; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.service.AuthorizeService; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.Item; +import org.dspace.content.service.CollectionService; +import org.dspace.content.service.CommunityService; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +public class CommunityCollectionItemParentIT extends AbstractControllerIntegrationTest { + + @Autowired + private CollectionService collectionService; + + @Autowired + private CommunityService communityService; + + @Autowired + private AuthorizeService authorizeService; + + Community communityA; + Community communityB; + Community communityAA; + Community communityAB; + + Collection colAA1; + Collection colAA2; + Collection colAB1; + + Item itemAA1; + Item itemAA1MappedInAA2; + Item itemAA2; + + + @Before + public void setup() throws SQLException, AuthorizeException { + //We turn off the authorization system in order to create the structure as defined below + context.turnOffAuthorisationSystem(); + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and one collection. + communityA = CommunityBuilder.createCommunity(context) + .withName("Parent CommunityA") + .build(); + communityB = CommunityBuilder.createCommunity(context) + .withName("Parent CommunityB") + .build(); + communityAA = CommunityBuilder.createSubCommunity(context, communityA) + .withName("Sub Community") + .build(); + communityAB = CommunityBuilder.createSubCommunity(context, communityA) + .withName("Sub Community Two") + .build(); + colAA1 = CollectionBuilder.createCollection(context, communityAA).withName("Collection 1").build(); + colAA2 = CollectionBuilder.createCollection(context, communityAA).withName("Collection 2").build(); + colAB1 = CollectionBuilder.createCollection(context, communityAB).withName("Collection 3").build(); + communityService.addCollection(context, communityAB, colAA2); + + + itemAA1 = ItemBuilder.createItem(context, colAA1) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + itemAA1MappedInAA2 = ItemBuilder.createItem(context, colAA1) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + collectionService.addItem(context, colAA2, itemAA1MappedInAA2); + itemAA2 = ItemBuilder.createItem(context, colAA2) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + context.restoreAuthSystemState(); + } + + @Test + public void itemAA1OwningCollectionTest() throws Exception { + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get("/api/core/items/" + itemAA1.getID() + "/owningCollection")) + .andExpect(jsonPath("$", is(CollectionMatcher.matchCollectionEntry(colAA1.getName(), + colAA1.getID(), + colAA1.getHandle())))) + .andExpect(jsonPath("$", Matchers + .not(is(CollectionMatcher + .matchCollectionEntry(colAA2.getName(), colAA2.getID(), colAA2.getHandle()))))); + + } + + @Test + public void itemAA1MappedInAA2OwningCollectionTest() throws Exception { + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get("/api/core/items/" + itemAA1MappedInAA2.getID() + "/owningCollection")) + .andExpect(jsonPath("$", is(CollectionMatcher.matchCollectionEntry(colAA1.getName(), + colAA1.getID(), + colAA1.getHandle())))) + .andExpect(jsonPath("$", Matchers + .not(is(CollectionMatcher + .matchCollectionEntry(colAA2.getName(), colAA2.getID(), colAA2.getHandle()))))); + } + + @Test + public void itemAA2OwningCollectionTest() throws Exception { + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get("/api/core/items/" + itemAA2.getID() + "/owningCollection")) + .andExpect(jsonPath("$", is(CollectionMatcher.matchCollectionEntry(colAA2.getName(), + colAA2.getID(), + colAA2.getHandle())))) + .andExpect(jsonPath("$", Matchers + .not(is(CollectionMatcher + .matchCollectionEntry(colAA1.getName(), colAA1.getID(), colAA1.getHandle()))))); + + } + + @Test + public void colAA1ParentCommunityTest() throws Exception { + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get("/api/core/collections/" + colAA1.getID() + "/parentCommunity")) + .andExpect(jsonPath("$", is(CommunityMatcher + .matchCommunityEntry(communityAA.getName(), communityAA.getID(), + communityAA.getHandle())))) + .andExpect(jsonPath("$", not(is(CommunityMatcher.matchCommunityEntry(communityA.getName(), + communityA.getID(), + communityA.getHandle()))))) + .andExpect(jsonPath("$", not(is(CommunityMatcher.matchCommunityEntry(communityAB.getName(), + communityAB.getID(), + communityAB + .getHandle()))))); + + } + + @Test + public void comAAParentCommunityTest() throws Exception { + + String token = getAuthToken(admin.getEmail(), password); + getClient(token) + .perform(get("/api/core/communities/" + communityAA.getID() + "/parentCommunity")) + .andExpect(jsonPath("$", Matchers + .is(CommunityMatcher.matchCommunityEntry(communityA.getID(), communityA.getHandle())))) + .andExpect(jsonPath("$", Matchers + .not(Matchers.is(CommunityMatcher.matchCommunityEntry(communityB.getID(), communityB.getHandle()))))); + + } + + @Test + public void comAParentCommunityTest() throws Exception { + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get("/api/core/communities/" + communityA.getID() + "/parentCommunity")) + .andExpect(status().isNoContent()); + + + } + + @Test + public void parentCommunityWrongUUIDTest() throws Exception { + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get("/api/core/communities/" + UUID.randomUUID() + "/parentCommunity")) + .andExpect(status().isNotFound()); + + + } + + @Test + public void parentCommunityPrivateCommunityUnAuthorizedTest() throws Exception { + + context.turnOffAuthorisationSystem(); + authorizeService.removeAllPolicies(context, communityAA); + context.restoreAuthSystemState(); + getClient().perform(get("/api/core/communities/" + communityAA.getID() + "/parentCommunity")) + .andExpect(status().isUnauthorized()); + + } + + //Enable this test when this security level has been supported + @Ignore + @Test + public void parentCommunityPrivateParentCommunityUnAuthorizedTest() throws Exception { + + context.turnOffAuthorisationSystem(); + authorizeService.removeAllPolicies(context, communityA); + context.restoreAuthSystemState(); + getClient().perform(get("/api/core/communities/" + communityAA.getID() + "/parentCommunity")) + .andExpect(status().isUnauthorized()); + + } + + @Test + public void parentCommunityPrivateCommunityForbiddenTest() throws Exception { + + context.turnOffAuthorisationSystem(); + authorizeService.removeAllPolicies(context, communityAA); + context.restoreAuthSystemState(); + String token = getAuthToken(eperson.getEmail(), password); + getClient(token).perform(get("/api/core/communities/" + communityAA.getID() + "/parentCommunity")) + .andExpect(status().isForbidden()); + + } + + //Enable this test when this security level has been supported + @Ignore + @Test + public void parentCommunityPrivateParentCommunityForbiddenTest() throws Exception { + + context.turnOffAuthorisationSystem(); + authorizeService.removeAllPolicies(context, communityA); + context.restoreAuthSystemState(); + String token = getAuthToken(eperson.getEmail(), password); + getClient(token).perform(get("/api/core/communities/" + communityAA.getID() + "/parentCommunity")) + .andExpect(status().isForbidden()); + + } + + @Test + public void parentCommunityForCollectionWrongUUIDTest() throws Exception { + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get("/api/core/collections/" + UUID.randomUUID() + "/parentCommunity")) + .andExpect(status().isNotFound()); + + + } + + @Test + public void parentCommunityForCollectionPrivateCollectionUnAuthorizedTest() throws Exception { + + context.turnOffAuthorisationSystem(); + authorizeService.removeAllPolicies(context, colAA1); + context.restoreAuthSystemState(); + getClient().perform(get("/api/core/collections/" + colAA1.getID() + "/parentCommunity")) + .andExpect(status().isUnauthorized()); + + } + + //Enable this test when this security level has been supported + @Ignore + @Test + public void parentCommunityForCollectionPrivateParentCommunityUnAuthorizedTest() throws Exception { + + context.turnOffAuthorisationSystem(); + authorizeService.removeAllPolicies(context, communityAA); + context.restoreAuthSystemState(); + getClient().perform(get("/api/core/collections/" + colAA1.getID() + "/parentCommunity")) + .andExpect(status().isUnauthorized()); + + } + + @Test + public void parentCommunityForCollectionPrivateCollectionForbiddenTest() throws Exception { + + context.turnOffAuthorisationSystem(); + authorizeService.removeAllPolicies(context, colAA1); + context.restoreAuthSystemState(); + String token = getAuthToken(eperson.getEmail(), password); + getClient(token).perform(get("/api/core/collections/" + colAA1.getID() + "/parentCommunity")) + .andExpect(status().isForbidden()); + + } + + //Enable this test when this security level has been supported + @Ignore + @Test + public void parentCommunityForCollectionPrivateParentCommunityForbiddenTest() throws Exception { + + context.turnOffAuthorisationSystem(); + authorizeService.removeAllPolicies(context, communityAA); + context.restoreAuthSystemState(); + String token = getAuthToken(eperson.getEmail(), password); + getClient(token).perform(get("/api/core/collections/" + colAA1.getID() + "/parentCommunity")) + .andExpect(status().isForbidden()); + + } + + @Test + public void owningCollectionForItemWrongUUIDTest() throws Exception { + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get("/api/core/items/" + UUID.randomUUID() + "/owningCollection")) + .andExpect(status().isNotFound()); + + + } + + @Test + public void owningCollectionForItemPrivateItemUnAuthorizedTest() throws Exception { + + context.turnOffAuthorisationSystem(); + authorizeService.removeAllPolicies(context, itemAA1); + context.restoreAuthSystemState(); + + getClient().perform(get("/api/core/items/" + itemAA1.getID() + "/owningCollection")) + .andExpect(status().isUnauthorized()); + + } + + + //Enable this test when this security level has been supported + @Ignore + @Test + public void owningCollectionForItemPrivateOwningCollectionUnAuthorizedTest() throws Exception { + + context.turnOffAuthorisationSystem(); + authorizeService.removeAllPolicies(context, colAA1); + context.restoreAuthSystemState(); + getClient().perform(get("/api/core/items/" + itemAA1.getID() + "/owningCollection")) + .andExpect(status().isUnauthorized()); + + } + + //Enable this test when this security level has been supported + @Ignore + @Test + public void owningCollectionForItemPrivateOwningCollectionForbiddenTest() throws Exception { + + context.turnOffAuthorisationSystem(); + authorizeService.removeAllPolicies(context, colAA1); + context.restoreAuthSystemState(); + String token = getAuthToken(eperson.getEmail(), password); + getClient(token).perform(get("/api/core/items/" + itemAA1.getID() + "/owningCollection")) + .andExpect(status().isForbidden()); + + } + @Test + public void owningCollectionForItemPrivateItemForbiddenTest() throws Exception { + + context.turnOffAuthorisationSystem(); + authorizeService.removeAllPolicies(context, itemAA1); + context.restoreAuthSystemState(); + String token = getAuthToken(eperson.getEmail(), password); + getClient(token).perform(get("/api/core/items/" + itemAA1.getID() + "/owningCollection")) + .andExpect(status().isForbidden()); + + } + +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityRestRepositoryIT.java index 5731c009bc..7ac1e325f1 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityRestRepositoryIT.java @@ -81,8 +81,10 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest public void createTest() throws Exception { ObjectMapper mapper = new ObjectMapper(); CommunityRest comm = new CommunityRest(); + CommunityRest commNoembeds = new CommunityRest(); // We send a name but the created community should set this to the title comm.setName("Test Top-Level Community"); + commNoembeds.setName("Test Top-Level Community Full"); MetadataRest metadataRest = new MetadataRest(); @@ -107,17 +109,22 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest metadataRest.put("dc.title", title); comm.setMetadata(metadataRest); + commNoembeds.setMetadata(metadataRest); + String authToken = getAuthToken(admin.getEmail(), password); // Capture the UUID of the created Community (see andDo() below) AtomicReference idRef = new AtomicReference(); + AtomicReference idRefNoEmbeds = new AtomicReference(); try { getClient(authToken).perform(post("/api/core/communities") .content(mapper.writeValueAsBytes(comm)) - .contentType(contentType)) + .contentType(contentType) + .param("projection", "full")) .andExpect(status().isCreated()) .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", CommunityMatcher.matchFullEmbeds())) .andExpect(jsonPath("$", Matchers.allOf( hasJsonPath("$.id", not(empty())), hasJsonPath("$.uuid", not(empty())), @@ -141,9 +148,19 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest // capture "id" returned in JSON response .andDo(result -> idRef .set(UUID.fromString(read(result.getResponse().getContentAsString(), "$.id")))); + + getClient(authToken).perform(post("/api/core/communities") + .content(mapper.writeValueAsBytes(commNoembeds)) + .contentType(contentType)) + .andExpect(status().isCreated()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", HalMatcher.matchNoEmbeds())) + .andDo(result -> idRefNoEmbeds + .set(UUID.fromString(read(result.getResponse().getContentAsString(), "$.id")))); } finally { // Delete the created community (cleanup after ourselves!) CommunityBuilder.deleteCommunity(idRef.get()); + CommunityBuilder.deleteCommunity(idRefNoEmbeds.get()); } } @@ -313,10 +330,11 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$._embedded.communities", Matchers.containsInAnyOrder( - CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), parentCommunity.getID(), - parentCommunity.getHandle()), + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(), + parentCommunity.getID(), + parentCommunity.getHandle()), CommunityMatcher - .matchCommunityEntry(child1.getName(), child1.getID(), child1.getHandle()) + .matchCommunityEntryFullProjection(child1.getName(), child1.getID(), child1.getHandle()) ))) .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities"))) .andExpect(jsonPath("$.page.size", is(20))) @@ -340,13 +358,15 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest .build(); - getClient().perform(get("/api/core/communities").param("size", "2").param("projection", "full")) + getClient().perform(get("/api/core/communities").param("size", "2").param("projection", + "full")) .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$._embedded.communities", Matchers.containsInAnyOrder( CommunityMatcher.matchCommunityEntryMultipleTitles(titles, parentCommunity.getID(), parentCommunity.getHandle()), - CommunityMatcher.matchCommunityEntry(child1.getID(), child1.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(child1.getName(), child1.getID(), + child1.getHandle()) ))) .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities"))) .andExpect(jsonPath("$.page.totalElements", is(2))) @@ -363,50 +383,60 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest .withTitle(titles.get(2)) .withTitle(titles.get(3)) .build(); - Community childCommunity = CommunityBuilder.createSubCommunity(context, parentCommunity).build(); + Community childCommunity = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("test") + .build(); Community secondParentCommunity = CommunityBuilder.createCommunity(context).withName("testing").build(); Community thirdParentCommunity = CommunityBuilder.createCommunity(context).withName("testingTitleTwo").build(); context.restoreAuthSystemState(); - getClient().perform(get("/api/core/communities").param("size", "2").param("projection", "full")) + getClient().perform(get("/api/core/communities").param("size", "2").param("projection", + "full")) .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$._embedded.communities", Matchers.containsInAnyOrder( CommunityMatcher.matchCommunityEntryMultipleTitles(titles, parentCommunity.getID(), parentCommunity.getHandle()), - CommunityMatcher.matchCommunityEntry(childCommunity.getID(), childCommunity.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(childCommunity.getName(), + childCommunity.getID(), + childCommunity.getHandle()) ))) - .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities"))) - .andExpect(jsonPath("$.page", PageMatcher.pageEntryWithTotalPagesAndElements(0, 2, 2, 4))); + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/core/communities"))) + .andExpect(jsonPath("$.page", PageMatcher.pageEntryWithTotalPagesAndElements(0, 2, + 2, 4))); getClient().perform(get("/api/core/communities").param("size", "2").param("page", "1") .param("projection", "full")) .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$._embedded.communities", Matchers.containsInAnyOrder( - CommunityMatcher.matchCommunityEntry(secondParentCommunity.getID(), - secondParentCommunity.getHandle()), - CommunityMatcher.matchCommunityEntry(thirdParentCommunity.getID(), - thirdParentCommunity.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(secondParentCommunity.getName(), + secondParentCommunity.getID(), + secondParentCommunity.getHandle()), + CommunityMatcher.matchCommunityEntryFullProjection(thirdParentCommunity.getName(), + thirdParentCommunity.getID(), + thirdParentCommunity.getHandle()) ))) .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities"))) - .andExpect(jsonPath("$.page", PageMatcher.pageEntryWithTotalPagesAndElements(1, 2, 2, 4))); + .andExpect(jsonPath("$.page", PageMatcher.pageEntryWithTotalPagesAndElements(1, 2, + 2, 4))); } @Test public void findAllNoNameCommunityIsReturned() throws Exception { context.turnOffAuthorisationSystem(); - parentCommunity = CommunityBuilder.createCommunity(context).build(); + parentCommunity = CommunityBuilder.createCommunity(context).withName("test").build(); getClient().perform(get("/api/core/communities") .param("projection", "full")) .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$._embedded.communities", Matchers.contains( - CommunityMatcher.matchCommunityEntry(parentCommunity.getID(), - parentCommunity.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(), + parentCommunity.getID(), + parentCommunity.getHandle()) ))) .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities"))) .andExpect(jsonPath("$.page.totalElements", is(1))); @@ -468,12 +498,14 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$._embedded.communities", Matchers.contains( - CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), parentCommunity.getID(), - parentCommunity.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(), + parentCommunity.getID(), + parentCommunity.getHandle()) ))) .andExpect(jsonPath("$._embedded.communities", Matchers.not( Matchers.contains( - CommunityMatcher.matchCommunityEntry(child1.getName(), child1.getID(), child1.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(child1.getName(), child1.getID(), + child1.getHandle()) ) ))) .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities"))) @@ -486,12 +518,14 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$._embedded.communities", Matchers.contains( - CommunityMatcher.matchCommunityEntry(child1.getName(), child1.getID(), child1.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(child1.getName(), child1.getID(), + child1.getHandle()) ))) .andExpect(jsonPath("$._embedded.communities", Matchers.not( Matchers.contains( - CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), parentCommunity.getID(), - parentCommunity.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(), + parentCommunity.getID(), + parentCommunity.getHandle()) ) ))) .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities"))) @@ -743,12 +777,14 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( - CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), parentCommunity.getID(), - parentCommunity.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(), + parentCommunity.getID(), + parentCommunity.getHandle()) ))) .andExpect(jsonPath("$", Matchers.not( Matchers.is( - CommunityMatcher.matchCommunityEntry(child1.getName(), child1.getID(), child1.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(child1.getName(), child1.getID(), + child1.getHandle()) ) ))) .andExpect(jsonPath("$._links.self.href", Matchers @@ -823,14 +859,18 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$._embedded.communities", Matchers.containsInAnyOrder( - CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), parentCommunity.getID(), - parentCommunity.getHandle()), - CommunityMatcher.matchCommunityEntry(parentCommunity2.getName(), parentCommunity2.getID(), - parentCommunity2.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(), + parentCommunity.getID(), + parentCommunity.getHandle()), + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity2.getName(), + parentCommunity2.getID(), + parentCommunity2.getHandle()) ))) .andExpect(jsonPath("$._embedded.communities", Matchers.not(Matchers.containsInAnyOrder( - CommunityMatcher.matchCommunityEntry(child1.getName(), child1.getID(), child1.getHandle()), - CommunityMatcher.matchCommunityEntry(child12.getName(), child12.getID(), child12.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(child1.getName(), child1.getID(), + child1.getHandle()), + CommunityMatcher.matchCommunityEntryFullProjection(child12.getName(), child12.getID(), + child12.getHandle()) )))) .andExpect( jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities/search/top"))) @@ -887,25 +927,25 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest .andExpect(content().contentType(contentType)) //Checking that these communities are present .andExpect(jsonPath("$._embedded.communities", Matchers.containsInAnyOrder( - CommunityMatcher.matchCommunityEntry(parentCommunityChild1.getName(), + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild1.getName(), parentCommunityChild1.getID(), parentCommunityChild1.getHandle()), - CommunityMatcher.matchCommunityEntry(parentCommunityChild2.getName(), + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild2.getName(), parentCommunityChild2.getID(), parentCommunityChild2.getHandle()) ))) //Checking that these communities are not present .andExpect(jsonPath("$._embedded.communities", Matchers.not(Matchers.anyOf( - CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(), parentCommunity.getID(), parentCommunity.getHandle()), - CommunityMatcher.matchCommunityEntry(parentCommunity2.getName(), + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity2.getName(), parentCommunity2.getID(), parentCommunity2.getHandle()), - CommunityMatcher.matchCommunityEntry(parentCommunity2Child1.getName(), + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity2Child1.getName(), parentCommunity2Child1.getID(), parentCommunity2Child1.getHandle()), - CommunityMatcher.matchCommunityEntry(parentCommunityChild2Child1.getName(), + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild2Child1.getName(), parentCommunityChild2Child1.getID(), parentCommunityChild2Child1.getHandle()) )))) @@ -922,25 +962,25 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest .andExpect(content().contentType(contentType)) //Checking that these communities are present .andExpect(jsonPath("$._embedded.communities", Matchers.contains( - CommunityMatcher.matchCommunityEntry(parentCommunityChild2Child1.getName(), + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild2Child1.getName(), parentCommunityChild2Child1.getID(), parentCommunityChild2Child1.getHandle()) ))) //Checking that these communities are not present .andExpect(jsonPath("$._embedded.communities", Matchers.not(Matchers.anyOf( - CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(), parentCommunity.getID(), parentCommunity.getHandle()), - CommunityMatcher.matchCommunityEntry(parentCommunity2.getName(), + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity2.getName(), parentCommunity2.getID(), parentCommunity2.getHandle()), - CommunityMatcher.matchCommunityEntry(parentCommunity2Child1.getName(), + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity2Child1.getName(), parentCommunity2Child1.getID(), parentCommunity2Child1.getHandle()), - CommunityMatcher.matchCommunityEntry(parentCommunityChild2Child1.getName(), + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild2Child1.getName(), parentCommunityChild2Child1.getID(), parentCommunityChild2Child1.getHandle()), - CommunityMatcher.matchCommunityEntry(parentCommunityChild1.getName(), + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild1.getName(), parentCommunityChild1.getID(), parentCommunityChild1.getHandle()) )))) @@ -1283,7 +1323,7 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest @Test public void findAllSubCommunitiesWithoutUUID() throws Exception { getClient().perform(get("/api/core/communities/search/subCommunities")) - .andExpect(status().isUnprocessableEntity()); + .andExpect(status().isBadRequest()); } @Test @@ -1333,12 +1373,14 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( - CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), parentCommunity.getID(), - parentCommunity.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(), + parentCommunity.getID(), + parentCommunity.getHandle()) ))) .andExpect(jsonPath("$", Matchers.not( Matchers.is( - CommunityMatcher.matchCommunityEntry(child1.getName(), child1.getID(), child1.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(child1.getName(), child1.getID(), + child1.getHandle()) ) ))) .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities"))) @@ -1366,7 +1408,7 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( - CommunityMatcher.matchCommunityEntry("Electronic theses and dissertations", + CommunityMatcher.matchCommunityEntryFullProjection("Electronic theses and dissertations", parentCommunity.getID(), parentCommunity.getHandle()) ))) @@ -1421,11 +1463,12 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( - CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), parentCommunity.getID(), - parentCommunity.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(), + parentCommunity.getID(), + parentCommunity.getHandle()) ))) .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("/api/core/communities"))) ; + Matchers.containsString("/api/core/communities"))); getClient(token).perform(delete("/api/core/communities/" + parentCommunity.getID().toString())) .andExpect(status().isNoContent()) ; @@ -1483,11 +1526,12 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( - CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), parentCommunity.getID(), - parentCommunity.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(), + parentCommunity.getID(), + parentCommunity.getHandle()) ))) .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("/api/core/communities"))) ; + Matchers.containsString("/api/core/communities"))); getClient().perform(delete("/api/core/communities/" + parentCommunity.getID().toString())) .andExpect(status().isUnauthorized()) ; @@ -1516,11 +1560,12 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( - CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), parentCommunity.getID(), - parentCommunity.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(), + parentCommunity.getID(), + parentCommunity.getHandle()) ))) .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("/api/core/communities"))) ; + Matchers.containsString("/api/core/communities"))); getClient(token).perform(delete("/api/core/communities/" + parentCommunity.getID().toString())) .andExpect(status().isNoContent()) ; @@ -1550,12 +1595,14 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( - CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), parentCommunity.getID(), - parentCommunity.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(), + parentCommunity.getID(), + parentCommunity.getHandle()) ))) .andExpect(jsonPath("$", Matchers.not( Matchers.is( - CommunityMatcher.matchCommunityEntry(child1.getName(), child1.getID(), child1.getHandle()) + CommunityMatcher.matchCommunityEntryFullProjection(child1.getName(), child1.getID(), + child1.getHandle()) ) ))) .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities"))) @@ -1586,7 +1633,7 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( - CommunityMatcher.matchCommunityEntry("Electronic theses and dissertations", + CommunityMatcher.matchCommunityEntryFullProjection("Electronic theses and dissertations", parentCommunity.getID(), parentCommunity.getHandle()) ))) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java index 6a79eb6a4b..49feaba6c5 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java @@ -56,6 +56,7 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest { // we should check how to get it from Spring ObjectMapper mapper = new ObjectMapper(); EPersonRest data = new EPersonRest(); + EPersonRest dataFull = new EPersonRest(); MetadataRest metadataRest = new MetadataRest(); data.setEmail("createtest@fake-email.com"); data.setCanLogIn(true); @@ -66,13 +67,18 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest { firstname.setValue("John"); metadataRest.put("eperson.firstname", firstname); data.setMetadata(metadataRest); + dataFull.setEmail("createtestFull@fake-email.com"); + dataFull.setCanLogIn(true); + dataFull.setMetadata(metadataRest); String authToken = getAuthToken(admin.getEmail(), password); getClient(authToken).perform(post("/api/eperson/epersons") .content(mapper.writeValueAsBytes(data)) - .contentType(contentType)) + .contentType(contentType) + .param("projection", "full")) .andExpect(status().isCreated()) .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", EPersonMatcher.matchFullEmbeds())) .andExpect(jsonPath("$", Matchers.allOf( hasJsonPath("$.uuid", not(empty())), // is it what you expect? EPerson.getName() returns the email... @@ -86,6 +92,13 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest { matchMetadata("eperson.firstname", "John"), matchMetadata("eperson.lastname", "Doe") ))))); + + getClient(authToken).perform(post("/api/eperson/epersons") + .content(mapper.writeValueAsBytes(dataFull)) + .contentType(contentType)) + .andExpect(status().isCreated()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", HalMatcher.matchNoEmbeds())); // TODO cleanup the context!!! } @@ -347,7 +360,7 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest { public void findByEmailUnprocessable() throws Exception { String authToken = getAuthToken(admin.getEmail(), password); getClient(authToken).perform(get("/api/eperson/epersons/search/byEmail")) - .andExpect(status().isUnprocessableEntity()); + .andExpect(status().isBadRequest()); } @Test @@ -419,7 +432,7 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest { public void findByNameUnprocessable() throws Exception { String authToken = getAuthToken(admin.getEmail(), password); getClient(authToken).perform(get("/api/eperson/epersons/search/byName")) - .andExpect(status().isUnprocessableEntity()); + .andExpect(status().isBadRequest()); } @Test @@ -988,6 +1001,32 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest { } + @Test + public void patchPasswordReplaceOnNonExistentValue() throws Exception { + + context.turnOffAuthorisationSystem(); + + EPerson ePerson = EPersonBuilder.createEPerson(context) + .withNameInMetadata("John", "Doe") + .withEmail("Johndoe@fake-email.com") + .build(); + + String newPassword = "newpassword"; + + List ops = new ArrayList(); + ReplaceOperation replaceOperation = new ReplaceOperation("/password", newPassword); + ops.add(replaceOperation); + String patchBody = getPatchContent(ops); + + String token = getAuthToken(admin.getEmail(), password); + + // replace of password should fail + getClient(token).perform(patch("/api/eperson/epersons/" + ePerson.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isBadRequest()); + } + @Test public void patchCanLoginNonAdminUser() throws Exception { context.turnOffAuthorisationSystem(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ExternalSourcesRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ExternalSourcesRestControllerIT.java index 7998864d8d..799f6b7892 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ExternalSourcesRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ExternalSourcesRestControllerIT.java @@ -114,6 +114,6 @@ public class ExternalSourcesRestControllerIT extends AbstractControllerIntegrati @Test public void findOneExternalSourceEntriesNoQuery() throws Exception { getClient().perform(get("/api/integration/externalsources/mock/entries")) - .andExpect(status().isUnprocessableEntity()); + .andExpect(status().isBadRequest()); } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java index efccd50582..d89968ef5b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java @@ -44,17 +44,23 @@ public class GroupRestRepositoryIT extends AbstractControllerIntegrationTest { // hold the id of the created workflow item AtomicReference idRef = new AtomicReference(); + AtomicReference idRefNoEmbeds = new AtomicReference(); try { ObjectMapper mapper = new ObjectMapper(); GroupRest groupRest = new GroupRest(); + GroupRest groupRestNoEmbeds = new GroupRest(); String groupName = "testGroup1"; + String groupNameNoEmbeds = "testGroup2"; groupRest.setName(groupName); + groupRestNoEmbeds.setName(groupNameNoEmbeds); String authToken = getAuthToken(admin.getEmail(), password); getClient(authToken).perform(post("/api/eperson/groups") - .content(mapper.writeValueAsBytes(groupRest)).contentType(contentType)) + .content(mapper.writeValueAsBytes(groupRest)).contentType(contentType) + .param("projection", "full")) .andExpect(status().isCreated()) + .andExpect(jsonPath("$", GroupMatcher.matchFullEmbeds())) .andDo(result -> idRef .set(UUID.fromString(read(result.getResponse().getContentAsString(), "$.id")))); @@ -66,9 +72,17 @@ public class GroupRestRepositoryIT extends AbstractControllerIntegrationTest { GroupMatcher.matchGroupWithName(groupName), GroupMatcher.matchGroupWithName("Administrator"), GroupMatcher.matchGroupWithName("Anonymous")))); + + getClient(authToken).perform(post("/api/eperson/groups") + .content(mapper.writeValueAsBytes(groupRestNoEmbeds)).contentType(contentType)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$", HalMatcher.matchNoEmbeds())) + .andDo(result -> idRefNoEmbeds + .set(UUID.fromString(read(result.getResponse().getContentAsString(), "$.id")))); } finally { // remove the created group if any GroupBuilder.deleteGroup(idRef.get()); + GroupBuilder.deleteGroup(idRefNoEmbeds.get()); } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestControllerIT.java index 209de2489c..9fac87a9e6 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestControllerIT.java @@ -98,8 +98,7 @@ public class ItemOwningCollectionUpdateRestControllerIT extends AbstractControll //We expect a 401 Unauthorized status when performed by anonymous .andExpect(status().isOk()); - getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/owningCollection") - .param("projection", "full")) + getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/owningCollection")) .andExpect(jsonPath("$", is(CollectionMatcher .matchCollectionEntry(col2.getName(), col2.getID(), col2.getHandle()) @@ -152,8 +151,7 @@ public class ItemOwningCollectionUpdateRestControllerIT extends AbstractControll //We expect a 401 Unauthorized status when performed by anonymous .andExpect(status().isOk()); - getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/owningCollection") - .param("projection", "full")) + getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/owningCollection")) .andExpect(jsonPath("$", is(CollectionMatcher .matchCollectionEntry(col2.getName(), col2.getID(), col2.getHandle()) @@ -279,7 +277,8 @@ public class ItemOwningCollectionUpdateRestControllerIT extends AbstractControll "https://localhost:8080/spring-rest/api/core/collections/" + col2.getID() )) - // we expect 200 Ok as the user have the right permissions + // we expect a 200 here as the user has ADMIN permission on the source collection and + // ADD permission on the target one. This is the normal behavior also in DSpace 6 .andExpect(status().isOk()); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java index 04ab5c8621..45c398ba85 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java @@ -8,6 +8,7 @@ package org.dspace.app.rest; import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; + import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -35,6 +36,8 @@ import org.dspace.app.rest.builder.EPersonBuilder; import org.dspace.app.rest.builder.GroupBuilder; import org.dspace.app.rest.builder.ItemBuilder; import org.dspace.app.rest.builder.WorkspaceItemBuilder; +import org.dspace.app.rest.matcher.BitstreamMatcher; +import org.dspace.app.rest.matcher.CollectionMatcher; import org.dspace.app.rest.matcher.HalMatcher; import org.dspace.app.rest.matcher.ItemMatcher; import org.dspace.app.rest.matcher.MetadataMatcher; @@ -50,15 +53,20 @@ import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.Item; import org.dspace.content.WorkspaceItem; +import org.dspace.content.service.CollectionService; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.hamcrest.Matcher; import org.hamcrest.Matchers; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.web.servlet.MvcResult; public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { + @Autowired + private CollectionService collectionService; + @Test public void findAllTest() throws Exception { context.turnOffAuthorisationSystem(); @@ -251,6 +259,16 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { .andExpect(status().isOk()) .andExpect(jsonPath("$", HalMatcher.matchNoEmbeds())) .andExpect(jsonPath("$", publicItem1Matcher)); + + // When exact embeds are requested, response should include expected properties, links, and exact embeds. + getClient().perform(get("/api/core/items/" + publicItem1.getID()) + .param("embed", "bundles,owningCollection")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", HalMatcher.matchEmbeds( + "bundles[]", + "owningCollection" + ))) + .andExpect(jsonPath("$", publicItem1Matcher)); } @Test @@ -1200,9 +1218,9 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { getClient(token).perform(delete("/api/core/items/" + templateItem.getID())) .andExpect(status().is(422)); - //Check templateItem is available after failed deletion + //Check templateItem is not deleted getClient(token).perform(get("/api/core/items/" + templateItem.getID())) - .andExpect(status().isOk()); + .andExpect(status().isBadRequest()); } @Test @@ -1294,6 +1312,122 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { } + @Test + public void embargoAccessGrantAdminsTest() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson adminParentCommunity = EPersonBuilder.createEPerson(context) + .withEmail("adminCommunity@mail.com") + .withPassword("qwerty01") + .build(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .withAdminGroup(adminParentCommunity) + .build(); + + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + + EPerson adminChild2 = EPersonBuilder.createEPerson(context) + .withEmail("adminChild2@mail.com") + .withPassword("qwerty05") + .build(); + Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community 2") + .withAdminGroup(adminChild2) + .build(); + + EPerson adminCollection1 = EPersonBuilder.createEPerson(context) + .withEmail("adminCollection1@mail.com") + .withPassword("qwerty02") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .withAdminGroup(adminCollection1) + .build(); + + EPerson adminCollection2 = EPersonBuilder.createEPerson(context) + .withEmail("adminCollection2@mail.com") + .withPassword("qwerty03") + .build(); + Collection col2 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 2") + .withAdminGroup(adminCollection2) + .build(); + + Item embargoedItem = ItemBuilder.createItem(context, col1) + .withTitle("Public item 1") + .withIssueDate("2015-10-21") + .withAuthor("Smith, Donald") + .withSubject("ExtraEntry") + .withEmbargoPeriod("1 week") + .build(); + + context.restoreAuthSystemState(); + // parent community's admin user is allowed access to embargoed item + String tokenAdminParentCommunity = getAuthToken(adminParentCommunity.getEmail(), "qwerty01"); + getClient(tokenAdminParentCommunity).perform(get("/api/core/items/" + embargoedItem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", is(ItemMatcher.matchItemProperties(embargoedItem)))); + + // collection1's admin user is allowed access to embargoed item + String tokenAdminCollection1 = getAuthToken(adminCollection1.getEmail(), "qwerty02"); + getClient(tokenAdminCollection1).perform(get("/api/core/items/" + embargoedItem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", is(ItemMatcher.matchItemProperties(embargoedItem)))); + + // collection2's admin user is NOT allowed access to embargoed item of collection1 + String tokenAdminCollection2 = getAuthToken(adminCollection2.getEmail(), "qwerty03"); + getClient(tokenAdminCollection2).perform(get("/api/core/items/" + embargoedItem.getID())) + .andExpect(status().isForbidden()); + + // full admin user is allowed access to embargoed item + String tokenAdmin = getAuthToken(admin.getEmail(), password); + getClient(tokenAdmin).perform(get("/api/core/items/" + embargoedItem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", is(ItemMatcher.matchItemProperties(embargoedItem)))); + + // child2's admin user is NOT allowed access to embargoed item of collection1 + String tokenAdminChild2 = getAuthToken(adminChild2.getEmail(), "qwerty05"); + getClient(tokenAdminChild2).perform(get("/api/core/items/" + embargoedItem.getID())) + .andExpect(status().isForbidden()); + } + + @Test + public void expiredEmbargoTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Collection col1 = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection 1") + .build(); + + Item embargoedItem = ItemBuilder.createItem(context, col1) + .withTitle("embargoed item 1") + .withIssueDate("2017-11-18") + .withAuthor("Smith, Donald") + .withEmbargoPeriod("-2 week") + .build(); + + context.restoreAuthSystemState(); + + // all are allowed access to item with embargoed expired + + String tokenEperson = getAuthToken(eperson.getEmail(), password); + getClient(tokenEperson).perform(get("/api/core/items/" + embargoedItem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.is(ItemMatcher.matchItemProperties(embargoedItem)))); + + getClient().perform(get("/api/core/items/" + embargoedItem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.is(ItemMatcher.matchItemProperties(embargoedItem)))); + } + @Test public void undiscoverableAccessTest() throws Exception { context.turnOffAuthorisationSystem(); @@ -1422,6 +1556,134 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { Matchers.containsString("/api/core/items"))); } + @Test + public void restrictedGroupAccessForbiddenTest() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson memberRestrictGroup = EPersonBuilder.createEPerson(context) + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); + + Group restrictGroup = GroupBuilder.createGroup(context) + .addMember(memberRestrictGroup) + .build(); + + 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 itemRestrictedByGroup = ItemBuilder.createItem(context, col1) + .withTitle("Public item 1") + .withIssueDate("2011-11-13") + .withAuthor("Smith, Donald") + .withReaderGroup(restrictGroup) + .build(); + + context.restoreAuthSystemState(); + + //A member of the restricted group is also allowed access to restricted item + String tokenMemberRestrictedGroup = getAuthToken(memberRestrictGroup.getEmail(), "qwerty01"); + getClient(tokenMemberRestrictedGroup).perform(get("/api/core/items/" + itemRestrictedByGroup.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", is(ItemMatcher.matchItemProperties(itemRestrictedByGroup)))); + + //members who are not part of the restricted group, have no access to the item + String tokenEPerson = getAuthToken(eperson.getEmail(), password); + getClient(tokenEPerson).perform(get("/api/core/items/" + itemRestrictedByGroup.getID())) + .andExpect(status().isForbidden()); + + getClient().perform(get("/api/core/items/" + itemRestrictedByGroup.getID())) + .andExpect(status().isUnauthorized()); + } + + @Test + public void restrictedGroupAccessGrantAdminsTest() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson adminParentCommunity = EPersonBuilder.createEPerson(context) + .withEmail("adminCommunity@mail.com") + .withPassword("qwerty01") + .build(); + + Group restrictedGroup = GroupBuilder.createGroup(context) + .build(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .withAdminGroup(adminParentCommunity) + .build(); + + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + + EPerson adminChild2 = EPersonBuilder.createEPerson(context) + .withEmail("adminChild2@mail.com") + .withPassword("qwerty05") + .build(); + Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community 2") + .build(); + + EPerson adminCollection1 = EPersonBuilder.createEPerson(context) + .withEmail("adminCollection1@mail.com") + .withPassword("qwerty02") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .withAdminGroup(adminCollection1) + .build(); + + EPerson adminCollection2 = EPersonBuilder.createEPerson(context) + .withEmail("adminCollection2@mail.com") + .withPassword("qwerty03") + .build(); + Collection col2 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 2") + .withAdminGroup(adminCollection2) + .build(); + + Item itemRestrictedByGroup = ItemBuilder.createItem(context, col1) + .withTitle("Public item 1") + .withIssueDate("2015-10-21") + .withAuthor("Smith, Donald") + .withSubject("ExtraEntry") + .withReaderGroup(restrictedGroup) + .build(); + + context.restoreAuthSystemState(); + // parent community's admin user is allowed access to restricted item + String tokenAdminParentCommunity = getAuthToken(adminParentCommunity.getEmail(), "qwerty01"); + getClient(tokenAdminParentCommunity).perform(get("/api/core/items/" + itemRestrictedByGroup.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", is(ItemMatcher.matchItemProperties(itemRestrictedByGroup)))); + + // collection1's admin user is allowed access to restricted item + String tokenAdminCollection1 = getAuthToken(adminCollection1.getEmail(), "qwerty02"); + getClient(tokenAdminCollection1).perform(get("/api/core/items/" + itemRestrictedByGroup.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", is(ItemMatcher.matchItemProperties(itemRestrictedByGroup)))); + + // collection2's admin user is NOT allowed access to restricted item of collection1 + String tokenAdminCollection2 = getAuthToken(adminCollection2.getEmail(), "qwerty03"); + getClient(tokenAdminCollection2).perform(get("/api/core/items/" + itemRestrictedByGroup.getID())) + .andExpect(status().isForbidden()); + + // child2's admin user is NOT allowed access to restricted item of collection1 + String tokenAdminChild2 = getAuthToken(adminChild2.getEmail(), "qwerty05"); + getClient(tokenAdminCollection2).perform(get("/api/core/items/" + itemRestrictedByGroup.getID())) + .andExpect(status().isForbidden()); + } + @Test public void testCreateItem() throws Exception { @@ -1441,6 +1703,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { ObjectMapper mapper = new ObjectMapper(); ItemRest itemRest = new ItemRest(); + ItemRest itemRestFull = new ItemRest(); itemRest.setName("Practices of research data curation in institutional repositories:" + " A qualitative view from repository staff"); itemRest.setInArchive(true); @@ -1454,11 +1717,25 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { .put("dc.rights", new MetadataValueRest("Custom Copyright Text")) .put("dc.title", new MetadataValueRest("Title Text"))); + itemRestFull.setName("Practices of research data curation in institutional repositories:" + + " A qualitative view from repository staff"); + itemRestFull.setInArchive(true); + itemRestFull.setDiscoverable(true); + itemRestFull.setWithdrawn(false); + + itemRestFull.setMetadata(new MetadataRest() + .put("dc.description", new MetadataValueRest("

    Some cool HTML code here

    ")) + .put("dc.description.abstract", new MetadataValueRest("Sample item created via the REST API")) + .put("dc.description.tableofcontents", new MetadataValueRest("

    HTML News

    ")) + .put("dc.rights", new MetadataValueRest("Custom Copyright Text")) + .put("dc.title", new MetadataValueRest("Title Text"))); + String token = getAuthToken(admin.getEmail(), password); MvcResult mvcResult = getClient(token).perform(post("/api/core/items?owningCollection=" + col1.getID().toString()) .content(mapper.writeValueAsBytes(itemRest)).contentType(contentType)) .andExpect(status().isCreated()) + .andExpect(jsonPath("$", HalMatcher.matchNoEmbeds())) .andReturn(); String content = mvcResult.getResponse().getContentAsString(); @@ -1487,6 +1764,13 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { MetadataMatcher.matchMetadata("dc.title", "Title Text") ))))); + + MvcResult mvcResultFull = getClient(token).perform(post("/api/core/items?owningCollection=" + + col1.getID().toString()).param("projection", "full") + .content(mapper.writeValueAsBytes(itemRestFull)).contentType(contentType)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$", ItemMatcher.matchFullEmbeds())) + .andReturn(); } @Test @@ -1744,6 +2028,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { .andExpect(status().is(404)); } + @Test public void patchItemMetadataAuthorized() throws Exception { runPatchMetadataTests(admin, 200); } @@ -2087,4 +2372,178 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { .content("https://localhost:8080/server/api/integration/externalsources/" + "mock/entryValues/one")).andExpect(status().isUnauthorized()); } + + @Test + public void specificEmbedTestMultipleLevelOfLinks() throws Exception { + context.turnOffAuthorisationSystem(); + + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and two collections. + 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(); + Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build(); + + //2. Three public items that are readable by Anonymous with different subjects + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + Item publicItem2 = ItemBuilder.createItem(context, col2) + .withTitle("Public item 2") + .withIssueDate("2016-02-13") + .withAuthor("Smith, Maria").withAuthor("Doe, Jane") + .withSubject("TestingForMore").withSubject("ExtraEntry") + .build(); + + Item publicItem3 = ItemBuilder.createItem(context, col2) + .withTitle("Public item 3") + .withIssueDate("2016-02-13") + .withAuthor("Smith, Maria").withAuthor("Doe, Jane") + .withSubject("AnotherTest").withSubject("TestingForMore") + .withSubject("ExtraEntry") + .build(); + + + //Add a bitstream to an item + String bitstreamContent = "ThisIsSomeDummyText"; + Bitstream bitstream1 = null; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + bitstream1 = BitstreamBuilder. + createBitstream(context, publicItem1, is) + .withName("Bitstream1") + .withMimeType("text/plain") + .build(); + } + context.restoreAuthSystemState(); + getClient().perform(get("/api/core/items/" + publicItem1.getID() + + "?embed=owningCollection/mappedItems/bundles/" + + "bitstreams&embed=owningCollection/logo")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", ItemMatcher.matchItemProperties(publicItem1))) + .andExpect(jsonPath("$._embedded.owningCollection", + CollectionMatcher.matchCollectionEntry(col1.getName(), + col1.getID(), + col1.getHandle()))) + // .doesNotExist() makes sure that this section is not embedded, it's not there at all + .andExpect(jsonPath("$._embedded.bundles").doesNotExist()) + // .doesNotExist() makes sure that this section is not embedded, it's not there at all + .andExpect(jsonPath("$._embedded.relationships").doesNotExist()) + // .doesNotExist() makes sure that this section is not embedded, it's not there at all + .andExpect(jsonPath("$._embedded.owningCollection._embedded.defaultAccessConditions") + .doesNotExist()) + // .nullValue() makes sure that it could be embedded, it's just null in this case + .andExpect(jsonPath("$._embedded.owningCollection._embedded.logo", Matchers.nullValue())) + // .empty() makes sure that the embed is there, but that there's no actual data + .andExpect(jsonPath("$._embedded.owningCollection._embedded.mappedItems._embedded.mappedItems", + Matchers.empty())) + ; + } + + @Test + public void specificEmbedTestMultipleLevelOfLinksWithData() throws Exception { + context.turnOffAuthorisationSystem(); + + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and two collections. + 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") + .withLogo("TestingContentForLogo").build(); + Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build(); + + //2. Three public items that are readable by Anonymous with different subjects + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + Item publicItem2 = ItemBuilder.createItem(context, col2) + .withTitle("Public item 2") + .withIssueDate("2016-02-13") + .withAuthor("Smith, Maria").withAuthor("Doe, Jane") + .withSubject("TestingForMore").withSubject("ExtraEntry") + .build(); + + Item publicItem3 = ItemBuilder.createItem(context, col2) + .withTitle("Public item 3") + .withIssueDate("2016-02-13") + .withAuthor("Smith, Maria").withAuthor("Doe, Jane") + .withSubject("AnotherTest").withSubject("TestingForMore") + .withSubject("ExtraEntry") + .build(); + + + collectionService.addItem(context, col1, publicItem2); + + //Add a bitstream to an item + String bitstreamContent = "ThisIsSomeDummyText"; + Bitstream bitstream1 = null; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + bitstream1 = BitstreamBuilder. + createBitstream(context, publicItem1, is) + .withName("Bitstream1") + .withMimeType("text/plain") + .build(); + } + + String bitstreamContent2 = "ThisIsSomeDummyText"; + Bitstream bitstream2 = null; + try (InputStream is = IOUtils.toInputStream(bitstreamContent2, CharEncoding.UTF_8)) { + bitstream2 = BitstreamBuilder. + createBitstream(context, publicItem2, is) + .withName("Bitstream2") + .withMimeType("text/plain") + .build(); + } + + + context.restoreAuthSystemState(); + getClient().perform(get("/api/core/items/" + publicItem1.getID() + + "?embed=owningCollection/mappedItems/bundles/" + + "bitstreams&embed=owningCollection/logo")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", ItemMatcher.matchItemProperties(publicItem1))) + .andExpect(jsonPath("$._embedded.owningCollection", + CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), + col1.getHandle()))) + // .doesNotExist() makes sure that this section is not embedded, it's not there at all + .andExpect(jsonPath("$._embedded.bundles").doesNotExist()) + .andExpect(jsonPath("$._embedded.relationships").doesNotExist()) + .andExpect(jsonPath("$._embedded.owningCollection._embedded.defaultAccessConditions") + .doesNotExist()) + // .notNullValue() makes sure that it's there and that it does actually contain a value, but not null + .andExpect(jsonPath("$._embedded.owningCollection._embedded.logo", Matchers.notNullValue())) + .andExpect(jsonPath("$._embedded.owningCollection._embedded.logo._embedded").doesNotExist()) + .andExpect(jsonPath("$._embedded.owningCollection._embedded.mappedItems._embedded.mappedItems", + Matchers.contains(ItemMatcher.matchItemProperties(publicItem2)))) + .andExpect(jsonPath("$._embedded.owningCollection._embedded.mappedItems._embedded" + + ".mappedItems[0]._embedded.bundles._embedded.bundles[0]._embedded" + + ".bitstreams._embedded.bitstreams", Matchers.contains( + BitstreamMatcher.matchBitstreamEntryWithoutEmbed(bitstream2.getID(), bitstream2.getSizeBytes()) + ))) + .andExpect(jsonPath("$._embedded.owningCollection._embedded.mappedItems." + + "_embedded.mappedItems[0]_embedded.relationships").doesNotExist()) + .andExpect(jsonPath("$._embedded.owningCollection._embedded.mappedItems" + + "._embedded.mappedItems[0]._embedded.bundles._embedded.bundles[0]." + + "_embedded.primaryBitstream").doesNotExist()) + .andExpect(jsonPath("$._embedded.owningCollection._embedded.mappedItems." + + "_embedded.mappedItems[0]._embedded.bundles._embedded.bundles[0]." + + "_embedded.bitstreams._embedded.bitstreams[0]._embedded.format") + .doesNotExist()) + ; + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemTemplateRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemTemplateRestControllerIT.java index 475fbbed58..2f8839f74e 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemTemplateRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemTemplateRestControllerIT.java @@ -25,9 +25,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.dspace.app.rest.builder.CollectionBuilder; import org.dspace.app.rest.builder.CommunityBuilder; import org.dspace.app.rest.matcher.MetadataMatcher; -import org.dspace.app.rest.model.ItemRest; import org.dspace.app.rest.model.MetadataRest; import org.dspace.app.rest.model.MetadataValueRest; +import org.dspace.app.rest.model.TemplateItemRest; import org.dspace.app.rest.model.patch.AddOperation; import org.dspace.app.rest.model.patch.Operation; import org.dspace.app.rest.model.patch.ReplaceOperation; @@ -43,31 +43,29 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT private ObjectMapper mapper; private String adminAuthToken; private Collection childCollection; - private ItemRest testTemplateItem; + private TemplateItemRest testTemplateItem; private String patchBody; @Before public void createStructure() throws Exception { context.turnOffAuthorisationSystem(); parentCommunity = CommunityBuilder.createCommunity(context) - .withName("Parent Community") - .build(); + .withName("Parent Community") + .build(); childCollection = CollectionBuilder.createCollection(context, parentCommunity) - .withName("Collection 1").build(); + .withName("Collection 1").build(); adminAuthToken = getAuthToken(admin.getEmail(), password); mapper = new ObjectMapper(); } private void setupTestTemplate() { - testTemplateItem = new ItemRest(); - testTemplateItem.setInArchive(false); - testTemplateItem.setDiscoverable(false); - testTemplateItem.setWithdrawn(false); + testTemplateItem = new TemplateItemRest(); testTemplateItem.setMetadata(new MetadataRest() - .put("dc.description", new MetadataValueRest("dc description content")) - .put("dc.description.abstract", new MetadataValueRest("dc description abstract content"))); + .put("dc.description", new MetadataValueRest("dc description content")) + .put("dc.description.abstract", + new MetadataValueRest("dc description abstract content"))); List ops = new ArrayList<>(); List> values = new ArrayList<>(); @@ -82,13 +80,14 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT private String installTestTemplate() throws Exception { MvcResult mvcResult = getClient(adminAuthToken).perform(post( getCollectionTemplateItemUrlTemplate(childCollection.getID().toString())) - .content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType)) - .andExpect(status().isCreated()) - .andReturn(); + .content(mapper.writeValueAsBytes(testTemplateItem)) + .contentType(contentType)) + .andExpect(status().isCreated()) + .andReturn(); String content = mvcResult.getResponse().getContentAsString(); - Map map = mapper.readValue(content, Map.class); - return String.valueOf(map.get("uuid")); + Map map = mapper.readValue(content, Map.class); + return String.valueOf(map.get("id")); } @Test @@ -96,9 +95,9 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT setupTestTemplate(); getClient().perform(post( - getCollectionTemplateItemUrlTemplate(childCollection.getID().toString())) - .content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType)) - .andExpect(status().isUnauthorized()); + getCollectionTemplateItemUrlTemplate(childCollection.getID().toString())) + .content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType)) + .andExpect(status().isUnauthorized()); } @Test @@ -107,48 +106,15 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT installTestTemplate(); } - @Test - public void createIllegalInArchiveTemplateItem() throws Exception { - setupTestTemplate(); - testTemplateItem.setInArchive(true); - - getClient(adminAuthToken).perform(post( - getCollectionTemplateItemUrlTemplate(childCollection.getID().toString())) - .content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType)) - .andExpect(status().isUnprocessableEntity()); - } - - @Test - public void createIllegalDiscoverableTemplateItem() throws Exception { - setupTestTemplate(); - testTemplateItem.setDiscoverable(true); - - getClient(adminAuthToken).perform(post( - getCollectionTemplateItemUrlTemplate(childCollection.getID().toString())) - .content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType)) - .andExpect(status().isUnprocessableEntity()); - } - - @Test - public void createIllegalWithdrawnTemplateItem() throws Exception { - setupTestTemplate(); - testTemplateItem.setWithdrawn(true); - - getClient(adminAuthToken).perform(post( - getCollectionTemplateItemUrlTemplate(childCollection.getID().toString())) - .content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType)) - .andExpect(status().isUnprocessableEntity()); - } - @Test public void createTemplateItemNoRights() throws Exception { setupTestTemplate(); String userToken = getAuthToken(eperson.getEmail(), password); getClient(userToken).perform(post( - getCollectionTemplateItemUrlTemplate(childCollection.getID().toString())) - .content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType)) - .andExpect(status().isForbidden()); + getCollectionTemplateItemUrlTemplate(childCollection.getID().toString())) + .content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType)) + .andExpect(status().isForbidden()); } @Test @@ -158,9 +124,10 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT installTestTemplate(); getClient(adminAuthToken).perform(post( - getCollectionTemplateItemUrlTemplate(childCollection.getID().toString())) - .content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType)) - .andExpect(status().isUnprocessableEntity()); + getCollectionTemplateItemUrlTemplate(childCollection.getID().toString())) + .content(mapper.writeValueAsBytes(testTemplateItem)) + .contentType(contentType)) + .andExpect(status().isUnprocessableEntity()); } @Test @@ -168,9 +135,10 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT setupTestTemplate(); getClient(adminAuthToken).perform(post( - getCollectionTemplateItemUrlTemplate("16a4b65b-3b3f-4ef5-8058-ef6f5a653ef9")) - .content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType)) - .andExpect(status().isNotFound()); + getCollectionTemplateItemUrlTemplate("16a4b65b-3b3f-4ef5-8058-ef6f5a653ef9")) + .content(mapper.writeValueAsBytes(testTemplateItem)) + .contentType(contentType)) + .andExpect(status().isNotFound()); } @Test @@ -179,20 +147,17 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT String itemUuidString = installTestTemplate(); getClient(adminAuthToken).perform(get(getCollectionTemplateItemUrlTemplate(childCollection.getID().toString()))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$", Matchers.allOf( - hasJsonPath("$.id", is(itemUuidString)), - hasJsonPath("$.uuid", is(itemUuidString)), - hasJsonPath("$.type", is("item")), - hasJsonPath("$.inArchive", is(false)), - hasJsonPath("$.discoverable", is(false)), - hasJsonPath("$.withdrawn", is(false)), - hasJsonPath("$.metadata", Matchers.allOf( - MetadataMatcher.matchMetadata("dc.description", - "dc description content"), - MetadataMatcher.matchMetadata("dc.description.abstract", - "dc description abstract content") - ))))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.id", is(itemUuidString)), + hasJsonPath("$.uuid", is(itemUuidString)), + hasJsonPath("$.type", is("itemtemplate")), + hasJsonPath("$.metadata", Matchers.allOf( + MetadataMatcher.matchMetadata("dc.description", + "dc description content"), + MetadataMatcher.matchMetadata("dc.description.abstract", + "dc description abstract content"))) + ))); } @Test @@ -201,20 +166,17 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT String itemUuidString = installTestTemplate(); getClient(adminAuthToken).perform(get(getTemplateItemUrlTemplate(itemUuidString))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$", Matchers.allOf( - hasJsonPath("$.id", is(itemUuidString)), - hasJsonPath("$.uuid", is(itemUuidString)), - hasJsonPath("$.type", is("item")), - hasJsonPath("$.inArchive", is(false)), - hasJsonPath("$.discoverable", is(false)), - hasJsonPath("$.withdrawn", is(false)), - hasJsonPath("$.metadata", Matchers.allOf( - MetadataMatcher.matchMetadata("dc.description", - "dc description content"), - MetadataMatcher.matchMetadata("dc.description.abstract", - "dc description abstract content") - ))))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.id", is(itemUuidString)), + hasJsonPath("$.uuid", is(itemUuidString)), + hasJsonPath("$.type", is("itemtemplate")), + hasJsonPath("$.metadata", Matchers.allOf( + MetadataMatcher.matchMetadata("dc.description", + "dc description content"), + MetadataMatcher.matchMetadata("dc.description.abstract", + "dc description abstract content"))) + ))); } @Test @@ -224,9 +186,9 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT String itemId = installTestTemplate(); getClient().perform(patch(getTemplateItemUrlTemplate(itemId)) - .content(patchBody) - .contentType(contentType)) - .andExpect(status().isUnauthorized()); + .content(patchBody) + .contentType(contentType)) + .andExpect(status().isUnauthorized()); } @Test @@ -236,38 +198,32 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT String itemId = installTestTemplate(); getClient(adminAuthToken).perform(patch(getTemplateItemUrlTemplate(itemId)) - .content(patchBody) - .contentType(contentType)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$", Matchers.allOf( - hasJsonPath("$.type", is("item")), - hasJsonPath("$.inArchive", is(false)), - hasJsonPath("$.discoverable", is(false)), - hasJsonPath("$.withdrawn", is(false)), - hasJsonPath("$.metadata", Matchers.allOf( - MetadataMatcher.matchMetadata("dc.description", - "dc description content"), - MetadataMatcher.matchMetadata("dc.description.abstract", - "dc description abstract content"), - MetadataMatcher.matchMetadata("dc.description.tableofcontents", - "table of contents") - ))))); + .content(patchBody) + .contentType(contentType)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.type", is("itemtemplate")), + hasJsonPath("$.metadata", Matchers.allOf( + MetadataMatcher.matchMetadata("dc.description", + "dc description content"), + MetadataMatcher.matchMetadata("dc.description.abstract", + "dc description abstract content"), + MetadataMatcher.matchMetadata("dc.description.tableofcontents", + "table of contents") + ))))); getClient(adminAuthToken).perform(get(getCollectionTemplateItemUrlTemplate(childCollection.getID().toString()))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$", Matchers.allOf( - hasJsonPath("$.type", is("item")), - hasJsonPath("$.inArchive", is(false)), - hasJsonPath("$.discoverable", is(false)), - hasJsonPath("$.withdrawn", is(false)), - hasJsonPath("$.metadata", Matchers.allOf( - MetadataMatcher.matchMetadata("dc.description", - "dc description content"), - MetadataMatcher.matchMetadata("dc.description.abstract", - "dc description abstract content"), - MetadataMatcher.matchMetadata("dc.description.tableofcontents", - "table of contents") - ))))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.type", is("itemtemplate")), + hasJsonPath("$.metadata", Matchers.allOf( + MetadataMatcher.matchMetadata("dc.description", + "dc description content"), + MetadataMatcher.matchMetadata("dc.description.abstract", + "dc description abstract content"), + MetadataMatcher.matchMetadata("dc.description.tableofcontents", + "table of contents") + ))))); } @Test @@ -282,9 +238,9 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT String illegalPatchBody = getPatchContent(ops); getClient(adminAuthToken).perform(patch(getTemplateItemUrlTemplate(itemId)) - .content(illegalPatchBody) - .contentType(contentType)) - .andExpect(status().isBadRequest()); + .content(illegalPatchBody) + .contentType(contentType)) + .andExpect(status().isBadRequest()); } @Test @@ -299,9 +255,9 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT String illegalPatchBody = getPatchContent(ops); getClient(adminAuthToken).perform(patch(getTemplateItemUrlTemplate(itemId)) - .content(illegalPatchBody) - .contentType(contentType)) - .andExpect(status().isUnprocessableEntity()); + .content(illegalPatchBody) + .contentType(contentType)) + .andExpect(status().isUnprocessableEntity()); } @Test @@ -316,9 +272,9 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT String illegalPatchBody = getPatchContent(ops); getClient(adminAuthToken).perform(patch(getTemplateItemUrlTemplate(itemId)) - .content(illegalPatchBody) - .contentType(contentType)) - .andExpect(status().isUnprocessableEntity()); + .content(illegalPatchBody) + .contentType(contentType)) + .andExpect(status().isUnprocessableEntity()); } @Test @@ -329,9 +285,9 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT String userToken = getAuthToken(eperson.getEmail(), password); getClient(userToken).perform(patch(getTemplateItemUrlTemplate(itemId)) - .content(patchBody) - .contentType(contentType)) - .andExpect(status().isForbidden()); + .content(patchBody) + .contentType(contentType)) + .andExpect(status().isForbidden()); } @Test @@ -339,9 +295,9 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT setupTestTemplate(); getClient(adminAuthToken).perform(patch(getTemplateItemUrlTemplate("16a4b65b-3b3f-4ef5-8058-ef6f5a653ef9")) - .content(patchBody) - .contentType(contentType)) - .andExpect(status().isNotFound()); + .content(patchBody) + .contentType(contentType)) + .andExpect(status().isNotFound()); } @Test @@ -351,7 +307,7 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT String itemId = installTestTemplate(); getClient().perform(delete(getTemplateItemUrlTemplate(itemId))) - .andExpect(status().isUnauthorized()); + .andExpect(status().isUnauthorized()); } @Test @@ -361,7 +317,7 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT String itemId = installTestTemplate(); getClient(adminAuthToken).perform(delete(getTemplateItemUrlTemplate(itemId))) - .andExpect(status().isNoContent()); + .andExpect(status().isNoContent()); } @Test @@ -372,13 +328,13 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT String userToken = getAuthToken(eperson.getEmail(), password); getClient(userToken).perform(delete(getTemplateItemUrlTemplate(itemId))) - .andExpect(status().isForbidden()); + .andExpect(status().isForbidden()); } @Test public void deleteTemplateItemForNonexisting() throws Exception { getClient(adminAuthToken).perform(delete(getTemplateItemUrlTemplate("16a4b65b-3b3f-4ef5-8058-ef6f5a653ef9"))) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()); } private String getCollectionTemplateItemUrlTemplate(String uuid) { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MappedCollectionRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MappedCollectionRestRepositoryIT.java index e3d64fce0d..4164f38c36 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MappedCollectionRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MappedCollectionRestRepositoryIT.java @@ -62,12 +62,11 @@ public class MappedCollectionRestRepositoryIT extends AbstractControllerIntegrat .build(); context.restoreAuthSystemState(); -// collectionService.addItem(context, col2, publicItem1); -// collectionService.update(context, col2); +// collectionService.addItem(context, colAA2, publicItem1); +// collectionService.update(context, colAA2); // itemService.update(context, publicItem1); - getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections") - .param("projection", "full")) + getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedCollections", Matchers.not(Matchers.contains( CollectionMatcher.matchCollectionEntry("Collection 1", col1.getID(), col1.getHandle()), @@ -76,14 +75,12 @@ public class MappedCollectionRestRepositoryIT extends AbstractControllerIntegrat .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/items"))) ; - getClient().perform(get("/api/core/collections/" + col1.getID() + "/mappedItems") - .param("projection", "full")) + getClient().perform(get("/api/core/collections/" + col1.getID() + "/mappedItems")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedItems", Matchers.not(Matchers.contains( ItemMatcher.matchItemProperties(publicItem1)) ))); - getClient().perform(get("/api/core/collections/" + col2.getID() + "/mappedItems") - .param("projection", "full")) + getClient().perform(get("/api/core/collections/" + col2.getID() + "/mappedItems")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedItems", Matchers.not(Matchers.contains( ItemMatcher.matchItemProperties(publicItem1)) @@ -124,8 +121,7 @@ public class MappedCollectionRestRepositoryIT extends AbstractControllerIntegrat ) ); - getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections") - .param("projection", "full")) + getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedCollections", Matchers.containsInAnyOrder( CollectionMatcher.matchCollectionEntry("Collection 2", col2.getID(), col2.getHandle()) @@ -133,15 +129,13 @@ public class MappedCollectionRestRepositoryIT extends AbstractControllerIntegrat .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/items"))) ; - getClient().perform(get("/api/core/collections/" + col1.getID() + "/mappedItems") - .param("projection", "full")) + getClient().perform(get("/api/core/collections/" + col1.getID() + "/mappedItems")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedItems", Matchers.not(Matchers.contains( ItemMatcher.matchItemProperties(publicItem1)) ))) .andExpect(jsonPath("$._embedded.mappedItems", Matchers.hasSize(0))); - getClient().perform(get("/api/core/collections/" + col2.getID() + "/mappedItems") - .param("projection", "full")) + getClient().perform(get("/api/core/collections/" + col2.getID() + "/mappedItems")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedItems", Matchers.contains( ItemMatcher.matchItemProperties(publicItem1)) @@ -184,8 +178,7 @@ public class MappedCollectionRestRepositoryIT extends AbstractControllerIntegrat ) ); - getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections") - .param("projection", "full")) + getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedCollections", Matchers.containsInAnyOrder( CollectionMatcher.matchCollectionEntry("Collection 2", col2.getID(), col2.getHandle()), @@ -251,8 +244,7 @@ public class MappedCollectionRestRepositoryIT extends AbstractControllerIntegrat ) ); - getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections") - .param("projection", "full")) + getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedCollections", Matchers.containsInAnyOrder( CollectionMatcher.matchCollectionEntry("Collection 2", col2.getID(), col2.getHandle()), @@ -307,8 +299,7 @@ public class MappedCollectionRestRepositoryIT extends AbstractControllerIntegrat ) ); - getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections") - .param("projection", "full")) + getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedCollections", Matchers.not(Matchers.contains( CollectionMatcher.matchCollectionEntry("Collection 1", col1.getID(), col1.getHandle()) @@ -359,16 +350,14 @@ public class MappedCollectionRestRepositoryIT extends AbstractControllerIntegrat ) ); - getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections") - .param("projection", "full")) + getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedCollections", Matchers.not(Matchers.contains( CollectionMatcher.matchCollectionEntry("Collection 1", col1.getID(), col1.getHandle())) ))) .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/items"))) ; - getClient().perform(get("/api/core/collections/" + col1.getID() + "/mappedItems") - .param("projection", "full")) + getClient().perform(get("/api/core/collections/" + col1.getID() + "/mappedItems")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedItems", Matchers.not(Matchers.contains( ItemMatcher.matchItemProperties(publicItem1)) @@ -378,30 +367,26 @@ public class MappedCollectionRestRepositoryIT extends AbstractControllerIntegrat getClient(adminToken) .perform(delete("/api/core/items/" + publicItem1.getID() + "/mappedCollections/" + col2.getID())); - getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections") - .param("projection", "full")) + getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedCollections", Matchers.not(Matchers.containsInAnyOrder( CollectionMatcher.matchCollectionEntry("Collection 2", col2.getID(), col2.getHandle()), CollectionMatcher.matchCollectionEntry("Collection 1", col1.getID(), col1.getHandle()) )))) .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/items"))); - getClient().perform(get("/api/core/collections/" + col1.getID() + "/mappedItems") - .param("projection", "full")) + getClient().perform(get("/api/core/collections/" + col1.getID() + "/mappedItems")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedItems", Matchers.not(Matchers.contains( ItemMatcher.matchItemProperties(publicItem1)) ))) .andExpect(jsonPath("$._embedded.mappedItems", Matchers.hasSize(0))); - getClient().perform(get("/api/core/collections/" + col2.getID() + "/mappedItems") - .param("projection", "full")) + getClient().perform(get("/api/core/collections/" + col2.getID() + "/mappedItems")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedItems", Matchers.not(Matchers.contains( ItemMatcher.matchItemProperties(publicItem1)) ))) .andExpect(jsonPath("$._embedded.mappedItems", Matchers.hasSize(0))); - getClient().perform(get("/api/core/collections/" + col3.getID() + "/mappedItems") - .param("projection", "full")) + getClient().perform(get("/api/core/collections/" + col3.getID() + "/mappedItems")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedItems", Matchers.contains( ItemMatcher.matchItemProperties(publicItem1)) @@ -411,8 +396,7 @@ public class MappedCollectionRestRepositoryIT extends AbstractControllerIntegrat getClient(adminToken) .perform(delete("/api/core/items/" + publicItem1.getID() + "/mappedCollections/" + col1.getID())); - getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections") - .param("projection", "full")) + getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedCollections", Matchers.not(Matchers.containsInAnyOrder( CollectionMatcher.matchCollectionEntry("Collection 2", col2.getID(), col2.getHandle()), @@ -420,22 +404,19 @@ public class MappedCollectionRestRepositoryIT extends AbstractControllerIntegrat )))) .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/items"))) ; - getClient().perform(get("/api/core/collections/" + col1.getID() + "/mappedItems") - .param("projection", "full")) + getClient().perform(get("/api/core/collections/" + col1.getID() + "/mappedItems")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedItems", Matchers.not(Matchers.contains( ItemMatcher.matchItemProperties(publicItem1)) ))) .andExpect(jsonPath("$._embedded.mappedItems", Matchers.hasSize(0))); - getClient().perform(get("/api/core/collections/" + col2.getID() + "/mappedItems") - .param("projection", "full")) + getClient().perform(get("/api/core/collections/" + col2.getID() + "/mappedItems")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedItems", Matchers.not(Matchers.contains( ItemMatcher.matchItemProperties(publicItem1)) ))) .andExpect(jsonPath("$._embedded.mappedItems", Matchers.hasSize(0)));; - getClient().perform(get("/api/core/collections/" + col3.getID() + "/mappedItems") - .param("projection", "full")) + getClient().perform(get("/api/core/collections/" + col3.getID() + "/mappedItems")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedItems", Matchers.contains( ItemMatcher.matchItemProperties(publicItem1)) @@ -626,14 +607,13 @@ public class MappedCollectionRestRepositoryIT extends AbstractControllerIntegrat .build(); context.restoreAuthSystemState(); -// collectionService.addItem(context, col2, publicItem1); -// collectionService.update(context, col2); +// collectionService.addItem(context, colAA2, publicItem1); +// collectionService.update(context, colAA2); // itemService.update(context, publicItem1); context.restoreAuthSystemState(); context.setCurrentUser(null); - getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections") - .param("projection", "full")) + getClient().perform(get("/api/core/items/" + publicItem1.getID() + "/mappedCollections")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.mappedCollections", Matchers.not(Matchers.contains( CollectionMatcher.matchCollectionEntry("Collection 1", col1.getID(), col1.getHandle()), @@ -667,8 +647,8 @@ public class MappedCollectionRestRepositoryIT extends AbstractControllerIntegrat .build(); context.restoreAuthSystemState(); -// collectionService.addItem(context, col2, publicItem1); -// collectionService.update(context, col2); +// collectionService.addItem(context, colAA2, publicItem1); +// collectionService.update(context, colAA2); // itemService.update(context, publicItem1); getClient().perform( @@ -704,8 +684,8 @@ public class MappedCollectionRestRepositoryIT extends AbstractControllerIntegrat .build(); context.restoreAuthSystemState(); -// collectionService.addItem(context, col2, publicItem1); -// collectionService.update(context, col2); +// collectionService.addItem(context, colAA2, publicItem1); +// collectionService.update(context, colAA2); // itemService.update(context, publicItem1); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataSchemaRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataSchemaRestRepositoryIT.java index 9aa2820eae..c116cb9d28 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataSchemaRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataSchemaRestRepositoryIT.java @@ -23,6 +23,7 @@ import java.util.concurrent.atomic.AtomicReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.dspace.app.rest.builder.MetadataSchemaBuilder; import org.dspace.app.rest.converter.ConverterService; +import org.dspace.app.rest.matcher.HalMatcher; import org.dspace.app.rest.matcher.MetadataschemaMatcher; import org.dspace.app.rest.model.MetadataSchemaRest; import org.dspace.app.rest.projection.Projection; @@ -102,6 +103,7 @@ public class MetadataSchemaRestRepositoryIT extends AbstractControllerIntegratio .content(new ObjectMapper().writeValueAsBytes(metadataSchemaRest)) .contentType(contentType)) .andExpect(status().isCreated()) + .andExpect(jsonPath("$", HalMatcher.matchNoEmbeds())) .andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id"))); getClient().perform(get("/api/core/metadataschemas/" + idRef.get())) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadatafieldRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadatafieldRestRepositoryIT.java index 49a5995be7..1bf2a37f96 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadatafieldRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadatafieldRestRepositoryIT.java @@ -165,7 +165,7 @@ public class MetadatafieldRestRepositoryIT extends AbstractControllerIntegration public void findByNullSchema() throws Exception { getClient().perform(get("/api/core/metadatafields/search/bySchema")) - .andExpect(status().isUnprocessableEntity()); + .andExpect(status().isBadRequest()); } @Test @@ -184,6 +184,7 @@ public class MetadatafieldRestRepositoryIT extends AbstractControllerIntegration getClient(authToken) .perform(post("/api/core/metadatafields") .param("schemaId", metadataSchema.getID() + "") + .param("projection", "full") .content(new ObjectMapper().writeValueAsBytes(metadataFieldRest)) .contentType(contentType)) .andExpect(status().isCreated()) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java index 67e086e13e..69a3ea86b0 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java @@ -514,6 +514,7 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest .param("relationshipType", isAuthorOfPublicationRelationshipType.getID() .toString()) + .param("projection", "full") .contentType(MediaType.parseMediaType (org.springframework.data.rest.webmvc.RestMediaTypes .TEXT_URI_LIST_VALUE)) @@ -523,6 +524,7 @@ public class RelationshipRestRepositoryIT extends AbstractEntityIntegrationTest "https://localhost:8080/server/api/core/items/" + author1 .getID())) .andExpect(status().isCreated()) + .andExpect(jsonPath("$", RelationshipMatcher.matchFullEmbeds())) .andReturn(); ObjectMapper mapper = new ObjectMapper(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java index 6d791b13a9..c08cef9289 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java @@ -6,6 +6,7 @@ * http://www.dspace.org/license/ */ package org.dspace.app.rest; + import static com.jayway.jsonpath.JsonPath.read; import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; @@ -37,7 +38,7 @@ import org.dspace.app.rest.builder.EPersonBuilder; import org.dspace.app.rest.builder.GroupBuilder; import org.dspace.app.rest.builder.ItemBuilder; import org.dspace.app.rest.builder.ResourcePolicyBuilder; -import org.dspace.app.rest.matcher.ResoucePolicyMatcher; +import org.dspace.app.rest.matcher.ResourcePolicyMatcher; import org.dspace.app.rest.model.ResourcePolicyRest; import org.dspace.app.rest.model.patch.AddOperation; import org.dspace.app.rest.model.patch.Operation; @@ -55,15 +56,13 @@ import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.eperson.factory.EPersonServiceFactory; import org.hamcrest.Matchers; -import org.junit.Ignore; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; /** * Integration test class for the resourcepolicies endpoint - * - * @author Mykhaylo Boychuk - 4Science * + * @author Mykhaylo Boychuk - 4Science */ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegrationTest { @@ -80,7 +79,7 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); ResourcePolicyBuilder.createResourcePolicy(context).withDspaceObject(community).withAction(Constants.READ) - .withUser(admin).build(); + .withUser(admin).build(); context.restoreAuthSystemState(); String authToken = getAuthToken(admin.getEmail(), password); @@ -94,7 +93,7 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); ResourcePolicyBuilder.createResourcePolicy(context).withDspaceObject(community).withAction(Constants.READ) - .withUser(admin).build(); + .withUser(admin).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/authz/resourcepolicies")).andExpect(status().isUnauthorized()); @@ -105,28 +104,28 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.READ) - .withUser(eperson1) - .build(); + .withDspaceObject(community) + .withAction(Constants.READ) + .withUser(eperson1) + .build(); context.restoreAuthSystemState(); String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()).andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$", is( - ResoucePolicyMatcher.matchResourcePolicy(resourcePolicy) - ))) - .andExpect(jsonPath("$._links.self.href", Matchers - .containsString("/api/authz/resourcepolicies/" + resourcePolicy.getID()))); + .andExpect(status().isOk()).andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", is( + ResourcePolicyMatcher.matchResourcePolicy(resourcePolicy) + ))) + .andExpect(jsonPath("$._links.self.href", Matchers + .containsString("/api/authz/resourcepolicies/" + resourcePolicy.getID()))); } @Test @@ -134,29 +133,29 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); Group groupAnonymous = EPersonServiceFactory.getInstance().getGroupService().findByName(context, - Group.ANONYMOUS); + Group.ANONYMOUS); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.READ) - .withGroup(groupAnonymous) - .build(); + .withDspaceObject(community) + .withAction(Constants.READ) + .withGroup(groupAnonymous) + .build(); context.restoreAuthSystemState(); String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()); + .andExpect(status().isOk()); getClient().perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isUnauthorized()); + .andExpect(status().isUnauthorized()); } @Test @@ -165,12 +164,12 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context).withDspaceObject(community) - .withAction(Constants.READ).withUser(eperson).build(); + .withAction(Constants.READ).withUser(eperson).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isUnauthorized()); + .andExpect(status().isUnauthorized()); } @@ -179,7 +178,7 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio String authToken = getAuthToken(admin.getEmail(), password); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + UUID.randomUUID().toString())) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()); } @Test @@ -187,33 +186,33 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); EPerson eperson2 = EPersonBuilder.createEPerson(context) - .withEmail("eperson2@mail.com") - .withPassword("qwerty02") - .build(); + .withEmail("eperson2@mail.com") + .withPassword("qwerty02") + .build(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withName("My collection").build(); + .withName("My collection").build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(collection) - .withAction(Constants.WRITE) - .withUser(eperson1).build(); + .withDspaceObject(collection) + .withAction(Constants.WRITE) + .withUser(eperson1).build(); context.restoreAuthSystemState(); String authToken = getAuthToken(eperson2.getEmail(), "qwerty02"); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isForbidden()); + .andExpect(status().isForbidden()); String authToken1 = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken1).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()); + .andExpect(status().isOk()); } @Test @@ -221,24 +220,23 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.WRITE) - .withUser(eperson1).build(); + .withDspaceObject(community) + .withAction(Constants.WRITE) + .withUser(eperson1).build(); context.restoreAuthSystemState(); String authToken = getAuthToken(admin.getEmail(), password); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$", is( - ResoucePolicyMatcher.matchResourcePolicy(resourcePolicy)))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", is(ResourcePolicyMatcher.matchResourcePolicy(resourcePolicy)))); } @Test @@ -248,36 +246,36 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Group group1 = GroupBuilder.createGroup(context).withName("My group").build(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .withGroupMembership(group1) - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .withGroupMembership(group1) + .build(); EPerson eperson2 = EPersonBuilder.createEPerson(context) - .withEmail("eperson2@mail.com") - .withPassword("qwerty02") - .build(); + .withEmail("eperson2@mail.com") + .withPassword("qwerty02") + .build(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withName("My collection").build(); + .withName("My collection").build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(collection) - .withAction(Constants.ADD) - .withGroup(group1) - .build(); + .withDspaceObject(collection) + .withAction(Constants.ADD) + .withGroup(group1) + .build(); context.restoreAuthSystemState(); String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$", is(ResoucePolicyMatcher.matchResourcePolicy(resourcePolicy)))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", is(ResourcePolicyMatcher.matchResourcePolicy(resourcePolicy)))); String authTokenEperson2 = getAuthToken(eperson2.getEmail(), "qwerty02"); getClient(authTokenEperson2).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isForbidden()); + .andExpect(status().isForbidden()); } @Test @@ -285,42 +283,43 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("myemail@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("myemail@mail.com") + .withPassword("qwerty01") + .build(); EPerson eperson2 = EPersonBuilder.createEPerson(context) - .withEmail("Xemail@mail.com") - .withPassword("qwerty02") - .build(); + .withEmail("Xemail@mail.com") + .withPassword("qwerty02") + .build(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); Community community2 = CommunityBuilder.createCommunity(context).withName("My community_2").build(); ResourcePolicy resourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.ADD) - .withUser(eperson1).build(); + .withDspaceObject(community) + .withAction(Constants.ADD) + .withUser(eperson1).build(); ResourcePolicy resourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community2) - .withAction(Constants.REMOVE) - .withUser(eperson2).build(); + .withDspaceObject(community2) + .withAction(Constants.REMOVE) + .withUser(eperson2).build(); context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(get("/api/authz/resourcepolicies/search/eperson") - .param("uuid",eperson1.getID().toString())) - .andExpect(status().isOk()) - .andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._embedded.resourcepolicies", Matchers.contains( - ResoucePolicyMatcher.matchResourcePolicy(resourcePolicyOfEPerson1)))) - .andExpect(jsonPath("$._embedded.resourcepolicies", - Matchers.not(is(ResoucePolicyMatcher.matchResourcePolicy(resourcePolicyOfEPerson2))))) - .andExpect(jsonPath("$._links.self.href", Matchers.containsString("api/authz/resourcepolicies/search/eperson"))) - .andExpect(jsonPath("$.page.totalElements", is(1))); + .param("uuid", eperson1.getID().toString())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.resourcepolicies", Matchers.contains( + ResourcePolicyMatcher.matchResourcePolicy(resourcePolicyOfEPerson1)))) + .andExpect(jsonPath("$._embedded.resourcepolicies", + Matchers.not(is(ResourcePolicyMatcher.matchResourcePolicy(resourcePolicyOfEPerson2))))) + .andExpect(jsonPath("$._links.self.href", Matchers.containsString( + "api/authz/resourcepolicies/search/eperson"))) + .andExpect(jsonPath("$.page.totalElements", is(1))); } @@ -329,59 +328,58 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context).withEmail("myemail@mail.com").withPassword("qwerty01") - .build(); + .build(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); Collection collection = CollectionBuilder.createCollection(context, community).withName("My collection") - .build(); + .build(); ResourcePolicy resourcePolicyOfCommunity = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.READ) - .withUser(eperson1).build(); + .withDspaceObject(community) + .withAction(Constants.READ) + .withUser(eperson1).build(); ResourcePolicy secondResourcePolicyOfCommunity = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.REMOVE) - .withUser(eperson1).build(); + .withDspaceObject(community) + .withAction(Constants.REMOVE) + .withUser(eperson1).build(); ResourcePolicy resourcePolicyOfCollection = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(collection) - .withAction(Constants.REMOVE) - .withUser(eperson1).build(); + .withDspaceObject(collection) + .withAction(Constants.REMOVE) + .withUser(eperson1).build(); context.restoreAuthSystemState(); String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken) - .perform(get("/api/authz/resourcepolicies/search/eperson") - .param("uuid", eperson1.getID().toString()) - .param("resource", community.getID().toString())) - .andExpect(status().isOk()).andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._embedded.resourcepolicies",Matchers.containsInAnyOrder( - ResoucePolicyMatcher.matchResourcePolicy(resourcePolicyOfCommunity), - ResoucePolicyMatcher.matchResourcePolicy(secondResourcePolicyOfCommunity) - ))) - .andExpect(jsonPath("$._embedded.resourcepolicies", - Matchers.not(is(ResoucePolicyMatcher.matchResourcePolicy(resourcePolicyOfCollection))))) - .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("api/authz/resourcepolicies/search/eperson"))) - .andExpect(jsonPath("$.page.totalElements", is(2))); + .perform(get("/api/authz/resourcepolicies/search/eperson") + .param("uuid", eperson1.getID().toString()) + .param("resource", community.getID().toString())) + .andExpect(status().isOk()).andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.resourcepolicies", Matchers.containsInAnyOrder( + ResourcePolicyMatcher.matchResourcePolicy(resourcePolicyOfCommunity), + ResourcePolicyMatcher.matchResourcePolicy(secondResourcePolicyOfCommunity) + ))) + .andExpect(jsonPath("$._embedded.resourcepolicies", + Matchers.not(is(ResourcePolicyMatcher.matchResourcePolicy(resourcePolicyOfCollection))))) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("api/authz/resourcepolicies/search/eperson"))) + .andExpect(jsonPath("$.page.totalElements", is(2))); } @Test - @Ignore("Currently fail due to https://jira.lyrasis.org/browse/DS-4428") public void findResoucesPoliciesEPersonWithoutParametersBadRequestTest() throws Exception { context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context).withEmail("myemail@mail.com").withPassword("qwerty01") - .build(); + .build(); context.restoreAuthSystemState(); String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(get("/api/authz/resourcepolicies/search/eperson")) - .andExpect(status().isBadRequest()); + .andExpect(status().isBadRequest()); } @Test @@ -389,326 +387,324 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("myemail@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("myemail@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.READ) - .withUser(eperson1).build(); + .withDspaceObject(community) + .withAction(Constants.READ) + .withUser(eperson1).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/authz/resourcepolicies/search/eperson") - .param("uuid", eperson1.getID().toString()) - .param("resource", community.getID().toString())) - .andExpect(status().isUnauthorized()); + .param("uuid", eperson1.getID().toString()) + .param("resource", community.getID().toString())) + .andExpect(status().isUnauthorized()); } @Test - public void findResoucesPoliciesByEPersonNotFoundTest() throws Exception { + public void findResourcesPoliciesByEPersonNotFoundTest() throws Exception { String authToken = getAuthToken(admin.getEmail(), password); getClient(authToken).perform(get("/api/authz/resourcepolicies/search/eperson") - .param("uuid", UUID.randomUUID().toString())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.page.totalElements", is(0))); + .param("uuid", UUID.randomUUID().toString())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.page.totalElements", is(0))); } @Test - public void findResoucesPoliciesByEPersonUuidForbiddenTest() throws Exception { + public void findResourcesPoliciesByEPersonUuidForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); EPerson eperson2 = EPersonBuilder.createEPerson(context) - .withEmail("eperson2@mail.com") - .withPassword("qwerty02") - .build(); + .withEmail("eperson2@mail.com") + .withPassword("qwerty02") + .build(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); Community community2 = CommunityBuilder.createCommunity(context).withName("My 2 community").build(); ResourcePolicy resourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community).withAction(Constants.WRITE) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(eperson1).build(); + .withDspaceObject(community).withAction(Constants.WRITE) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .withUser(eperson1).build(); ResourcePolicy resourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community2).withAction(Constants.ADD) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(eperson2).build(); + .withDspaceObject(community2).withAction(Constants.ADD) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .withUser(eperson2).build(); context.restoreAuthSystemState(); String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(get("/api/authz/resourcepolicies/search/eperson") - .param("uuid", eperson2.getID().toString()) - .param("resource", community2.getID().toString())) - .andExpect(status().isForbidden()); + .param("uuid", eperson2.getID().toString()) + .param("resource", community2.getID().toString())) + .andExpect(status().isForbidden()); } - @Test - public void findResourcePoliciesOfOneResourceWithoutActionTest() throws Exception { - context.turnOffAuthorisationSystem(); + @Test + public void findResourcePoliciesOfOneResourceWithoutActionTest() throws Exception { + context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); EPerson eperson2 = EPersonBuilder.createEPerson(context) - .withEmail("eperson2@mail.com") - .withPassword("qwerty02") - .build(); + .withEmail("eperson2@mail.com") + .withPassword("qwerty02") + .build(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); Community community2 = CommunityBuilder.createCommunity(context).withName("My second community").build(); ResourcePolicy firstResourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.ADMIN) - .withUser(eperson1).build(); + .withDspaceObject(community) + .withAction(Constants.ADMIN) + .withUser(eperson1).build(); ResourcePolicy firstResourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community2) - .withAction(Constants.ADD) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(eperson2).build(); + .withDspaceObject(community2) + .withAction(Constants.ADD) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .withUser(eperson2).build(); ResourcePolicy resourcePolicyAnonymous = authorizeService - .findByTypeGroupAction(context,community, EPersonServiceFactory.getInstance() + .findByTypeGroupAction(context, community, EPersonServiceFactory.getInstance() .getGroupService() - .findByName(context, Group.ANONYMOUS),Constants.READ); + .findByName(context, Group.ANONYMOUS), Constants.READ); - context.restoreAuthSystemState(); + context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); - getClient(authToken).perform(get("/api/authz/resourcepolicies/search/resource") - .param("uuid", community.getID().toString())) - .andExpect(status().isOk()) - .andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._embedded.resourcepolicies",Matchers.containsInAnyOrder( - ResoucePolicyMatcher.matchResourcePolicy(firstResourcePolicyOfEPerson1), - ResoucePolicyMatcher.matchResourcePolicy(resourcePolicyAnonymous) - ))) - .andExpect(jsonPath("$._embedded.resourcepolicies", - Matchers.not(is(ResoucePolicyMatcher.matchResourcePolicy(firstResourcePolicyOfEPerson2))))) - .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("api/authz/resourcepolicies/search/resource"))) - .andExpect(jsonPath("$.page.totalElements", is(2))); - } + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); + getClient(authToken).perform(get("/api/authz/resourcepolicies/search/resource") + .param("uuid", community.getID().toString())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.resourcepolicies", Matchers.containsInAnyOrder( + ResourcePolicyMatcher.matchResourcePolicy(firstResourcePolicyOfEPerson1), + ResourcePolicyMatcher.matchResourcePolicy(resourcePolicyAnonymous) + ))) + .andExpect(jsonPath("$._embedded.resourcepolicies", + Matchers.not(is(ResourcePolicyMatcher.matchResourcePolicy(firstResourcePolicyOfEPerson2))))) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("api/authz/resourcepolicies/search/resource"))) + .andExpect(jsonPath("$.page.totalElements", is(2))); + } - @Test - public void findResourcePoliciesOfOneResourceWithActionTest() throws Exception { - context.turnOffAuthorisationSystem(); + @Test + public void findResourcePoliciesOfOneResourceWithActionTest() throws Exception { + context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); EPerson eperson2 = EPersonBuilder.createEPerson(context) - .withEmail("eperson2@mail.com") - .withPassword("qwerty02") - .build(); + .withEmail("eperson2@mail.com") + .withPassword("qwerty02") + .build(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); Community community2 = CommunityBuilder.createCommunity(context).withName("My 2 community").build(); ResourcePolicy firstResourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.ADMIN) - .withUser(eperson1).build(); - - ResourcePolicy firstResourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.ADD) - .withUser(eperson2).build(); - - ResourcePolicy secondResourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community2) - .withAction(Constants.ADD) - .withUser(eperson2).build(); - - context.restoreAuthSystemState(); - - String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); - getClient(authToken).perform(get("/api/authz/resourcepolicies/search/resource") - .param("uuid", community.getID().toString()) - .param("action", "ADD")) - .andExpect(status().isOk()) - .andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._embedded.resourcepolicies",Matchers.contains( - ResoucePolicyMatcher.matchResourcePolicy(firstResourcePolicyOfEPerson2) - ))) - .andExpect(jsonPath("$._embedded.resourcepolicies", - Matchers.not(is(ResoucePolicyMatcher.matchResourcePolicy(secondResourcePolicyOfEPerson2))))) - .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("api/authz/resourcepolicies/search/resource"))) - .andExpect(jsonPath("$.page.totalElements", is(1))); - - String authToken2 = getAuthToken(eperson2.getEmail(), "qwerty02"); - getClient(authToken2).perform(get("/api/authz/resourcepolicies/search/resource") - .param("uuid", community.getID().toString()) - .param("action", "ADD")) - .andExpect(status().isForbidden()); - } - - @Test - public void findResourcePoliciesOfOneResourcePaginationTest() throws Exception { - context.turnOffAuthorisationSystem(); - - EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); - - EPerson eperson2 = EPersonBuilder.createEPerson(context) - .withEmail("eperson2@mail.com") - .withPassword("qwerty02") - .build(); - - Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - - - ResourcePolicy firstResourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context) .withDspaceObject(community) .withAction(Constants.ADMIN) .withUser(eperson1).build(); - ResourcePolicy firstResourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy firstResourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) + .withDspaceObject(community) + .withAction(Constants.ADD) + .withUser(eperson2).build(); + + ResourcePolicy secondResourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) + .withDspaceObject(community2) + .withAction(Constants.ADD) + .withUser(eperson2).build(); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); + getClient(authToken).perform(get("/api/authz/resourcepolicies/search/resource") + .param("uuid", community.getID().toString()) + .param("action", "ADD")) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.resourcepolicies", Matchers.contains( + ResourcePolicyMatcher.matchResourcePolicy(firstResourcePolicyOfEPerson2) + ))) + .andExpect(jsonPath("$._embedded.resourcepolicies", + Matchers.not(is(ResourcePolicyMatcher.matchResourcePolicy(secondResourcePolicyOfEPerson2))))) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("api/authz/resourcepolicies/search/resource"))) + .andExpect(jsonPath("$.page.totalElements", is(1))); + + String authToken2 = getAuthToken(eperson2.getEmail(), "qwerty02"); + getClient(authToken2).perform(get("/api/authz/resourcepolicies/search/resource") + .param("uuid", community.getID().toString()) + .param("action", "ADD")) + .andExpect(status().isForbidden()); + } + + @Test + public void findResourcePoliciesOfOneResourcePaginationTest() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson eperson1 = EPersonBuilder.createEPerson(context) + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); + + EPerson eperson2 = EPersonBuilder.createEPerson(context) + .withEmail("eperson2@mail.com") + .withPassword("qwerty02") + .build(); + + Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); + + + ResourcePolicy firstResourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context) + .withDspaceObject(community) + .withAction(Constants.ADMIN) + .withUser(eperson1).build(); + + ResourcePolicy firstResourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) .withDspaceObject(community) .withAction(Constants.ADD) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) .withUser(eperson2).build(); - ResourcePolicy resourcePolicyAnonymous = authorizeService - .findByTypeGroupAction(context,community, EPersonServiceFactory.getInstance() - .getGroupService() - .findByName(context, Group.ANONYMOUS),Constants.READ); + ResourcePolicy resourcePolicyAnonymous = authorizeService + .findByTypeGroupAction(context, community, EPersonServiceFactory.getInstance() + .getGroupService() + .findByName(context, Group.ANONYMOUS), Constants.READ); - context.restoreAuthSystemState(); + context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); - getClient(authToken).perform(get("/api/authz/resourcepolicies/search/resource") - .param("uuid", community.getID().toString()) - .param("page","0") - .param("size","2")) - .andExpect(status().isOk()) - .andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._embedded.resourcepolicies", - Matchers.containsInAnyOrder( - hasJsonPath("$.type", is("resourcepolicy")), - hasJsonPath("$.type", is("resourcepolicy")) - ))) - .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("api/authz/resourcepolicies/search/resource"))) - .andExpect(jsonPath("$.page.totalElements", is(3))) - .andExpect(jsonPath("$.page.size", is(2))); - } + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); + getClient(authToken).perform(get("/api/authz/resourcepolicies/search/resource") + .param("uuid", community.getID().toString()) + .param("page", "0") + .param("size", "2")) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.resourcepolicies", + Matchers.containsInAnyOrder( + hasJsonPath("$.type", is("resourcepolicy")), + hasJsonPath("$.type", is("resourcepolicy")) + ))) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("api/authz/resourcepolicies/search/resource"))) + .andExpect(jsonPath("$.page.totalElements", is(3))) + .andExpect(jsonPath("$.page.size", is(2))); + } @Test - @Ignore("Currently fail due to https://jira.lyrasis.org/browse/DS-4428") public void findResoucesPoliciesOfResourceWithoutParametersBadRequestTest() throws Exception { context.turnOffAuthorisationSystem(); - EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + EPerson eperson1 = EPersonBuilder.createEPerson(context) + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); - context.restoreAuthSystemState(); + context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); - getClient(authToken).perform(get("/api/authz/resourcepolicies/search/resource")) - .andExpect(status().isBadRequest()); - } + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); + getClient(authToken).perform(get("/api/authz/resourcepolicies/search/resource")) + .andExpect(status().isBadRequest()); + } - @Test - public void findResoucesPoliciesByResourceUuidUnAuthenticatedTest() throws Exception { - context.turnOffAuthorisationSystem(); + @Test + public void findResoucesPoliciesByResourceUuidUnAuthenticatedTest() throws Exception { + context.turnOffAuthorisationSystem(); - EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("myemail@mail.com") - .withPassword("qwerty01") - .build(); + EPerson eperson1 = EPersonBuilder.createEPerson(context) + .withEmail("myemail@mail.com") + .withPassword("qwerty01") + .build(); - Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); + Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.READ) - .withUser(eperson1).build(); + ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) + .withDspaceObject(community) + .withAction(Constants.READ) + .withUser(eperson1).build(); - context.restoreAuthSystemState(); + context.restoreAuthSystemState(); - getClient().perform(get("/api/authz/resourcepolicies/search/resource") - .param("uuid", community.getID().toString())) - .andExpect(status().isUnauthorized()); + getClient().perform(get("/api/authz/resourcepolicies/search/resource") + .param("uuid", community.getID().toString())) + .andExpect(status().isUnauthorized()); - } + } - @Test - public void findResourcePolisiesNotFoundTest() throws Exception { - - String authToken = getAuthToken(admin.getEmail(), password); - getClient(authToken).perform(get("/api/authz/resourcepolicies/search/resource") - .param("uuid", UUID.randomUUID().toString())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.page.totalElements", is(0))); - } + @Test + public void findResourcePoliciesNotFoundTest() throws Exception { + String authToken = getAuthToken(admin.getEmail(), password); + getClient(authToken).perform(get("/api/authz/resourcepolicies/search/resource") + .param("uuid", UUID.randomUUID().toString())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.page.totalElements", is(0))); + } - @Test - public void findResoucesPoliciesByResourceUuidForbiddenTest() throws Exception { - context.turnOffAuthorisationSystem(); + @Test + public void findResourcesPoliciesByResourceUuidForbiddenTest() throws Exception { + context.turnOffAuthorisationSystem(); - EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + EPerson eperson1 = EPersonBuilder.createEPerson(context) + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); - EPerson eperson2 = EPersonBuilder.createEPerson(context) - .withEmail("eperson2@mail.com") - .withPassword("qwerty02") - .build(); + EPerson eperson2 = EPersonBuilder.createEPerson(context) + .withEmail("eperson2@mail.com") + .withPassword("qwerty02") + .build(); - Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); + Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); - Community community2 = CommunityBuilder.createCommunity(context).withName("My 2 community").build(); + Community community2 = CommunityBuilder.createCommunity(context).withName("My 2 community").build(); - ResourcePolicy resourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.REMOVE) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(eperson1).build(); + ResourcePolicy resourcePolicyOfEPerson1 = ResourcePolicyBuilder.createResourcePolicy(context) + .withDspaceObject(community) + .withAction(Constants.REMOVE) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .withUser(eperson1).build(); - ResourcePolicy resourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community2) - .withAction(Constants.ADD) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(eperson2).build(); + ResourcePolicy resourcePolicyOfEPerson2 = ResourcePolicyBuilder.createResourcePolicy(context) + .withDspaceObject(community2) + .withAction(Constants.ADD) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .withUser(eperson2).build(); - context.restoreAuthSystemState(); + context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); - getClient(authToken).perform(get("/api/authz/resourcepolicies/search/eperson") - .param("uuid", eperson2.getID().toString()) - .param("resource", community2.getID().toString())) - .andExpect(status().isForbidden()); - } + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); + getClient(authToken).perform(get("/api/authz/resourcepolicies/search/eperson") + .param("uuid", eperson2.getID().toString()) + .param("resource", community2.getID().toString())) + .andExpect(status().isForbidden()); + } @Test @@ -720,73 +716,73 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Group group2 = GroupBuilder.createGroup(context).withName("My 2 group").build(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .withGroupMembership(group1) - .withGroupMembership(group2) - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .withGroupMembership(group1) + .withGroupMembership(group2) + .build(); Community community = CommunityBuilder.createCommunity(context) - .withName("My community") - .build(); + .withName("My community") + .build(); Community community2 = CommunityBuilder.createCommunity(context) - .withName("My 2 community") - .build(); + .withName("My 2 community") + .build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withName("My collection") - .build(); + .withName("My collection") + .build(); ResourcePolicy firstResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.ADD) - .withGroup(group1).build(); + .withDspaceObject(community) + .withAction(Constants.ADD) + .withGroup(group1).build(); ResourcePolicy secondResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.READ) - .withGroup(group1).build(); + .withDspaceObject(community) + .withAction(Constants.READ) + .withGroup(group1).build(); ResourcePolicy collectionResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(collection) - .withAction(Constants.WRITE) - .withGroup(group1).build(); + .withDspaceObject(collection) + .withAction(Constants.WRITE) + .withGroup(group1).build(); ResourcePolicy firstResourcePolicyOfGroup2 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community2) - .withAction(Constants.ADD) - .withGroup(group2).build(); + .withDspaceObject(community2) + .withAction(Constants.ADD) + .withGroup(group2).build(); context.restoreAuthSystemState(); String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken) - .perform(get("/api/authz/resourcepolicies/search/group") - .param("uuid", group1.getID().toString())) - .andExpect(status().isOk()).andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._embedded.resourcepolicies", - Matchers.containsInAnyOrder( - ResoucePolicyMatcher.matchResourcePolicy(firstResourcePolicyOfGroup1), - ResoucePolicyMatcher.matchResourcePolicy(secondResourcePolicyOfGroup1), - ResoucePolicyMatcher.matchResourcePolicy(collectionResourcePolicyOfGroup1)))) - .andExpect(jsonPath("$._embedded.resourcepolicies", - Matchers.not(is(ResoucePolicyMatcher.matchResourcePolicy(firstResourcePolicyOfGroup2))))) - .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("api/authz/resourcepolicies/search/group"))) - .andExpect(jsonPath("$.page.totalElements", is(3))); + .perform(get("/api/authz/resourcepolicies/search/group") + .param("uuid", group1.getID().toString())) + .andExpect(status().isOk()).andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.resourcepolicies", + Matchers.containsInAnyOrder( + ResourcePolicyMatcher.matchResourcePolicy(firstResourcePolicyOfGroup1), + ResourcePolicyMatcher.matchResourcePolicy(secondResourcePolicyOfGroup1), + ResourcePolicyMatcher.matchResourcePolicy(collectionResourcePolicyOfGroup1)))) + .andExpect(jsonPath("$._embedded.resourcepolicies", + Matchers.not(is(ResourcePolicyMatcher.matchResourcePolicy(firstResourcePolicyOfGroup2))))) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("api/authz/resourcepolicies/search/group"))) + .andExpect(jsonPath("$.page.totalElements", is(3))); String authToken2 = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken2) - .perform(get("/api/authz/resourcepolicies/search/group") - .param("uuid", group2.getID().toString())) - .andExpect(status().isOk()).andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._embedded.resourcepolicies", - Matchers.contains( - ResoucePolicyMatcher.matchResourcePolicy(firstResourcePolicyOfGroup2)))) - .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("api/authz/resourcepolicies/search/group"))) - .andExpect(jsonPath("$.page.totalElements", is(1))); + .perform(get("/api/authz/resourcepolicies/search/group") + .param("uuid", group2.getID().toString())) + .andExpect(status().isOk()).andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.resourcepolicies", + Matchers.contains( + ResourcePolicyMatcher.matchResourcePolicy(firstResourcePolicyOfGroup2)))) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("api/authz/resourcepolicies/search/group"))) + .andExpect(jsonPath("$.page.totalElements", is(1))); } @Test @@ -796,55 +792,54 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Group group1 = GroupBuilder.createGroup(context).withName("My group").build(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("myemail@mail.com") - .withPassword("qwerty01") - .withGroupMembership(group1) - .build(); + .withEmail("myemail@mail.com") + .withPassword("qwerty01") + .withGroupMembership(group1) + .build(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); Community community2 = CommunityBuilder.createCommunity(context).withName("My second community").build(); ResourcePolicy firstResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.ADD) - .withGroup(group1).build(); + .withDspaceObject(community) + .withAction(Constants.ADD) + .withGroup(group1).build(); ResourcePolicy secondResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community2) - .withAction(Constants.WRITE) - .withGroup(group1).build(); + .withDspaceObject(community2) + .withAction(Constants.WRITE) + .withGroup(group1).build(); context.restoreAuthSystemState(); String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(get("/api/authz/resourcepolicies/search/group") - .param("uuid", group1.getID().toString()) - .param("resource", community.getID().toString())) - .andExpect(status().isOk()).andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._embedded.resourcepolicies", - Matchers.contains(ResoucePolicyMatcher.matchResourcePolicy(firstResourcePolicyOfGroup1)))) - .andExpect(jsonPath("$._embedded.resourcepolicies", - Matchers.not(is(ResoucePolicyMatcher.matchResourcePolicy(secondResourcePolicyOfGroup1))))) - .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("api/authz/resourcepolicies/search/group"))) - .andExpect(jsonPath("$.page.totalElements", is(1))); + .param("uuid", group1.getID().toString()) + .param("resource", community.getID().toString())) + .andExpect(status().isOk()).andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.resourcepolicies", + Matchers.contains(ResourcePolicyMatcher.matchResourcePolicy(firstResourcePolicyOfGroup1)))) + .andExpect(jsonPath("$._embedded.resourcepolicies", + Matchers.not(is(ResourcePolicyMatcher.matchResourcePolicy(secondResourcePolicyOfGroup1))))) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("api/authz/resourcepolicies/search/group"))) + .andExpect(jsonPath("$.page.totalElements", is(1))); } @Test - @Ignore("Currently fail due to https://jira.lyrasis.org/browse/DS-4428") public void findResoucesPoliciesByGroupWithoutParametersBadRequestTest() throws Exception { context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); context.restoreAuthSystemState(); String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(get("/api/authz/resourcepolicies/search/group")) - .andExpect(status().isBadRequest()); + .andExpect(status().isBadRequest()); } @Test @@ -854,97 +849,96 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Group group1 = GroupBuilder.createGroup(context).withName("My group").build(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@gmail.com") - .withPassword("qwerty01") - .withGroupMembership(group1) - .build(); + .withEmail("eperson1@gmail.com") + .withPassword("qwerty01") + .withGroupMembership(group1) + .build(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); ResourcePolicy firstResourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.ADD) - .withGroup(group1).build(); + .withDspaceObject(community) + .withAction(Constants.ADD) + .withGroup(group1).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/authz/resourcepolicies/search/group") - .param("uuid", group1.getID().toString())) - .andExpect(status().isUnauthorized()); + .param("uuid", group1.getID().toString())) + .andExpect(status().isUnauthorized()); } @Test public void findGroupNotFoundTest() throws Exception { - String authToken = getAuthToken(admin.getEmail(), password); getClient(authToken).perform(get("/api/authz/resourcepolicies/search/group") - .param("uuid", UUID.randomUUID().toString())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.page.totalElements", is(0))); + .param("uuid", UUID.randomUUID().toString())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.page.totalElements", is(0))); } @Test - public void findResoucesPoliciesByGroupUuidForbiddenTest() throws Exception { + public void findResourcesPoliciesByGroupUuidForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); Group group1 = GroupBuilder.createGroup(context).withName("My group").build(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .withGroupMembership(group1) - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .withGroupMembership(group1) + .build(); EPerson eperson2 = EPersonBuilder.createEPerson(context) - .withEmail("eperson2@mail.com") - .withPassword("qwerty02") - .build(); + .withEmail("eperson2@mail.com") + .withPassword("qwerty02") + .build(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); ResourcePolicy resourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community).withAction(Constants.WRITE) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withGroup(group1).build(); + .withDspaceObject(community).withAction(Constants.WRITE) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .withGroup(group1).build(); context.restoreAuthSystemState(); String authToken = getAuthToken(eperson2.getEmail(), "qwerty02"); getClient(authToken).perform(get("/api/authz/resourcepolicies/search/group") - .param("uuid", group1.getID().toString()) - .param("resource", community.getID().toString())) - .andExpect(status().isForbidden()); + .param("uuid", group1.getID().toString()) + .param("resource", community.getID().toString())) + .andExpect(status().isForbidden()); } @Test - public void findResoucesPoliciesByGroupAnonymousTest() throws Exception { + public void findResourcesPoliciesByGroupAnonymousTest() throws Exception { context.turnOffAuthorisationSystem(); Group groupAnonymous = EPersonServiceFactory.getInstance().getGroupService().findByName(context, - Group.ANONYMOUS); + Group.ANONYMOUS); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); ResourcePolicy resourcePolicyOfGroup1 = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community).withAction(Constants.WRITE) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withGroup(groupAnonymous).build(); + .withDspaceObject(community).withAction(Constants.WRITE) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .withGroup(groupAnonymous).build(); context.restoreAuthSystemState(); String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(get("/api/authz/resourcepolicies/search/group") - .param("uuid", groupAnonymous.getID().toString())) - .andExpect(status().isOk()); + .param("uuid", groupAnonymous.getID().toString())) + .andExpect(status().isOk()); getClient().perform(get("/api/authz/resourcepolicies/search/group") - .param("uuid", groupAnonymous.getID().toString())) - .andExpect(status().isUnauthorized()); + .param("uuid", groupAnonymous.getID().toString())) + .andExpect(status().isUnauthorized()); } @Test @@ -954,13 +948,13 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio AtomicReference idRef = new AtomicReference(); try { Community community = CommunityBuilder.createCommunity(context) - .withName("My commynity") - .build(); + .withName("My commynity") + .build(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); context.restoreAuthSystemState(); @@ -972,29 +966,31 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio String authToken = getAuthToken(admin.getEmail(), password); getClient(authToken) - .perform(post("/api/authz/resourcepolicies") - .content(mapper.writeValueAsBytes(resourcePolicyRest)) - .param("resource", community.getID().toString()) - .param("eperson", eperson1.getID().toString()) - .contentType(contentType)) - .andExpect(status().isCreated()) - .andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.name", is(resourcePolicyRest.getName())), - hasJsonPath("$.description", is(resourcePolicyRest.getDescription())), - hasJsonPath("$.policyType", is(resourcePolicyRest.getPolicyType())), - hasJsonPath("$.action", is(resourcePolicyRest.getAction())), - hasJsonPath("$.startDate", is(resourcePolicyRest.getStartDate())), - hasJsonPath("$.endDate", is(resourcePolicyRest.getEndDate())), - hasJsonPath("$.type", is(resourcePolicyRest.getType()))))) - .andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id"))); + .perform(post("/api/authz/resourcepolicies") + .content(mapper.writeValueAsBytes(resourcePolicyRest)) + .param("resource", community.getID().toString()) + .param("eperson", eperson1.getID().toString()) + .param("projections", "full") + .contentType(contentType)) + .andExpect(status().isCreated()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", ResourcePolicyMatcher.matchFullEmbeds())) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(resourcePolicyRest.getName())), + hasJsonPath("$.description", is(resourcePolicyRest.getDescription())), + hasJsonPath("$.policyType", is(resourcePolicyRest.getPolicyType())), + hasJsonPath("$.action", is(resourcePolicyRest.getAction())), + hasJsonPath("$.startDate", is(resourcePolicyRest.getStartDate())), + hasJsonPath("$.endDate", is(resourcePolicyRest.getEndDate())), + hasJsonPath("$.type", is(resourcePolicyRest.getType()))))) + .andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id"))); String authToken1 = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken1).perform(get("/api/authz/resourcepolicies/" + idRef.get())) - .andExpect(status().isOk()) - .andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("/api/authz/resourcepolicies/" + idRef.get()))); + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + idRef.get()))); } finally { ResourcePolicyBuilder.delete(idRef.get()); } @@ -1004,13 +1000,13 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio public void createOneUnAuthenticatedTest() throws Exception { context.turnOffAuthorisationSystem(); Community community = CommunityBuilder.createCommunity(context) - .withName("My commynity") - .build(); + .withName("My commynity") + .build(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); context.restoreAuthSystemState(); @@ -1021,34 +1017,34 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio resourcePolicyRest.setAction(Constants.actionText[Constants.ADMIN]); getClient().perform(post("/api/authz/resourcepolicies") - .content(mapper.writeValueAsBytes(resourcePolicyRest)) - .param("resource", community.getID().toString()) - .param("eperson", eperson1.getID().toString()) - .contentType(contentType)) - .andExpect(status().isUnauthorized()); + .content(mapper.writeValueAsBytes(resourcePolicyRest)) + .param("resource", community.getID().toString()) + .param("eperson", eperson1.getID().toString()) + .contentType(contentType)) + .andExpect(status().isUnauthorized()); String authToken = getAuthToken(admin.getEmail(), password); getClient(authToken).perform(get("/api/authz/resourcepolicies/search/resource") - .param("uuid", community.getID().toString()) - .param("action", "ADMIN")) - .andExpect(status().isOk()) - .andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("api/authz/resourcepolicies/search/resource"))) - .andExpect(jsonPath("$.page.totalElements", is(0))); + .param("uuid", community.getID().toString()) + .param("action", "ADMIN")) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("api/authz/resourcepolicies/search/resource"))) + .andExpect(jsonPath("$.page.totalElements", is(0))); } @Test public void createOneForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); Community community = CommunityBuilder.createCommunity(context) - .withName("My commynity") - .build(); + .withName("My commynity") + .build(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); context.restoreAuthSystemState(); @@ -1060,21 +1056,21 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(post("/api/authz/resourcepolicies") - .content(mapper.writeValueAsBytes(resourcePolicyRest)) - .param("resource", community.getID().toString()) - .param("eperson", eperson1.getID().toString()) - .contentType(contentType)) - .andExpect(status().isForbidden()); + .content(mapper.writeValueAsBytes(resourcePolicyRest)) + .param("resource", community.getID().toString()) + .param("eperson", eperson1.getID().toString()) + .contentType(contentType)) + .andExpect(status().isForbidden()); String authToken2 = getAuthToken(admin.getEmail(), password); getClient(authToken2).perform(get("/api/authz/resourcepolicies/search/resource") - .param("uuid", community.getID().toString()) - .param("action", "ADMIN")) - .andExpect(status().isOk()) - .andExpect(content().contentType(contentType)) - .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("api/authz/resourcepolicies/search/resource"))) - .andExpect(jsonPath("$.page.totalElements", is(0))); + .param("uuid", community.getID().toString()) + .param("action", "ADMIN")) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("api/authz/resourcepolicies/search/resource"))) + .andExpect(jsonPath("$.page.totalElements", is(0))); } @Test @@ -1082,29 +1078,29 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context) - .withName("My community") - .build(); + .withName("My community") + .build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withUser(eperson1) - .withAction(Constants.ADMIN) - .build(); + .withDspaceObject(community) + .withUser(eperson1) + .withAction(Constants.ADMIN) + .build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); getClient(token).perform(delete("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().is(204)); + .andExpect(status().is(204)); // Verify 404 after delete getClient(token).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()); } @Test @@ -1113,25 +1109,25 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(community) - .withAction(Constants.DELETE) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(eperson1) - .build(); + .withDspaceObject(community) + .withAction(Constants.DELETE) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .withUser(eperson1) + .build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isUnauthorized()); + .andExpect(status().isUnauthorized()); String token = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(token).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()); + .andExpect(status().isOk()); } @Test @@ -1139,38 +1135,38 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).withName("My community").build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withName("My collection").build(); + .withName("My collection").build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withDspaceObject(collection) - .withAction(Constants.ADD) - .withUser(eperson1).build(); + .withDspaceObject(collection) + .withAction(Constants.ADD) + .withUser(eperson1).build(); context.restoreAuthSystemState(); String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(delete("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isForbidden()); + .andExpect(status().isForbidden()); String token = getAuthToken(admin.getEmail(), password); getClient(token).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$", is(ResoucePolicyMatcher.matchResourcePolicy(resourcePolicy)))) - .andExpect(jsonPath("$._links.self.href", Matchers - .containsString("/api/authz/resourcepolicies/" + resourcePolicy.getID()))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", is(ResourcePolicyMatcher.matchResourcePolicy(resourcePolicy)))) + .andExpect(jsonPath("$._links.self.href", Matchers + .containsString("/api/authz/resourcepolicies/" + resourcePolicy.getID()))); } @Test public void deleteOneNotFoundTest() throws Exception { String authToken = getAuthToken(admin.getEmail(), password); getClient(authToken).perform(delete("/api/authz/resourcepolicies/" + Integer.MAX_VALUE)) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()); } @Test @@ -1178,19 +1174,19 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withAdminGroup(eperson1) - .build(); + .withAdminGroup(eperson1) + .build(); Item publicItem1 = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); Calendar calendar = Calendar.getInstance(); @@ -1201,12 +1197,12 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Date data = calendar.getTime(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(publicItem1) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withStartDate(data) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(publicItem1) + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withStartDate(data) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -1224,23 +1220,23 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(replaceOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.name", is(resourcePolicy.getRpName())), - hasJsonPath("$.description", is(resourcePolicy.getRpDescription())), - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.startDate", is(formatDate.format(newDate))), - hasJsonPath("$.endDate", is(resourcePolicy.getEndDate()))))); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(resourcePolicy.getRpName())), + hasJsonPath("$.description", is(resourcePolicy.getRpDescription())), + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.startDate", is(formatDate.format(newDate))), + hasJsonPath("$.endDate", is(resourcePolicy.getEndDate()))))); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.startDate", is(formatDate.format(newDate)))))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.startDate", is(formatDate.format(newDate)))))); } @Test @@ -1248,26 +1244,26 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withAdminGroup(eperson1) - .build(); + .withAdminGroup(eperson1) + .build(); Item publicItem1 = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(publicItem1) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(publicItem1) + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -1285,23 +1281,23 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(addOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.name", is(resourcePolicy.getRpName())), - hasJsonPath("$.description", is(resourcePolicy.getRpDescription())), - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.startDate", is(formatDate.format(newDate))), - hasJsonPath("$.endDate", is(resourcePolicy.getEndDate()))))); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(resourcePolicy.getRpName())), + hasJsonPath("$.description", is(resourcePolicy.getRpDescription())), + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.startDate", is(formatDate.format(newDate))), + hasJsonPath("$.endDate", is(resourcePolicy.getEndDate()))))); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.startDate", is(formatDate.format(newDate)))))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.startDate", is(formatDate.format(newDate)))))); } @Test @@ -1309,19 +1305,19 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withAdminGroup(eperson1) - .build(); + .withAdminGroup(eperson1) + .build(); Item publicItem1 = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); Calendar calendar = Calendar.getInstance(); @@ -1332,12 +1328,12 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Date data = calendar.getTime(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(publicItem1) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withStartDate(data) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(publicItem1) + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withStartDate(data) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -1346,23 +1342,23 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(removeOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.name", is(resourcePolicy.getRpName())), - hasJsonPath("$.description", is(resourcePolicy.getRpDescription())), - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.startDate",nullValue()), - hasJsonPath("$.endDate", is(resourcePolicy.getEndDate()))))); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(resourcePolicy.getRpName())), + hasJsonPath("$.description", is(resourcePolicy.getRpDescription())), + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.startDate", nullValue()), + hasJsonPath("$.endDate", is(resourcePolicy.getEndDate()))))); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.startDate", nullValue())))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.startDate", nullValue())))); } @Test @@ -1370,19 +1366,19 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withAdminGroup(eperson1) - .build(); + .withAdminGroup(eperson1) + .build(); Item publicItem1 = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); Calendar calendar = Calendar.getInstance(); @@ -1393,13 +1389,13 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Date date = calendar.getTime(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(publicItem1) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withStartDate(date) - .withDescription("my description") - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(publicItem1) + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withStartDate(date) + .withDescription("my description") + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -1411,18 +1407,18 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(replaceOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isBadRequest()); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isBadRequest()); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.startDate", is(formatDate.format(date))), - hasJsonPath("$.description", is(resourcePolicy.getRpDescription()))))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.startDate", is(formatDate.format(date))), + hasJsonPath("$.description", is(resourcePolicy.getRpDescription()))))); } @Test @@ -1444,11 +1440,11 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Date date = calendar.getTime(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.WRITE) - .withDspaceObject(item) - .withStartDate(date) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.WRITE) + .withDspaceObject(item) + .withStartDate(date) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -1467,14 +1463,14 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio String patchBody = getPatchContent(ops); getClient().perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isUnauthorized()); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isUnauthorized()); String authToken = getAuthToken(admin.getEmail(), password); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), hasJsonPath("$.startDate", is(formatDate.format(date))), hasJsonPath("$.description", is(resourcePolicy.getRpDescription()))))); @@ -1485,17 +1481,17 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community).build(); Item item = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); Calendar calendar = Calendar.getInstance(); @@ -1506,12 +1502,12 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Date date = calendar.getTime(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(item) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withStartDate(date) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(item) + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withStartDate(date) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -1529,29 +1525,29 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(replaceOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isForbidden()); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isForbidden()); String authToken2 = getAuthToken(admin.getEmail(), password); getClient(authToken2).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), hasJsonPath("$.startDate", is(formatDate.format(date))), hasJsonPath("$.description", is(resourcePolicy.getRpDescription()))))); } @Test - public void patchReplaceStartDataNotFoundTest() throws Exception { + public void patchReplaceStartDateNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); context.restoreAuthSystemState(); @@ -1569,11 +1565,11 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(replaceOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + Integer.MAX_VALUE) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isNotFound()); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isNotFound()); } @Test @@ -1581,19 +1577,19 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withAdminGroup(eperson1) - .build(); + .withAdminGroup(eperson1) + .build(); Item publicItem1 = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); Calendar calendarStartDate = Calendar.getInstance(); @@ -1612,13 +1608,13 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Date endDate = calendarEndDate.getTime(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(publicItem1) - .withUser(eperson1) - .withStartDate(startDate) - .withEndDate(endDate) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(publicItem1) + .withUser(eperson1) + .withStartDate(startDate) + .withEndDate(endDate) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -1636,18 +1632,18 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(replaceOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isBadRequest()); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isBadRequest()); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.startDate", is(formatDate.format(startDate))), - hasJsonPath("$.endDate", is(formatDate.format(endDate)))))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.startDate", is(formatDate.format(startDate))), + hasJsonPath("$.endDate", is(formatDate.format(endDate)))))); } @Test @@ -1655,27 +1651,27 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withAdminGroup(eperson1) - .build(); + .withAdminGroup(eperson1) + .build(); Item publicItem1 = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(publicItem1) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withDescription("my description") - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(publicItem1) + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withDescription("my description") + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -1685,21 +1681,21 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(replaceOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.name", is(resourcePolicy.getRpName())), - hasJsonPath("$.description", is(newDescription)), - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()]))))); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(resourcePolicy.getRpName())), + hasJsonPath("$.description", is(newDescription)), + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()]))))); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.description", is(newDescription))))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.description", is(newDescription))))); } @Test @@ -1707,26 +1703,26 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withAdminGroup(eperson1) - .build(); + .withAdminGroup(eperson1) + .build(); Item item = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(item) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(item) + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -1736,21 +1732,21 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(replaceOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.name", is(resourcePolicy.getRpName())), - hasJsonPath("$.description", is(description)), - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()]))))); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(resourcePolicy.getRpName())), + hasJsonPath("$.description", is(description)), + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()]))))); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.description", is(description))))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.description", is(description))))); } @Test @@ -1758,27 +1754,27 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withAdminGroup(eperson1) - .build(); + .withAdminGroup(eperson1) + .build(); Item item = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(item) - .withDescription("my description") - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(item) + .withDescription("my description") + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -1787,21 +1783,21 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(replaceOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.name", is(resourcePolicy.getRpName())), - hasJsonPath("$.description", nullValue()), - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()]))))); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(resourcePolicy.getRpName())), + hasJsonPath("$.description", nullValue()), + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()]))))); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.description", nullValue())))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.description", nullValue())))); } @Test @@ -1815,11 +1811,11 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Item item = ItemBuilder.createItem(context, collection).build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.WRITE) - .withDspaceObject(item) - .withDescription("My Description") - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.WRITE) + .withDspaceObject(item) + .withDescription("My Description") + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -1831,14 +1827,14 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio String patchBody = getPatchContent(ops); getClient().perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isUnauthorized()); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isUnauthorized()); String authToken = getAuthToken(admin.getEmail(), password); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), hasJsonPath("$.description", is(resourcePolicy.getRpDescription()))))); } @@ -1848,25 +1844,25 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community).build(); Item item = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(item) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withDescription("My Description") - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(item) + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withDescription("My Description") + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -1876,26 +1872,25 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(replaceOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isForbidden()); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isForbidden()); } @Test - public void patchRemoveDescriptionNotFounfdTest() throws Exception { - + public void patchRemoveDescriptionNotFoundTest() throws Exception { List ops = new ArrayList(); RemoveOperation removeOperation = new RemoveOperation("/description"); ops.add(removeOperation); String patchBody = getPatchContent(ops); String authToken = getAuthToken(admin.getEmail(), password); - getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + Integer.MAX_VALUE ) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isNotFound()); + getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + Integer.MAX_VALUE) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isNotFound()); } @Test @@ -1903,27 +1898,27 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withAdminGroup(eperson1) - .build(); + .withAdminGroup(eperson1) + .build(); Item publicItem1 = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(publicItem1) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withDescription("my description") - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(publicItem1) + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withDescription("my description") + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -1933,17 +1928,17 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(replaceOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isBadRequest()); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isBadRequest()); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.description", is(resourcePolicy.getRpDescription()))))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.description", is(resourcePolicy.getRpDescription()))))); } @Test @@ -1951,27 +1946,27 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withAdminGroup(eperson1) - .build(); + .withAdminGroup(eperson1) + .build(); Item myItem = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(myItem) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withName("My name") - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(myItem) + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withName("My name") + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -1981,20 +1976,20 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(replaceOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.name", is(newName)), - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()]))))); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(newName)), + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()]))))); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.name", is(newName))))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.name", is(newName))))); } @Test @@ -2002,27 +1997,27 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withAdminGroup(eperson1) - .build(); + .withAdminGroup(eperson1) + .build(); Item myItem = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(myItem) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withName("My name") - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(myItem) + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withName("My name") + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -2032,17 +2027,17 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(replaceOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isBadRequest()); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isBadRequest()); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.name", is(resourcePolicy.getRpName()))))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.name", is(resourcePolicy.getRpName()))))); } @Test @@ -2050,26 +2045,26 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withAdminGroup(eperson1) - .build(); + .withAdminGroup(eperson1) + .build(); Item myItem = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(myItem) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(myItem) + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -2079,20 +2074,20 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(replaceOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.name", is(name)), - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()]))))); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(name)), + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()]))))); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.name", is(name))))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.name", is(name))))); } @Test @@ -2100,27 +2095,27 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withAdminGroup(eperson1) - .build(); + .withAdminGroup(eperson1) + .build(); Item myItem = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(myItem) - .withName("My name") - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(myItem) + .withName("My name") + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -2130,17 +2125,17 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(replaceOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isBadRequest()); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isBadRequest()); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.name", is(resourcePolicy.getRpName()))))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.name", is(resourcePolicy.getRpName()))))); } @Test @@ -2148,27 +2143,27 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withAdminGroup(eperson1) - .build(); + .withAdminGroup(eperson1) + .build(); Item myItem = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(myItem) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withName("My Name") - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(myItem) + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withName("My Name") + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -2177,20 +2172,20 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(replaceOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.name", nullValue())))); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.name", nullValue())))); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.name", nullValue())))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.name", nullValue())))); } @Test @@ -2198,26 +2193,26 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .build(); + .build(); Item myItem = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(myItem) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withName("My Name") - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(myItem) + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withName("My Name") + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -2226,17 +2221,17 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio ops.add(replaceOperation); String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isForbidden()); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isForbidden()); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.name", is(resourcePolicy.getRpName()))))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.name", is(resourcePolicy.getRpName()))))); } @Test @@ -2244,19 +2239,19 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withAdminGroup(eperson1) - .build(); + .withAdminGroup(eperson1) + .build(); Item publicItem1 = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); Calendar calendarStartDate = Calendar.getInstance(); @@ -2275,13 +2270,13 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Date endDate = calendarEndDate.getTime(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(publicItem1) - .withStartDate(startDate) - .withEndDate(endDate) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(publicItem1) + .withStartDate(startDate) + .withEndDate(endDate) + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -2307,7 +2302,7 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Date newStartDate = calendarNewStartDate.getTime(); ReplaceOperation replaceStartDateOperation = new ReplaceOperation("/startDate", - formatDate.format(newStartDate)); + formatDate.format(newStartDate)); ops.add(replaceStartDateOperation); RemoveOperation removeEndDateOperation = new RemoveOperation("/endDate"); @@ -2315,26 +2310,26 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.name", is(newName)), - hasJsonPath("$.description", is(addDescription)), - hasJsonPath("$.startDate", is(formatDate.format(newStartDate))), - hasJsonPath("$.endDate", nullValue()), - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()]))))); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(newName)), + hasJsonPath("$.description", is(addDescription)), + hasJsonPath("$.startDate", is(formatDate.format(newStartDate))), + hasJsonPath("$.endDate", nullValue()), + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()]))))); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.name", is(newName)), - hasJsonPath("$.startDate", is(formatDate.format(newStartDate))), - hasJsonPath("$.endDate", nullValue()), - hasJsonPath("$.description", is(addDescription))))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.name", is(newName)), + hasJsonPath("$.startDate", is(formatDate.format(newStartDate))), + hasJsonPath("$.endDate", nullValue()), + hasJsonPath("$.description", is(addDescription))))); } @Test @@ -2342,19 +2337,19 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio context.turnOffAuthorisationSystem(); EPerson eperson1 = EPersonBuilder.createEPerson(context) - .withEmail("eperson1@mail.com") - .withPassword("qwerty01") - .build(); + .withEmail("eperson1@mail.com") + .withPassword("qwerty01") + .build(); Community community = CommunityBuilder.createCommunity(context).build(); Collection collection = CollectionBuilder.createCollection(context, community) - .withAdminGroup(eperson1) - .build(); + .withAdminGroup(eperson1) + .build(); Item publicItem1 = ItemBuilder.createItem(context, collection) - .withTitle("Public item") - .build(); + .withTitle("Public item") + .build(); Calendar calendarEndDate = Calendar.getInstance(); @@ -2365,13 +2360,13 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Date endDate = calendarEndDate.getTime(); ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context) - .withAction(Constants.READ) - .withDspaceObject(publicItem1) - .withName("My Name") - .withEndDate(endDate) - .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) - .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .build(); + .withAction(Constants.READ) + .withDspaceObject(publicItem1) + .withName("My Name") + .withEndDate(endDate) + .withGroup(EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS)) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .build(); context.restoreAuthSystemState(); @@ -2389,7 +2384,7 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio Date newStartDate = calendarNewStartDate.getTime(); ReplaceOperation replaceStartDateOperation = new ReplaceOperation("/startDate", - formatDate.format(newStartDate)); + formatDate.format(newStartDate)); ops.add(replaceStartDateOperation); RemoveOperation removeEndDateOperation = new RemoveOperation("/endDate"); @@ -2397,19 +2392,19 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio String patchBody = getPatchContent(ops); - String authToken = getAuthToken(eperson1.getEmail(),"qwerty01"); + String authToken = getAuthToken(eperson1.getEmail(), "qwerty01"); getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isBadRequest()); + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isBadRequest()); getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.allOf( - hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), - hasJsonPath("$.name", is(resourcePolicy.getRpName())), - hasJsonPath("$.startDate", nullValue()), - hasJsonPath("$.endDate", is(formatDate.format(endDate))), - hasJsonPath("$.description", nullValue())))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])), + hasJsonPath("$.name", is(resourcePolicy.getRpName())), + hasJsonPath("$.startDate", nullValue()), + hasJsonPath("$.endDate", is(formatDate.format(endDate))), + hasJsonPath("$.description", nullValue())))); } -} \ No newline at end of file +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/TaskRestRepositoriesIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/TaskRestRepositoriesIT.java index 1bc7ef7ffc..39a67e98a7 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/TaskRestRepositoriesIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/TaskRestRepositoriesIT.java @@ -1439,7 +1439,7 @@ public class TaskRestRepositoriesIT extends AbstractControllerIntegrationTest { .withName("Sub Community") .build(); - // the reviewer2 is a reviewer in a different step for the col1 and with the same role than reviewer1 for + // the reviewer2 is a reviewer in a different step for the colAA1 and with the same role than reviewer1 for // another collection Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1") .withWorkflowGroup(1, reviewer1) @@ -1613,7 +1613,7 @@ public class TaskRestRepositoriesIT extends AbstractControllerIntegrationTest { .withName("Sub Community") .build(); - // the reviewer2 is a reviewer in a different step for the col1 and with the same role than reviewer1 for + // the reviewer2 is a reviewer in a different step for the colAA1 and with the same role than reviewer1 for // another collection Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1") .withWorkflowGroup(1, reviewer1) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/UUIDLookupRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/UUIDLookupRestControllerIT.java index 8f99811fa6..41e68f08c8 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/UUIDLookupRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/UUIDLookupRestControllerIT.java @@ -281,13 +281,13 @@ public class UUIDLookupRestControllerIT extends AbstractControllerIntegrationTes @Test /** - * Test that a request with an uuid parameter that is not an actual UUID return a 422 Unprocessable Entity status + * Test that a request with an uuid parameter that is not an actual UUID return a 400 Bad Request status * * @throws Exception */ public void testInvalidUUID() throws Exception { getClient().perform(get("/api/dso/find?uuid={uuid}","invalidUUID")) - .andExpect(status().isUnprocessableEntity()); + .andExpect(status().isBadRequest()); } @Test diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowActionRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowActionRestRepositoryIT.java new file mode 100644 index 0000000000..884fc6cfa5 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowActionRestRepositoryIT.java @@ -0,0 +1,128 @@ +/** + * 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.app.rest; + +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.not; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.dspace.app.rest.matcher.WorkflowActionMatcher; +import org.dspace.app.rest.model.WorkflowActionRest; +import org.dspace.app.rest.repository.WorkflowActionRestRepository; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; +import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory; +import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig; +import org.hamcrest.Matchers; +import org.junit.Test; + +/** + * Integration tests for the {@link WorkflowActionRestRepository} controlled endpoints + * + * @author Maria Verdonck (Atmire) on 06/01/2020 + */ +public class WorkflowActionRestRepositoryIT extends AbstractControllerIntegrationTest { + + private XmlWorkflowFactory xmlWorkflowFactory = XmlWorkflowServiceFactory.getInstance().getWorkflowFactory(); + + private static final String WORKFLOW_ACTIONS_ENDPOINT + = "/api/" + WorkflowActionRest.CATEGORY + "/" + WorkflowActionRest.NAME_PLURAL; + + @Test + public void getAllWorkflowActions_NonImplementedEndpoint() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT)) + //We expect a 405 Method not allowed status + .andExpect(status().isMethodNotAllowed()); + } + + @Test + public void getAllWorkflowActions_NonImplementedEndpoint_NonValidToken() throws Exception { + String token = "nonValidToken"; + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT)) + //We expect a 403 Forbidden status + .andExpect(status().isForbidden()); + } + + @Test + public void getAllWorkflowActions_NonImplementedEndpoint_NoToken() throws Exception { + //When we call this facets endpoint + getClient().perform(get(WORKFLOW_ACTIONS_ENDPOINT)) + //We expect a 401 Unauthorized + .andExpect(status().isUnauthorized()); + } + + @Test + public void getWorkflowActionByName_NonExistentWorkflowAction() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + String nameNonExistentWorkflowActionName = "TestNameNonExistentWorkflowAction9999"; + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT + "/" + nameNonExistentWorkflowActionName)) + //We expect a 404 Not Found status + .andExpect(status().isNotFound()); + } + + @Test + public void getWorkflowActionByName_ExistentWithOptions_editaction() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + String nameActionWithOptions = "editaction"; + WorkflowActionConfig existentWorkflow = xmlWorkflowFactory.getActionByName(nameActionWithOptions); + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT + "/" + nameActionWithOptions)) + //We expect a 200 is ok status + .andExpect(status().isOk()) + // has options + .andExpect(jsonPath("$.options", not(empty()))) + //Matches expected corresponding rest action values + .andExpect(jsonPath("$", Matchers.is( + WorkflowActionMatcher.matchWorkflowActionEntry(existentWorkflow) + ))); + } + + @Test + public void getWorkflowActionByName_ExistentWithoutOptions_claimaction() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + String nameActionWithoutOptions = "claimaction"; + WorkflowActionConfig existentWorkflowNoOptions = xmlWorkflowFactory.getActionByName(nameActionWithoutOptions); + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT + "/" + nameActionWithoutOptions)) + //We expect a 200 is ok status + .andExpect(status().isOk()) + // has no options + .andExpect(jsonPath("$.options", empty())) + //Matches expected corresponding rest action values + .andExpect(jsonPath("$", Matchers.is( + WorkflowActionMatcher.matchWorkflowActionEntry(existentWorkflowNoOptions) + ))); + } + + @Test + public void getWorkflowActionByName_ExistentWithOptions_NonValidToken() throws Exception { + String token = "nonValidToken"; + String nameActionWithOptions = "editaction"; + WorkflowActionConfig existentWorkflow = xmlWorkflowFactory.getActionByName(nameActionWithOptions); + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT + "/" + nameActionWithOptions)) + //We expect a 403 Forbidden status + .andExpect(status().isForbidden()); + } + + @Test + public void getWorkflowActionByName_ExistentWithOptions_NoToken() throws Exception { + String nameActionWithOptions = "editaction"; + WorkflowActionConfig existentWorkflow = xmlWorkflowFactory.getActionByName(nameActionWithOptions); + //When we call this facets endpoint + getClient().perform(get(WORKFLOW_ACTIONS_ENDPOINT + "/" + nameActionWithOptions)) + //We expect a 401 Unauthorized + .andExpect(status().isUnauthorized()); + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowDefinitionRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowDefinitionRestRepositoryIT.java new file mode 100644 index 0000000000..4a877825b9 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowDefinitionRestRepositoryIT.java @@ -0,0 +1,458 @@ +/** + * 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.app.rest; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalToIgnoringCase; +import static org.hamcrest.Matchers.is; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.List; +import java.util.UUID; + +import org.apache.commons.lang3.StringUtils; +import org.dspace.app.rest.builder.CollectionBuilder; +import org.dspace.app.rest.builder.CommunityBuilder; +import org.dspace.app.rest.matcher.WorkflowDefinitionMatcher; +import org.dspace.app.rest.model.WorkflowDefinitionRest; +import org.dspace.app.rest.repository.WorkflowDefinitionRestRepository; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; +import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory; +import org.dspace.xmlworkflow.state.Workflow; +import org.hamcrest.Matchers; +import org.junit.Test; + +/** + * Integration tests for the {@link WorkflowDefinitionRestRepository} + * and {@link WorkflowDefinitionCollectionsLinkRepository} controlled endpoints + * + * @author Maria Verdonck (Atmire) on 17/12/2019 + */ +public class WorkflowDefinitionRestRepositoryIT extends AbstractControllerIntegrationTest { + + private XmlWorkflowFactory xmlWorkflowFactory = XmlWorkflowServiceFactory.getInstance().getWorkflowFactory(); + + private static final String WORKFLOW_DEFINITIONS_ENDPOINT + = "/api/" + WorkflowDefinitionRest.CATEGORY + "/" + WorkflowDefinitionRest.NAME_PLURAL; + + @Test + public void getAllWorkflowDefinitionsEndpoint() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + List allConfiguredWorkflows = xmlWorkflowFactory.getAllConfiguredWorkflows(); + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT)) + //We expect a 200 OK status + .andExpect(status().isOk()) + //Number of total workflows is equals to number of configured workflows + .andExpect(jsonPath("$.page.totalElements", is(allConfiguredWorkflows.size()))) + //There needs to be a self link to this endpoint + .andExpect(jsonPath("$._links.self.href", containsString(WORKFLOW_DEFINITIONS_ENDPOINT))); + } + + @Test + public void getAllWorkflowDefinitionsEndpoint_Pagination_Size1() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + List allConfiguredWorkflows = xmlWorkflowFactory.getAllConfiguredWorkflows(); + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT) + .param("size", "1")) + //We expect a 200 OK status + .andExpect(status().isOk()) + //Number of total workflows is equals to number of configured workflows + .andExpect(jsonPath("$.page.totalElements", is(allConfiguredWorkflows.size()))) + //Page size is 1 + .andExpect(jsonPath("$.page.size", is(1))) + //Page nr is 1 + .andExpect(jsonPath("$.page.number", is(0))) + //Contains only the first configured workflow + .andExpect(jsonPath("$._embedded.workflowdefinitions", Matchers.contains( + WorkflowDefinitionMatcher.matchWorkflowDefinitionEntry(allConfiguredWorkflows.get(0)) + ))) + //Doesn't contain the other workflows + .andExpect(jsonPath("$._embedded.workflowdefinitions", Matchers.not( + Matchers.contains( + WorkflowDefinitionMatcher.matchWorkflowDefinitionEntry(allConfiguredWorkflows.get(1)) + ) + ))); + } + + @Test + public void getAllWorkflowDefinitionsEndpoint_Pagination_Size1_Page1() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + List allConfiguredWorkflows = xmlWorkflowFactory.getAllConfiguredWorkflows(); + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT) + .param("size", "1") + .param("page", "1")) + //We expect a 200 OK status + .andExpect(status().isOk()) + //Number of total workflows is equals to number of configured workflows + .andExpect(jsonPath("$.page.totalElements", is(allConfiguredWorkflows.size()))) + //Page size is 1 + .andExpect(jsonPath("$.page.size", is(1))) + //Page nr is 2 + .andExpect(jsonPath("$.page.number", is(1))) + //Contains only the second configured workflow + .andExpect(jsonPath("$._embedded.workflowdefinitions", Matchers.contains( + WorkflowDefinitionMatcher.matchWorkflowDefinitionEntry(allConfiguredWorkflows.get(1)) + ))) + //Doesn't contain 1st configured workflow + .andExpect(jsonPath("$._embedded.workflowdefinitions", Matchers.not( + Matchers.contains( + WorkflowDefinitionMatcher.matchWorkflowDefinitionEntry(allConfiguredWorkflows.get(0)) + ) + ))); + } + + @Test + public void getAllWorkflowDefinitionsEndpoint_NonValidToken() throws Exception { + String token = "NonValidToken"; + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT)) + //We expect a 403 Forbidden status + .andExpect(status().isForbidden()); + } + + @Test + public void getAllWorkflowDefinitionsEndpoint_NoToken() throws Exception { + //When we call this facets endpoint + getClient().perform(get(WORKFLOW_DEFINITIONS_ENDPOINT)) + //We expect a 401 Unauthorized + .andExpect(status().isUnauthorized()); + } + + @Test + public void getWorkflowDefinitionByName_DefaultWorkflow() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + Workflow defaultWorkflow = xmlWorkflowFactory.getDefaultWorkflow(); + String workflowName = defaultWorkflow.getID(); + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + workflowName)) + //We expect a 200 OK status + .andExpect(status().isOk()) + //There needs to be a self link to this endpoint + .andExpect(jsonPath("$._links.self.href", containsString(WORKFLOW_DEFINITIONS_ENDPOINT))) + // its name is default + .andExpect(jsonPath("$.name", equalToIgnoringCase(workflowName))) + // is default + .andExpect(jsonPath("$.isDefault", is(true))); + } + + @Test + public void getWorkflowDefinitionByName_NonDefaultWorkflow() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + Workflow defaultWorkflow = xmlWorkflowFactory.getDefaultWorkflow(); + List allConfiguredWorkflows = xmlWorkflowFactory.getAllConfiguredWorkflows(); + String firstNonDefaultWorkflowName = ""; + for (Workflow workflow : allConfiguredWorkflows) { + if (!workflow.getID().equalsIgnoreCase(defaultWorkflow.getID())) { + firstNonDefaultWorkflowName = workflow.getID(); + } + } + if (StringUtils.isNotBlank(firstNonDefaultWorkflowName)) { + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + firstNonDefaultWorkflowName)) + //We expect a 200 OK status + .andExpect(status().isOk()) + //There needs to be a self link to this endpoint + .andExpect(jsonPath("$._links.self.href", containsString(WORKFLOW_DEFINITIONS_ENDPOINT))) + // its name is name of non-default workflow + .andExpect(jsonPath("$.name", equalToIgnoringCase(firstNonDefaultWorkflowName))) + // is not default + .andExpect(jsonPath("$.isDefault", is(false))); + } + } + + @Test + public void getWorkflowDefinitionByName_NonExistentWorkflow() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + String workflowName = "TestNameNonExistentWorkflow9999"; + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + workflowName)) + //We expect a 404 Not Found status + .andExpect(status().isNotFound()); + } + + @Test + public void getWorkflowDefinitionByName_DefaultWorkflow_NonValidToken() throws Exception { + String token = "UnvalidToken"; + Workflow defaultWorkflow = xmlWorkflowFactory.getDefaultWorkflow(); + String workflowName = defaultWorkflow.getID(); + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + workflowName)) + //We expect a 403 Forbidden status + .andExpect(status().isForbidden()); + } + + @Test + public void getWorkflowDefinitionByName_DefaultWorkflow_NoToken() throws Exception { + Workflow defaultWorkflow = xmlWorkflowFactory.getDefaultWorkflow(); + String workflowName = defaultWorkflow.getID(); + //When we call this facets endpoint + getClient().perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + workflowName)) + //We expect a 401 Unauthorized + .andExpect(status().isUnauthorized()); + } + + @Test + public void getWorkflowDefinitionByCollectionId_ExistentCollection() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + //We turn off the authorization system in order to create the structure as defined below + context.turnOffAuthorisationSystem(); + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and one collection. + 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(); + context.restoreAuthSystemState(); + + Workflow workflowForThisCollection = xmlWorkflowFactory.getWorkflow(col1); + + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/search/findByCollection?uuid=" + col1.getID())) + //We expect a 200 OK status + .andExpect(status().isOk()) + // its name is name of corresponding workflow + .andExpect(jsonPath("$.name", equalToIgnoringCase(workflowForThisCollection.getID()))); + } + + @Test + public void getWorkflowDefinitionByCollectionId_nonValidUUID() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + String nonValidUUID = "TestNonValidUUID"; + + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/search/findByCollection?uuid=" + nonValidUUID)) + //We expect a 400 Illegal Argument Exception (Bad Request) cannot convert UUID + .andExpect(status().isBadRequest()) + .andExpect(status().reason(containsString("Failed to convert " + nonValidUUID))); + } + + @Test + public void getWorkflowDefinitionByCollectionId_nonExistentCollection() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + UUID nonExistentCollectionUUID = UUID.randomUUID(); + + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/search/findByCollection?uuid=" + + nonExistentCollectionUUID)) + //We expect a 404 Not Found status + .andExpect(status().isNotFound()); + } + + @Test + public void getCollectionsOfWorkflowByName_DefaultWorkflow_AllNonMappedCollections() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + Workflow defaultWorkflow = xmlWorkflowFactory.getDefaultWorkflow(); + List allNonMappedCollections = xmlWorkflowFactory.getAllNonMappedCollectionsHandles(context); + + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + defaultWorkflow.getID() + + "/collections")) + //We expect a 200 OK status + .andExpect(status().isOk()) + //Number of total workflows is equals to number of non-mapped collections + .andExpect(jsonPath("$.page.totalElements", is(allNonMappedCollections.size()))); + } + + @Test + public void getCollectionsOfWorkflowByName_DefaultWorkflow_AllNonMappedCollections_Paginated_Size1() + throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + //We turn off the authorization system in order to create the structure as defined below + context.turnOffAuthorisationSystem(); + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and two collections. + 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(); + Collection col2 = CollectionBuilder.createCollection(context, child1, "123456789/non-mapped-collection") + .withName("Collection 2") + .build(); + context.restoreAuthSystemState(); + + Workflow defaultWorkflow = xmlWorkflowFactory.getDefaultWorkflow(); + List allNonMappedCollections = xmlWorkflowFactory.getAllNonMappedCollectionsHandles(context); + + if (allNonMappedCollections.size() > 0) { + Collection firstNonMappedCollection = allNonMappedCollections.get(0); + + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + defaultWorkflow.getID() + + "/collections") + .param("size", "1")) + //We expect a 200 OK status + .andExpect(status().isOk()) + //Number of total workflows is equals to number of configured workflows + .andExpect(jsonPath("$.page.totalElements", is(allNonMappedCollections.size()))) + //Page size is 1 + .andExpect(jsonPath("$.page.size", is(1))) + //Page nr is 1 + .andExpect(jsonPath("$.page.number", is(0))) + //Contains only the first non-mapped collection + .andExpect(jsonPath("$._embedded.collections", Matchers.contains( + WorkflowDefinitionMatcher.matchCollectionEntry(firstNonMappedCollection.getName(), + firstNonMappedCollection.getID(), firstNonMappedCollection.getHandle()) + ))); + } + } + + @Test + public void getCollectionsOfWorkflowByName_NonDefaultWorkflow() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + //We turn off the authorization system in order to create the structure as defined below + context.turnOffAuthorisationSystem(); + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and one collection. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + // Collection with handle used in workflow.xml! + Collection col1 = CollectionBuilder.createCollection(context, child1, "123456789/workflow-test-1") + .withName("Collection 1") + .build(); + context.restoreAuthSystemState(); + + Workflow defaultWorkflow = xmlWorkflowFactory.getDefaultWorkflow(); + List allConfiguredWorkflows = xmlWorkflowFactory.getAllConfiguredWorkflows(); + String firstNonDefaultWorkflowName = ""; + for (Workflow workflow : allConfiguredWorkflows) { + if (!workflow.getID().equalsIgnoreCase(defaultWorkflow.getID())) { + firstNonDefaultWorkflowName = workflow.getID(); + } + } + + if (StringUtils.isNotBlank(firstNonDefaultWorkflowName)) { + List mappedCollections + = xmlWorkflowFactory.getCollectionHandlesMappedToWorklow(context, firstNonDefaultWorkflowName); + //When we call this facets endpoint + if (mappedCollections.size() > 0) { + //returns array of collection jsons that are mapped to given workflow + //When we call this facets endpoint + Collection firstMappedCollection = mappedCollections.get(0); + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + firstNonDefaultWorkflowName + + "/collections") + .param("size", "1")) + //We expect a 200 OK status + .andExpect(status().isOk()) + //Number of total workflows is equals to number of configured workflows + .andExpect(jsonPath("$.page.totalElements", is(mappedCollections.size()))) + //Page size is 1 + .andExpect(jsonPath("$.page.size", is(1))) + //Page nr is 1 + .andExpect(jsonPath("$.page.number", is(0))) + //Contains only the first mapped collection + .andExpect(jsonPath("$._embedded.collections", Matchers.contains( + WorkflowDefinitionMatcher.matchCollectionEntry(firstMappedCollection.getName(), + firstMappedCollection.getID(), firstMappedCollection.getHandle()) + ))); + } else { + //no collections mapped to this workflow + getClient().perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + + firstNonDefaultWorkflowName + "/collections")) + //We expect a 200 OK status + .andExpect(status().isOk()) + //results in empty list + .andExpect(jsonPath("$._embedded.collections", empty())); + } + } + } + + @Test + public void getCollectionsOfWorkflowByName_NonExistentWorkflow() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + String workflowName = "TestNameNonExistentWorkflow9999"; + + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + workflowName + "/collections")) + //We expect a 404 Not Found + .andExpect(status().isNotFound()); + } + + @Test + public void getCollectionsOfWorkflowByName_DefaultWorkflow_NoValidToken() throws Exception { + String token = "NonValidToken"; + Workflow defaultWorkflow = xmlWorkflowFactory.getDefaultWorkflow(); + + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + defaultWorkflow.getID() + + "/collections")) + //We expect a 403 Forbidden status + .andExpect(status().isForbidden()); + } + + @Test + public void getCollectionsOfWorkflowByName_DefaultWorkflow_NoToken() throws Exception { + Workflow defaultWorkflow = xmlWorkflowFactory.getDefaultWorkflow(); + List allNonMappedCollections = xmlWorkflowFactory.getAllNonMappedCollectionsHandles(context); + + //When we call this facets endpoint + getClient().perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + defaultWorkflow.getID() + + "/collections")) + //We expect a 401 Unauthorized + .andExpect(status().isUnauthorized()); + } + + @Test + public void getStepsOfWorkflowByName_DefaultWorkflow() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + Workflow defaultWorkflow = xmlWorkflowFactory.getDefaultWorkflow(); + + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + defaultWorkflow.getID() + + "/steps") + .param("projection", "full")) + //We expect a 200 OK status + .andExpect(status().isOk()) + //Number of total workflows is equals to number of non-mapped collections + .andExpect(jsonPath("$.page.totalElements", is(defaultWorkflow.getSteps().size()))); + } + + @Test + public void getStepsOfWorkflowByName_DefaultWorkflow_NoValidToken() throws Exception { + String token = "NonValidToken"; + Workflow defaultWorkflow = xmlWorkflowFactory.getDefaultWorkflow(); + + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + defaultWorkflow.getID() + + "/steps")) + //We expect a 403 Forbidden status + .andExpect(status().isForbidden()); + } + + @Test + public void getStepsOfWorkflowByName_DefaultWorkflow_NoToken() throws Exception { + Workflow defaultWorkflow = xmlWorkflowFactory.getDefaultWorkflow(); + List allNonMappedCollections = xmlWorkflowFactory.getAllNonMappedCollectionsHandles(context); + + //When we call this facets endpoint + getClient().perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + defaultWorkflow.getID() + + "/steps")) + //We expect a 401 Unauthorized + .andExpect(status().isUnauthorized()); + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java index af2d6b6c53..373d5fe4d2 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java @@ -458,8 +458,7 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT String authToken = getAuthToken(admin.getEmail(), password); - getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID() + "/collection") - .param("projection", "full")) + getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID() + "/collection")) .andExpect(status().isOk()) .andExpect(jsonPath("$", Matchers .is(CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle())))); @@ -767,6 +766,7 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT // submit the workspaceitem to start the workflow getClient(authToken) .perform(post(BASE_REST_SERVER_URL + "/api/workflow/workflowitems") + .param("projection", "full") .content("/api/submission/workspaceitems/" + wsitem.getID()) .contentType(textUriContentType)) .andExpect(status().isCreated()) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowStepRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowStepRestRepositoryIT.java new file mode 100644 index 0000000000..a9a5b12d94 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowStepRestRepositoryIT.java @@ -0,0 +1,87 @@ +/** + * 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.app.rest; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.dspace.app.rest.matcher.WorkflowStepMatcher; +import org.dspace.app.rest.model.WorkflowStepRest; +import org.dspace.app.rest.repository.WorkflowStepRestRepository; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; +import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory; +import org.dspace.xmlworkflow.state.Step; +import org.hamcrest.Matchers; +import org.junit.Test; + +/** + * Integration tests for the {@link WorkflowStepRestRepository} controlled endpoints + * + * @author Maria Verdonck (Atmire) on 13/01/2020 + */ +public class WorkflowStepRestRepositoryIT extends AbstractControllerIntegrationTest { + + private XmlWorkflowFactory xmlWorkflowFactory = XmlWorkflowServiceFactory.getInstance().getWorkflowFactory(); + + private static final String WORKFLOW_ACTIONS_ENDPOINT + = "/api/" + WorkflowStepRest.CATEGORY + "/" + WorkflowStepRest.NAME_PLURAL; + + @Test + public void getAllWorkflowSteps_NonImplementedEndpoint() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT)) + //We expect a 405 Method not allowed status + .andExpect(status().isMethodNotAllowed()); + } + + @Test + public void getAllWorkflowSteps_NonImplementedEndpoint_NonValidToken() throws Exception { + String token = "NonValidToken"; + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT)) + //We expect a 403 Forbidden status + .andExpect(status().isForbidden()); + } + + @Test + public void getAllWorkflowSteps_NonImplementedEndpoint_NoToken() throws Exception { + //When we call this facets endpoint + getClient().perform(get(WORKFLOW_ACTIONS_ENDPOINT)) + //We expect a 401 Unauthorized + .andExpect(status().isUnauthorized()); + } + + @Test + public void getWorkflowStepByName_NonExistentWorkflowStep() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + String nameNonExistentWorkflowActionName = "TestNameNonExistentWorkflowStep9999"; + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT + "/" + nameNonExistentWorkflowActionName)) + //We expect a 404 Not Found status + .andExpect(status().isNotFound()); + } + + @Test + public void getWorkflowStepByName_ExistentStep_reviewstep() throws Exception { + String token = getAuthToken(eperson.getEmail(), password); + String nameStep = "reviewstep"; + Step existentStep = xmlWorkflowFactory.getStepByName(nameStep); + //When we call this facets endpoint + getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT + "/" + nameStep) + .param("projection", "full")) + //We expect a 200 is ok status + .andExpect(status().isOk()) + //Matches expected step + .andExpect(jsonPath("$", Matchers.is( + WorkflowStepMatcher.matchWorkflowStepEntry(existentStep) + ))); + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java index 448fd06bbd..c1408100f0 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java @@ -8,6 +8,7 @@ package org.dspace.app.rest; import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.springframework.data.rest.webmvc.RestMediaTypes.TEXT_URI_LIST_VALUE; import static org.springframework.http.MediaType.parseMediaType; @@ -35,6 +36,7 @@ import org.dspace.app.rest.builder.BitstreamBuilder; import org.dspace.app.rest.builder.CollectionBuilder; import org.dspace.app.rest.builder.CommunityBuilder; import org.dspace.app.rest.builder.EPersonBuilder; +import org.dspace.app.rest.builder.GroupBuilder; import org.dspace.app.rest.builder.WorkspaceItemBuilder; import org.dspace.app.rest.matcher.CollectionMatcher; import org.dspace.app.rest.matcher.ItemMatcher; @@ -51,6 +53,8 @@ import org.dspace.content.Community; import org.dspace.content.Item; import org.dspace.content.WorkspaceItem; import org.dspace.eperson.EPerson; +import org.dspace.eperson.Group; +import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.services.ConfigurationService; import org.hamcrest.Matchers; import org.junit.Before; @@ -69,14 +73,34 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration @Autowired private ConfigurationService configurationService; + private Group embargoedGroups; + private Group embargoedGroup1; + private Group embargoedGroup2; + private Group anonymousGroup; + @Before @Override public void setUp() throws Exception { - super.setUp(); + context.turnOffAuthorisationSystem(); - //disable file upload mandatory - configurationService.setProperty("webui.submit.upload.required", false); + embargoedGroups = GroupBuilder.createGroup(context) + .withName("Embargoed Groups") + .build(); + + embargoedGroup1 = GroupBuilder.createGroup(context) + .withName("Embargoed Group 1") + .withParent(embargoedGroups) + .build(); + + embargoedGroup2 = GroupBuilder.createGroup(context) + .withName("Embargoed Group 2") + .withParent(embargoedGroups) + .build(); + + anonymousGroup = EPersonServiceFactory.getInstance().getGroupService().findByName(context, Group.ANONYMOUS); + + context.restoreAuthSystemState(); } @Test @@ -259,8 +283,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration String token = getAuthToken(admin.getEmail(), password); - getClient(token).perform(get("/api/submission/workspaceitems/" + witem.getID() + "/collection") - .param("projection", "full")) + getClient(token).perform(get("/api/submission/workspaceitems/" + witem.getID() + "/collection")) .andExpect(status().isOk()) .andExpect(jsonPath("$", Matchers .is(CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle())) @@ -468,14 +491,14 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build(); String authToken = getAuthToken(admin.getEmail(), password); - // create a workspaceitem explicitly in the col1 + // create a workspaceitem explicitly in the colAA1 getClient(authToken).perform(post("/api/submission/workspaceitems") .param("owningCollection", col1.getID().toString()) .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$._embedded.collection.id", is(col1.getID().toString()))); - // create a workspaceitem explicitly in the col2 + // create a workspaceitem explicitly in the colAA2 getClient(authToken).perform(post("/api/submission/workspaceitems") .param("owningCollection", col2.getID().toString()) .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) @@ -484,10 +507,11 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration // create a workspaceitem without an explicit collection, this will go in the first valid collection for the // user: the col1 - getClient(authToken).perform(post("/api/submission/workspaceitems") - .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) + getClient(authToken).perform(post("/api/submission/workspaceitems").param("projection", "full") + .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) - .andExpect(jsonPath("$._embedded.collection.id", is(col1.getID().toString()))); + .andExpect(jsonPath("$._embedded.collection.id", is(col1.getID().toString()))) + .andExpect(jsonPath("$", WorkspaceItemMatcher.matchFullEmbeds())); // TODO cleanup the context!!! } @@ -518,7 +542,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration final MockMultipartFile bibtexFile = new MockMultipartFile("file", "bibtex-test.bib", "application/x-bibtex", bibtex); - // bulk create workspaceitems in the default collection (col1) + // bulk create workspaceitems in the default collection (colAA1) getClient(authToken).perform(fileUpload("/api/submission/workspaceitems") .file(bibtexFile)) // bulk create should return 200, 201 (created) is better for single resource @@ -539,7 +563,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration jsonPath("$._embedded.workspaceitems[*]._embedded.upload").doesNotExist()) ; - // bulk create workspaceitems explicitly in the col2 + // bulk create workspaceitems explicitly in the colAA2 getClient(authToken).perform(fileUpload("/api/submission/workspaceitems") .file(bibtexFile) .param("collection", col2.getID().toString())) @@ -636,6 +660,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration .withIssueDate("2017-10-17") .build(); + //disable file upload mandatory + configurationService.setProperty("webui.submit.upload.required", false); + getClient(authToken).perform(get("/api/submission/workspaceitems/" + workspaceItem1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$.errors").doesNotExist()) @@ -654,7 +681,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration ))))) ; - // create an empty workspaceitem explicitly in the col1, check validation on creation + // create an empty workspaceitem explicitly in the colAA1, check validation on creation getClient(authToken).perform(post("/api/submission/workspaceitems") .param("collection", col1.getID().toString()) .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) @@ -696,6 +723,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration .withSubject("ExtraEntry") .build(); + //disable file upload mandatory + configurationService.setProperty("webui.submit.upload.required", false); + // a simple patch to update an existent metadata List updateTitle = new ArrayList(); Map value = new HashMap(); @@ -802,6 +832,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration null, "2017-10-17", "ExtraEntry")))) ; + //disable file upload mandatory + configurationService.setProperty("webui.submit.upload.required", false); + // try to remove a metadata in a specific position List removeMidSubject = new ArrayList(); removeMidSubject.add(new RemoveOperation("/sections/traditionalpagetwo/dc.subject/1")); @@ -931,6 +964,8 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration .withSubject("ExtraEntry") .build(); + //disable file upload mandatory + configurationService.setProperty("webui.submit.upload.required", false); // try to add the title List addTitle = new ArrayList(); @@ -988,6 +1023,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration .withIssueDate("2017-10-17") .build(); + //disable file upload mandatory + configurationService.setProperty("webui.submit.upload.required", false); + // try to add multiple subjects at once List addSubjects = new ArrayList(); // create a list of values to use in add operation @@ -1209,6 +1247,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration .andExpect(jsonPath("$.sections.license.url").isEmpty()) ; + //disable file upload mandatory + configurationService.setProperty("webui.submit.upload.required", false); + // try to grant the license with an add operation List addGrant = new ArrayList(); addGrant.add(new AddOperation("/sections/license/granted", true)); @@ -1358,6 +1399,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration .grantLicense() .build(); + //disable file upload mandatory + configurationService.setProperty("webui.submit.upload.required", false); + // check that our workspaceitems come with a license (all are build in the same way, just check the first) getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())) .andExpect(status().isOk()) @@ -1600,6 +1644,147 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration ; } + @Test + public void patchUploadAddAccessConditionTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + + Collection collection1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .build(); + + InputStream pdf = getClass().getResourceAsStream("simple-article.pdf"); + + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, collection1) + .withTitle("Test WorkspaceItem") + .withIssueDate("2019-10-01") + .withFulltext("simple-article.pdf", "/local/path/simple-article.pdf", pdf) + .build(); + + context.restoreAuthSystemState(); + + // create a list of values to use in add accessCondition + List addAccessCondition = new ArrayList(); + Map value = new HashMap(); + value.put("name", "embargoedWithGroupSelect"); + value.put("groupUUID", embargoedGroup1.getID().toString()); + value.put("endDate", "2030-10-02"); + addAccessCondition.add(new AddOperation("/sections/upload/files/0/accessConditions/-", value)); + + String patchBody = getPatchContent(addAccessCondition); + String authToken = getAuthToken(admin.getEmail(), password); + + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$",Matchers.allOf( + hasJsonPath("$.sections.upload.files[0].accessConditions[0].name", + is("embargoedWithGroupSelect")), + hasJsonPath("$.sections.upload.files[0].accessConditions[0].groupUUID", + is(embargoedGroup1.getID().toString())) + ))); + + // verify that the patch changes have been persisted + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$",Matchers.allOf( + hasJsonPath("$.sections.upload.files[0].accessConditions[0].name", + is("embargoedWithGroupSelect")), + hasJsonPath("$.sections.upload.files[0].accessConditions[0].groupUUID", + is(embargoedGroup1.getID().toString())) + ))); + } + + @Test + public void patchUploadRemoveAccessConditionTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + + Collection collection1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .build(); + + InputStream pdf = getClass().getResourceAsStream("simple-article.pdf"); + + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, collection1) + .withTitle("Test WorkspaceItem") + .withIssueDate("2019-10-01") + .withFulltext("simple-article.pdf", "/local/path/simple-article.pdf", pdf) + .build(); + + context.restoreAuthSystemState(); + + // create a list of values to use in add operation + List addAccessCondition = new ArrayList(); + Map value = new HashMap(); + value.put("name", "embargoedWithGroupSelect"); + value.put("groupUUID", embargoedGroup1.getID().toString()); + value.put("endDate", "2020-01-01"); + addAccessCondition.add(new AddOperation("/sections/upload/files/0/accessConditions/-", value)); + + String patchBody = getPatchContent(addAccessCondition); + String authToken = getAuthToken(admin.getEmail(), password); + + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$",Matchers.allOf( + hasJsonPath("$.sections.upload.files[0].accessConditions[0].name", + is("embargoedWithGroupSelect")), + hasJsonPath("$.sections.upload.files[0].accessConditions[0].endDate", + is("2020-01-01")), + hasJsonPath("$.sections.upload.files[0].accessConditions[0].groupUUID", + is(embargoedGroup1.getID().toString())) + ))); + + // verify that the patch changes have been persisted + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$",Matchers.allOf( + hasJsonPath("$.sections.upload.files[0].accessConditions[0].name", + is("embargoedWithGroupSelect")), + hasJsonPath("$.sections.upload.files[0].accessConditions[0].endDate", + is("2020-01-01")), + hasJsonPath("$.sections.upload.files[0].accessConditions[0].groupUUID", + is(embargoedGroup1.getID().toString())) + ))); + + // create a list of values to use in remove operation + List removeAccessCondition = new ArrayList(); + removeAccessCondition.add(new RemoveOperation("/sections/upload/files/0/accessConditions")); + + String patchReplaceBody = getPatchContent(removeAccessCondition); + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchReplaceBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$",Matchers.allOf( + hasJsonPath("$.sections.upload.files[0].accessConditions",hasSize(0))))); + + // verify that the patch changes have been persisted + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$",Matchers.allOf( + hasJsonPath("$.sections.upload.files[0].accessConditions", hasSize(0)) + ))); + } + @Test /** * Test the upload of files in the upload over section @@ -1671,8 +1856,6 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration .withIssueDate("2017-10-17") .build(); - configurationService.setProperty("webui.submit.upload.required", true); - InputStream pdf = getClass().getResourceAsStream("simple-article.pdf"); final MockMultipartFile pdfFile = new MockMultipartFile("file", "/local/path/simple-article.pdf", "application/pdf", pdf); @@ -1714,8 +1897,6 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration .withIssueDate("2017-10-17") .build(); - configurationService.setProperty("webui.submit.upload.required", true); - //Verify there is an error since no file was uploaded (with upload required set to true) getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID())) .andExpect(status().isOk()) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/CollectionBuilder.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/CollectionBuilder.java index ffc8f56931..269206a8ce 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/CollectionBuilder.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/CollectionBuilder.java @@ -42,6 +42,13 @@ public class CollectionBuilder extends AbstractDSpaceObjectBuilder { return builder.create(parent); } + public static CollectionBuilder createCollection(final Context context, + final Community parent, + final String handle) { + CollectionBuilder builder = new CollectionBuilder(context); + return builder.create(parent, handle); + } + private CollectionBuilder create(final Community parent) { try { this.collection = collectionService.create(context, parent); @@ -51,6 +58,20 @@ public class CollectionBuilder extends AbstractDSpaceObjectBuilder { return this; } + private CollectionBuilder create(final Community parent, final String handle) { + try { + for (Collection collection : this.collectionService.findAll(context)) { + if (collection.getHandle().equalsIgnoreCase(handle)) { + this.collection = collection; + } + } + this.collection = this.collectionService.create(context, parent, handle); + } catch (Exception e) { + return handleException(e); + } + return this; + } + public CollectionBuilder withName(final String name) { return setMetadataSingleValue(collection, MetadataSchemaEnum.DC.getName(), "title", null, name); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/CommunityBuilder.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/CommunityBuilder.java index cf46c70908..788aa502a6 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/CommunityBuilder.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/CommunityBuilder.java @@ -93,6 +93,12 @@ public class CommunityBuilder extends AbstractDSpaceObjectBuilder { return this; } + public CommunityBuilder addParentCommunity(final Context context, final Community parent) + throws SQLException, AuthorizeException { + communityService.addSubcommunity(context, parent, community); + return this; + } + @Override public Community build() { try { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/RelationshipTypeBuilder.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/RelationshipTypeBuilder.java index c03deeb7bc..995b88488c 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/RelationshipTypeBuilder.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/RelationshipTypeBuilder.java @@ -37,12 +37,15 @@ public class RelationshipTypeBuilder extends AbstractBuilder byRelationshipType = relationshipService.findByRelationshipType(context, relationshipType); - for (Relationship relationship : byRelationshipType) { - relationshipService.delete(context, relationship); + try (Context c = new Context()) { + c.turnOffAuthorisationSystem(); + List byRelationshipType = relationshipService + .findByRelationshipType(c, relationshipType); + for (Relationship relationship : byRelationshipType) { + relationshipService.delete(c, relationship); + } + c.complete(); } - context.restoreAuthSystemState(); delete(relationshipType); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/converter/DiscoverConfigurationConverterTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/converter/DiscoverConfigurationConverterTest.java index 333d624010..f69c6b85e9 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/converter/DiscoverConfigurationConverterTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/converter/DiscoverConfigurationConverterTest.java @@ -26,7 +26,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; /** * This class' purpose is to test the DiscoverConfigurationConverter diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/converter/DiscoverFacetConfigurationConverterTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/converter/DiscoverFacetConfigurationConverterTest.java index bdfdc9ef8b..e4186816bd 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/converter/DiscoverFacetConfigurationConverterTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/converter/DiscoverFacetConfigurationConverterTest.java @@ -21,7 +21,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; /** * This class has the purpose to test the DiscoverFacetConfigurationConverter diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/converter/RootConverterTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/converter/RootConverterTest.java index 46328aca66..36124f9502 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/converter/RootConverterTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/converter/RootConverterTest.java @@ -18,7 +18,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; /** * This class' purpose is to test the RootConvertor class. @@ -34,9 +34,9 @@ public class RootConverterTest { @Before public void setUp() throws Exception { - when(configurationService.getProperty("dspace.url")).thenReturn("dspaceurl"); + when(configurationService.getProperty("dspace.ui.url")).thenReturn("dspaceurl"); when(configurationService.getProperty("dspace.name")).thenReturn("dspacename"); - when(configurationService.getProperty("dspace.baseUrl")).thenReturn("rest"); + when(configurationService.getProperty("dspace.server.url")).thenReturn("rest"); } @Test diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/csv/CSVMetadataImportReferenceTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/csv/CSVMetadataImportReferenceIT.java similarity index 99% rename from dspace-server-webapp/src/test/java/org/dspace/app/rest/csv/CSVMetadataImportReferenceTest.java rename to dspace-server-webapp/src/test/java/org/dspace/app/rest/csv/CSVMetadataImportReferenceIT.java index 57a0475b63..baad6f0904 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/csv/CSVMetadataImportReferenceTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/csv/CSVMetadataImportReferenceIT.java @@ -41,7 +41,7 @@ import org.springframework.beans.factory.annotation.Autowired; * Created by: Andrew Wood * Date: 26 Jul 2019 */ -public class CSVMetadataImportReferenceTest extends AbstractEntityIntegrationTest { +public class CSVMetadataImportReferenceIT extends AbstractEntityIntegrationTest { //Common collection to utilize for test private Collection col1; diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamFormatMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamFormatMatcher.java index 057c80598f..0bde0a2c54 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamFormatMatcher.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamFormatMatcher.java @@ -75,5 +75,4 @@ public class BitstreamFormatMatcher { hasJsonPath("$.type", is("bitstreamformat")) ); } - } \ No newline at end of file diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamMatcher.java index 58840bb798..d9497f182b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamMatcher.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamMatcher.java @@ -66,6 +66,19 @@ public class BitstreamMatcher { ); } + public static Matcher matchBitstreamEntryWithoutEmbed(UUID uuid, long size) { + return allOf( + //Check ID and size + hasJsonPath("$.uuid", is(uuid.toString())), + hasJsonPath("$.sizeBytes", is((int) size)), + //Make sure we have a checksum + hasJsonPath("$.checkSum", matchChecksum()), + //Make sure we have a valid format + //Check links + matchLinks(uuid) + ); + } + private static Matcher matchChecksum() { return allOf( hasJsonPath("$.checkSumAlgorithm", not(empty())), diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/CollectionMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/CollectionMatcher.java index e65b3329ba..fcedd9b5a2 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/CollectionMatcher.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/CollectionMatcher.java @@ -33,8 +33,22 @@ public class CollectionMatcher { public static Matcher matchCollectionEntry(String name, UUID uuid, String handle, Bitstream logo) { return allOf( matchProperties(name, uuid, handle), - matchLinks(uuid), - matchLogo(logo) + matchLinks(uuid) + ); + } + + public static Matcher matchCollectionEntryFullProjection(String name, UUID uuid, String handle) { + return matchCollectionEntryFullProjection(name, uuid, handle, null); + + } + + public static Matcher matchCollectionEntryFullProjection(String name, UUID uuid, String handle, + Bitstream logo) { + return allOf( + matchProperties(name, uuid, handle), + matchLinks(uuid), + matchLogo(logo), + matchFullEmbeds() ); } @@ -56,6 +70,7 @@ public class CollectionMatcher { return matchEmbeds( "license", "logo", + "parentCommunity", "mappedItems[]" ); } @@ -70,6 +85,7 @@ public class CollectionMatcher { "license", "logo", "mappedItems", + "parentCommunity", "self" ); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/CommunityMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/CommunityMatcher.java index 9d25ee05e2..37a5cfb061 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/CommunityMatcher.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/CommunityMatcher.java @@ -32,8 +32,6 @@ public class CommunityMatcher { hasJsonPath("$.uuid", is(uuid.toString())), hasJsonPath("$.handle", is(handle)), hasJsonPath("$.type", is("community")), - hasJsonPath("$._embedded.collections", Matchers.not(Matchers.empty())), - hasJsonPath("$._embedded.logo", Matchers.not(Matchers.empty())), matchLinks(uuid) ); } @@ -54,6 +52,13 @@ public class CommunityMatcher { } public static Matcher matchCommunityEntry(String name, UUID uuid, String handle) { + return allOf( + matchProperties(name, uuid, handle), + matchLinks(uuid) + ); + } + + public static Matcher matchCommunityEntryFullProjection(String name, UUID uuid, String handle) { return allOf( matchProperties(name, uuid, handle), hasJsonPath("$._embedded.collections", Matchers.not(Matchers.empty())), @@ -81,6 +86,7 @@ public class CommunityMatcher { return matchEmbeds( "collections[]", "logo", + "parentCommunity", "subcommunities[]" ); } @@ -93,6 +99,7 @@ public class CommunityMatcher { "collections", "logo", "self", + "parentCommunity", "subcommunities" ); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RelationshipMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RelationshipMatcher.java index df7520cc83..068788a546 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RelationshipMatcher.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RelationshipMatcher.java @@ -8,6 +8,7 @@ package org.dspace.app.rest.matcher; import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; +import static org.dspace.app.rest.matcher.HalMatcher.matchEmbeds; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; @@ -48,4 +49,13 @@ public class RelationshipMatcher { RelationshipTypeMatcher.matchRelationshipTypeEntry(relationshipType)) ); } + + /** + * Gets a matcher for all expected embeds when the full projection is requested. + */ + public static Matcher matchFullEmbeds() { + return matchEmbeds( + "relationshipType" + ); + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ResoucePolicyMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ResourcePolicyMatcher.java similarity index 84% rename from dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ResoucePolicyMatcher.java rename to dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ResourcePolicyMatcher.java index 2780c2b392..6c0d77c4f5 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ResoucePolicyMatcher.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ResourcePolicyMatcher.java @@ -10,6 +10,7 @@ package org.dspace.app.rest.matcher; import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasNoJsonPath; +import static org.dspace.app.rest.matcher.HalMatcher.matchEmbeds; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; @@ -24,9 +25,9 @@ import org.hamcrest.Matcher; * @author Mykhaylo Boychuk (4science.it) * */ -public class ResoucePolicyMatcher { +public class ResourcePolicyMatcher { - private ResoucePolicyMatcher() { + private ResourcePolicyMatcher() { } public static Matcher matchResourcePolicy(ResourcePolicy resourcePolicy) { @@ -52,4 +53,15 @@ public class ResoucePolicyMatcher { ); } + /** + * Gets a matcher for all expected embeds when the full projection is requested. + */ + public static Matcher matchFullEmbeds() { + return matchEmbeds( + "eperson", + "group", + "resource" + ); + } + } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/WorkflowActionMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/WorkflowActionMatcher.java new file mode 100644 index 0000000000..69f9c501aa --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/WorkflowActionMatcher.java @@ -0,0 +1,38 @@ +/** + * 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.app.rest.matcher; + +import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +import org.dspace.app.rest.model.WorkflowActionRest; +import org.dspace.xmlworkflow.state.actions.WorkflowActionConfig; +import org.hamcrest.Matcher; + +/** + * @author Maria Verdonck (Atmire) on 06/01/2020 + */ +public class WorkflowActionMatcher { + + private static final String WORKFLOW_ACTIONS_ENDPOINT + = "/api/" + WorkflowActionRest.CATEGORY + "/" + WorkflowActionRest.NAME_PLURAL + "/"; + + private WorkflowActionMatcher() { + + } + + public static Matcher matchWorkflowActionEntry(WorkflowActionConfig workflowAction) { + return allOf( + hasJsonPath("$.id", is(workflowAction.getId())), + hasJsonPath("$.options", is(workflowAction.getOptions())), + hasJsonPath("$._links.self.href", containsString(WORKFLOW_ACTIONS_ENDPOINT + workflowAction.getId())) + ); + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/WorkflowDefinitionMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/WorkflowDefinitionMatcher.java new file mode 100644 index 0000000000..2dd10f6b8c --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/WorkflowDefinitionMatcher.java @@ -0,0 +1,65 @@ +/** + * 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.app.rest.matcher; + +import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +import java.util.UUID; + +import org.dspace.app.rest.model.WorkflowDefinitionRest; +import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; +import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory; +import org.dspace.xmlworkflow.state.Workflow; + +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; + +/** + * @author Maria Verdonck (Atmire) on 03/01/2020 + */ +public class WorkflowDefinitionMatcher { + + private static XmlWorkflowFactory xmlWorkflowFactory = XmlWorkflowServiceFactory.getInstance().getWorkflowFactory(); + + private static final String WORKFLOW_DEFINITIONS_ENDPOINT + = "/api/" + WorkflowDefinitionRest.CATEGORY + "/" + WorkflowDefinitionRest.NAME_PLURAL + "/"; + + private WorkflowDefinitionMatcher() { + } + + public static Matcher matchWorkflowDefinitionEntry(Workflow workflow) { + return allOf( + hasJsonPath("$.name", is(workflow.getID())), + hasJsonPath("$.isDefault", is(xmlWorkflowFactory.isDefaultWorkflow(workflow.getID()))), + hasJsonPath("$._links.self.href", containsString(WORKFLOW_DEFINITIONS_ENDPOINT + workflow.getID())) + ); + } + + public static Matcher matchWorkflowOnWorkflowName(String workflowName) { + return allOf( + hasJsonPath("$.name", is(workflowName)), + hasJsonPath("$.isDefault", is(xmlWorkflowFactory.isDefaultWorkflow(workflowName))), + hasJsonPath("$._links.self.href", containsString(WORKFLOW_DEFINITIONS_ENDPOINT + workflowName)) + ); + } + + public static Matcher matchCollectionEntry(String name, UUID uuid, String handle) { + return allOf( + hasJsonPath("$.uuid", is(uuid.toString())), + hasJsonPath("$.name", is(name)), + hasJsonPath("$.handle", is(handle)), + hasJsonPath("$.type", is("collection")), + hasJsonPath("$.metadata", Matchers.allOf( + MetadataMatcher.matchMetadata("dc.title", name) + )) + ); + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/WorkflowStepMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/WorkflowStepMatcher.java new file mode 100644 index 0000000000..2dc37d3586 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/WorkflowStepMatcher.java @@ -0,0 +1,46 @@ +/** + * 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.app.rest.matcher; + +import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +import java.util.stream.Collectors; + +import org.dspace.app.rest.model.WorkflowStepRest; +import org.dspace.xmlworkflow.state.Step; +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; + +/** + * @author Maria Verdonck (Atmire) on 13/01/2020 + */ +public class WorkflowStepMatcher { + + private static final String WORKFLOW_ACTIONS_ENDPOINT + = "/api/" + WorkflowStepRest.CATEGORY + "/" + WorkflowStepRest.NAME_PLURAL + "/";; + + private WorkflowStepMatcher() {} + + public static Matcher matchWorkflowStepEntry(Step workflowStep) { + return allOf( + hasJsonPath("$.id", is(workflowStep.getId())), + hasJsonPath("$._links.self.href", containsString(WORKFLOW_ACTIONS_ENDPOINT + workflowStep.getId())), + hasJsonPath("$._embedded.workflowactions._embedded.workflowactions", Matchers.containsInAnyOrder( + workflowStep.getActions() + .stream() + .map(x -> WorkflowActionMatcher.matchWorkflowActionEntry(x)) + .collect(Collectors.toList()) + )) + ); + } + + +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/WorkspaceItemMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/WorkspaceItemMatcher.java index 24711e157e..d2d2491171 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/WorkspaceItemMatcher.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/WorkspaceItemMatcher.java @@ -9,6 +9,7 @@ package org.dspace.app.rest.matcher; import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasNoJsonPath; +import static org.dspace.app.rest.matcher.HalMatcher.matchEmbeds; import static org.dspace.app.rest.test.AbstractControllerIntegrationTest.REST_SERVER_URL; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.is; @@ -127,4 +128,16 @@ public class WorkspaceItemMatcher { hasJsonPath("$._links.submissionDefinition.href", startsWith(REST_SERVER_URL))); } } + + /** + * Gets a matcher for all expected embeds when the full projection is requested. + */ + public static Matcher matchFullEmbeds() { + return matchEmbeds( + "collection", + "item", + "submitter", + "submissionDefinition" + ); + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/projection/MockProjection.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/projection/MockProjection.java index 0c8c976a03..b34c945065 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/projection/MockProjection.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/projection/MockProjection.java @@ -12,6 +12,7 @@ import javax.annotation.Nullable; import org.dspace.app.rest.model.LinkRest; import org.dspace.app.rest.model.MockObject; import org.dspace.app.rest.model.MockObjectRest; +import org.dspace.app.rest.model.RestAddressableModel; import org.dspace.app.rest.model.RestModel; import org.dspace.app.rest.model.hateoas.HALResource; import org.springframework.hateoas.Link; @@ -87,8 +88,9 @@ public class MockProjection implements Projection { return halResource; } - public boolean allowEmbedding(HALResource halResource, LinkRest linkRest) { - return true; + public boolean allowEmbedding(HALResource halResource, LinkRest linkRest, + Link... oldLinks) { + return halResource.getContent().getEmbedLevel() < 2; } public boolean allowLinking(HALResource halResource, LinkRest linkRest) { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/EPersonRestAuthenticationProviderTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/EPersonRestAuthenticationProviderTest.java index 74c2adde4b..7b1939c0ae 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/EPersonRestAuthenticationProviderTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/EPersonRestAuthenticationProviderTest.java @@ -21,7 +21,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import org.springframework.security.core.GrantedAuthority; /** @@ -65,4 +65,4 @@ public class EPersonRestAuthenticationProviderTest { } -} \ No newline at end of file +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/jwt/EPersonClaimProviderTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/jwt/EPersonClaimProviderTest.java index 895baec162..8ca6bbcc3b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/jwt/EPersonClaimProviderTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/jwt/EPersonClaimProviderTest.java @@ -8,7 +8,7 @@ package org.dspace.app.rest.security.jwt; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -26,7 +26,7 @@ import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; /** * @author Frederic Van Reet (frederic dot vanreet at atmire dot com) @@ -80,4 +80,4 @@ public class EPersonClaimProviderTest { assertEquals(parsed.getID(), UUID.fromString("c3bae216-a481-496b-a524-7df5aabdb609")); } -} \ No newline at end of file +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/jwt/JWTTokenHandlerTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/jwt/JWTTokenHandlerTest.java index 5cba14b658..0fabd2dbd4 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/jwt/JWTTokenHandlerTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/jwt/JWTTokenHandlerTest.java @@ -8,7 +8,7 @@ package org.dspace.app.rest.security.jwt; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import java.text.ParseException; @@ -32,7 +32,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.security.crypto.keygen.KeyGenerators; import org.springframework.security.crypto.keygen.StringKeyGenerator; @@ -135,4 +135,4 @@ public class JWTTokenHandlerTest { assertEquals(null, parsed); } -} \ No newline at end of file +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/jwt/SpecialGroupClaimProviderTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/jwt/SpecialGroupClaimProviderTest.java index 9a41409cb2..42ac74be85 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/jwt/SpecialGroupClaimProviderTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/jwt/SpecialGroupClaimProviderTest.java @@ -9,7 +9,7 @@ package org.dspace.app.rest.security.jwt; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import java.util.ArrayList; import java.util.List; @@ -25,7 +25,7 @@ import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; /** * @author Frederic Van Reet (frederic dot vanreet at atmire dot com) @@ -84,4 +84,4 @@ public class SpecialGroupClaimProviderTest { } -} \ No newline at end of file +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/utils/MultipartFileSenderTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/utils/MultipartFileSenderTest.java index c2665f4145..e800b021e9 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/utils/MultipartFileSenderTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/utils/MultipartFileSenderTest.java @@ -9,7 +9,7 @@ package org.dspace.app.rest.utils; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -533,4 +533,4 @@ public class MultipartFileSenderTest { } -} \ No newline at end of file +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/discovery/MockSolrSearchCore.java b/dspace-server-webapp/src/test/java/org/dspace/discovery/MockSolrSearchCore.java index 0aca4ea355..06722f3d2c 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/discovery/MockSolrSearchCore.java +++ b/dspace-server-webapp/src/test/java/org/dspace/discovery/MockSolrSearchCore.java @@ -18,7 +18,7 @@ import org.springframework.stereotype.Service; *

    * NOTE: this class overrides one of the same name * defined in dspace-api and declared as a bean there. - * See {@code config/spring/api/Z-mock-services.xml}. Some kind of classpath + * See {@code test/data/dspaceFolder/config/spring/api/solr-services.xml}. Some kind of classpath * magic makes this work. */ @Service diff --git a/dspace-server-webapp/src/test/java/org/dspace/identifier/MockDOIConnector.java b/dspace-server-webapp/src/test/java/org/dspace/identifier/MockDOIConnector.java deleted file mode 100644 index 73952c4af1..0000000000 --- a/dspace-server-webapp/src/test/java/org/dspace/identifier/MockDOIConnector.java +++ /dev/null @@ -1,124 +0,0 @@ -/** - * 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.identifier; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import mockit.Mock; -import mockit.MockUp; -import org.dspace.content.DSpaceObject; -import org.dspace.core.Context; -import org.dspace.identifier.doi.DOIConnector; -import org.dspace.identifier.doi.DOIIdentifierException; - -/** - * @author Pascal-Nicolas Becker (p dot becker at tu hyphen berlin dot de) - */ -public class MockDOIConnector - extends MockUp - implements DOIConnector { - - public Map reserved; - public Map registered; - - public MockDOIConnector() { - reserved = new HashMap(); - registered = new HashMap(); - } - - public void reset() { - reserved.clear(); - registered.clear(); - } - - @Override - @Mock - public boolean isDOIReserved(Context context, String doi) - throws DOIIdentifierException { - return reserved.containsKey(doi); - } - - @Override - @Mock - public boolean isDOIRegistered(Context context, String doi) - throws DOIIdentifierException { - return registered.containsKey(doi); - } - - @Override - @Mock - public void deleteDOI(Context context, String doi) - throws DOIIdentifierException { - if (reserved.remove(doi) == null) { - throw new DOIIdentifierException("Trying to delete a DOI that was " - + "never reserved!", DOIIdentifierException.DOI_DOES_NOT_EXIST); - } - registered.remove(doi); - } - - @Override - @Mock - public void reserveDOI(Context context, DSpaceObject dso, String doi) - throws DOIIdentifierException { - UUID itemId = reserved.get(doi); - if (null != itemId) { - if (dso.getID().equals(itemId)) { - return; - } else { - throw new DOIIdentifierException("Trying to reserve a DOI that " - + "is reserved for another object.", - DOIIdentifierException.MISMATCH); - } - } - reserved.put(doi, dso.getID()); - } - - @Override - @Mock - public void registerDOI(Context context, DSpaceObject dso, String doi) - throws DOIIdentifierException { - if (!reserved.containsKey(doi)) { - throw new DOIIdentifierException("Trying to register an unreserverd " - + "DOI.", DOIIdentifierException.RESERVE_FIRST); - } - - if (!reserved.get(doi).equals(dso.getID())) { - throw new DOIIdentifierException("Trying to register a DOI that is" - + " reserved for another item.", DOIIdentifierException.MISMATCH); - } - - if (registered.containsKey(doi)) { - if (registered.get(doi).equals(dso.getID())) { - return; - } else { - throw new DOIIdentifierException("Trying to register a DOI that " - + "is registered for another item.", - DOIIdentifierException.MISMATCH); - } - } - - registered.put(doi, dso.getID()); - } - - @Override - @Mock - public void updateMetadata(Context context, DSpaceObject dso, String doi) - throws DOIIdentifierException { - if (!reserved.containsKey(doi)) { - throw new DOIIdentifierException("Trying to update a DOI that is not " - + "registered!", DOIIdentifierException.DOI_DOES_NOT_EXIST); - } - if (!reserved.get(doi).equals(dso.getID())) { - throw new DOIIdentifierException("Trying to update metadata of an " - + "unreserved DOI.", DOIIdentifierException.DOI_DOES_NOT_EXIST); - } - } - -} \ No newline at end of file diff --git a/dspace-server-webapp/src/test/java/org/dspace/statistics/FakeDatabaseReader.java b/dspace-server-webapp/src/test/java/org/dspace/statistics/FakeDatabaseReader.java deleted file mode 100644 index 47628451e2..0000000000 --- a/dspace-server-webapp/src/test/java/org/dspace/statistics/FakeDatabaseReader.java +++ /dev/null @@ -1,141 +0,0 @@ -/** - * 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.statistics; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import com.maxmind.geoip2.DatabaseReader; -import com.maxmind.geoip2.model.CityResponse; -import com.maxmind.geoip2.record.City; -import com.maxmind.geoip2.record.Continent; -import com.maxmind.geoip2.record.Country; -import com.maxmind.geoip2.record.Location; -import com.maxmind.geoip2.record.MaxMind; -import com.maxmind.geoip2.record.Postal; -import com.maxmind.geoip2.record.RepresentedCountry; -import com.maxmind.geoip2.record.Subdivision; -import com.maxmind.geoip2.record.Traits; -import mockit.Deencapsulation; -import mockit.Mock; -import mockit.MockUp; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -/** - * Mock service to mock the location Lookup Service used by the SOLR statistics - * logger. - */ -public class FakeDatabaseReader - extends MockUp { - private static final Logger LOG = LogManager.getLogger(); - - FakeDatabaseReader() { - } - - private FakeDatabaseReader(Builder builder) { - LOG.debug("constructor({})", () -> builder.toString()); - } - - public FakeDatabaseReader $init(Builder builder) { - LOG.debug("$init({})", () -> builder.toString()); - return this; - } - - /* - @Override - public Location getLocation(String str) { - Location location = new Location(); - location.countryCode = "US"; - location.countryName = "United States"; - location.region = "NY"; - location.city = "New York"; - location.postalCode = "10036"; - location.latitude = 40.760498F; - location.longitude = -73.9933F; - location.dma_code = 501; - location.area_code = 212; - location.metro_code = 501; - - return location; - } - */ - - @Mock - public CityResponse city(InetAddress address) { - LOG.debug("city({})", () -> address.toString()); - - List names = new ArrayList<>(1); - - names.add("New York"); - City city = new City(names, 1, 1, new HashMap()); - - Continent continent = new Continent(); - - names.clear(); - names.add("United States"); - Country country = new Country(names, 1, 1, "US", new HashMap()); - - Location location = new Location(1, 1, 40.760498D, -73.9933D, 501, 1, "EST"); - - MaxMind maxmind = new MaxMind(); - - Postal postal = new Postal("10036", 1); - - RepresentedCountry representedCountry = new RepresentedCountry(); - - ArrayList subdivisions = new ArrayList<>(0); - - Traits traits = new Traits(); - - CityResponse response = new CityResponse(city, continent, country, - location, maxmind, postal, country, representedCountry, - subdivisions, traits); - return response; - } - - public static class Builder - extends MockUp { - - public Builder() {} - - /** - * Fake constructor. - * @param stream ignored. - */ - @Mock - public void $init(InputStream stream) { - LOG.debug("Builder.$init(\"{}\")", () -> stream.toString()); - } - - /** - * Fake constructor. - * @param file ignored. - */ - @Mock - public void $init(File file) { - try { - LOG.debug("Builder.$init(\"{}\")", file.getCanonicalPath()); - } catch (IOException e) { - LOG.warn("Cannot getCanonicalPath(file): {}", e.getMessage(), e); - } - } - - @Mock - public DatabaseReader build() - throws IOException { - LOG.debug("build"); - return Deencapsulation.newUninitializedInstance(DatabaseReader.class); - } - } -} diff --git a/dspace-server-webapp/src/test/java/org/dspace/statistics/MockSolrLoggerServiceImpl.java b/dspace-server-webapp/src/test/java/org/dspace/statistics/MockSolrLoggerServiceImpl.java index 556dac7dda..245bb8f86f 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/statistics/MockSolrLoggerServiceImpl.java +++ b/dspace-server-webapp/src/test/java/org/dspace/statistics/MockSolrLoggerServiceImpl.java @@ -7,20 +7,29 @@ */ package org.dspace.statistics; -import java.io.File; -import java.io.IOException; -import java.sql.SQLException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; -import javax.servlet.http.HttpServletRequest; - import com.maxmind.geoip2.DatabaseReader; +import com.maxmind.geoip2.model.CityResponse; +import com.maxmind.geoip2.record.City; +import com.maxmind.geoip2.record.Continent; +import com.maxmind.geoip2.record.Country; +import com.maxmind.geoip2.record.Location; +import com.maxmind.geoip2.record.MaxMind; +import com.maxmind.geoip2.record.Postal; +import com.maxmind.geoip2.record.RepresentedCountry; +import com.maxmind.geoip2.record.Traits; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.dspace.content.DSpaceObject; -import org.dspace.eperson.EPerson; import org.dspace.solr.MockSolrServer; -import org.dspace.usage.UsageWorkflowEvent; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Service; @@ -31,7 +40,7 @@ import org.springframework.stereotype.Service; *

    * NOTE: this class overrides one of the same name * defined in dspace-api and declared as a bean there. - * See {@code config/spring/api/Z-mock-services.xml}. Some kind of classpath + * See {@code test/data/dspaceFolder/config/spring/api/solr-services.xml}. Some kind of classpath * magic makes this work. */ @Service @@ -51,41 +60,33 @@ public class MockSolrLoggerServiceImpl // Initialize our service with a Mock Solr statistics core mockSolrServer = new MockSolrServer("statistics"); solr = mockSolrServer.getSolrServer(); + + // Mock GeoIP's DatabaseReader + DatabaseReader reader = mock(DatabaseReader.class); + // Ensure that any tests requesting a city() get a mock/fake CityResponse + when(reader.city(any(InetAddress.class))).thenReturn(mockCityResponse()); + // Save this mock DatabaseReader to be used by SolrLoggerService + locationService = reader; } - @Override - public void postView(DSpaceObject dspaceObject, HttpServletRequest request, - EPerson currentUser) { - // Load our FakeDatabaseReader before each view event is logged - loadFakeDatabaseReader(); - // Call overridden method - super.postView(dspaceObject, request, currentUser); - } + /** + * A mock/fake GeoIP CityResponse, which will be used for *all* test statistical requests + * @return faked CityResponse + */ + private CityResponse mockCityResponse() { + List cityNames = new ArrayList(Collections.singleton("New York")); + City city = new City(cityNames, 1, 1, new HashMap()); - @Override - public void postView(DSpaceObject dspaceObject, - String ip, String userAgent, String xforwardedfor, EPerson currentUser) { - // Load our FakeDatabaseReader before each view event is logged - loadFakeDatabaseReader(); - // Call overridden method - super.postView(dspaceObject, ip, userAgent, xforwardedfor, currentUser); - } + List countryNames = new ArrayList(Collections.singleton("United States")); + Country country = new Country(countryNames, 1, 1, "US", new HashMap()); - @Override - public void postSearch(DSpaceObject resultObject, HttpServletRequest request, EPerson currentUser, - List queries, int rpp, String sortBy, String order, int page, DSpaceObject scope) { - // Load our FakeDatabaseReader before each search event is logged - loadFakeDatabaseReader(); - // Call overridden method - super.postSearch(resultObject, request, currentUser, queries, rpp, sortBy, order, page, scope); - } + Location location = new Location(1, 1, 40.760498D, -73.9933D, 501, 1, "EST"); - @Override - public void postWorkflow(UsageWorkflowEvent usageWorkflowEvent) throws SQLException { - // Load our FakeDatabaseReader before each workflow event is logged - loadFakeDatabaseReader(); - // Call overridden method - super.postWorkflow(usageWorkflowEvent); + Postal postal = new Postal("10036", 1); + + return new CityResponse(city, new Continent(), country, location, new MaxMind(), postal, + country, new RepresentedCountry(), new ArrayList<>(0), + new Traits()); } /** Remove all records. */ @@ -97,22 +98,4 @@ public class MockSolrLoggerServiceImpl public void destroy() throws Exception { mockSolrServer.destroy(); } - - /** - * Load / activate our FakeDatabaseReader which uses JMockit to replace specific - * methods of the DatabaseReader. This MUST be called for each method that uses - * DatabaseReader as JMockit fake classes are only in effect for tests in which - * they are defined: http://jmockit.github.io/tutorial/Faking.html - */ - private void loadFakeDatabaseReader() { - try { - new FakeDatabaseReader(); // Activate fake - new FakeDatabaseReader.Builder(); // Activate fake - File locationDb = File.createTempFile("GeoIP", ".mmdb"); - locationDb.deleteOnExit(); - locationService = new DatabaseReader.Builder(locationDb).build(); - } catch (IOException e) { - log.error("Unable to load FakeDatabaseReader", e); - } - } } diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 45c74448d7..a37cebc8fe 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -44,23 +44,33 @@ org.codehaus.gmaven groovy-maven-plugin - 2.0 setproperty - generate-test-resources - + initialize execute project.properties['agnostic.build.dir'] = project.build.directory.replace(File.separator, '/'); - println("Initializing Maven property 'agnostic.build.dir' to: " + project.properties['agnostic.build.dir']); + log.info("Initializing Maven property 'agnostic.build.dir' to: {}", project.properties['agnostic.build.dir']); + + @@ -69,6 +79,7 @@ + ${agnostic.build.dir}/test-classes/ @@ -109,9 +120,9 @@ javax.mail mail - - org.jmockit - jmockit + + org.mockito + mockito-core test @@ -146,10 +157,14 @@ commons-beanutils commons-beanutils + + + commons-io + commons-io + javax.annotation javax.annotation-api - 1.3.2 diff --git a/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceEnvironmentConfiguration.java b/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceEnvironmentConfiguration.java index e7e6cf894c..fcfe1ccbe1 100644 --- a/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceEnvironmentConfiguration.java +++ b/dspace-services/src/main/java/org/dspace/servicemanager/config/DSpaceEnvironmentConfiguration.java @@ -19,7 +19,7 @@ import org.slf4j.LoggerFactory; * Bash does not allow environment variables that contain dots in their name. * This Configuration loads environment variables that contains two underlines * and replaces "__P__" -> "." and "__D__" -> "-" - * E.g.: dspace__P__baseUrl will be read as dspace.baseUrl. + * E.g.: dspace__P__dir will be read as dspace.dir. * E.g.: my__D__dspace__P__prop will be read as my-dspace.prop. * * Most of this file was copied from org.apache.commons.configuration2.EnvironmentConfiguration. diff --git a/dspace-services/src/test/java/org/dspace/servicemanager/config/DSpaceConfigurationServiceTest.java b/dspace-services/src/test/java/org/dspace/servicemanager/config/DSpaceConfigurationServiceTest.java index e57b05ea50..b56b990b56 100644 --- a/dspace-services/src/test/java/org/dspace/servicemanager/config/DSpaceConfigurationServiceTest.java +++ b/dspace-services/src/test/java/org/dspace/servicemanager/config/DSpaceConfigurationServiceTest.java @@ -12,17 +12,20 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import java.io.File; +import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Properties; -import mockit.Expectations; import org.apache.commons.configuration2.PropertiesConfiguration; import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder; import org.apache.commons.configuration2.builder.fluent.Configurations; import org.apache.commons.configuration2.ex.ConfigurationException; +import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -523,7 +526,7 @@ public class DSpaceConfigurationServiceTest { * of time. */ @Test - public void testAutomaticReload() throws ConfigurationException, InterruptedException { + public void testAutomaticReload() throws ConfigurationException, IOException, InterruptedException { // Initialize new config service DSpaceConfigurationService dscs = new DSpaceConfigurationService(); @@ -531,6 +534,10 @@ public class DSpaceConfigurationServiceTest { assertNotNull(dscs.getProperty("prop.to.auto.reload")); assertEquals("D-space", dscs.getProperty("prop.to.auto.reload")); + // Copy our test local.properties file to a temp location (so we can restore it after tests below) + File tempPropFile = File.createTempFile("temp", "properties"); + FileUtils.copyFile(new File(propertyFilePath), tempPropFile); + // Now, change the value of that Property in the file itself (using a separate builder instance) FileBasedConfigurationBuilder builder = new Configurations() .propertiesBuilder(propertyFilePath); @@ -538,7 +545,7 @@ public class DSpaceConfigurationServiceTest { // Clear out current value. Add in a new value config.clearProperty("prop.to.auto.reload"); config.addProperty("prop.to.auto.reload", "DSpace"); - // Save updates to file + // Save updates to file (this changes our test local.properties) builder.save(); // Check immediately. Property should be unchanged @@ -551,6 +558,9 @@ public class DSpaceConfigurationServiceTest { // Check again. Property should have reloaded // NOTE: reload time is set in config-definition.xml to reload every 2 seconds assertEquals("DSpace", dscs.getProperty("prop.to.auto.reload")); + + // Restore our test local.properties file to original content + FileUtils.copyFile(tempPropFile, new File(propertyFilePath)); } /** @@ -605,60 +615,58 @@ public class DSpaceConfigurationServiceTest { */ @Test public void testGetDSpaceHomeSysProperty() { - final DSpaceConfigurationService dscs = new DSpaceConfigurationService(); + // Capture current value of DSPACE_HOME (so we can reset it after test) + String previousValue = System.getProperty(DSpaceConfigurationService.DSPACE_HOME); + // Change to a mocked value + System.setProperty(DSpaceConfigurationService.DSPACE_HOME, "/mydspace"); - // Set System Property for DSpace Home - new Expectations(System.class) {{ - // return "/mydspace" two times - System.getProperty(DSpaceConfigurationService.DSPACE_HOME); - result = "/mydspace"; - }}; - // Ensure /mydspace looks like a valid DSpace home directory - new Expectations(dscs.getClass()) {{ - dscs.isValidDSpaceHome("/mydspace"); - result = true; - }}; + // Create a spy of our loaded configurationService, and tell it to return true + // when "isValidDSpaceHome()" is called with "/mydspace" + DSpaceConfigurationService spy = spy(configurationService); + when(spy.isValidDSpaceHome("/mydspace")).thenReturn(true); // Assert Home is the same as System Property - assertEquals("System property set", "/mydspace", dscs.getDSpaceHome(null)); + assertEquals("System property set", "/mydspace", spy.getDSpaceHome(null)); + + // reset DSPACE_HOME to previous value + System.setProperty(DSpaceConfigurationService.DSPACE_HOME, previousValue); } @Test public void testGetDSpaceHomeSysPropertyOverride() { - final DSpaceConfigurationService dscs = new DSpaceConfigurationService(); + // Capture current value of DSPACE_HOME (so we can reset it after test) + String previousValue = System.getProperty(DSpaceConfigurationService.DSPACE_HOME); + // Change to a mocked value + System.setProperty(DSpaceConfigurationService.DSPACE_HOME, "/mydspace"); - // Set System Property for DSpace Home - new Expectations(System.class) {{ - System.getProperty(DSpaceConfigurationService.DSPACE_HOME); - result = "/mydspace"; - }}; - // Ensure /mydspace looks like a valid DSpace home directory - new Expectations(dscs.getClass()) {{ - dscs.isValidDSpaceHome("/mydspace"); - result = true; - }}; + // Create a spy of our loaded configurationService, and tell it to return true + // when "isValidDSpaceHome()" is called with "/mydspace" + DSpaceConfigurationService spy = spy(configurationService); + when(spy.isValidDSpaceHome("/mydspace")).thenReturn(true); // Assert System Property overrides the value passed in, if it is valid - assertEquals("System property override", "/mydspace", dscs.getDSpaceHome("/myotherdspace")); + assertEquals("System property override", "/mydspace", spy.getDSpaceHome("/myotherdspace")); + + // reset DSPACE_HOME to previous value + System.setProperty(DSpaceConfigurationService.DSPACE_HOME, previousValue); } @Test public void testGetDSpaceHomeNoSysProperty() { + // Capture current value of DSPACE_HOME (so we can reset it after test) + String previousValue = System.getProperty(DSpaceConfigurationService.DSPACE_HOME); + // Clear the value + System.clearProperty(DSpaceConfigurationService.DSPACE_HOME); - final DSpaceConfigurationService dscs = new DSpaceConfigurationService(); - - // No system property set - new Expectations(System.class) {{ - System.getProperty(DSpaceConfigurationService.DSPACE_HOME); - result = null; - }}; - // Ensure /mydspace looks like a valid DSpace home directory - new Expectations(dscs.getClass()) {{ - dscs.isValidDSpaceHome("/mydspace"); - result = true; - }}; + // Create a spy of our loaded configurationService, and tell it to return true + // when "isValidDSpaceHome()" is called with "/mydspace" + DSpaceConfigurationService spy = spy(configurationService); + when(spy.isValidDSpaceHome("/mydspace")).thenReturn(true); // Assert provided home is used - assertEquals("Home based on passed in value", "/mydspace", dscs.getDSpaceHome("/mydspace")); + assertEquals("Home based on passed in value", "/mydspace", spy.getDSpaceHome("/mydspace")); + + // reset DSPACE_HOME to previous value + System.setProperty(DSpaceConfigurationService.DSPACE_HOME, previousValue); } } diff --git a/dspace-services/src/test/java/org/dspace/test/DSpaceAbstractTest.java b/dspace-services/src/test/java/org/dspace/test/DSpaceAbstractTest.java index c0ce424f08..46a0824876 100644 --- a/dspace-services/src/test/java/org/dspace/test/DSpaceAbstractTest.java +++ b/dspace-services/src/test/java/org/dspace/test/DSpaceAbstractTest.java @@ -46,8 +46,11 @@ public abstract class DSpaceAbstractTest { * do not run this after each individual test */ public static void _initializeKernel() { + // Init the Kernel and start it + // NOTE: the "dspace.dir" system property MUST be specified via Maven + // For example: by using of maven-surefire-plugin or maven-failsafe-plugin kernelImpl = DSpaceKernelInit.getKernel(null); - kernelImpl.start(); // init the kernel + kernelImpl.start(); kernel = kernelImpl.getManagedBean(); } diff --git a/dspace-services/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/dspace-services/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..1f0955d450 --- /dev/null +++ b/dspace-services/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline diff --git a/dspace-sword/src/main/java/org/dspace/sword/BitstreamEntryGenerator.java b/dspace-sword/src/main/java/org/dspace/sword/BitstreamEntryGenerator.java index 14e65d81e1..fe0962ca48 100644 --- a/dspace-sword/src/main/java/org/dspace/sword/BitstreamEntryGenerator.java +++ b/dspace-sword/src/main/java/org/dspace/sword/BitstreamEntryGenerator.java @@ -106,7 +106,7 @@ public class BitstreamEntryGenerator extends DSpaceATOMEntry { if (this.deposit != null && this.deposit.isNoOp()) { // just use the dspace url as the // property - String cfg = ConfigurationManager.getProperty("dspace.url"); + String cfg = ConfigurationManager.getProperty("dspace.ui.url"); entry.setId(cfg); return; diff --git a/dspace-sword/src/main/java/org/dspace/sword/CollectionLocation.java b/dspace-sword/src/main/java/org/dspace/sword/CollectionLocation.java index caa9a1fdca..555ee1b97d 100644 --- a/dspace-sword/src/main/java/org/dspace/sword/CollectionLocation.java +++ b/dspace-sword/src/main/java/org/dspace/sword/CollectionLocation.java @@ -97,9 +97,9 @@ public class CollectionLocation { * If the configuration sword.deposit.url is set, this will be returned, * but if not, it will construct the url as follows: * - * [dspace.baseUrl]/sword/deposit + * [dspace.server.url]/sword/deposit * - * where dspace.baseUrl is also in the configuration file. + * where dspace.server.url is also in the configuration file. * * @return the base URL for sword deposit * @throws DSpaceSWORDException can be thrown by the internals of the DSpace SWORD implementation @@ -110,11 +110,11 @@ public class CollectionLocation { "sword-server", "deposit.url"); if (depositUrl == null || "".equals(depositUrl)) { String dspaceUrl = ConfigurationManager - .getProperty("dspace.baseUrl"); + .getProperty("dspace.server.url"); if (dspaceUrl == null || "".equals(dspaceUrl)) { throw new DSpaceSWORDException( "Unable to construct deposit urls, due to missing/invalid config in sword.deposit.url and/or " + - "dspace.baseUrl"); + "dspace.server.url"); } try { @@ -123,7 +123,7 @@ public class CollectionLocation { url.getPort(), "/sword/deposit").toString(); } catch (MalformedURLException e) { throw new DSpaceSWORDException( - "Unable to construct deposit urls, due to invalid dspace.baseUrl " + + "Unable to construct deposit urls, due to invalid dspace.server.url " + e.getMessage(), e); } diff --git a/dspace-sword/src/main/java/org/dspace/sword/ItemEntryGenerator.java b/dspace-sword/src/main/java/org/dspace/sword/ItemEntryGenerator.java index da7dcead4c..c0a78e1866 100644 --- a/dspace-sword/src/main/java/org/dspace/sword/ItemEntryGenerator.java +++ b/dspace-sword/src/main/java/org/dspace/sword/ItemEntryGenerator.java @@ -162,7 +162,7 @@ public class ItemEntryGenerator extends DSpaceATOMEntry { // if we get this far, then we just use the dspace url as the // property - String cfg = ConfigurationManager.getProperty("dspace.url"); + String cfg = ConfigurationManager.getProperty("dspace.ui.url"); entry.setId(cfg); // FIXME: later on we will maybe have a workflow page supplied @@ -246,7 +246,7 @@ public class ItemEntryGenerator extends DSpaceATOMEntry { return; } - String base = ConfigurationManager.getProperty("dspace.url"); + String base = ConfigurationManager.getProperty("dspace.ui.url"); // if there's no base URL, we are stuck if (base == null) { diff --git a/dspace-sword/src/main/java/org/dspace/sword/SWORDUrlManager.java b/dspace-sword/src/main/java/org/dspace/sword/SWORDUrlManager.java index 2a6ce92761..4f0c79f908 100644 --- a/dspace-sword/src/main/java/org/dspace/sword/SWORDUrlManager.java +++ b/dspace-sword/src/main/java/org/dspace/sword/SWORDUrlManager.java @@ -304,11 +304,11 @@ public class SWORDUrlManager { "sword-server", "servicedocument.url"); if (depositUrl == null || "".equals(depositUrl)) { String dspaceUrl = ConfigurationManager.getProperty( - "dspace.baseUrl"); + "dspace.server.url"); if (dspaceUrl == null || "".equals(dspaceUrl)) { throw new DSpaceSWORDException( "Unable to construct service document urls, due to missing/invalid " + - "config in sword.servicedocument.url and/or dspace.baseUrl"); + "config in sword.servicedocument.url and/or dspace.server.url"); } try { @@ -317,7 +317,7 @@ public class SWORDUrlManager { url.getPort(), "/sword/servicedocument").toString(); } catch (MalformedURLException e) { throw new DSpaceSWORDException( - "Unable to construct service document urls, due to invalid dspace.baseUrl " + + "Unable to construct service document urls, due to invalid dspace.server.url " + e.getMessage(), e); } @@ -334,9 +334,9 @@ public class SWORDUrlManager { * If the configuration sword.deposit.url is set, this will be returned, * but if not, it will construct the url as follows: * - * [dspace.baseUrl]/sword/deposit + * [dspace.server.url]/sword/deposit * - * where dspace.baseUrl is also in the configuration file. + * where dspace.server.url is also in the configuration file. * * @return the base URL for SWORD deposit * @throws DSpaceSWORDException can be thrown by the internals of the DSpace SWORD implementation @@ -347,11 +347,11 @@ public class SWORDUrlManager { "sword-server", "deposit.url"); if (depositUrl == null || "".equals(depositUrl)) { String dspaceUrl = ConfigurationManager.getProperty( - "dspace.baseUrl"); + "dspace.server.url"); if (dspaceUrl == null || "".equals(dspaceUrl)) { throw new DSpaceSWORDException( "Unable to construct deposit urls, due to missing/invalid config in " + - "sword.deposit.url and/or dspace.baseUrl"); + "sword.deposit.url and/or dspace.server.url"); } try { @@ -360,7 +360,7 @@ public class SWORDUrlManager { url.getPort(), "/sword/deposit").toString(); } catch (MalformedURLException e) { throw new DSpaceSWORDException( - "Unable to construct deposit urls, due to invalid dspace.baseUrl " + + "Unable to construct deposit urls, due to invalid dspace.server.url " + e.getMessage(), e); } @@ -421,7 +421,7 @@ public class SWORDUrlManager { } String handle = item.getHandle(); - String bsLink = ConfigurationManager.getProperty("dspace.url"); + String bsLink = ConfigurationManager.getProperty("dspace.ui.url"); if (handle != null && !"".equals(handle)) { bsLink = bsLink + "/bitstream/" + handle + "/" + @@ -439,7 +439,7 @@ public class SWORDUrlManager { /** * Get the base media link URL. It can be configured using * {@code sword-server.media-link.url}. If not configured, it will be - * calculated using {@code dspace.baseUrl} and the constant path + * calculated using {@code dspace.server.url} and the constant path * {@code /sword/media-link}. * * @return that URL. @@ -451,11 +451,11 @@ public class SWORDUrlManager { "sword-server", "media-link.url"); if (StringUtils.isBlank(mlUrl)) { String dspaceUrl = ConfigurationManager.getProperty( - "dspace.baseUrl"); + "dspace.server.url"); if (dspaceUrl == null || "".equals(dspaceUrl)) { throw new DSpaceSWORDException( "Unable to construct media-link urls, due to missing/invalid config in " + - "media-link.url and/or dspace.baseUrl"); + "media-link.url and/or dspace.server.url"); } try { @@ -464,7 +464,7 @@ public class SWORDUrlManager { "/sword/media-link").toString(); } catch (MalformedURLException e) { throw new DSpaceSWORDException( - "Unable to construct media-link urls, due to invalid dspace.baseUrl " + + "Unable to construct media-link urls, due to invalid dspace.server.url " + e.getMessage(), e); } diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index ec2539ef44..3a34f904b2 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -69,6 +69,8 @@ jar classes + org.apache.abdera abdera-client @@ -77,6 +79,7 @@ javax.servlet servlet-api + log4j log4j @@ -112,6 +115,7 @@ + org.apache.logging.log4j log4j-api @@ -120,26 +124,24 @@ org.apache.logging.log4j log4j-core - + org.apache.logging.log4j log4j-web - + + + - org.apache.abdera - abdera-client - 1.1.3 + org.apache.ws.commons.axiom + fom-impl + ${axiom.version} + - org.apache.ws.commons.axiom - axiom-impl - - - org.apache.ws.commons.axiom - axiom-api - - - org.codehaus.woodstox - wstx-asl + org.apache.geronimo.specs + * diff --git a/dspace-swordv2/src/main/java/org/dspace/sword2/SwordUrlManager.java b/dspace-swordv2/src/main/java/org/dspace/sword2/SwordUrlManager.java index 6fee60c386..7088f31c1a 100644 --- a/dspace-swordv2/src/main/java/org/dspace/sword2/SwordUrlManager.java +++ b/dspace-swordv2/src/main/java/org/dspace/sword2/SwordUrlManager.java @@ -95,11 +95,11 @@ public class SwordUrlManager { String sUrl = ConfigurationManager.getProperty("swordv2-server", "url"); if (sUrl == null || "".equals(sUrl)) { String dspaceUrl = ConfigurationManager - .getProperty("dspace.baseUrl"); + .getProperty("dspace.server.url"); if (dspaceUrl == null || "".equals(dspaceUrl)) { throw new DSpaceSwordException( "Unable to construct service document urls, due to missing/invalid " + - "config in sword2.url and/or dspace.baseUrl"); + "config in sword2.url and/or dspace.server.url"); } try { @@ -108,7 +108,7 @@ public class SwordUrlManager { "/swordv2").toString(); } catch (MalformedURLException e) { throw new DSpaceSwordException( - "Unable to construct service document urls, due to invalid dspace.baseUrl " + + "Unable to construct service document urls, due to invalid dspace.server.url " + e.getMessage(), e); } } @@ -303,11 +303,11 @@ public class SwordUrlManager { .getProperty("swordv2-server", "servicedocument.url"); if (sdUrl == null || "".equals(sdUrl)) { String dspaceUrl = ConfigurationManager - .getProperty("dspace.baseUrl"); + .getProperty("dspace.server.url"); if (dspaceUrl == null || "".equals(dspaceUrl)) { throw new DSpaceSwordException( "Unable to construct service document urls, due to missing/invalid " + - "config in swordv2-server.cfg servicedocument.url and/or dspace.baseUrl"); + "config in swordv2-server.cfg servicedocument.url and/or dspace.server.url"); } try { @@ -316,7 +316,7 @@ public class SwordUrlManager { "/swordv2/servicedocument").toString(); } catch (MalformedURLException e) { throw new DSpaceSwordException( - "Unable to construct service document urls, due to invalid dspace.baseUrl " + + "Unable to construct service document urls, due to invalid dspace.server.url " + e.getMessage(), e); } } @@ -332,9 +332,9 @@ public class SwordUrlManager { * If the configuration sword.deposit.url is set, this will be returned, * but if not, it will construct the url as follows: * - * [dspace.baseUrl]/sword/deposit + * [dspace.server.url]/sword/deposit * - * where dspace.baseUrl is also in the configuration file. + * where dspace.server.url is also in the configuration file. * * @return the base URL for SWORD deposit * @throws DSpaceSwordException can be thrown by the internals of the DSpace SWORD implementation @@ -345,11 +345,11 @@ public class SwordUrlManager { .getProperty("swordv2-server", "collection.url"); if (depositUrl == null || "".equals(depositUrl)) { String dspaceUrl = ConfigurationManager - .getProperty("dspace.baseUrl"); + .getProperty("dspace.server.url"); if (dspaceUrl == null || "".equals(dspaceUrl)) { throw new DSpaceSwordException( "Unable to construct deposit urls, due to missing/invalid config in " + - "swordv2-server.cfg deposit.url and/or dspace.baseUrl"); + "swordv2-server.cfg deposit.url and/or dspace.server.url"); } try { @@ -358,7 +358,7 @@ public class SwordUrlManager { url.getPort(), "/swordv2/collection").toString(); } catch (MalformedURLException e) { throw new DSpaceSwordException( - "Unable to construct deposit urls, due to invalid dspace.baseUrl " + + "Unable to construct deposit urls, due to invalid dspace.server.url " + e.getMessage(), e); } @@ -407,7 +407,7 @@ public class SwordUrlManager { } String handle = item.getHandle(); - String bsLink = ConfigurationManager.getProperty("dspace.url"); + String bsLink = ConfigurationManager.getProperty("dspace.ui.url"); if (handle != null && !"".equals(handle)) { bsLink = bsLink + "/bitstream/" + handle + "/" + diff --git a/dspace/bin/make-handle-config b/dspace/bin/make-handle-config index 00daf3c29b..127034ee5d 100755 --- a/dspace/bin/make-handle-config +++ b/dspace/bin/make-handle-config @@ -16,8 +16,11 @@ BINDIR=`dirname $0` echo "Writing simple Handle server configuration" -# Read parameters from DSpace config -dshostname=`$BINDIR/dspace dsprop --property dspace.hostname` +# Read server URL from DSpace config +dsurl=`$BINDIR/dspace dsprop --property dspace.server.url` +# Parse hostname from URL +dshostname=`echo $dsurl | awk -F/ '{ print $3; exit }'` +# Determine IP address from hostname dshostip=`host $dshostname | awk '/has address/ { print $4; exit }'` if [ "$dshostip" = "127.0.0.1" ]; then # Just use default. SimpleSetup will fail when trying to bind to localhost addresses. diff --git a/dspace/config/crosswalks/oai/description.xml b/dspace/config/crosswalks/oai/description.xml index 369682e6b3..509b17c374 100644 --- a/dspace/config/crosswalks/oai/description.xml +++ b/dspace/config/crosswalks/oai/description.xml @@ -1,6 +1,6 @@ oai -${dspace.hostname} +${oai.identifier.prefix} : -oai:${dspace.hostname}:${handle.prefix}/1234 +oai:${oai.identifier.prefix}:${handle.prefix}/1234 diff --git a/dspace/config/crosswalks/oai/metadataFormats/mets.xsl b/dspace/config/crosswalks/oai/metadataFormats/mets.xsl index 41d683f075..ca41fcdb2b 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/mets.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/mets.xsl @@ -1,9 +1,7 @@ + xmlns:doc="http://www.lyncode.com/xoai" version="2.0"> @@ -20,7 +18,7 @@ - + diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index bce46aaba6..5090f5ea34 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -22,18 +22,13 @@ # Windows note: Please remember to use forward slashes for all paths (e.g. C:/dspace) dspace.dir = /dspace -# DSpace host name - should match base URL. Do not include port number. -dspace.hostname = localhost +# URL of DSpace backend ('server' webapp). Include port number etc. +# This is where REST API and all enabled server modules (OAI-PMH, SWORD, SWORDv2, RDF, etc) will respond +dspace.server.url = http://localhost:8080/server -# DSpace server backend webapp URL. Include port number etc. -dspace.baseUrl = http://localhost:8080/server - -# Full link your end users will use to access DSpace. This is the URL of your angular UI -dspace.url = http://localhost:3000 - -# Optional: DSpace URL for mobile access -# This -#dspace.mobileUrl = http://mobile.example.com +# URL of DSpace frontend (Angular UI). Include port number etc +# This is used by the backend to provide links in emails, RSS feeds, Sitemaps, etc. +dspace.ui.url = http://localhost:3000 # Name of the site dspace.name = DSpace at My University @@ -46,7 +41,7 @@ default.language = en_US # Solr server/webapp. # DSpace uses Solr for all search/browse capability (and for usage statistics). -# since DSpace 7, SOLR must be installed as a stand-alone service +# Since DSpace 7, SOLR must be installed as a stand-alone service solr.server = http://localhost:8983/solr ##### Database settings ##### @@ -139,7 +134,8 @@ mail.charset = UTF-8 # A comma-separated list of hostnames that are allowed to refer browsers to email forms. # Default behaviour is to accept referrals only from dspace.hostname -mail.allowed.referrers = ${dspace.hostname} +# TODO: Needs removal/replacement. No longer used in DSpace 7 codebase and dspace.hostname config no longer exists. +#mail.allowed.referrers = ${dspace.hostname} # Pass extra settings to the Java mail library. Comma-separated, equals sign between # the key and the value. For example: @@ -234,7 +230,7 @@ identifier.doi.namespaceseparator = dspace/ # # Items in DSpace receive a unique URL, stored in dc.identifier.uri # after it is generated during the submission process. -handle.canonical.prefix = ${dspace.url}/handle/ +handle.canonical.prefix = ${dspace.ui.url}/handle/ # If you register with CNRI's handle service at http://www.handle.net/, # these links can be generated as permalinks using http://hdl.handle.net/ @@ -1349,7 +1345,7 @@ webui.feed.item.author = dc.contributor.author # Customize the image icon included with the site-wide feeds: # Must be an absolute URL, e.g. -## webui.feed.logo.url = ${dspace.url}/themes/mysite/images/mysite-logo.png +## webui.feed.logo.url = ${dspace.ui.url}/themes/mysite/images/mysite-logo.png # iTunes Podcast Enhanced RSS Feed Properties # Add all the communities / collections, separated by commas (no spaces) that should diff --git a/dspace/config/local.cfg.EXAMPLE b/dspace/config/local.cfg.EXAMPLE index ec462fe1e6..b64e3e7260 100644 --- a/dspace/config/local.cfg.EXAMPLE +++ b/dspace/config/local.cfg.EXAMPLE @@ -30,14 +30,13 @@ # Windows note: Please remember to use forward slashes for all paths (e.g. C:/dspace) dspace.dir=/dspace -# DSpace host name - should match base URL. Do not include port number -dspace.hostname = localhost +# URL of DSpace backend ('server' webapp). Include port number etc. +# This is where REST API and all enabled server modules (OAI-PMH, SWORD, SWORDv2, RDF, etc) will respond +dspace.server.url = http://localhost:8080/server -# DSpace server backend webapp URL. Include port number etc. -dspace.baseUrl = http://localhost:8080/server - -# Full link your end users will use to access DSpace. This is the URL of your angular UI -dspace.url = http://localhost:3000 +# URL of DSpace frontend (Angular UI). Include port number etc +# This is used by the backend to provide links in emails, RSS feeds, Sitemaps, etc. +dspace.ui.url = http://localhost:3000 # Name of the site dspace.name = DSpace at My University @@ -51,7 +50,7 @@ dspace.name = DSpace at My University # Solr server/webapp. # DSpace uses Solr for all search/browse capability (and for usage statistics). -# since DSpace 7, SOLR must be installed as a stand-alone service +# Since DSpace 7, SOLR must be installed as a stand-alone service #solr.server = http://localhost:8983/solr ########################## @@ -138,7 +137,7 @@ db.schema = public # # Items in DSpace receive a unique URL, stored in dc.identifier.uri # after it is generated during the submission process. -# +# # If you register with CNRI's handle service at http://www.handle.net/, # these links can be generated as permalinks using http://hdl.handle.net/ # as canonical prefix. Please make sure to change handle.canonical.prefix diff --git a/dspace/config/modules/oai.cfg b/dspace/config/modules/oai.cfg index c691e1fe5f..4509d7511f 100644 --- a/dspace/config/modules/oai.cfg +++ b/dspace/config/modules/oai.cfg @@ -8,11 +8,11 @@ # When "true", the OAI module is accessible on ${oai.path} # When "false" or commented out, OAI is disabled/inaccessible. # (Requires reboot of servlet container, e.g. Tomcat, to reload) -#oai.enabled = true +oai.enabled = true # Path where OAI module is available # Defaults to "oai", which means the OAI module would be available -# at ${dspace.baseURL}/oai/ +# at ${dspace.server.url}/oai/ # (Requires reboot of servlet container, e.g. Tomcat, to reload) oai.path = oai @@ -21,15 +21,22 @@ oai.storage=solr # The base URL of the OAI webapp (do not include the context e.g. /request, /openaire, etc). # Note: Comment out if you want to fallback to the request's URL. -oai.url = ${dspace.baseUrl}/${oai.path} +oai.url = ${dspace.server.url}/${oai.path} # Base solr index oai.solr.url=${solr.server}/oai -# OAI persistent identifier prefix. -# Format - oai:PREFIX:HANDLE -oai.identifier.prefix = ${dspace.hostname} + +# OAI persistent identifier prefix +# This field is used for two purposes: +# 1. As your OAI-PMH +# 2. As the prefix for all Identifiers in OAI-PMH (Format is "oai:${oai.identifier.prefix}:${handle.prefix}") +# The OAI-PMH spec requires this prefix to correspond to your site's hostname. Therefore, by default, +# DSpace will set this configuration to the hostname from your ${dspace.ui.url} configuration. +# However, you may override that default value by uncommenting this configuration. +# oai.identifier.prefix = YOUR_SITE_HOSTNAME + # Base url for bitstreams -oai.bitstream.baseUrl = ${dspace.url} +oai.bitstream.baseUrl = ${dspace.ui.url} # Base Configuration Directory oai.config.dir = ${dspace.dir}/config/crosswalks/oai @@ -47,7 +54,7 @@ oai.cache.dir = ${dspace.dir}/var/oai #--------------OAI IMPORT CONFIGURATION ------------------------# #---------------------------------------------------------------# -# Size of batches to commit to solr at a time +# Size of batches to commit to solr at a time oai.import.batch.size = 1000 #---------------------------------------------------------------# diff --git a/dspace/config/modules/rdf.cfg b/dspace/config/modules/rdf.cfg index 8391b892c0..4d4191c291 100644 --- a/dspace/config/modules/rdf.cfg +++ b/dspace/config/modules/rdf.cfg @@ -14,7 +14,7 @@ # Path where RDF module is available (in the Spring Server webapp) # Defaults to "rdf", which means the RDF module would be available -# at ${dspace.baseURL}/rdf/ +# at ${dspace.server.url}/rdf/ # (Requires reboot of servlet container, e.g. Tomcat, to reload) rdf.path = rdf @@ -23,7 +23,7 @@ rdf.contentNegotiation.enable = false # Set the url of the RDF module here. This is necessary to use content # negotiation -rdf.contextPath = ${dspace.baseUrl}/${rdf.path} +rdf.contextPath = ${dspace.server.url}/${rdf.path} # Address of the public SPARQL endpoint @@ -41,7 +41,7 @@ rdf.storage.graphstore.authentication = no # please set the authentication credentials #rdf.storage.graphstore.login = dspace #rdf.storage.graphstore.password = ecapsd -# Address DSpace should use to query the SPARQL endpoint, e.g. the +# Address DSpace should use to query the SPARQL endpoint, e.g. the # RDFStorageImpl uses this address to determine a list of all stored # graphs. The SPARQL endpoint can be read-only, all commands which change # data will be performed using the SPARQL 1.1 Graph Store HTTP Protocoll. diff --git a/dspace/config/modules/rest.cfg b/dspace/config/modules/rest.cfg index 2d1149c4d3..2e13d73f97 100644 --- a/dspace/config/modules/rest.cfg +++ b/dspace/config/modules/rest.cfg @@ -8,6 +8,12 @@ # (Requires reboot of servlet container, e.g. Tomcat, to reload) rest.cors.allowed-origins = * +# This property determines the max embeddepth for a FullProjection. This is also used by the SpecificLevelProjection +# as a fallback incase the property is defined on the bean +rest.projections.full.max = 2 + +# This property determines the max embed depth for a SpecificLevelProjection +rest.projection.specificLevel.maxEmbed = 5 #---------------------------------------------------------------# # These configs are used by the deprecated REST (v4-6) module # diff --git a/dspace/config/modules/sword-server.cfg b/dspace/config/modules/sword-server.cfg index 440d3341d9..de7b09f4a0 100644 --- a/dspace/config/modules/sword-server.cfg +++ b/dspace/config/modules/sword-server.cfg @@ -14,7 +14,7 @@ # Path where SWORD v1 module is available (in the Spring Server webapp) # Defaults to "sword", which means the SWORD mould would be available -# at ${dspace.baseUrl}/sword/ +# at ${dspace.server.url}/sword/ # (Requires reboot of servlet container, e.g. Tomcat, to reload) #sword-server.path = sword @@ -46,7 +46,7 @@ # which DSpace will construct the deposit location urls for # collections. # -# The default is ${dspace.baseUrl}/sword/deposit +# The default is ${dspace.server.url}/sword/deposit # # In the event that you are not deploying DSpace as the ROOT # application in the servlet container, this will generate @@ -59,7 +59,7 @@ # URL from which DSpace will construct the service document # location urls for the site, and for individual collections # -# The default is {dspace.baseUrl}/sword/servicedocument +# The default is {dspace.server.url}/sword/servicedocument # # In the event that you are not deploying DSpace as the ROOT # application in the servlet container, this will generate @@ -72,7 +72,7 @@ # which DSpace will use to construct the media link urls # for items which are deposited via sword # -# The default is {dspace.baseUrl}/sword/media-link +# The default is {dspace.server.url}/sword/media-link # # In the event that you are not deploying DSpace as the ROOT # application in the servlet container, this will generate diff --git a/dspace/config/modules/swordv2-server.cfg b/dspace/config/modules/swordv2-server.cfg index 8c38d93fac..2e60a7d2c2 100644 --- a/dspace/config/modules/swordv2-server.cfg +++ b/dspace/config/modules/swordv2-server.cfg @@ -14,13 +14,13 @@ # Path where SWORD v2 module is available (in the Spring Server webapp) # Defaults to "swordv2", which means the SWORD v2 module would be available -# at ${dspace.baseURL}/swordv2/ +# at ${dspace.server.url}/swordv2/ # (Requires reboot of servlet container, e.g. Tomcat, to reload) #swordv2-server.path = swordv2 # the base url of the sword 2.0 system # -# the default if {dspace.baseUrl}/swordv2 +# the default if {dspace.server.url}/swordv2 # #swordv2-server.url = http://www.myu.ac.uk/swordv2 @@ -28,7 +28,7 @@ # which DSpace will construct the deposit location urls for # collections. # -# The default is {dspace.baseUrl}/swordv2/collection +# The default is {dspace.server.url}/swordv2/collection # # In the event that you are not deploying DSpace as the ROOT # application in the servlet container, this will generate @@ -41,7 +41,7 @@ # URL from which DSpace will construct the service document # location urls for the site, and for individual collections # -# The default is {dspace.baseUrl}/swordv2/servicedocument +# The default is {dspace.server.url}/swordv2/servicedocument # # In the event that you are not deploying DSpace as the ROOT # application in the servlet container, this will generate @@ -141,7 +141,7 @@ swordv2-server.keep-original-package = true # which belongs to the On-Behalf-Of user, which represents a significant # security risk. It is therefore recommended to either disable # mediated deposit, or to expressly set a list of accounts which -# are allowed to mediate on behalf of other users +# are allowed to mediate on behalf of other users # (see swordv2-server.on-behalf-of.update.mediators) # # See the SWORD specification for a detailed explanation of deposit @@ -173,10 +173,9 @@ swordv2-server.verbose-description.error.enable = true # The error document can contain an alternate url, which the client # can use to follow up any issues. # -# This is the Contact-Us page on the XMLUI (localise the url space -# first) +# This is the Contact-Us page on the UI (localise the url space first) # -swordv2-server.error.alternate.url = ${dspace.baseUrl}/contact +swordv2-server.error.alternate.url = ${dspace.ui.url}/contact # The URL may have an associated content type; if you know what it # is, you can enter it here @@ -494,13 +493,7 @@ swordv2-server.state.archive.description = The item has been archived swordv2-server.state.withdrawn.uri = http://dspace.org/state/withdrawn swordv2-server.state.withdrawn.description = The item has been withdrawn from the archive and is no longer available -# URL template for items in the workspace (items in the archive will use -# the handle) -# -# JSPUI -# swordv2-server.workspace.url-template = ${dspace.baseUrl}/view-workspaceitem?submit_view=Yes&workspace_id=#wsid# - -# XMLUI -swordv2-server.workspace.url-template = ${dspace.baseUrl}/submit?workspaceID=#wsid# +# URL template for items in the workspace (items in the archive will use the handle) +swordv2-server.workspace.url-template = ${dspace.ui.url}/workspaceitems/#wsid#/edit diff --git a/dspace/config/spring/rest/projections.xml b/dspace/config/spring/rest/projections.xml new file mode 100644 index 0000000000..6f082b7b5b --- /dev/null +++ b/dspace/config/spring/rest/projections.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index a66ecd1dbb..92918d6b4e 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -61,7 +61,6 @@ install of DSpace, against which Tests can be run. --> maven-dependency-plugin - 2.8 ${project.build.directory}/testing @@ -105,7 +104,6 @@ org.codehaus.gmaven groovy-maven-plugin - 2.0 setproperty @@ -197,12 +195,6 @@ - - - org.jmockit - jmockit - test - junit junit diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index 58a1902a8c..cd53fa0e65 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -39,19 +39,18 @@ compiling of the corresponding source module. --> + dspace-rest - - rest/pom.xml - + false rest - dspace-server-webapp` + dspace-server-webapp server/pom.xml diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index 7a9b9b89b5..6852f3d8bd 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -18,8 +18,8 @@ just adding new jar in the classloader - - ${basedir}/../../.. + + ${basedir}/../../.. @@ -139,19 +139,17 @@ just adding new jar in the classloader org.codehaus.gmaven groovy-maven-plugin - 2.0 setproperty - generate-test-resources - + initialize execute project.properties['agnostic.build.dir'] = project.build.directory.replace(File.separator, '/'); - println("Initializing Maven property 'agnostic.build.dir' to: " + project.properties['agnostic.build.dir']); + log.info("Initializing Maven property 'agnostic.build.dir' to: {}", project.properties['agnostic.build.dir']); @@ -164,6 +162,7 @@ just adding new jar in the classloader + ${agnostic.build.dir}/testing/dspace/ @@ -179,6 +178,7 @@ just adding new jar in the classloader + ${agnostic.build.dir}/testing/dspace/ true @@ -269,11 +269,6 @@ just adding new jar in the classloader json-path-assert test - - org.jmockit - jmockit - test - junit junit diff --git a/dspace/src/main/assembly/assembly.xml b/dspace/src/main/assembly/assembly.xml index b016d850a3..6591d7bb73 100644 --- a/dspace/src/main/assembly/assembly.xml +++ b/dspace/src/main/assembly/assembly.xml @@ -81,9 +81,6 @@ *:jar:* - - org.glassfish.hk2.external:bean-validator:jar:* - lib false @@ -108,9 +105,6 @@ *:jar:* - - org.glassfish.hk2.external:bean-validator:jar:* - diff --git a/dspace/src/main/config/build.xml b/dspace/src/main/config/build.xml index 8afddb421a..4a48dce374 100644 --- a/dspace/src/main/config/build.xml +++ b/dspace/src/main/config/build.xml @@ -349,8 +349,6 @@ Common usage: autoconfiguring itself (e.g. see DS-3104). --> - - @@ -849,9 +847,9 @@ Common usage: ${dspace.dir}/bin/dspace create-administrator - You should then be able to access your DSpace's 'home page': + You should then be able to access your DSpace's REST API: - ${dspace.url} + ${dspace.server.url} ==================================================================== diff --git a/dspace/src/main/docker-compose/local.cfg b/dspace/src/main/docker-compose/local.cfg index 947b3c8248..70bc45c112 100644 --- a/dspace/src/main/docker-compose/local.cfg +++ b/dspace/src/main/docker-compose/local.cfg @@ -1,6 +1,5 @@ dspace.dir=/dspace db.url=jdbc:postgresql://dspacedb:5432/dspace -dspace.hostname=dspace -dspace.baseUrl=http://localhost:8080/server +dspace.server.url=http://localhost:8080/server dspace.name=DSpace Started with Docker Compose solr.server=http://dspacesolr:8983/solr diff --git a/dspace/src/main/docker/README.md b/dspace/src/main/docker/README.md index db530966aa..a885d16ab4 100644 --- a/dspace/src/main/docker/README.md +++ b/dspace/src/main/docker/README.md @@ -16,7 +16,7 @@ Admins to our DockerHub repo can publish with the following command. docker push dspace/dspace-dependencies:dspace-7_x ``` -## Dockerfile.jdk8-test +## Dockerfile.test This Dockerfile builds a DSpace 7 Tomcat image (for testing/development). This image deploys two DSpace webapps: @@ -24,7 +24,7 @@ This image deploys two DSpace webapps: 2. The legacy (v6) REST API (at `http://localhost:8080//rest`), deployed without requiring HTTPS access. ``` -docker build -t dspace/dspace:dspace-7_x-jdk8-test -f Dockerfile.jdk8-test . +docker build -t dspace/dspace:dspace-7_x-test -f Dockerfile.test . ``` This image is built *automatically* after each commit is made to the `master` branch. @@ -33,17 +33,17 @@ A corresponding image exists for DSpace 4-6. Admins to our DockerHub repo can publish with the following command. ``` -docker push dspace/dspace:dspace-7_x-jdk8-test +docker push dspace/dspace:dspace-7_x-test ``` -## Dockerfile.jdk8 +## Dockerfile This Dockerfile builds a DSpace 7 tomcat image. This image deploys two DSpace webapps: 1. The DSpace 7 REST API (at `http://localhost:8080/server`) 2. The legacy (v6) REST API (at `http://localhost:8080//rest`), deployed *requiring* HTTPS access. ``` -docker build -t dspace/dspace:dspace-7_x-jdk8 -f Dockerfile.jdk8 . +docker build -t dspace/dspace:dspace-7_x -f Dockerfile . ``` This image is built *automatically* after each commit is made to the `master` branch. @@ -52,14 +52,14 @@ A corresponding image exists for DSpace 4-6. Admins to our DockerHub repo can publish with the following command. ``` -docker push dspace/dspace:dspace-7_x-jdk8 +docker push dspace/dspace:dspace-7_x ``` -## Dockefile.cli.jdk8 +## Dockefile.cli This Dockerfile builds a DSpace 7 CLI image, which can be used to run commandline tools via Docker. ``` -docker build -t dspace/dspace-cli:dspace-7_x -f Dockerfile.cli.jdk8 . +docker build -t dspace/dspace-cli:dspace-7_x -f Dockerfile.cli . ``` This image is built *automatically* after each commit is made to the master branch. @@ -110,13 +110,13 @@ docker push dspace/dspace-postgres-pgcrypto:loadsql This is a standalone Solr image containing DSpace Solr cores & schemas required by DSpace 7. -**WARNING:** Rebuilding this image first **requires** rebuilding `dspace-7_x-jdk8` (i.e. `Dockerfile.jdk8` listed above), +**WARNING:** Rebuilding this image first **requires** rebuilding `dspace-7_x` (i.e. `Dockerfile` listed above), as this Solr image copies the latest DSpace-specific Solr schemas & settings from that other image. ``` -# First, rebuild dspace-7_x-jdk8 to grab the latest Solr configs +# First, rebuild dspace-7_x to grab the latest Solr configs cd [src] -docker build -t dspace/dspace:dspace-7_x-jdk8 -f Dockerfile.jdk8 . +docker build -t dspace/dspace:dspace-7_x -f Dockerfile . # Then, rebuild dspace-solr based on that build of DSpace 7. cd dspace/src/main/docker/solr diff --git a/dspace/src/main/docker/local.cfg b/dspace/src/main/docker/local.cfg index 325d17ddf4..480e05372f 100644 --- a/dspace/src/main/docker/local.cfg +++ b/dspace/src/main/docker/local.cfg @@ -4,6 +4,5 @@ # ------------------------------------------------------------------------ dspace.dir = /dspace db.url = jdbc:postgresql://dspacedb:5432/dspace -dspace.hostname = localhost -dspace.baseUrl = http://localhost:8080/server +dspace.server.url=http://localhost:8080/server solr.server=http://dspacesolr:8983/solr diff --git a/dspace/src/main/docker/solr/Dockerfile b/dspace/src/main/docker/solr/Dockerfile index a90201514e..2caf5e5fba 100644 --- a/dspace/src/main/docker/solr/Dockerfile +++ b/dspace/src/main/docker/solr/Dockerfile @@ -7,7 +7,7 @@ # # Define a 'dspace' alias for our latest v7 image -FROM dspace/dspace:dspace-7_x-jdk8 as dspace +FROM dspace/dspace:dspace-7_x as dspace # Pin to Solr v7 # Note: The WORKDIR changes to /var/solr in v8 and directory layout is different diff --git a/pom.xml b/pom.xml index cc952c52e9..422781dbe4 100644 --- a/pom.xml +++ b/pom.xml @@ -19,19 +19,24 @@ UTF-8 ${project.build.sourceEncoding} - 1.8 + 11 2.0.15 3.17 - 42.2.1 + 42.2.9 2.13.0 + 2.3.1 + 2.3.1 + 1.3.2 + 1.2.22 + 2.3.4 2.11.2 1.7.25 2.10.2 2.28 - 5.3.11.Final + 5.4.10.Final 6.0.17.Final @@ -89,36 +94,30 @@ + org.apache.maven.plugins maven-compiler-plugin - 3.5.1 + 3.8.1 - - javac-with-errorprone - true - ${java.version} - ${java.version} + 11 + + + -XDcompilePolicy=simple + -Xplugin:ErrorProne + + + + com.google.errorprone + error_prone_core + ${errorprone.version} + + - - - - org.codehaus.plexus - plexus-compiler-javac-errorprone - 2.8.2 - - - - com.google.errorprone - error_prone_core - 2.1.1 - - org.apache.maven.plugins maven-jar-plugin - 2.6 + 3.2.0 @@ -132,7 +131,7 @@ org.apache.maven.plugins maven-war-plugin - 2.6 + 3.2.3 false @@ -149,7 +148,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.19.1 + 2.22.2 @@ -171,7 +170,7 @@ maven-failsafe-plugin - 2.19.1 + 2.22.2 @@ -199,7 +198,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.0.0 + 3.1.0 verify-style @@ -236,7 +235,7 @@ org.codehaus.mojo findbugs-maven-plugin - 3.0.3 + 3.0.5 Max Low @@ -255,7 +254,7 @@ maven-clean-plugin - 2.5 + 3.1.0 @@ -267,41 +266,37 @@ - - maven-antrun-plugin - 1.8 - maven-assembly-plugin - 2.6 + 3.2.0 org.apache.maven.plugins maven-dependency-plugin - 2.10 + 3.1.1 org.apache.maven.plugins maven-resources-plugin - 2.7 + 3.1.0 com.mycila license-maven-plugin - 2.11 + 3.0 org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 + 1.6.8 org.apache.maven.plugins maven-javadoc-plugin - 2.10.3 + 3.1.1 false @@ -311,7 +306,14 @@ org.apache.maven.plugins maven-source-plugin - 2.4 + 3.2.1 + + + + org.codehaus.gmaven + groovy-maven-plugin + 2.1.1 @@ -319,15 +321,26 @@ maven-gpg-plugin 1.6 + org.jacoco jacoco-maven-plugin - 0.7.9 + 0.8.5 + org.eluder.coveralls coveralls-maven-plugin 4.3.0 + + + + javax.xml.bind + jaxb-api + ${jaxb-api.version} + + @@ -337,6 +350,7 @@ + org.apache.maven.plugins maven-release-plugin 2.5.3 @@ -381,11 +395,7 @@ - XML_STYLE - XML_STYLE - XML_STYLE - XML_STYLE + *.ttl, *.LICENSE --> SCRIPT_STYLE TEXT @@ -404,6 +414,38 @@ + + + org.codehaus.mojo + xml-maven-plugin + 1.0.2 + + + validate-ALL-xml-and-xsl + process-test-resources + + validate + + + + + + + +

    ${root.basedir} + + **/*.xml + **/*.xsl + **/*.xsd + + + **/target/** + + + + + + org.apache.maven.plugins @@ -596,7 +638,6 @@ org.codehaus.mojo license-maven-plugin - 1.8 false @@ -749,7 +790,7 @@ dspace-oai @@ -764,7 +805,7 @@ dspace-rdf @@ -778,13 +819,12 @@ - + + dspace-rest - - dspace-rest/pom.xml - + false dspace-rest @@ -793,7 +833,7 @@ dspace-sword @@ -809,7 +849,7 @@ dspace-swordv2 @@ -823,6 +863,9 @@ + dspace-server-webapp @@ -1410,7 +1453,6 @@ xerces xercesImpl 2.11.0 - xml-apis @@ -1505,13 +1547,14 @@ log4j-over-slf4j ${slf4j.version} - - - org.jmockit - jmockit - 1.21 - test + + + com.google.errorprone + error_prone_core + ${errorprone.version} + compile + junit junit @@ -1530,6 +1573,12 @@ 1.3 test + + org.mockito + mockito-core + 2.28.2 + test + com.h2database @@ -1592,12 +1641,6 @@ 3.0.1u2 provided - - org.mockito - mockito-core - 2.23.4 - test - com.fasterxml @@ -1649,6 +1692,23 @@ xom 1.2.5 + + + javax.xml.bind + jaxb-api + ${jaxb-api.version} + + + org.glassfish.jaxb + jaxb-runtime + ${jaxb-runtime.version} + runtime + + + javax.annotation + javax.annotation-api + ${javax-annotation.version} + @@ -1781,14 +1841,17 @@ true - +