From 0dad4dd71387d0d48dbb8d3788cb7ba0b0b5ce15 Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Thu, 9 Mar 2023 18:27:32 +0100 Subject: [PATCH 01/82] [DSC-963] Create new project to run dspace with embedded tomcat --- dspace-server-webapp/pom.xml | 54 --- .../{Application.java => WebApplication.java} | 32 +- .../java/org/dspace/app/TestApplication.java | 16 + .../AbstractControllerIntegrationTest.java | 4 +- .../AbstractWebClientIntegrationTest.java | 4 +- dspace-webapp-boot/pom.xml | 135 +++++++ .../main/java/org/dspace/app/Application.java | 45 +++ .../src/main/resources/application.properties | 1 + .../src/main/resources/static}/index.html | 0 .../resources/static}/js/hal/http/client.js | 0 .../static}/js/vendor/CustomPostForm.js | 0 .../src/main/resources/static}/login.html | 0 .../src/main/resources/static}/styles.css | 0 .../app/rest/example/ExampleController.java | 0 .../app/rest/example/ExampleControllerIT.java | 0 dspace/config/log4j2-console.xml | 2 +- dspace/modules/server/pom.xml | 355 ------------------ .../modules/server/src/main/webapp/.gitignore | 0 pom.xml | 33 +- 19 files changed, 226 insertions(+), 455 deletions(-) rename dspace-server-webapp/src/main/java/org/dspace/app/rest/{Application.java => WebApplication.java} (87%) create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/TestApplication.java create mode 100644 dspace-webapp-boot/pom.xml create mode 100644 dspace-webapp-boot/src/main/java/org/dspace/app/Application.java rename {dspace-server-webapp => dspace-webapp-boot}/src/main/resources/application.properties (99%) rename {dspace-server-webapp/src/main/webapp => dspace-webapp-boot/src/main/resources/static}/index.html (100%) rename {dspace-server-webapp/src/main/webapp => dspace-webapp-boot/src/main/resources/static}/js/hal/http/client.js (100%) rename {dspace-server-webapp/src/main/webapp => dspace-webapp-boot/src/main/resources/static}/js/vendor/CustomPostForm.js (100%) rename {dspace-server-webapp/src/main/webapp => dspace-webapp-boot/src/main/resources/static}/login.html (100%) rename {dspace-server-webapp/src/main/webapp => dspace-webapp-boot/src/main/resources/static}/styles.css (100%) rename {dspace/modules/server => dspace-webapp-boot}/src/test/java/org/dspace/app/rest/example/ExampleController.java (100%) rename {dspace/modules/server => dspace-webapp-boot}/src/test/java/org/dspace/app/rest/example/ExampleControllerIT.java (100%) delete mode 100644 dspace/modules/server/pom.xml delete mode 100644 dspace/modules/server/src/main/webapp/.gitignore diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index d00d6364cad..e6423a7bc8b 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -2,7 +2,6 @@ 4.0.0 org.dspace dspace-server-webapp - war DSpace Server Webapp DSpace Server Webapp (Spring Boot) @@ -25,55 +24,10 @@ @ - - org.dspace.app.rest.Application - - org.apache.maven.plugins - maven-war-plugin - - true - - true - - - - prepare-package - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - - test-jar - - - - - - - com.mycila - license-maven-plugin - - - **/src/test/resources/** - **/src/test/data/** - - src/main/webapp/index.html - src/main/webapp/login.html - src/main/webapp/styles.css - src/main/webapp/js/hal/** - src/main/webapp/js/vendor/** - - - + + org.dspace + dspace-parent + cris-2022.03.01-SNAPSHOT + .. + + + + + ${basedir}/.. + + @ + + + + + + org.dspace.modules + additions + + + org.dspace + dspace-server-webapp + + + org.apache.solr + solr-solrj + + + + + org.dspace + dspace-api + test-jar + test + + + org.dspace + dspace-server-webapp + test-jar + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + ${spring-security.version} + test + + + com.jayway.jsonpath + json-path-assert + ${json-path.version} + test + + + junit + junit + test + + + com.h2database + h2 + test + + + org.mockito + mockito-inline + test + + + + + org.apache.solr + solr-core + ${solr.client.version} + test + + + + org.apache.commons + commons-text + + + + + org.apache.lucene + lucene-analyzers-icu + test + + + + + + + + + com.mycila + license-maven-plugin + + + **/src/test/resources/** + **/src/test/data/** + + src/main/resources/static/index.html + src/main/resources/static/login.html + src/main/resources/static/styles.css + src/main/resources/static/js/hal/** + src/main/resources/static/js/vendor/** + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/dspace-webapp-boot/src/main/java/org/dspace/app/Application.java b/dspace-webapp-boot/src/main/java/org/dspace/app/Application.java new file mode 100644 index 00000000000..90039887f86 --- /dev/null +++ b/dspace-webapp-boot/src/main/java/org/dspace/app/Application.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; + +import org.dspace.app.rest.WebApplication; +import org.dspace.app.rest.utils.DSpaceConfigurationInitializer; +import org.dspace.app.rest.utils.DSpaceKernelInitializer; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +@SpringBootApplication(scanBasePackageClasses = WebApplication.class) +public class Application extends SpringBootServletInitializer { + + public static void main(String[] args) { + new SpringApplicationBuilder(Application.class) + .initializers(new DSpaceKernelInitializer(), new DSpaceConfigurationInitializer()) + .run(args); + } + + /** + * Override the default SpringBootServletInitializer.configure() method, + * passing it this Application class. + *

+ * This is necessary to allow us to build a deployable WAR, rather than + * always relying on embedded Tomcat. + *

+ * See: http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-deployable-war-file + * + * @param application + * @return + */ + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + // Pass this Application class, and our initializers for DSpace Kernel and Configuration + // NOTE: Kernel must be initialized before Configuration + return application.sources(Application.class) + .initializers(new DSpaceKernelInitializer(), new DSpaceConfigurationInitializer()); + } +} diff --git a/dspace-server-webapp/src/main/resources/application.properties b/dspace-webapp-boot/src/main/resources/application.properties similarity index 99% rename from dspace-server-webapp/src/main/resources/application.properties rename to dspace-webapp-boot/src/main/resources/application.properties index a10e0f98a00..8dd8e2903cc 100644 --- a/dspace-server-webapp/src/main/resources/application.properties +++ b/dspace-webapp-boot/src/main/resources/application.properties @@ -27,6 +27,7 @@ # http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html # +server.servlet.context-path=/server ######################## # DSpace Settings # diff --git a/dspace-server-webapp/src/main/webapp/index.html b/dspace-webapp-boot/src/main/resources/static/index.html similarity index 100% rename from dspace-server-webapp/src/main/webapp/index.html rename to dspace-webapp-boot/src/main/resources/static/index.html diff --git a/dspace-server-webapp/src/main/webapp/js/hal/http/client.js b/dspace-webapp-boot/src/main/resources/static/js/hal/http/client.js similarity index 100% rename from dspace-server-webapp/src/main/webapp/js/hal/http/client.js rename to dspace-webapp-boot/src/main/resources/static/js/hal/http/client.js diff --git a/dspace-server-webapp/src/main/webapp/js/vendor/CustomPostForm.js b/dspace-webapp-boot/src/main/resources/static/js/vendor/CustomPostForm.js similarity index 100% rename from dspace-server-webapp/src/main/webapp/js/vendor/CustomPostForm.js rename to dspace-webapp-boot/src/main/resources/static/js/vendor/CustomPostForm.js diff --git a/dspace-server-webapp/src/main/webapp/login.html b/dspace-webapp-boot/src/main/resources/static/login.html similarity index 100% rename from dspace-server-webapp/src/main/webapp/login.html rename to dspace-webapp-boot/src/main/resources/static/login.html diff --git a/dspace-server-webapp/src/main/webapp/styles.css b/dspace-webapp-boot/src/main/resources/static/styles.css similarity index 100% rename from dspace-server-webapp/src/main/webapp/styles.css rename to dspace-webapp-boot/src/main/resources/static/styles.css diff --git a/dspace/modules/server/src/test/java/org/dspace/app/rest/example/ExampleController.java b/dspace-webapp-boot/src/test/java/org/dspace/app/rest/example/ExampleController.java similarity index 100% rename from dspace/modules/server/src/test/java/org/dspace/app/rest/example/ExampleController.java rename to dspace-webapp-boot/src/test/java/org/dspace/app/rest/example/ExampleController.java diff --git a/dspace/modules/server/src/test/java/org/dspace/app/rest/example/ExampleControllerIT.java b/dspace-webapp-boot/src/test/java/org/dspace/app/rest/example/ExampleControllerIT.java similarity index 100% rename from dspace/modules/server/src/test/java/org/dspace/app/rest/example/ExampleControllerIT.java rename to dspace-webapp-boot/src/test/java/org/dspace/app/rest/example/ExampleControllerIT.java diff --git a/dspace/config/log4j2-console.xml b/dspace/config/log4j2-console.xml index 3d51b123367..a0322abf19d 100644 --- a/dspace/config/log4j2-console.xml +++ b/dspace/config/log4j2-console.xml @@ -25,7 +25,7 @@ For command line / Ant scripts, we are only concerned about significant warnings/errors. For the full detail, change this to INFO and re-run Ant. --> - + diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml deleted file mode 100644 index f890d39e699..00000000000 --- a/dspace/modules/server/pom.xml +++ /dev/null @@ -1,355 +0,0 @@ - - 4.0.0 - org.dspace.modules - server - war - DSpace Server Webapp:: Local Customizations - Overlay customizations. -This is probably a temporary solution to the build problems. We like to investigate about -the possibility to remove the overlays enable a more flexible extension mechanism. -The use of web-fragment and spring mvc technology allow us to add request handlers -just adding new jar in the classloader - - - modules - org.dspace - cris-2022.03.01-SNAPSHOT - .. - - - - - ${basedir}/../../.. - - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - unpack - prepare-package - - unpack-dependencies - - - org.dspace.modules - additions - - ${project.build.directory}/additions - META-INF/** - - - - - - org.apache.maven.plugins - maven-war-plugin - - false - - true - - - - ${project.build.directory}/additions - WEB-INF/classes - - - - - - prepare-package - - - - - - org.codehaus.gmaven - groovy-maven-plugin - - - setproperty - initialize - - execute - - - - project.properties['agnostic.build.dir'] = project.build.directory.replace(File.separator, '/'); - log.info("Initializing Maven property 'agnostic.build.dir' to: {}", project.properties['agnostic.build.dir']); - - - - - - - - - - - - unit-test-environment - - false - - skipUnitTests - false - - - - - - - maven-dependency-plugin - - ${project.build.directory}/testing - - - org.dspace - dspace-parent - ${project.version} - zip - testEnvironment - - - - - - setupUnitTestEnvironment - generate-test-resources - - unpack - - - - - - - - maven-surefire-plugin - - - - - - ${agnostic.build.dir}/testing/dspace - - true - ${agnostic.build.dir}/testing/dspace/solr/ - - - - - - - - - org.dspace - dspace-server-webapp - test-jar - test - - - - - - - integration-test-environment - - false - - skipIntegrationTests - false - - - - - - - maven-dependency-plugin - - ${project.build.directory}/testing - - - org.dspace - dspace-parent - ${project.version} - zip - testEnvironment - - - - - - setupIntegrationTestEnvironment - pre-integration-test - - unpack - - - - - - - - maven-failsafe-plugin - - - - - ${agnostic.build.dir}/testing/dspace - - true - ${agnostic.build.dir}/testing/dspace/solr/ - - - - - - - - - org.dspace - dspace-server-webapp - test-jar - test - - - - - - oracle-support - - - db.name - oracle - - - - - com.oracle - ojdbc6 - - - - - - - - - org.dspace.modules - additions - - - org.dspace - dspace-server-webapp - classes - - - org.dspace - dspace-server-webapp - war - - - org.apache.solr - solr-solrj - ${solr.client.version} - - - - - org.dspace - dspace-api - test-jar - test - - - org.dspace - dspace-server-webapp - test-jar - test - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.security - spring-security-test - ${spring-security.version} - test - - - com.jayway.jsonpath - json-path-assert - ${json-path.version} - test - - - junit - junit - test - - - com.h2database - h2 - test - - - org.mockito - mockito-inline - test - - - - - org.apache.solr - solr-core - ${solr.client.version} - test - - - - org.apache.commons - commons-text - - - - - org.apache.lucene - lucene-analyzers-icu - test - - - - - diff --git a/dspace/modules/server/src/main/webapp/.gitignore b/dspace/modules/server/src/main/webapp/.gitignore deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/pom.xml b/pom.xml index d30aa94e2a2..6a0ed9272cb 100644 --- a/pom.xml +++ b/pom.xml @@ -798,6 +798,21 @@ + + + dspace-webapp-boot + + + dspace-webapp-boot/pom.xml + + + + dspace-webapp-boot + + + @@ -1133,32 +1148,24 @@ org.dspace dspace-server-webapp - test-jar - cris-2022.03.01-SNAPSHOT - test - - - org.dspace - dspace-rdf cris-2022.03.01-SNAPSHOT org.dspace - dspace-iiif + dspace-server-webapp + test-jar cris-2022.03.01-SNAPSHOT + test org.dspace - dspace-server-webapp + dspace-rdf cris-2022.03.01-SNAPSHOT - jar - classes org.dspace - dspace-server-webapp + dspace-iiif cris-2022.03.01-SNAPSHOT - war From e9ed6d2d6327f1ce8aab8df6f498a462b37e6f56 Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Fri, 10 Mar 2023 15:32:59 +0100 Subject: [PATCH 02/82] [DSC-963] Fixed test configuration --- .../src/test/resources/application.properties | 64 +++++++++++++++++++ .../src/main/resources/application.properties | 7 +- 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 dspace-server-webapp/src/test/resources/application.properties diff --git a/dspace-server-webapp/src/test/resources/application.properties b/dspace-server-webapp/src/test/resources/application.properties new file mode 100644 index 00000000000..9b408d9612d --- /dev/null +++ b/dspace-server-webapp/src/test/resources/application.properties @@ -0,0 +1,64 @@ +# +# 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/ +# + +# Spring Boot's Test application.properties + +######################## +# Jackson serialization settings +# +spring.jackson.serialization.fail-on-empty-beans=false + +######################## +# Internationalization +# +# Base Path for our messages file (i18n) +spring.messages.basename=i18n/messages +spring.messages.encoding=UTF-8 + +######################## +# URI Encoding and Decoding +# +# +# Charset of HTTP requests and responses. Added to the "Content-Type" header if not set explicitly. +server.servlet.encoding.charset=UTF-8 +# Force the encoding to the configured charset on HTTP requests and responses. +server.servlet.encoding.force=true + +########################### +# Server Properties +# +# Error handling settings +server.error.include-stacktrace = never + +# When to include the error message in error responses (introduced in Spring 2.3.x) +server.error.include-message = always + +# Spring Boot proxy configuration (can be overridden in local.cfg). +server.forward-headers-strategy=FRAMEWORK + +###################### +# Cache Properties +# Added for IIIF cache support. +# Path to configuration file. +spring.cache.jcache.config=classpath:iiif/cache/ehcache.xml + +###################### +# Spring Boot Autoconfigure +# +# TODO: At some point we may want to investigate whether we can re-enable these and remove the custom DSpace init code +spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, \ + org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, \ + org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, \ + org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration, \ + org.springframework.boot.autoconfigure.velocity.VelocityAutoConfiguration + +spring.main.allow-bean-definition-overriding = true + +######################### +# Spring Boot Logging levels +logging.config = classpath:log4j2-test.xml diff --git a/dspace-webapp-boot/src/main/resources/application.properties b/dspace-webapp-boot/src/main/resources/application.properties index 8dd8e2903cc..0c26d530b74 100644 --- a/dspace-webapp-boot/src/main/resources/application.properties +++ b/dspace-webapp-boot/src/main/resources/application.properties @@ -27,7 +27,6 @@ # http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html # -server.servlet.context-path=/server ######################## # DSpace Settings # @@ -38,6 +37,12 @@ server.servlet.context-path=/server # NOTE: this configuration is filled out by Apache Ant during the DSpace install/update process. It does NOT # interact with or read its configuration from dspace.cfg. dspace.dir=${dspace.dir} + +######################## +# Servlet context path configuration for spring boot application running with embedded tomcat +# +server.servlet.context-path=/server + ######################## # Jackson serialization settings # From 98ef4e560f9625df842cd4a5d442c9b852eff503 Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Fri, 10 Mar 2023 15:37:57 +0100 Subject: [PATCH 03/82] [DSC-963] Fixed dspace pom --- dspace/pom.xml | 6 +++++- pom.xml | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/dspace/pom.xml b/dspace/pom.xml index 47ef9c46475..e3e05f0c096 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -217,7 +217,11 @@ org.dspace dspace-server-webapp - war + compile + + + org.dspace + dspace-webapp-boot compile diff --git a/pom.xml b/pom.xml index 6a0ed9272cb..fd6542421c7 100644 --- a/pom.xml +++ b/pom.xml @@ -1157,6 +1157,11 @@ cris-2022.03.01-SNAPSHOT test + + org.dspace + dspace-webapp-boot + cris-2022.03.01-SNAPSHOT + org.dspace dspace-rdf From baeab16708a01f0c6a2d32886040292288174190 Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Fri, 10 Mar 2023 16:14:50 +0100 Subject: [PATCH 04/82] [DSC-963] Improved tests configuration --- .../src/main/resources/application.properties | 0 .../src/test/resources/application.properties | 64 ------------------- 2 files changed, 64 deletions(-) rename {dspace-webapp-boot => dspace-server-webapp}/src/main/resources/application.properties (100%) delete mode 100644 dspace-server-webapp/src/test/resources/application.properties diff --git a/dspace-webapp-boot/src/main/resources/application.properties b/dspace-server-webapp/src/main/resources/application.properties similarity index 100% rename from dspace-webapp-boot/src/main/resources/application.properties rename to dspace-server-webapp/src/main/resources/application.properties diff --git a/dspace-server-webapp/src/test/resources/application.properties b/dspace-server-webapp/src/test/resources/application.properties deleted file mode 100644 index 9b408d9612d..00000000000 --- a/dspace-server-webapp/src/test/resources/application.properties +++ /dev/null @@ -1,64 +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/ -# - -# Spring Boot's Test application.properties - -######################## -# Jackson serialization settings -# -spring.jackson.serialization.fail-on-empty-beans=false - -######################## -# Internationalization -# -# Base Path for our messages file (i18n) -spring.messages.basename=i18n/messages -spring.messages.encoding=UTF-8 - -######################## -# URI Encoding and Decoding -# -# -# Charset of HTTP requests and responses. Added to the "Content-Type" header if not set explicitly. -server.servlet.encoding.charset=UTF-8 -# Force the encoding to the configured charset on HTTP requests and responses. -server.servlet.encoding.force=true - -########################### -# Server Properties -# -# Error handling settings -server.error.include-stacktrace = never - -# When to include the error message in error responses (introduced in Spring 2.3.x) -server.error.include-message = always - -# Spring Boot proxy configuration (can be overridden in local.cfg). -server.forward-headers-strategy=FRAMEWORK - -###################### -# Cache Properties -# Added for IIIF cache support. -# Path to configuration file. -spring.cache.jcache.config=classpath:iiif/cache/ehcache.xml - -###################### -# Spring Boot Autoconfigure -# -# TODO: At some point we may want to investigate whether we can re-enable these and remove the custom DSpace init code -spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, \ - org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, \ - org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, \ - org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration, \ - org.springframework.boot.autoconfigure.velocity.VelocityAutoConfiguration - -spring.main.allow-bean-definition-overriding = true - -######################### -# Spring Boot Logging levels -logging.config = classpath:log4j2-test.xml From 05b6251469d5873a7bda8edc27cf81b17d22e2de Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Fri, 10 Mar 2023 16:42:00 +0100 Subject: [PATCH 05/82] [DSC-963] Fixed dspace-server-webapp pom --- dspace-server-webapp/pom.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index e6423a7bc8b..fa607629e7d 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -57,6 +57,25 @@ + + org.apache.maven.plugins + maven-jar-plugin + + + + true + true + + + + + + + test-jar + + + + From ab2ff11216eb38598e9fbe32ada6a496e2633f8f Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Fri, 10 Mar 2023 17:39:33 +0100 Subject: [PATCH 06/82] [DSC-963] Fixed Sword tests --- .../src/test/java/org/dspace/app/rdf/RdfIT.java | 2 +- .../src/test/java/org/dspace/app/sword/Swordv1IT.java | 2 +- .../src/test/java/org/dspace/app/sword2/Swordv2IT.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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 85ab3dcadd7..10f06370ad5 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 @@ -47,7 +47,7 @@ */ // Ensure the RDF endpoint IS ENABLED before any tests run. // This annotation overrides default DSpace config settings loaded into Spring Context -@TestPropertySource(properties = {"rdf.enabled = true"}) +@TestPropertySource(properties = {"rdf.enabled = true", "server.servlet.context-path = /"}) public class RdfIT extends AbstractWebClientIntegrationTest { @Autowired diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/sword/Swordv1IT.java b/dspace-server-webapp/src/test/java/org/dspace/app/sword/Swordv1IT.java index 24244e1773e..ffef89316b9 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/sword/Swordv1IT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/sword/Swordv1IT.java @@ -34,7 +34,7 @@ */ // Ensure the SWORD SERVER IS ENABLED before any tests run. // This annotation overrides default DSpace config settings loaded into Spring Context -@TestPropertySource(properties = {"sword-server.enabled = true"}) +@TestPropertySource(properties = { "sword-server.enabled = true", "server.servlet.context-path = /" }) public class Swordv1IT extends AbstractWebClientIntegrationTest { @Autowired diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/sword2/Swordv2IT.java b/dspace-server-webapp/src/test/java/org/dspace/app/sword2/Swordv2IT.java index 95ec7625141..f9caeead664 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/sword2/Swordv2IT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/sword2/Swordv2IT.java @@ -34,7 +34,7 @@ */ // Ensure the SWORDv2 SERVER IS ENABLED before any tests run. // This annotation overrides default DSpace config settings loaded into Spring Context -@TestPropertySource(properties = {"swordv2-server.enabled = true"}) +@TestPropertySource(properties = {"swordv2-server.enabled = true", "server.servlet.context-path = /"}) public class Swordv2IT extends AbstractWebClientIntegrationTest { @Autowired From 819bf788081dbc69c82ce050ba64aeb3a964415e Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Fri, 10 Mar 2023 17:50:08 +0100 Subject: [PATCH 07/82] [DSC-963] Fixed dspace pom --- dspace/pom.xml | 1 + pom.xml | 13 +++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/dspace/pom.xml b/dspace/pom.xml index e3e05f0c096..0dba032e688 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -222,6 +222,7 @@ org.dspace dspace-webapp-boot + war compile diff --git a/pom.xml b/pom.xml index fd6542421c7..e17dfbf384c 100644 --- a/pom.xml +++ b/pom.xml @@ -1148,29 +1148,30 @@ org.dspace dspace-server-webapp + test-jar cris-2022.03.01-SNAPSHOT + test org.dspace - dspace-server-webapp - test-jar + dspace-rdf cris-2022.03.01-SNAPSHOT - test org.dspace - dspace-webapp-boot + dspace-iiif cris-2022.03.01-SNAPSHOT org.dspace - dspace-rdf + dspace-server-webapp cris-2022.03.01-SNAPSHOT org.dspace - dspace-iiif + dspace-webapp-boot cris-2022.03.01-SNAPSHOT + war From 382105dfdae418eb6a238f1bfa9d968c82a58727 Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Fri, 10 Mar 2023 19:32:46 +0100 Subject: [PATCH 08/82] [DSC-963] Set default servlet context path on application-test.properties --- .../src/test/java/org/dspace/app/rdf/RdfIT.java | 2 +- .../src/test/java/org/dspace/app/sword/Swordv1IT.java | 2 +- .../src/test/java/org/dspace/app/sword2/Swordv2IT.java | 2 +- .../src/test/resources/application-test.properties | 4 +++- 4 files changed, 6 insertions(+), 4 deletions(-) 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 10f06370ad5..85ab3dcadd7 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 @@ -47,7 +47,7 @@ */ // Ensure the RDF endpoint IS ENABLED before any tests run. // This annotation overrides default DSpace config settings loaded into Spring Context -@TestPropertySource(properties = {"rdf.enabled = true", "server.servlet.context-path = /"}) +@TestPropertySource(properties = {"rdf.enabled = true"}) public class RdfIT extends AbstractWebClientIntegrationTest { @Autowired diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/sword/Swordv1IT.java b/dspace-server-webapp/src/test/java/org/dspace/app/sword/Swordv1IT.java index ffef89316b9..24244e1773e 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/sword/Swordv1IT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/sword/Swordv1IT.java @@ -34,7 +34,7 @@ */ // Ensure the SWORD SERVER IS ENABLED before any tests run. // This annotation overrides default DSpace config settings loaded into Spring Context -@TestPropertySource(properties = { "sword-server.enabled = true", "server.servlet.context-path = /" }) +@TestPropertySource(properties = {"sword-server.enabled = true"}) public class Swordv1IT extends AbstractWebClientIntegrationTest { @Autowired diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/sword2/Swordv2IT.java b/dspace-server-webapp/src/test/java/org/dspace/app/sword2/Swordv2IT.java index f9caeead664..95ec7625141 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/sword2/Swordv2IT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/sword2/Swordv2IT.java @@ -34,7 +34,7 @@ */ // Ensure the SWORDv2 SERVER IS ENABLED before any tests run. // This annotation overrides default DSpace config settings loaded into Spring Context -@TestPropertySource(properties = {"swordv2-server.enabled = true", "server.servlet.context-path = /"}) +@TestPropertySource(properties = {"swordv2-server.enabled = true"}) public class Swordv2IT extends AbstractWebClientIntegrationTest { @Autowired diff --git a/dspace-server-webapp/src/test/resources/application-test.properties b/dspace-server-webapp/src/test/resources/application-test.properties index 9a396cf8e5b..e92e1166e35 100644 --- a/dspace-server-webapp/src/test/resources/application-test.properties +++ b/dspace-server-webapp/src/test/resources/application-test.properties @@ -14,4 +14,6 @@ ## Log4j2 configuration for test environment ## This file is found on classpath at src/test/resources/log4j2-test.xml -logging.config = classpath:log4j2-test.xml \ No newline at end of file +logging.config = classpath:log4j2-test.xml + +server.servlet.context-path=/ \ No newline at end of file From 7524053a5c10071b99edf453a112ecc30f06779f Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Fri, 10 Mar 2023 20:41:07 +0100 Subject: [PATCH 09/82] [DSC-963] Improved TestApplication configuration --- .../test/java/org/dspace/app/{ => rest}/TestApplication.java | 5 ++--- .../app/rest/test/AbstractControllerIntegrationTest.java | 2 +- .../app/rest/test/AbstractWebClientIntegrationTest.java | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) rename dspace-server-webapp/src/test/java/org/dspace/app/{ => rest}/TestApplication.java (70%) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/TestApplication.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/TestApplication.java similarity index 70% rename from dspace-server-webapp/src/test/java/org/dspace/app/TestApplication.java rename to dspace-server-webapp/src/test/java/org/dspace/app/rest/TestApplication.java index 0f80e866edd..e387e3f0024 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/TestApplication.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/TestApplication.java @@ -5,12 +5,11 @@ * * http://www.dspace.org/license/ */ -package org.dspace.app; +package org.dspace.app.rest; -import org.dspace.app.rest.WebApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -@SpringBootApplication(scanBasePackageClasses = WebApplication.class) +@SpringBootApplication public class TestApplication { } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractControllerIntegrationTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractControllerIntegrationTest.java index 4ec66fb0008..a27e0ab75c8 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractControllerIntegrationTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractControllerIntegrationTest.java @@ -23,7 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.StringUtils; import org.dspace.AbstractIntegrationTestWithDatabase; -import org.dspace.app.TestApplication; +import org.dspace.app.rest.TestApplication; import org.dspace.app.rest.model.patch.Operation; import org.dspace.app.rest.utils.DSpaceConfigurationInitializer; import org.dspace.app.rest.utils.DSpaceKernelInitializer; diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java index be0a27b4ebd..7f58a9999dd 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java @@ -9,7 +9,7 @@ import org.apache.commons.lang3.StringUtils; import org.dspace.AbstractIntegrationTestWithDatabase; -import org.dspace.app.TestApplication; +import org.dspace.app.rest.TestApplication; import org.dspace.app.rest.utils.DSpaceConfigurationInitializer; import org.dspace.app.rest.utils.DSpaceKernelInitializer; import org.junit.runner.RunWith; From fa651fea6d986c7433b93e93c4bb2f531b9eefc9 Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Mon, 13 Mar 2023 10:33:06 +0100 Subject: [PATCH 10/82] [DSC-963] Added @Order on AdminRestPermissionEvaluatorPlugin --- .../app/rest/security/AdminRestPermissionEvaluatorPlugin.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/AdminRestPermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/AdminRestPermissionEvaluatorPlugin.java index 0d251f6400f..338eed4a734 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/AdminRestPermissionEvaluatorPlugin.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/AdminRestPermissionEvaluatorPlugin.java @@ -20,6 +20,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Component; @@ -29,6 +31,7 @@ * the authenticated EPerson is allowed to perform the requested action. */ @Component +@Order(value = Ordered.HIGHEST_PRECEDENCE) public class AdminRestPermissionEvaluatorPlugin extends RestObjectPermissionEvaluatorPlugin { private static final Logger log = LoggerFactory.getLogger(RestObjectPermissionEvaluatorPlugin.class); From 4436549f0b33e90de3069d38a5a5272d889db3fe Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Mon, 13 Mar 2023 11:46:20 +0100 Subject: [PATCH 11/82] [DSC-963] Minor improvements --- dspace-server-webapp/pom.xml | 31 +++++++------------ .../org/dspace/app/rest/WebApplication.java | 10 ++---- .../app/{rest => }/TestApplication.java | 11 +++++-- .../AbstractControllerIntegrationTest.java | 2 +- .../AbstractWebClientIntegrationTest.java | 2 +- .../main/java/org/dspace/app/Application.java | 13 ++++++++ dspace/config/log4j2-console.xml | 2 +- 7 files changed, 39 insertions(+), 32 deletions(-) rename dspace-server-webapp/src/test/java/org/dspace/app/{rest => }/TestApplication.java (55%) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index fa607629e7d..c884ea7d57c 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -28,6 +28,18 @@ + + org.apache.maven.plugins + maven-jar-plugin + + + + + test-jar + + + + - + From 5cd9476fb812c56e0fe44b04d82ec1e73c857bbf Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Mon, 13 Mar 2023 17:44:07 +0100 Subject: [PATCH 12/82] [DSC-963] Fixed ItemRestRepositoryIT and GenericAuthorizationFeatureIT integration tests --- .../ExternalSourceItemUriListHandler.java | 8 +++++--- .../GenericAuthorizationFeatureIT.java | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/handler/ExternalSourceItemUriListHandler.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/handler/ExternalSourceItemUriListHandler.java index d619100bf67..201a7ba1633 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/handler/ExternalSourceItemUriListHandler.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/handler/ExternalSourceItemUriListHandler.java @@ -30,16 +30,19 @@ @Component public class ExternalSourceItemUriListHandler extends ExternalSourceEntryItemUriListHandler { + private Pattern pattern = Pattern.compile("\\/api\\/core\\/items\\/(.*)"); + @Autowired private ItemService itemService; @Override @SuppressWarnings("rawtypes") public boolean supports(List uriList, String method,Class clazz) { - if (clazz != Item.class) { + if (clazz != Item.class || uriList.size() != 1) { return false; } - return true; + + return pattern.matcher(uriList.get(0)).find(); } @Override @@ -61,7 +64,6 @@ public boolean validate(Context context, HttpServletRequest request, List uriList) { Item item = null; String url = uriList.get(0); - Pattern pattern = Pattern.compile("\\/api\\/core\\/items\\/(.*)"); Matcher matcher = pattern.matcher(url); if (!matcher.find()) { throw new DSpaceBadRequestException("The uri: " + url + " doesn't resolve to an item"); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/GenericAuthorizationFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/GenericAuthorizationFeatureIT.java index 1d3b5b05160..e6ccf5954c7 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/GenericAuthorizationFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/GenericAuthorizationFeatureIT.java @@ -757,7 +757,8 @@ public void testCanMoveAdmin() throws Exception { // Verify the general admin has this feature on item 1 getClient(adminToken).perform( get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + + "http://localhost/api/core/items/" + item1.getID()) + .param("size", "1000")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); @@ -765,7 +766,8 @@ public void testCanMoveAdmin() throws Exception { // Verify community A admin has this feature on item 1 getClient(communityAAdminToken).perform( get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + + "http://localhost/api/core/items/" + item1.getID()) + .param("size", "1000")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); @@ -773,7 +775,8 @@ public void testCanMoveAdmin() throws Exception { // Verify collection X admin has this feature on item 1 getClient(collectionXAdminToken).perform( get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + + "http://localhost/api/core/items/" + item1.getID()) + .param("size", "1000")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); @@ -781,7 +784,8 @@ public void testCanMoveAdmin() throws Exception { // Verify item 1 admin doesn’t have this feature on item 1 getClient(item1AdminToken).perform( get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + + "http://localhost/api/core/items/" + item1.getID()) + .param("size", "1000")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -789,7 +793,8 @@ public void testCanMoveAdmin() throws Exception { // Verify community A admin doesn’t have this feature on item 2 getClient(communityAAdminToken).perform( get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/items/" + item2.getID())) + + "http://localhost/api/core/items/" + item2.getID()) + .param("size", "1000")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -808,7 +813,8 @@ public void testCanMoveAdmin() throws Exception { // verify item 1 write has this feature on item 1 getClient(item1WriterToken).perform( get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + + "http://localhost/api/core/items/" + item1.getID()) + .param("size", "1000")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canMove')]") .exists()); From 4c303770d56ef863155d03c74c54267324f82172 Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Tue, 14 Mar 2023 11:31:12 +0100 Subject: [PATCH 13/82] [DSC-963] Added size parameter on GenericAuthorizationFeatureIT tests --- .../GenericAuthorizationFeatureIT.java | 340 ++++++++++-------- 1 file changed, 188 insertions(+), 152 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/GenericAuthorizationFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/GenericAuthorizationFeatureIT.java index e6ccf5954c7..d59ef00018b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/GenericAuthorizationFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/GenericAuthorizationFeatureIT.java @@ -209,7 +209,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { String siteId = ContentServiceFactory.getInstance().getSiteService().findSite(context).getID().toString(); // Verify the general admin has this feature on the site - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/sites/" + siteId)) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -217,14 +217,14 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify community A admin doesn’t have this feature on the site getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/sites/" + siteId)) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify the general admin has this feature on community A - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -232,7 +232,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify community A admin has this feature on community A getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -240,7 +240,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify community A admin has this feature on community AA getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityAA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -248,7 +248,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify collection X admin doesn’t have this feature on community A getClient(collectionXAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -256,7 +256,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify community A admin doesn’t have this feature on community B getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityB.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -264,7 +264,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify the general admin has this feature on collection X getClient(adminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionX.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -272,7 +272,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify community A admin has this feature on collection X getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionX.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -280,7 +280,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify collection X admin has this feature on collection X getClient(collectionXAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionX.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -288,7 +288,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify item 1 admin doesn’t have this feature on collection X getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionX.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -296,7 +296,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify collection X admin doesn’t have this feature on collection Y getClient(collectionXAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionY.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -304,7 +304,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify the general admin has this feature on item 1 getClient(adminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -312,7 +312,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify community A admin has this feature on item 1 getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -320,7 +320,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify collection X admin has this feature on item 1 getClient(collectionXAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -328,7 +328,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify item 1 admin has this feature on item 1 getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -336,7 +336,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify item 1 admin doesn’t have this feature on item 2 getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item2.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -344,7 +344,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify the general admin has this feature on the bundle in item 1 getClient(adminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -352,7 +352,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify community A admin has this feature on the bundle in item 1 getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -360,7 +360,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify collection X admin has this feature on the bundle in item 1 getClient(collectionXAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -368,7 +368,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify item 1 admin has this feature on the bundle in item 1 getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -376,7 +376,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify item 1 admin doesn’t have this feature on the bundle in item 2 getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle2.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -384,7 +384,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify the general admin has this feature on the bitstream in item 1 getClient(adminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -392,7 +392,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify community A admin has this feature on the bitstream in item 1 getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -400,7 +400,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify collection X admin has this feature on the bitstream in item 1 getClient(collectionXAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -408,7 +408,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify item 1 admin has this feature on the bitstream in item 1 getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -416,7 +416,7 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { // Verify item 1 admin doesn’t have this feature on the bitstream in item 2 getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bitstreams/" + bitstream2.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -431,7 +431,7 @@ private void testAdminsHavePermissionsItem(String feature) throws Exception { // Verify the general admin has this feature on item 1 getClient(adminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -439,7 +439,7 @@ private void testAdminsHavePermissionsItem(String feature) throws Exception { // Verify community A admin has this feature on item 1 getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -447,7 +447,7 @@ private void testAdminsHavePermissionsItem(String feature) throws Exception { // Verify collection X admin has this feature on item 1 getClient(collectionXAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -455,7 +455,7 @@ private void testAdminsHavePermissionsItem(String feature) throws Exception { // Verify item 1 admin has this feature on item 1 getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -463,7 +463,7 @@ private void testAdminsHavePermissionsItem(String feature) throws Exception { // Verify community A admin doesn’t have this feature on item 2 getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item2.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -480,14 +480,14 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // (or doesn’t have access otherwise) if (hasDSOAccess) { getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); } else { getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -496,7 +496,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify community A write doesn’t have this feature on community AA getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityAA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -504,7 +504,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify community A write doesn’t have this feature on collection X getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionX.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -512,7 +512,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify community A write doesn’t have this feature on item 1 getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -520,7 +520,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify community A write doesn’t have this feature on the bundle in item 1 getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -528,7 +528,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify community A write doesn’t have this feature on the bitstream in item 1 getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -536,7 +536,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify collection X write doesn’t have this feature on community A getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -544,7 +544,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify collection X write doesn’t have this feature on community AA getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityAA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -554,14 +554,14 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // (or doesn’t have access otherwise) if (hasDSOAccess) { getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionX.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); } else { getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionX.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -570,7 +570,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify collection X write doesn’t have this feature on item 1 getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -578,7 +578,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify collection X write doesn’t have this feature on the bundle in item 1 getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -586,7 +586,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify collection X write doesn’t have this feature on the bitstream in item 1 getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -594,7 +594,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify item 1 write doesn’t have this feature on community A getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -602,7 +602,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify item 1 write doesn’t have this feature on community AA getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityAA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -610,7 +610,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify item 1 write doesn’t have this feature on collection X getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionX.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -620,14 +620,14 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // (or doesn’t have access otherwise) if (hasDSOAccess) { getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); } else { getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -636,7 +636,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify item 1 write doesn’t have this feature on the bundle in item 1 getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -644,7 +644,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify item 1 write doesn’t have this feature on the bitstream in item 1 getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -652,7 +652,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify community A write doesn’t have this feature on community B getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityB.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -660,7 +660,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify collection X write doesn’t have this feature on collection Y getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionY.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -668,7 +668,7 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify item 1 write doesn’t have this feature on item 2 getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item2.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -682,7 +682,7 @@ private void testWriteUsersHavePermissionsItem(String feature, boolean hasDSOAcc // Verify community A write doesn’t have this feature on item 1 getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -690,7 +690,7 @@ private void testWriteUsersHavePermissionsItem(String feature, boolean hasDSOAcc // Verify collection X write doesn’t have this feature on item 1 getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -700,14 +700,14 @@ private void testWriteUsersHavePermissionsItem(String feature, boolean hasDSOAcc // (or doesn’t have access otherwise) if (hasDSOAccess) { getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); } else { getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -716,7 +716,7 @@ private void testWriteUsersHavePermissionsItem(String feature, boolean hasDSOAcc // Verify item 1 write doesn’t have this feature on item 2 getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item2.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -756,45 +756,40 @@ public void testCanMoveAdmin() throws Exception { // Verify the general admin has this feature on item 1 getClient(adminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID()) - .param("size", "1000")) + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on item 1 getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID()) - .param("size", "1000")) + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on item 1 getClient(collectionXAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID()) - .param("size", "1000")) + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin doesn’t have this feature on item 1 getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID()) - .param("size", "1000")) + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify community A admin doesn’t have this feature on item 2 getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/items/" + item2.getID()) - .param("size", "1000")) + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + + "http://localhost/api/core/items/" + item2.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -812,9 +807,8 @@ public void testCanMoveAdmin() throws Exception { // verify item 1 write has this feature on item 1 getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID()) - .param("size", "1000")) + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canMove')]") .exists()); @@ -836,7 +830,7 @@ public void testCanMoveWriter() throws Exception { String item1WriterToken = getAuthToken(item1Writer.getEmail(), password); // verify item 1 write has this feature on item 1 getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canMove')]") @@ -873,28 +867,30 @@ public void testCanDeleteAdmin() throws Exception { final String feature = "canDelete"; // Verify the general admin doesn’t have this feature on the site - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/sites/" + siteId)) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify the general admin has this feature on community A - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on community A - getClient(communityAAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on community AA - getClient(communityAAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityAA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -914,161 +910,173 @@ public void testCanDeleteAdmin() throws Exception { .build(); context.restoreAuthSystemState(); String communityAAAdminToken = getAuthToken(communityAAAdmin.getEmail(), password); - getClient(communityAAAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAAAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityAA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X admin doesn’t have this feature on community A - getClient(collectionXAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(collectionXAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify community A admin doesn’t have this feature on community B - getClient(communityAAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityB.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify the general admin has this feature on collection X - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionX.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on collection X - getClient(communityAAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionX.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin doesn’t have this feature on collection X - getClient(collectionXAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(collectionXAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionX.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify item 1 admin doesn’t have this feature on collection X - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionX.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X admin doesn’t have this feature on collection Y - getClient(collectionXAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(collectionXAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionY.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify the general admin has this feature on item 1 - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on item 1 - getClient(communityAAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on item 1 - getClient(collectionXAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(collectionXAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin doesn’t have this feature on item 1 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify item 1 admin doesn’t have this feature on item 2 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item2.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify the general admin has this feature on the bundle in item 1 - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on the bundle in item 1 - getClient(communityAAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on the bundle in item 1 - getClient(collectionXAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(collectionXAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin has this feature on the bundle in item 1 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin doesn’t have this feature on the bundle in item 2 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle2.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify the general admin has this feature on the bitstream in item 1 - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on the bitstream in item 1 - getClient(communityAAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on the bitstream in item 1 - getClient(collectionXAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(collectionXAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin has this feature on the bitstream in item 1 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin doesn’t have this feature on the bitstream in item 2 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bitstreams/" + bitstream2.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1096,7 +1104,8 @@ public void testCanDeleteAdminParent() throws Exception { context.restoreAuthSystemState(); String communityAAAdminToken = getAuthToken(communityAAAdmin.getEmail(), password); //verify the community AA admin has this feature on community AA - getClient(communityAAAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAAAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityAA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1111,7 +1120,8 @@ public void testCanDeleteAdminParent() throws Exception { .build(); context.restoreAuthSystemState(); // verify collection X admin has this feature on collection X - getClient(collectionXAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(collectionXAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionX.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1126,7 +1136,7 @@ public void testCanDeleteAdminParent() throws Exception { .build(); context.restoreAuthSystemState(); // verify item 1 admin has this feature on item 1 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1157,13 +1167,15 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String communityADeleterToken = getAuthToken(communityADeleter.getEmail(), password); // Verify the user has this feature on community A - getClient(communityADeleterToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityADeleterToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify this user doesn’t have this feature on community AA - getClient(communityADeleterToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityADeleterToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityAA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1185,19 +1197,22 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String communityARemoverToken = getAuthToken(communityARemover.getEmail(), password); // Verify the user has this feature on community AA - getClient(communityARemoverToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityARemoverToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityAA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify this user doesn’t have this feature on community A - getClient(communityARemoverToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityARemoverToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify this user doesn’t have this feature on collection X - getClient(communityARemoverToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityARemoverToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionX.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1218,19 +1233,22 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String communityAARemoverToken = getAuthToken(communityAARemover.getEmail(), password); // Verify the user has this feature on collection X - getClient(communityAARemoverToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAARemoverToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionX.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify this user doesn’t have this feature on community AA - getClient(communityAARemoverToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAARemoverToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/communities/" + communityAA.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify this user doesn’t have this feature on item 1 - getClient(communityAARemoverToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAARemoverToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1251,7 +1269,8 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String collectionXRemoverToken = getAuthToken(collectionXRemover.getEmail(), password); // Verify the user doesn’t have this feature on item 1 - getClient(collectionXRemoverToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(collectionXRemoverToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1272,7 +1291,7 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String item1DeleterToken = getAuthToken(item1Deleter.getEmail(), password); // Verify the user doesn’t have this feature on item 1 - getClient(item1DeleterToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1DeleterToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1299,21 +1318,21 @@ public void testCanDeleteMinimalPermissions() throws Exception { String collectionXRemoverItem1DeleterToken = getAuthToken(collectionXRemoverItem1Deleter.getEmail(), password); // Verify the user has this feature on item 1 getClient(collectionXRemoverItem1DeleterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify this user doesn’t have this feature on collection X getClient(collectionXRemoverItem1DeleterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/collections/" + collectionX.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify this user doesn’t have this feature on the bundle in item 1 getClient(collectionXRemoverItem1DeleterToken).perform( - get("/api/authz/authorizations/search/object?embed=feature&uri=" + get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1334,19 +1353,19 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String item1RemoverToken = getAuthToken(item1Remover.getEmail(), password); // Verify the user has this feature on the bundle in item 1 - getClient(item1RemoverToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1RemoverToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify this user doesn’t have this feature on item 1 - getClient(item1RemoverToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1RemoverToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify this user doesn’t have this feature on the bitstream in item 1 - getClient(item1RemoverToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1RemoverToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1367,7 +1386,8 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String bundle1RemoverToken = getAuthToken(bundle1Remover.getEmail(), password); // Verify the user doesn’t have this feature on the bitstream in item 1 - getClient(bundle1RemoverToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(bundle1RemoverToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1394,7 +1414,8 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String bundle1item1RemoverToken = getAuthToken(bundle1item1Remover.getEmail(), password); // Verify the user has this feature on the bitstream in item 1 - getClient(bundle1item1RemoverToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(bundle1item1RemoverToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1410,35 +1431,38 @@ public void testCanReorderBitstreamsAdmin() throws Exception { final String feature = "canReorderBitstreams"; // Verify the general admin has this feature on the bundle in item 1 - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on the bundle in item 1 - getClient(communityAAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on the bundle in item 1 - getClient(collectionXAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(collectionXAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin has this feature on the bundle in item 1 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin doesn’t have this feature on the bundle in item 2 - getClient(communityAAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle2.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1453,19 +1477,21 @@ public void testCanReorderBitstreamsWriter() throws Exception { final String feature = "canReorderBitstreams"; // Verify community A write doesn’t have this feature on the bundle in item 1 - getClient(communityAWriterToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAWriterToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X write doesn’t have this feature on the bundle in item 1 - getClient(collectionXWriterToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(collectionXWriterToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify item 1 write doesn’t have this feature on the bundle in item 1 - getClient(item1WriterToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1WriterToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1473,7 +1499,8 @@ public void testCanReorderBitstreamsWriter() throws Exception { // Create a new user, grant WRITE permissions on the bundle in item 1 to this user // Verify the user has this feature on the bundle in item 1 - getClient(communityAWriterToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAWriterToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1489,35 +1516,38 @@ public void testCanCreateBitstreamAdmin() throws Exception { final String feature = "canCreateBitstream"; // Verify the general admin has this feature on the bundle in item 1 - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on the bundle in item 1 - getClient(communityAAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on the bundle in item 1 - getClient(collectionXAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(collectionXAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin has this feature on the bundle in item 1 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin doesn’t have this feature on the bundle in item 2 - getClient(communityAAdminToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAAdminToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle2.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1532,21 +1562,23 @@ public void testCanCreateBitstreamWriter() throws Exception { final String feature = "canCreateBitstream"; // Verify community A write doesn’t have this feature on the bundle in item 1 - getClient(communityAWriterToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAWriterToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X write doesn’t have this feature on the bundle in item 1 - getClient(collectionXWriterToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(collectionXWriterToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify item 1 write doesn’t have this feature on the bundle in item 1 - getClient(item1WriterToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1WriterToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1567,7 +1599,7 @@ public void testCanCreateBitstreamWriter() throws Exception { context.restoreAuthSystemState(); String bundle1WriterToken = getAuthToken(bundle1Writer.getEmail(), password); // Verify the user doesn’t have this feature on the bundle in item 1 - getClient(bundle1WriterToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(bundle1WriterToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1588,7 +1620,7 @@ public void testCanCreateBitstreamWriter() throws Exception { context.restoreAuthSystemState(); String bundle1AdderToken = getAuthToken(bundle1Adder.getEmail(), password); // Verify the user doesn’t have this feature on the bundle in item 1 - getClient(bundle1AdderToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(bundle1AdderToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1625,7 +1657,8 @@ public void testCanCreateBitstreamWriter() throws Exception { context.restoreAuthSystemState(); String bundle1WriterAdderToken = getAuthToken(bundle1WriterAdder.getEmail(), password); // Verify the user has this feature on the bundle in item 1 - getClient(bundle1WriterAdderToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(bundle1WriterAdderToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/bundles/" + bundle1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1645,21 +1678,23 @@ public void testCanCreateBundleWriter() throws Exception { final String feature = "canCreateBundle"; // Verify community A write doesn’t have this feature on item 1 - getClient(communityAWriterToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(communityAWriterToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X write doesn’t have this feature on item 1 - getClient(collectionXWriterToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(collectionXWriterToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify item 1 write doesn’t have this feature on item 1 - getClient(item1WriterToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1WriterToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" @@ -1685,7 +1720,8 @@ public void testCanCreateBundleWriter() throws Exception { context.restoreAuthSystemState(); String item1AdderWriterToken = getAuthToken(item1AdderWriter.getEmail(), password); // Verify the user has this feature on item 1 - getClient(item1AdderWriterToken).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" + getClient(item1AdderWriterToken) + .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" + "http://localhost/api/core/items/" + item1.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" From 76fdd16a9b3d374c3fe7f47f8e732bd9fd57025b Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Tue, 14 Mar 2023 12:57:50 +0100 Subject: [PATCH 14/82] [DSC-963] Fixed SubmissionCCLicenseUrlRepositoryIT tests --- .../org/dspace/app/rest/link/DSpaceResourceHalLinkFactory.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/DSpaceResourceHalLinkFactory.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/DSpaceResourceHalLinkFactory.java index c306691eb35..30404e030ab 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/DSpaceResourceHalLinkFactory.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/DSpaceResourceHalLinkFactory.java @@ -21,6 +21,8 @@ import org.dspace.app.rest.model.hateoas.DSpaceResource; import org.dspace.app.rest.utils.Utils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; import org.springframework.data.domain.Pageable; import org.springframework.hateoas.IanaLinkRelations; import org.springframework.hateoas.Link; @@ -33,6 +35,7 @@ * @author Tom Desair (tom dot desair at atmire dot com) */ @Component +@Order(Ordered.HIGHEST_PRECEDENCE) public class DSpaceResourceHalLinkFactory extends HalLinkFactory { @Autowired From fd955c49884073474842fb75d4ced2761207f83e Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Tue, 14 Mar 2023 17:12:09 +0100 Subject: [PATCH 15/82] [DSC-963] Refactoring to maintain server module --- dspace-server-webapp/pom.xml | 17 + .../src/main/resources/static/index.html | 0 .../resources/static/js/hal/http/client.js | 0 .../static/js/vendor/CustomPostForm.js | 0 .../src/main/resources/static/login.html | 0 .../src/main/resources/static/styles.css | 0 dspace/modules/pom.xml | 11 + .../modules/server-boot}/pom.xml | 28 +- .../org/dspace/app/ServerBootApplication.java | 36 ++ dspace/modules/server/pom.xml | 349 ++++++++++++++++++ .../org/dspace/app/ServerApplication.java | 10 +- .../modules/server/src/main/webapp/.gitignore | 0 .../app/rest/example/ExampleController.java | 0 .../app/rest/example/ExampleControllerIT.java | 0 dspace/pom.xml | 6 - pom.xml | 21 -- 16 files changed, 419 insertions(+), 59 deletions(-) rename {dspace-webapp-boot => dspace-server-webapp}/src/main/resources/static/index.html (100%) rename {dspace-webapp-boot => dspace-server-webapp}/src/main/resources/static/js/hal/http/client.js (100%) rename {dspace-webapp-boot => dspace-server-webapp}/src/main/resources/static/js/vendor/CustomPostForm.js (100%) rename {dspace-webapp-boot => dspace-server-webapp}/src/main/resources/static/login.html (100%) rename {dspace-webapp-boot => dspace-server-webapp}/src/main/resources/static/styles.css (100%) rename {dspace-webapp-boot => dspace/modules/server-boot}/pom.xml (73%) create mode 100644 dspace/modules/server-boot/src/main/java/org/dspace/app/ServerBootApplication.java create mode 100644 dspace/modules/server/pom.xml rename dspace-webapp-boot/src/main/java/org/dspace/app/Application.java => dspace/modules/server/src/main/java/org/dspace/app/ServerApplication.java (85%) create mode 100644 dspace/modules/server/src/main/webapp/.gitignore rename {dspace-webapp-boot => dspace/modules/server}/src/test/java/org/dspace/app/rest/example/ExampleController.java (100%) rename {dspace-webapp-boot => dspace/modules/server}/src/test/java/org/dspace/app/rest/example/ExampleControllerIT.java (100%) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index c884ea7d57c..0d3458199d3 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -40,6 +40,23 @@ + + + com.mycila + license-maven-plugin + + + **/src/test/resources/** + **/src/test/data/** + + src/main/resources/static/index.html + src/main/resources/static/login.html + src/main/resources/static/styles.css + src/main/resources/static/js/hal/** + src/main/resources/static/js/vendor/** + + + + modules org.dspace - dspace-parent cris-2022.03.01-SNAPSHOT .. - ${basedir}/.. - - @ + ${basedir}/../../.. @@ -108,23 +105,6 @@ - - - com.mycila - license-maven-plugin - - - **/src/test/resources/** - **/src/test/data/** - - src/main/resources/static/index.html - src/main/resources/static/login.html - src/main/resources/static/styles.css - src/main/resources/static/js/hal/** - src/main/resources/static/js/vendor/** - - - org.springframework.boot spring-boot-maven-plugin diff --git a/dspace/modules/server-boot/src/main/java/org/dspace/app/ServerBootApplication.java b/dspace/modules/server-boot/src/main/java/org/dspace/app/ServerBootApplication.java new file mode 100644 index 00000000000..f46532ff14b --- /dev/null +++ b/dspace/modules/server-boot/src/main/java/org/dspace/app/ServerBootApplication.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; + +import org.dspace.app.rest.WebApplication; +import org.dspace.app.rest.utils.DSpaceConfigurationInitializer; +import org.dspace.app.rest.utils.DSpaceKernelInitializer; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +/** + * Define the Spring Boot Application settings itself to be runned using an + * embedded application server. + * + * @author Luca Giamminonni (luca.giamminonni at 4science.it) + * + */ +@SpringBootApplication(scanBasePackageClasses = WebApplication.class) +public class ServerBootApplication { + + private ServerBootApplication() { + + } + + public static void main(String[] args) { + new SpringApplicationBuilder(ServerBootApplication.class) + .initializers(new DSpaceKernelInitializer(), new DSpaceConfigurationInitializer()) + .run(args); + } + +} diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml new file mode 100644 index 00000000000..65849295e8c --- /dev/null +++ b/dspace/modules/server/pom.xml @@ -0,0 +1,349 @@ + + 4.0.0 + org.dspace.modules + server + war + DSpace Server Webapp:: Local Customizations + + modules + org.dspace + cris-2022.03.01-SNAPSHOT + .. + + + + + ${basedir}/../../.. + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack + prepare-package + + unpack-dependencies + + + org.dspace.modules + additions + + ${project.build.directory}/additions + META-INF/** + + + + + + org.apache.maven.plugins + maven-war-plugin + + false + + true + + + + ${project.build.directory}/additions + WEB-INF/classes + + + + + + prepare-package + + + + + + org.codehaus.gmaven + groovy-maven-plugin + + + setproperty + initialize + + execute + + + + project.properties['agnostic.build.dir'] = project.build.directory.replace(File.separator, '/'); + log.info("Initializing Maven property 'agnostic.build.dir' to: {}", project.properties['agnostic.build.dir']); + + + + + + + + + + + + unit-test-environment + + false + + skipUnitTests + false + + + + + + + maven-dependency-plugin + + ${project.build.directory}/testing + + + org.dspace + dspace-parent + ${project.version} + zip + testEnvironment + + + + + + setupUnitTestEnvironment + generate-test-resources + + unpack + + + + + + + + maven-surefire-plugin + + + + + + ${agnostic.build.dir}/testing/dspace + + true + ${agnostic.build.dir}/testing/dspace/solr/ + + + + + + + + + org.dspace + dspace-server-webapp + test-jar + test + + + + + + + integration-test-environment + + false + + skipIntegrationTests + false + + + + + + + maven-dependency-plugin + + ${project.build.directory}/testing + + + org.dspace + dspace-parent + ${project.version} + zip + testEnvironment + + + + + + setupIntegrationTestEnvironment + pre-integration-test + + unpack + + + + + + + + maven-failsafe-plugin + + + + + ${agnostic.build.dir}/testing/dspace + + true + ${agnostic.build.dir}/testing/dspace/solr/ + + + + + + + + + org.dspace + dspace-server-webapp + test-jar + test + + + + + + oracle-support + + + db.name + oracle + + + + + com.oracle + ojdbc6 + + + + + + + + + org.dspace.modules + additions + + + org.dspace + dspace-server-webapp + + + org.springframework.boot + spring-boot-starter-tomcat + provided + ${spring-boot.version} + + + org.apache.solr + solr-solrj + ${solr.client.version} + + + + + org.dspace + dspace-api + test-jar + test + + + org.dspace + dspace-server-webapp + test-jar + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + ${spring-security.version} + test + + + com.jayway.jsonpath + json-path-assert + ${json-path.version} + test + + + junit + junit + test + + + com.h2database + h2 + test + + + org.mockito + mockito-inline + test + + + + + org.apache.solr + solr-core + ${solr.client.version} + test + + + + org.apache.commons + commons-text + + + + + org.apache.lucene + lucene-analyzers-icu + test + + + + + diff --git a/dspace-webapp-boot/src/main/java/org/dspace/app/Application.java b/dspace/modules/server/src/main/java/org/dspace/app/ServerApplication.java similarity index 85% rename from dspace-webapp-boot/src/main/java/org/dspace/app/Application.java rename to dspace/modules/server/src/main/java/org/dspace/app/ServerApplication.java index dc84b29a562..34acc778b7f 100644 --- a/dspace-webapp-boot/src/main/java/org/dspace/app/Application.java +++ b/dspace/modules/server/src/main/java/org/dspace/app/ServerApplication.java @@ -28,13 +28,7 @@ * */ @SpringBootApplication(scanBasePackageClasses = WebApplication.class) -public class Application extends SpringBootServletInitializer { - - public static void main(String[] args) { - new SpringApplicationBuilder(Application.class) - .initializers(new DSpaceKernelInitializer(), new DSpaceConfigurationInitializer()) - .run(args); - } +public class ServerApplication extends SpringBootServletInitializer { /** * Override the default SpringBootServletInitializer.configure() method, @@ -52,7 +46,7 @@ public static void main(String[] args) { protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { // Pass this Application class, and our initializers for DSpace Kernel and Configuration // NOTE: Kernel must be initialized before Configuration - return application.sources(Application.class) + return application.sources(ServerApplication.class) .initializers(new DSpaceKernelInitializer(), new DSpaceConfigurationInitializer()); } } diff --git a/dspace/modules/server/src/main/webapp/.gitignore b/dspace/modules/server/src/main/webapp/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dspace-webapp-boot/src/test/java/org/dspace/app/rest/example/ExampleController.java b/dspace/modules/server/src/test/java/org/dspace/app/rest/example/ExampleController.java similarity index 100% rename from dspace-webapp-boot/src/test/java/org/dspace/app/rest/example/ExampleController.java rename to dspace/modules/server/src/test/java/org/dspace/app/rest/example/ExampleController.java diff --git a/dspace-webapp-boot/src/test/java/org/dspace/app/rest/example/ExampleControllerIT.java b/dspace/modules/server/src/test/java/org/dspace/app/rest/example/ExampleControllerIT.java similarity index 100% rename from dspace-webapp-boot/src/test/java/org/dspace/app/rest/example/ExampleControllerIT.java rename to dspace/modules/server/src/test/java/org/dspace/app/rest/example/ExampleControllerIT.java diff --git a/dspace/pom.xml b/dspace/pom.xml index 0dba032e688..d5e7108fa52 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -219,12 +219,6 @@ dspace-server-webapp compile - - org.dspace - dspace-webapp-boot - war - compile - org.dspace dspace-sword diff --git a/pom.xml b/pom.xml index e17dfbf384c..a5d5b12f079 100644 --- a/pom.xml +++ b/pom.xml @@ -798,21 +798,6 @@ - - - dspace-webapp-boot - - - dspace-webapp-boot/pom.xml - - - - dspace-webapp-boot - - - @@ -1167,12 +1152,6 @@ dspace-server-webapp cris-2022.03.01-SNAPSHOT - - org.dspace - dspace-webapp-boot - cris-2022.03.01-SNAPSHOT - war - org.dspace From 47fc9169179d738739cd9d0a56b51bc852a6e6b6 Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Tue, 14 Mar 2023 18:36:50 +0100 Subject: [PATCH 16/82] [DSC-963] Configured spring boot maven plugin --- dspace/modules/server-boot/pom.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dspace/modules/server-boot/pom.xml b/dspace/modules/server-boot/pom.xml index 313cf0e78f6..a1dd702f97c 100644 --- a/dspace/modules/server-boot/pom.xml +++ b/dspace/modules/server-boot/pom.xml @@ -108,6 +108,14 @@ org.springframework.boot spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + From 06e77f354ca4d4ca1cdbd75200e97d824954c70b Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Wed, 15 Mar 2023 17:58:16 +0100 Subject: [PATCH 17/82] [DSC-963] Suppress checkstyle warning --- .../src/main/java/org/dspace/app/ServerBootApplication.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dspace/modules/server-boot/src/main/java/org/dspace/app/ServerBootApplication.java b/dspace/modules/server-boot/src/main/java/org/dspace/app/ServerBootApplication.java index f46532ff14b..5efa79a02ac 100644 --- a/dspace/modules/server-boot/src/main/java/org/dspace/app/ServerBootApplication.java +++ b/dspace/modules/server-boot/src/main/java/org/dspace/app/ServerBootApplication.java @@ -20,13 +20,10 @@ * @author Luca Giamminonni (luca.giamminonni at 4science.it) * */ +@SuppressWarnings({ "checkstyle:hideutilityclassconstructor" }) @SpringBootApplication(scanBasePackageClasses = WebApplication.class) public class ServerBootApplication { - private ServerBootApplication() { - - } - public static void main(String[] args) { new SpringApplicationBuilder(ServerBootApplication.class) .initializers(new DSpaceKernelInitializer(), new DSpaceConfigurationInitializer()) From 82a0ccf9ba2380498cda6219b62ebf83cd201b15 Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Thu, 23 Mar 2023 15:06:32 +0100 Subject: [PATCH 18/82] [DSC-963] Fixed webjars classpath --- .../src/main/java/org/dspace/app/rest/WebApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/WebApplication.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/WebApplication.java index b04b6ebc9ee..c67830e9257 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/WebApplication.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/WebApplication.java @@ -214,7 +214,7 @@ public void addResourceHandlers(ResourceHandlerRegistry registry) { // Make all other Webjars available off the /webjars path registry .addResourceHandler("/webjars/**") - .addResourceLocations("/webjars/"); + .addResourceLocations("/webjars/", "classpath:/META-INF/resources/webjars/"); } @Override From d80330f2b654469ae9084836c781d3c29b93f06e Mon Sep 17 00:00:00 2001 From: mohamed eskander Date: Thu, 7 Dec 2023 12:03:20 +0200 Subject: [PATCH 19/82] [DSC-1386] Added support to create template item via struct builder --- .../org/dspace/administer/StructBuilder.java | 102 +++++++++++++++++- .../dspace/administer/StructBuilderIT.java | 29 +++++ dspace/config/sample-structure.xml | 70 ++++++++++++ 3 files changed, 200 insertions(+), 1 deletion(-) 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 cb65604a262..c08566dc5b0 100644 --- a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java +++ b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java @@ -9,6 +9,7 @@ import static org.dspace.content.Item.ANY; import static org.dspace.content.MetadataSchemaEnum.CRIS; +import static org.dspace.content.authority.Choices.CF_UNSET; import static org.dspace.content.service.DSpaceObjectService.MD_COPYRIGHT_TEXT; import static org.dspace.content.service.DSpaceObjectService.MD_INTRODUCTORY_TEXT; import static org.dspace.content.service.DSpaceObjectService.MD_LICENSE; @@ -49,12 +50,14 @@ import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.Item; +import org.dspace.content.MetadataField; import org.dspace.content.MetadataFieldName; import org.dspace.content.MetadataSchemaEnum; import org.dspace.content.MetadataValue; 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.core.Context; import org.dspace.core.CrisConstants; import org.dspace.eperson.factory.EPersonServiceFactory; @@ -122,7 +125,8 @@ public class StructBuilder { = EPersonServiceFactory.getInstance().getEPersonService(); protected static final HandleService handleService = HandleServiceFactory.getInstance().getHandleService(); - + protected static final ItemService itemService + = ContentServiceFactory.getInstance().getItemService(); /** * Default constructor */ @@ -407,6 +411,9 @@ private static Element exportACollection(Collection collection) { Element element = new Element("collection"); element.setAttribute("identifier", collection.getHandle()); element.addContent(new Element("name").setText(collection.getName())); + + buildTemplateItem(collection, element); + element.addContent(new Element("description") .setText(collectionService.getMetadataFirstValue(collection, MetadataSchemaEnum.DC.getName(), "description", "abstract", Item.ANY))); @@ -833,6 +840,8 @@ private static Element[] handleCollections(Context context, collectionService.setMetadataSingleValue(context, collection, MD_SHORT_DESCRIPTION, Item.ANY, " "); + handleTemplateItem(context, collection, tn); + // import the rest of the metadata for (Map.Entry entry : collectionMap.entrySet()) { NodeList nl = (NodeList) xPath.compile(entry.getKey()).evaluate(tn, XPathConstants.NODESET); @@ -854,6 +863,8 @@ private static Element[] handleCollections(Context context, String fieldValue; + buildTemplateItem(collection, element); + fieldValue = collectionService.getMetadataFirstValue(collection, CollectionService.MD_SHORT_DESCRIPTION, Item.ANY); if (fieldValue != null) { @@ -930,4 +941,93 @@ private static Element[] handleCollections(Context context, return elements; } + + private static void handleTemplateItem(Context context, Collection collection, Node tn) + throws XPathExpressionException, SQLException, AuthorizeException { + + XPath xPath = XPathFactory.newInstance().newXPath(); + Node node = (Node) xPath.compile("templateItem").evaluate(tn, XPathConstants.NODE); + + if (node == null) { + return; + } + + Item templateItem = itemService.createTemplateItem(context, collection); + + NodeList metadataNodes = (NodeList) xPath.compile("metadata").evaluate(node, XPathConstants.NODESET); + + for (int i = 0; i < metadataNodes.getLength(); i++) { + Node metadataNode = metadataNodes.item(i); + MetadataFieldName metadataFieldName = buildMetadataFieldName(metadataNode); + + Node valueAttribute = (Node) xPath.compile("value").evaluate(metadataNode, XPathConstants.NODE); + Node authorityAttribute = (Node) xPath.compile("authority").evaluate(metadataNode, XPathConstants.NODE); + Node confidenceAttribute = (Node) xPath.compile("confidence").evaluate(metadataNode, XPathConstants.NODE); + + String authority = null; + int confidence = CF_UNSET; + + if (authorityAttribute != null) { + authority = authorityAttribute.getTextContent(); + confidence = confidenceAttribute != null ? Integer.parseInt(confidenceAttribute.getTextContent()) : 600; + } + + itemService.addMetadata(context, templateItem, metadataFieldName.schema, metadataFieldName.element, + metadataFieldName.qualifier, ANY, valueAttribute.getTextContent(), authority, confidence); + itemService.update(context, templateItem); + } + } + + private static MetadataFieldName buildMetadataFieldName(Node node) { + Node schemaAttribute = node.getAttributes().getNamedItem("schema"); + Node elementAttribute = node.getAttributes().getNamedItem("element"); + Node qualifierAttribute = node.getAttributes().getNamedItem("qualifier"); + + if (qualifierAttribute == null) { + return new MetadataFieldName(schemaAttribute.getTextContent(), elementAttribute.getTextContent()); + } else { + return new MetadataFieldName(schemaAttribute.getTextContent(), + elementAttribute.getTextContent(), qualifierAttribute.getTextContent()); + } + } + + private static void buildTemplateItem(Collection collection, Element element) { + + try { + Item templateItem = collection.getTemplateItem(); + + if (templateItem == null) { + return; + } + + Element templateItemElement = new Element("templateItem"); + + for (MetadataValue metadataValue : templateItem.getMetadata()) { + MetadataField metadataField = metadataValue.getMetadataField(); + Element metadata = new Element("metadata"); + metadata.setAttribute("schema", metadataField.getMetadataSchema().getName()); + metadata.setAttribute("element", metadataField.getElement()); + + if (metadataField.getQualifier() != null) { + metadata.setAttribute("qualifier", metadataField.getQualifier()); + } + + metadata.addContent(new Element("value").setText(metadataValue.getValue())); + + if (metadataValue.getAuthority() != null) { + metadata.addContent(new Element("authority").setText(metadataValue.getAuthority())); + metadata.addContent(new Element("confidence").setText( + String.valueOf(metadataValue.getConfidence()) + )); + } + + templateItemElement.addContent(metadata); + } + + element.addContent(templateItemElement); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } 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 a113aa0438d..e69fc85b497 100644 --- a/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java +++ b/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java @@ -17,6 +17,7 @@ import java.nio.charset.StandardCharsets; import java.sql.SQLException; import java.util.Iterator; +import java.util.UUID; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; @@ -27,10 +28,12 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; import org.dspace.content.Community; +import org.dspace.content.Item; import org.dspace.content.MetadataSchemaEnum; 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.handle.Handle; import org.junit.AfterClass; import org.junit.Before; @@ -61,6 +64,8 @@ public class StructBuilderIT = ContentServiceFactory.getInstance().getCommunityService(); private static final CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService(); + private static final ItemService itemService + = ContentServiceFactory.getInstance().getItemService(); public StructBuilderIT() { } @@ -114,6 +119,21 @@ public void setUp() throws SQLException, AuthorizeException, IOException { " Another sidebar\n" + " \n" + " Collection 0.0.0\n" + + " \n" + + " \n" + + " template item\n" + + " \n" + + " \n" + + " Walter White\n" + + " " + UUID.randomUUID() + "\n" + + " 600\n" + + " \n" + + " \n" + + " Donald, Smith\n" + + " " + UUID.randomUUID() + "\n" + + " 400\n" + + " \n" + + " \n" + " A collection\n" + " Our next guest needs no introduction\n" + " 1776\n" + @@ -149,6 +169,11 @@ public void setUp() throws SQLException, AuthorizeException, IOException { " \n" + " \n" + " Collection 0.0\n" + + " \n" + + " \n" + + " template item\n" + + " \n" + + " \n" + " \n" + " \n" + " \n" + @@ -301,6 +326,10 @@ public void testExportStructure() MetadataSchemaEnum.DC.getName(), "title", null, null, "Collection 0.0"); + Item item = itemService.createTemplateItem(context, collection0_0); + itemService.addMetadata(context, item, MetadataSchemaEnum.DC.getName(), "title", null, + Item.ANY, "template item", null, -1); + // Export the current structure. System.out.println("exportStructure"); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); diff --git a/dspace/config/sample-structure.xml b/dspace/config/sample-structure.xml index 89c577bdcfa..1d9095841e0 100644 --- a/dspace/config/sample-structure.xml +++ b/dspace/config/sample-structure.xml @@ -10,6 +10,13 @@ Person Person person + + + + + + + @@ -21,6 +28,13 @@ Project Project project + + + + + + + @@ -32,6 +46,13 @@ Funding Funding funding + + + + + + + @@ -43,6 +64,13 @@ OrgUnit OrgUnit orgunit + + + + + + + @@ -54,6 +82,13 @@ Journal Journal journal + + + + + + + @@ -65,6 +100,13 @@ Publication Publication publication + + + + + + + @@ -76,6 +118,13 @@ Patent Patent patent + + + + + + + @@ -86,6 +135,13 @@ Dataset or other products Product + + + + + + + @@ -97,6 +153,13 @@ Event Event event + + + + + + + @@ -108,6 +171,13 @@ Equipment Equipment equipment + + + + + + + From 77a107e1910503031ce5e0b8ecb77f6cb2685884 Mon Sep 17 00:00:00 2001 From: mohamed eskander Date: Thu, 14 Dec 2023 16:51:19 +0200 Subject: [PATCH 20/82] [DSC-1377] added support for alternative tab/box for tabpolicy/boxpolicy sheets of layout config --- .../java/org/dspace/layout/CrisLayoutBox.java | 29 +-- .../layout/CrisLayoutBox2SecurityGroup.java | 124 ++++++++++ .../java/org/dspace/layout/CrisLayoutTab.java | 29 +-- .../layout/CrisLayoutTab2SecurityGroup.java | 124 ++++++++++ .../service/CrisLayoutToolValidator.java | 2 + .../impl/CrisLayoutToolConverterImpl.java | 32 ++- .../impl/CrisLayoutToolParserImpl.java | 127 ++++++++++- .../service/CrisLayoutTabAccessService.java | 3 +- .../layout/service/CrisLayoutTabService.java | 2 + .../impl/CrisLayoutTabAccessServiceImpl.java | 16 +- .../impl/CrisLayoutTabServiceImpl.java | 9 + .../cris-layout-configuration-template.xls | Bin 686080 -> 686080 bytes ...ab_id_to_cris_layout_tab2securitygroup.sql | 14 ++ ...ox_id_to_cris_layout_box2securitygroup.sql | 14 ++ ...ab_id_to_cris_layout_tab2securitygroup.sql | 14 ++ ...ox_id_to_cris_layout_box2securitygroup.sql | 14 ++ ...ab_id_to_cris_layout_tab2securitygroup.sql | 14 ++ ...ox_id_to_cris_layout_box2securitygroup.sql | 14 ++ .../dspace/builder/CrisLayoutBoxBuilder.java | 13 ++ .../dspace/builder/CrisLayoutTabBuilder.java | 13 ++ .../converter/CrisLayoutTabConverter.java | 89 ++++++-- .../org/dspace/app/rest/LayoutSecurityIT.java | 26 ++- .../layout/CrisLayoutTabRestRepositoryIT.java | 211 ++++++++++++++++++ dspace/config/hibernate.cfg.xml | 2 + .../conftool/cris-layout-configuration.xls | Bin 126464 -> 126464 bytes 25 files changed, 852 insertions(+), 83 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/layout/CrisLayoutBox2SecurityGroup.java create mode 100644 dspace-api/src/main/java/org/dspace/layout/CrisLayoutTab2SecurityGroup.java create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql diff --git a/dspace-api/src/main/java/org/dspace/layout/CrisLayoutBox.java b/dspace-api/src/main/java/org/dspace/layout/CrisLayoutBox.java index 47b426b135c..9fb9a725c5b 100644 --- a/dspace-api/src/main/java/org/dspace/layout/CrisLayoutBox.java +++ b/dspace-api/src/main/java/org/dspace/layout/CrisLayoutBox.java @@ -11,6 +11,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import javax.persistence.Cacheable; import javax.persistence.CascadeType; import javax.persistence.Column; @@ -79,13 +80,8 @@ public class CrisLayoutBox implements ReloadableEntity { ) private Set metadataSecurityFields = new HashSet<>(); - @ManyToMany(fetch = FetchType.LAZY) - @JoinTable( - name = "cris_layout_box2securitygroup", - joinColumns = {@JoinColumn(name = "box_id")}, - inverseJoinColumns = {@JoinColumn(name = "group_id")} - ) - private Set groupSecurityFields = new HashSet<>(); + @OneToMany(fetch = FetchType.LAZY, mappedBy = "box", cascade = CascadeType.ALL, orphanRemoval = true) + private Set box2SecurityGroups = new HashSet<>(); @OneToMany(fetch = FetchType.LAZY, mappedBy = "box", cascade = CascadeType.ALL) @OrderBy(value = "row, cell, priority") @@ -288,20 +284,19 @@ public void setContainer(Boolean container) { this.container = container; } - public void setGroupSecurityFields(Set groupSecurityFields) { - this.groupSecurityFields = groupSecurityFields; - } - - public void addGroupSecurityFields(Set groupSecurityFields) { - this.groupSecurityFields.addAll(groupSecurityFields); + public Set getGroupSecurityFields() { + return box2SecurityGroups.stream() + .map(crisLayoutBox2SecurityGroup -> + crisLayoutBox2SecurityGroup.getGroup()) + .collect(Collectors.toSet()); } - public void addGroupSecurityFields(Group group) { - this.groupSecurityFields.add(group); + public Set getBox2SecurityGroups() { + return box2SecurityGroups; } - public Set getGroupSecurityFields() { - return groupSecurityFields; + public void setBox2SecurityGroups(Set box2SecurityGroups) { + this.box2SecurityGroups = box2SecurityGroups; } @Override diff --git a/dspace-api/src/main/java/org/dspace/layout/CrisLayoutBox2SecurityGroup.java b/dspace-api/src/main/java/org/dspace/layout/CrisLayoutBox2SecurityGroup.java new file mode 100644 index 00000000000..d0ee1cd5841 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/layout/CrisLayoutBox2SecurityGroup.java @@ -0,0 +1,124 @@ +/** + * 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.layout; + +import java.io.Serializable; +import javax.persistence.CascadeType; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MapsId; +import javax.persistence.Table; + +import org.dspace.eperson.Group; + +@Entity +@Table(name = "cris_layout_box2securitygroup") +public class CrisLayoutBox2SecurityGroup implements Serializable { + + @Embeddable + public static class CrisLayoutBox2SecurityGroupId implements Serializable { + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "box_id") + private CrisLayoutBox boxId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "group_id") + private Group groupId; + + public CrisLayoutBox2SecurityGroupId() { + + } + + public CrisLayoutBox2SecurityGroupId(CrisLayoutBox boxId, Group groupId) { + this.boxId = boxId; + this.groupId = groupId; + } + + public CrisLayoutBox getBoxId() { + return boxId; + } + + public void setBoxId(CrisLayoutBox boxId) { + this.boxId = boxId; + } + + public Group getGroupId() { + return groupId; + } + + public void setGroupId(Group groupId) { + this.groupId = groupId; + } + } + + @EmbeddedId + private CrisLayoutBox2SecurityGroupId id; + + @ManyToOne(fetch = FetchType.LAZY) + @MapsId("boxId") + @JoinColumn(name = "box_id", insertable = false, updatable = false) + private CrisLayoutBox box; + + @ManyToOne(fetch = FetchType.LAZY) + @MapsId("groupId") + @JoinColumn(name = "group_id", insertable = false, updatable = false) + private Group group; + + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn(name = "alternative_box_id", nullable = true) + private CrisLayoutBox alternativeBox; + + public CrisLayoutBox2SecurityGroup() { + + } + + public CrisLayoutBox2SecurityGroup(CrisLayoutBox2SecurityGroupId id, + CrisLayoutBox box, Group group, + CrisLayoutBox alternativeBox) { + this.id = id; + this.box = box; + this.group = group; + this.alternativeBox = alternativeBox; + } + + public CrisLayoutBox2SecurityGroupId getId() { + return id; + } + + public void setId(CrisLayoutBox2SecurityGroupId id) { + this.id = id; + } + + public CrisLayoutBox getBox() { + return box; + } + + public void setBox(CrisLayoutBox box) { + this.box = box; + } + + public Group getGroup() { + return group; + } + + public void setGroup(Group group) { + this.group = group; + } + + public CrisLayoutBox getAlternativeBox() { + return alternativeBox; + } + + public void setAlternativeBox(CrisLayoutBox alternativeBox) { + this.alternativeBox = alternativeBox; + } +} diff --git a/dspace-api/src/main/java/org/dspace/layout/CrisLayoutTab.java b/dspace-api/src/main/java/org/dspace/layout/CrisLayoutTab.java index 48bd0dc5611..9c0f4ef1e2b 100644 --- a/dspace-api/src/main/java/org/dspace/layout/CrisLayoutTab.java +++ b/dspace-api/src/main/java/org/dspace/layout/CrisLayoutTab.java @@ -87,14 +87,8 @@ public class CrisLayoutTab implements ReloadableEntity { @JoinColumn(name = "tab_id") }, inverseJoinColumns = { @JoinColumn(name = "metadata_field_id") }) private Set metadataSecurityFields = new HashSet<>(); - @ManyToMany(fetch = FetchType.LAZY) - @JoinTable( - name = "cris_layout_tab2securitygroup", - joinColumns = {@JoinColumn(name = "tab_id")}, - inverseJoinColumns = {@JoinColumn(name = "group_id")} - ) - private Set groupSecurityFields = new HashSet<>(); - + @OneToMany(fetch = FetchType.LAZY, mappedBy = "tab", cascade = CascadeType.ALL) + private Set tab2SecurityGroups = new HashSet<>(); @Column(name = "is_leading") private Boolean leading; @@ -230,20 +224,19 @@ public List getBoxes() { .collect(Collectors.toList()); } - public void setGroupSecurityFields(Set groupSecurityFields) { - this.groupSecurityFields = groupSecurityFields; - } - - public void addGroupSecurityFields(Set groupSecurityFields) { - this.groupSecurityFields.addAll(groupSecurityFields); + public Set getGroupSecurityFields() { + return tab2SecurityGroups.stream() + .map(crisLayoutTab2SecurityGroup -> + crisLayoutTab2SecurityGroup.getGroup()) + .collect(Collectors.toSet()); } - public void addGroupSecurityFields(Group group) { - this.groupSecurityFields.add(group); + public Set getTab2SecurityGroups() { + return tab2SecurityGroups; } - public Set getGroupSecurityFields() { - return groupSecurityFields; + public void setTab2SecurityGroups(Set tab2SecurityGroups) { + this.tab2SecurityGroups = tab2SecurityGroups; } @Override diff --git a/dspace-api/src/main/java/org/dspace/layout/CrisLayoutTab2SecurityGroup.java b/dspace-api/src/main/java/org/dspace/layout/CrisLayoutTab2SecurityGroup.java new file mode 100644 index 00000000000..f41b3ec53e8 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/layout/CrisLayoutTab2SecurityGroup.java @@ -0,0 +1,124 @@ +/** + * 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.layout; + +import java.io.Serializable; +import javax.persistence.CascadeType; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MapsId; +import javax.persistence.Table; + +import org.dspace.eperson.Group; + +@Entity +@Table(name = "cris_layout_tab2securitygroup") +public class CrisLayoutTab2SecurityGroup implements Serializable { + + @Embeddable + public static class CrisLayoutTab2SecurityGroupId implements Serializable { + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "tab_id") + private CrisLayoutTab tabId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "group_id") + private Group groupId; + + public CrisLayoutTab2SecurityGroupId() { + + } + + public CrisLayoutTab2SecurityGroupId(CrisLayoutTab tabId, Group groupId) { + this.tabId = tabId; + this.groupId = groupId; + } + + public CrisLayoutTab getTabId() { + return tabId; + } + + public void setTabId(CrisLayoutTab tabId) { + this.tabId = tabId; + } + + public Group getGroupId() { + return groupId; + } + + public void setGroupId(Group groupId) { + this.groupId = groupId; + } + } + + @EmbeddedId + private CrisLayoutTab2SecurityGroupId id; + + @ManyToOne(fetch = FetchType.LAZY) + @MapsId("tabId") + @JoinColumn(name = "tab_id", insertable = false, updatable = false) + private CrisLayoutTab tab; + + @ManyToOne(fetch = FetchType.LAZY) + @MapsId("groupId") + @JoinColumn(name = "group_id", insertable = false, updatable = false) + private Group group; + + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn(name = "alternative_tab_id") + private CrisLayoutTab alternativeTab; + + public CrisLayoutTab2SecurityGroup() { + + } + + public CrisLayoutTab2SecurityGroup(CrisLayoutTab2SecurityGroupId id, + CrisLayoutTab tab, Group group, + CrisLayoutTab alternativeTab) { + this.id = id; + this.tab = tab; + this.group = group; + this.alternativeTab = alternativeTab; + } + + public CrisLayoutTab2SecurityGroupId getId() { + return id; + } + + public void setId(CrisLayoutTab2SecurityGroupId id) { + this.id = id; + } + + public CrisLayoutTab getTab() { + return tab; + } + + public void setTab(CrisLayoutTab tab) { + this.tab = tab; + } + + public Group getGroup() { + return group; + } + + public void setGroup(Group group) { + this.group = group; + } + + public CrisLayoutTab getAlternativeTab() { + return alternativeTab; + } + + public void setAlternativeTab(CrisLayoutTab alternativeTab) { + this.alternativeTab = alternativeTab; + } +} diff --git a/dspace-api/src/main/java/org/dspace/layout/script/service/CrisLayoutToolValidator.java b/dspace-api/src/main/java/org/dspace/layout/script/service/CrisLayoutToolValidator.java index c1a9cff5dbb..74302960cff 100644 --- a/dspace-api/src/main/java/org/dspace/layout/script/service/CrisLayoutToolValidator.java +++ b/dspace-api/src/main/java/org/dspace/layout/script/service/CrisLayoutToolValidator.java @@ -108,6 +108,8 @@ public interface CrisLayoutToolValidator { String GROUP_COLUMN = "GROUP"; + String ALTERNATIVE_TO_COLUMN = "ALTERNATIVE_TO"; + String METADATA_TYPE = "METADATA"; String BITSTREAM_TYPE = "BITSTREAM"; diff --git a/dspace-api/src/main/java/org/dspace/layout/script/service/impl/CrisLayoutToolConverterImpl.java b/dspace-api/src/main/java/org/dspace/layout/script/service/impl/CrisLayoutToolConverterImpl.java index 1aec1c34937..52ba3ddedc1 100644 --- a/dspace-api/src/main/java/org/dspace/layout/script/service/impl/CrisLayoutToolConverterImpl.java +++ b/dspace-api/src/main/java/org/dspace/layout/script/service/impl/CrisLayoutToolConverterImpl.java @@ -31,13 +31,14 @@ import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; import org.dspace.content.MetadataField; -import org.dspace.eperson.Group; import org.dspace.layout.CrisLayoutBox; +import org.dspace.layout.CrisLayoutBox2SecurityGroup; import org.dspace.layout.CrisLayoutCell; import org.dspace.layout.CrisLayoutField; import org.dspace.layout.CrisLayoutFieldBitstream; import org.dspace.layout.CrisLayoutMetric2Box; import org.dspace.layout.CrisLayoutTab; +import org.dspace.layout.CrisLayoutTab2SecurityGroup; import org.dspace.layout.CrisMetadataGroup; import org.dspace.layout.LayoutSecurity; import org.dspace.layout.script.service.CrisLayoutToolConverter; @@ -247,9 +248,9 @@ private void buildTabPolicy(Workbook workbook, CrisLayoutTab tab) { buildTabPolicyMetadataSecurityFieldRow(sheet, tab, metadataField) ); - tab.getGroupSecurityFields() - .forEach(group -> - buildTabPolicyGroupSecurityFieldRow(sheet, tab, group) + tab.getTab2SecurityGroups() + .forEach(tab2SecurityGroup -> + buildTabPolicyGroupSecurityFieldRow(sheet, tab, tab2SecurityGroup) ); } @@ -259,14 +260,18 @@ private void buildTabPolicyMetadataSecurityFieldRow(Sheet sheet, CrisLayoutTab t createCell(row, 1, tab.getShortName()); createCell(row, 2, metadataField.toString('.')); createCell(row, 3, ""); + createCell(row, 4, ""); } - private void buildTabPolicyGroupSecurityFieldRow(Sheet sheet, CrisLayoutTab tab, Group group) { + private void buildTabPolicyGroupSecurityFieldRow(Sheet sheet, CrisLayoutTab tab, + CrisLayoutTab2SecurityGroup tab2SecurityGroup) { + CrisLayoutTab alternativeTab = tab2SecurityGroup.getAlternativeTab(); Row row = sheet.createRow(sheet.getLastRowNum() + 1); createCell(row, 0, tab.getEntity().getLabel()); createCell(row, 1, tab.getShortName()); createCell(row, 2, ""); - createCell(row, 3, group.getName()); + createCell(row, 3, tab2SecurityGroup.getGroup().getName()); + createCell(row, 4, alternativeTab == null ? "" : alternativeTab.getShortName()); } private void buildBoxPolicy(Workbook workbook, List boxes) { @@ -277,9 +282,9 @@ private void buildBoxPolicy(Workbook workbook, List boxes) { buildBoxPolicyMetadataSecurityFieldRow(sheet, box, metadataField) ); - box.getGroupSecurityFields() - .forEach(group -> - buildBoxPolicyGroupSecurityFieldRow(sheet, box, group) + box.getBox2SecurityGroups() + .forEach(box2SecurityGroup -> + buildBoxPolicyGroupSecurityFieldRow(sheet, box, box2SecurityGroup) ); }); } @@ -290,14 +295,19 @@ private void buildBoxPolicyMetadataSecurityFieldRow(Sheet sheet, CrisLayoutBox b createCell(row, 1, box.getShortname()); createCell(row, 2, metadataField.toString('.')); createCell(row, 3, ""); + createCell(row, 4, ""); } - private void buildBoxPolicyGroupSecurityFieldRow(Sheet sheet, CrisLayoutBox box, Group group) { + private void buildBoxPolicyGroupSecurityFieldRow(Sheet sheet, CrisLayoutBox box, + CrisLayoutBox2SecurityGroup box2SecurityGroup) { + + CrisLayoutBox alternativeBox = box2SecurityGroup.getAlternativeBox(); Row row = sheet.createRow(sheet.getLastRowNum() + 1); createCell(row, 0, box.getCell().getRow().getTab().getEntity().getLabel()); createCell(row, 1, box.getShortname()); createCell(row, 2, ""); - createCell(row, 3, group.getName()); + createCell(row, 3, box2SecurityGroup.getGroup().getName()); + createCell(row, 4, alternativeBox == null ? "" : alternativeBox.getShortname()); } private String convertToString(boolean value) { diff --git a/dspace-api/src/main/java/org/dspace/layout/script/service/impl/CrisLayoutToolParserImpl.java b/dspace-api/src/main/java/org/dspace/layout/script/service/impl/CrisLayoutToolParserImpl.java index 9c368c2785b..a4f5fec248e 100644 --- a/dspace-api/src/main/java/org/dspace/layout/script/service/impl/CrisLayoutToolParserImpl.java +++ b/dspace-api/src/main/java/org/dspace/layout/script/service/impl/CrisLayoutToolParserImpl.java @@ -7,6 +7,7 @@ */ package org.dspace.layout.script.service.impl; +import static org.dspace.layout.script.service.CrisLayoutToolValidator.ALTERNATIVE_TO_COLUMN; import static org.dspace.layout.script.service.CrisLayoutToolValidator.BITSTREAM_TYPE; import static org.dspace.layout.script.service.CrisLayoutToolValidator.BOX2METADATA_SHEET; import static org.dspace.layout.script.service.CrisLayoutToolValidator.BOX2METRICS_SHEET; @@ -53,6 +54,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -76,6 +78,7 @@ import org.dspace.eperson.Group; import org.dspace.eperson.service.GroupService; import org.dspace.layout.CrisLayoutBox; +import org.dspace.layout.CrisLayoutBox2SecurityGroup; import org.dspace.layout.CrisLayoutBoxTypes; import org.dspace.layout.CrisLayoutCell; import org.dspace.layout.CrisLayoutField; @@ -84,6 +87,7 @@ import org.dspace.layout.CrisLayoutMetric2Box; import org.dspace.layout.CrisLayoutRow; import org.dspace.layout.CrisLayoutTab; +import org.dspace.layout.CrisLayoutTab2SecurityGroup; import org.dspace.layout.CrisMetadataGroup; import org.dspace.layout.LayoutSecurity; import org.dspace.layout.script.service.CrisLayoutToolParser; @@ -110,9 +114,21 @@ public class CrisLayoutToolParserImpl implements CrisLayoutToolParser { @Override public List parse(Context context, Workbook workbook) { Sheet tabSheet = getSheetByName(workbook, TAB_SHEET); - return WorkbookUtils.getNotEmptyRowsSkippingHeader(tabSheet).stream() - .map(row -> buildTab(context, row)) - .collect(Collectors.toList()); + List tabs = + WorkbookUtils.getNotEmptyRowsSkippingHeader(tabSheet).stream() + .map(row -> buildTab(context, row)) + .collect(Collectors.toList()); + + tabs.forEach(tab -> { + tab.setTab2SecurityGroups(buildTab2SecurityGroups(context, + workbook, TAB_POLICY_SHEET, tab.getEntity().getLabel(), tab.getShortName(), tab, tabs)); + + tab.getBoxes().forEach(box -> + box.setBox2SecurityGroups(buildBox2SecurityGroups(context, + workbook, BOX_POLICY_SHEET, box.getEntitytype().getLabel(), box.getShortname(), box, tabs))); + }); + + return tabs; } private CrisLayoutTab buildTab(Context context, Row tabRow) { @@ -136,8 +152,6 @@ private CrisLayoutTab buildTab(Context context, Row tabRow) { buildTabRows(context, workbook, entityType, name).forEach(tab::addRow); tab.setMetadataSecurityFields(buildMetadataSecurityField(context, workbook, TAB_POLICY_SHEET, entityType, name)); - tab.setGroupSecurityFields(buildGroupSecurityField(context, workbook, - TAB_POLICY_SHEET, entityType, name)); return tab; } @@ -218,8 +232,6 @@ private CrisLayoutBox buildBox(Context context, Sheet boxSheet, String entityTyp box.setStyle(getCellValue(boxRow, STYLE_COLUMN)); box.setMetadataSecurityFields(buildMetadataSecurityField(context, workbook, BOX_POLICY_SHEET, entityType, boxName)); - box.setGroupSecurityFields(buildGroupSecurityField(context, workbook, - BOX_POLICY_SHEET, entityType, boxName)); if (boxType.equals(CrisLayoutBoxTypes.METADATA.name())) { buildCrisLayoutFields(context, workbook, entityType, boxName).forEach(box::addLayoutField); @@ -376,6 +388,107 @@ private Set buildGroupSecurityField(Context context, Workbook workbook, .collect(Collectors.toSet()); } + private Set buildBox2SecurityGroups(Context context, Workbook workbook, + String sheetName, String entity, String name, + CrisLayoutBox crisLayoutBox, + List tabs) { + Sheet sheet = getSheetByName(workbook, sheetName); + Set box2SecurityGroups = new HashSet<>(); + + getRowsByEntityAndColumnValue(sheet, entity, SHORTNAME_COLUMN, name) + .forEach(row -> { + String groupName = getCellValue(row, GROUP_COLUMN); + String alternativeBox = getCellValue(row, ALTERNATIVE_TO_COLUMN); + + if (StringUtils.isNotBlank(groupName)) { + Group group = getGroupField(context, groupName); + if (group != null) { + box2SecurityGroups.add( + buildBox2SecurityGroup(group, crisLayoutBox, entity, alternativeBox, tabs) + ); + } + } + }); + + return box2SecurityGroups; + } + + private CrisLayoutBox2SecurityGroup buildBox2SecurityGroup(Group group, CrisLayoutBox box, + String entity, + String alternativeBox, List tabs) { + + CrisLayoutBox2SecurityGroup.CrisLayoutBox2SecurityGroupId box2SecurityGroupId = + new CrisLayoutBox2SecurityGroup.CrisLayoutBox2SecurityGroupId(box, group); + + return new CrisLayoutBox2SecurityGroup(box2SecurityGroupId, box, group, + findAlternativeBox(alternativeBox, entity, tabs)); + } + + private CrisLayoutBox findAlternativeBox(String alternativeBox, String entityType, List tabs) { + + if (alternativeBox == null) { + return null; + } + + return tabs.stream() + .flatMap(tab -> tab.getBoxes().stream()) + .filter(crisLayoutBox -> crisLayoutBox.getShortname().equals(alternativeBox) && + crisLayoutBox.getEntitytype().getLabel().equals(entityType)) + .findFirst() + .orElseThrow(() -> new RuntimeException("Alternative box not found for shortname: " + + alternativeBox + ", entityType: " + entityType)); + } + + private Set buildTab2SecurityGroups(Context context, Workbook workbook, + String sheetName, String entity, String name, + CrisLayoutTab crisLayoutTab, + List tabs) { + Sheet sheet = getSheetByName(workbook, sheetName); + Set tab2SecurityGroups = new HashSet<>(); + + getRowsByEntityAndColumnValue(sheet, entity, SHORTNAME_COLUMN, name) + .forEach(row -> { + String groupName = getCellValue(row, GROUP_COLUMN); + String alternativeTab = getCellValue(row, ALTERNATIVE_TO_COLUMN); + + if (StringUtils.isNotBlank(groupName)) { + Group group = getGroupField(context, groupName); + if (group != null) { + tab2SecurityGroups.add( + buildTab2SecurityGroup(group, crisLayoutTab, entity, alternativeTab, tabs) + ); + } + } + }); + + return tab2SecurityGroups; + } + + private CrisLayoutTab2SecurityGroup buildTab2SecurityGroup(Group group, CrisLayoutTab tab, + String entity, + String alternativeTab, List tabs) { + + CrisLayoutTab2SecurityGroup.CrisLayoutTab2SecurityGroupId tab2SecurityGroupId = + new CrisLayoutTab2SecurityGroup.CrisLayoutTab2SecurityGroupId(tab, group); + + return new CrisLayoutTab2SecurityGroup(tab2SecurityGroupId, tab, group, + findAlternativeTab(alternativeTab, entity, tabs)); + } + + private CrisLayoutTab findAlternativeTab(String alternativeTab, String entityType, List tabs) { + + if (alternativeTab == null) { + return null; + } + + return tabs.stream() + .filter(crisLayoutTab -> crisLayoutTab.getShortName().equals(alternativeTab) && + crisLayoutTab.getEntity().getLabel().equals(entityType)) + .findFirst() + .orElseThrow(() -> new RuntimeException("Alternative tab not found for shortname: " + + alternativeTab + ", entityType: " + entityType)); + } + private Stream getRowsByEntityAndColumnValue(Sheet sheet, String entity, String columnName, String value) { return WorkbookUtils.getNotEmptyRowsSkippingHeader(sheet).stream() .filter(row -> value.equals(getCellValue(row, columnName))) diff --git a/dspace-api/src/main/java/org/dspace/layout/service/CrisLayoutTabAccessService.java b/dspace-api/src/main/java/org/dspace/layout/service/CrisLayoutTabAccessService.java index 12d7d08084e..2679d34865d 100644 --- a/dspace-api/src/main/java/org/dspace/layout/service/CrisLayoutTabAccessService.java +++ b/dspace-api/src/main/java/org/dspace/layout/service/CrisLayoutTabAccessService.java @@ -32,6 +32,5 @@ public interface CrisLayoutTabAccessService { * @return true if access has to be granded, false otherwise * @throws SQLException in case of error during database access */ - boolean hasAccess(Context context, EPerson user, CrisLayoutTab tab, Item item) - throws SQLException; + boolean hasAccess(Context context, EPerson user, CrisLayoutTab tab, Item item); } diff --git a/dspace-api/src/main/java/org/dspace/layout/service/CrisLayoutTabService.java b/dspace-api/src/main/java/org/dspace/layout/service/CrisLayoutTabService.java index 7224b8f2630..919dc7eb431 100644 --- a/dspace-api/src/main/java/org/dspace/layout/service/CrisLayoutTabService.java +++ b/dspace-api/src/main/java/org/dspace/layout/service/CrisLayoutTabService.java @@ -152,4 +152,6 @@ public List getMetadataField(Context context, Integer tabId, Inte * @throws SQLException An exception that provides information on a database errors. */ public List findByItem(Context context, String itemUuid) throws SQLException; + + public boolean hasAccess(Context context, CrisLayoutTab tab, Item item); } diff --git a/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutTabAccessServiceImpl.java b/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutTabAccessServiceImpl.java index 71e20fd883f..331c5df679b 100644 --- a/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutTabAccessServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutTabAccessServiceImpl.java @@ -31,12 +31,14 @@ public CrisLayoutTabAccessServiceImpl(LayoutSecurityService layoutSecurityServic } @Override - public boolean hasAccess(Context context, EPerson user, CrisLayoutTab tab, Item item) throws SQLException { - return layoutSecurityService.hasAccess(LayoutSecurity.valueOf(tab.getSecurity()), - context, - user, - tab.getMetadataSecurityFields(), - tab.getGroupSecurityFields(), - item); + public boolean hasAccess(Context context, EPerson user, CrisLayoutTab tab, Item item) { + try { + return layoutSecurityService.hasAccess( + LayoutSecurity.valueOf(tab.getSecurity()), context, user, tab.getMetadataSecurityFields(), + tab.getGroupSecurityFields(), item + ); + } catch (SQLException e) { + throw new RuntimeException(e); + } } } diff --git a/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutTabServiceImpl.java b/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutTabServiceImpl.java index 2ce7ee7ac4f..980305e67d6 100644 --- a/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutTabServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutTabServiceImpl.java @@ -29,6 +29,7 @@ import org.dspace.core.Context; import org.dspace.layout.CrisLayoutTab; import org.dspace.layout.dao.CrisLayoutTabDAO; +import org.dspace.layout.service.CrisLayoutTabAccessService; import org.dspace.layout.service.CrisLayoutTabService; import org.dspace.services.ConfigurationService; import org.springframework.beans.factory.annotation.Autowired; @@ -54,6 +55,9 @@ public class CrisLayoutTabServiceImpl implements CrisLayoutTabService { private SubmissionConfigReader submissionConfigReader; + @Autowired + CrisLayoutTabAccessService crisLayoutTabAccessService; + @PostConstruct private void setup() throws SubmissionConfigReaderException { submissionConfigReader = new SubmissionConfigReader(); @@ -217,6 +221,11 @@ public List findByItem(Context context, String itemUuid) throws S return layoutTabs; } + @Override + public boolean hasAccess(Context context, CrisLayoutTab tab, Item item) { + return crisLayoutTabAccessService.hasAccess(context, context.getCurrentUser(), tab, item); + } + private String getSubmissionDefinitionName(Item item) { if (submissionConfigReader == null || item.getOwningCollection() == null) { return ""; diff --git a/dspace-api/src/main/resources/org/dspace/layout/script/service/impl/cris-layout-configuration-template.xls b/dspace-api/src/main/resources/org/dspace/layout/script/service/impl/cris-layout-configuration-template.xls index a921219abfad173dde9253ce4368468386eda6c7..2eddc88c677749d144d96421e076cd3a8938de3d 100644 GIT binary patch delta 1476 zcmb7@OKcNY6o${eH>9Br91=(!Gc`@36xd{#5v=U$55$N6OF9z zzyEp8+`_Z2!n3aB1K$2y9>6~d^4Y`lph1_P&`sEXc=q%>c;HGy#bIFxwupka>D;Lp ziGDctMLzp^HlO|S#O%qr+&ly!)j=m7Kzl!)o|ry@asbTDB-c~ox!IZNBVUvFDqa!M z>xjdNig+?VJ(Hu{^Uwf?k|Zh=J~f>hY&lNiH+Q%tVmOJY>98jHe9gSI#~Am`F!u}o z89H_uEB+xCz|nT^pB(w*!0yS+rz2C76UJZK_l)f_%7I1}8whD=1vm>{;P}6~!C?s( z^qcW}V>2rbeJ2a?F#Ht@ikG3N)b7?rG!Y531oUdO3^K%x5k19YH{VNA2slYy;DYl; zu_?>hCGomD8e4NaIjG{}8YXi|q+5~)2g8m_1r=5`C#47Xey^s47iUS;ocq8Bk89YE zZ?I(i)N#bNCnKd`b9I<{d}n%tQ$iI7^$o`Dc!%9JVm`P#mTauLm8LsgfpRCknSdL0oI6_&E_1{=PQPYb*j7p+T$A zd(Yszm5`a$w;fL5(B@P=QW)sEf@IjTx*X|R=CGiZ}7Tdy5dXB{T1M6LGO>@n~7@S;(w&d?Yio z?iybTnl)F0VxLTHGO?u}|Gk|dtDS|#vW!oixmb3OFR^9w{;2ZHe*`Y9WE6br&6SM$ wN9T<>^+QQb!^b!zoSM*!`*LcV!k_OSzR-J@iI^*77INxo#>(d3dDShw1(10Fod5s; delta 1377 zcmZ`&O=weD6h8O9{J#9WK9d(?ZJTClJ8h#)nrf}>ECdT;78arCPKi2>g~k>etur94 zKegyW(JN#j8M>;on1y%8n8PFgeCg>%YJver11_JO~T}5HRD8$5caP8EQB#~h# z{9Mc*&KL8)93ML|J~0CkXx>1P0H8dJ(c>eBkk(pBoU&E5UQH>1YGz5X;e{r3I6&%Mi`C(0*l@9#JCrgR4yFpxuo@9pb z7zw4#Yluv$QE$+Vs)ZZe&?)yC-8}hpmD5?>adv?V&dWd6<~h44!l^{^h1c<*vL9es zs8xj5Cq*ICEcSIxu9$C3Os2=#qWCA>C|D%^-Mkx_m3b%G1=*0@!KRw9&@wg#z;F=~ zRm5REG~0;b(*RmtQZh^VLltrS0$OZDt7YImSCn`!M6}wJNB1Z-#yz-CRGxS@meydSt#VtXH`&Nz#pv5uh9MO^7}Owb z2fbD>Vi%g=Kboh`(eS%~r6>&W{$XD4Xp*9SFmwYSBb+UdKz+PiOs_cqIW zc43L*QOt!US8iUvTeHt(_phn{?v`9z)nw_?65lxcbaPuc#8k|s@eP`s*m*vkdnMk^#=JbSm Nim@K^|Dx(uz5#Vw{(}Gj diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql new file mode 100644 index 00000000000..6ae50fb29bf --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql @@ -0,0 +1,14 @@ +-- +-- 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/ +-- + +----------------------------------------------------------------------------------- +-- Alter TABLE cris_layout_tab2securitygroup ADD alternative_tab_id +----------------------------------------------------------------------------------- + +ALTER TABLE cris_layout_tab2securitygroup ADD COLUMN alternative_tab_id INTEGER; +ALTER TABLE cris_layout_tab2securitygroup ADD CONSTRAINT cris_layout_tab2securitygroup_tab_id2 FOREIGN KEY (alternative_tab_id) REFERENCES cris_layout_tab (id) ON DELETE SET NULL; \ No newline at end of file diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql new file mode 100644 index 00000000000..38360bb13cd --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql @@ -0,0 +1,14 @@ +-- +-- 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/ +-- + +----------------------------------------------------------------------------------- +-- Alter TABLE cris_layout_box2securitygroup ADD alternative_box_id +----------------------------------------------------------------------------------- + +ALTER TABLE cris_layout_box2securitygroup ADD COLUMN alternative_box_id INTEGER; +ALTER TABLE cris_layout_box2securitygroup ADD CONSTRAINT cris_layout_box2securitygroup_box_id2 FOREIGN KEY (alternative_box_id) REFERENCES cris_layout_box (id) ON DELETE SET NULL; \ No newline at end of file diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql new file mode 100644 index 00000000000..6ae50fb29bf --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql @@ -0,0 +1,14 @@ +-- +-- 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/ +-- + +----------------------------------------------------------------------------------- +-- Alter TABLE cris_layout_tab2securitygroup ADD alternative_tab_id +----------------------------------------------------------------------------------- + +ALTER TABLE cris_layout_tab2securitygroup ADD COLUMN alternative_tab_id INTEGER; +ALTER TABLE cris_layout_tab2securitygroup ADD CONSTRAINT cris_layout_tab2securitygroup_tab_id2 FOREIGN KEY (alternative_tab_id) REFERENCES cris_layout_tab (id) ON DELETE SET NULL; \ No newline at end of file diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql new file mode 100644 index 00000000000..38360bb13cd --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql @@ -0,0 +1,14 @@ +-- +-- 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/ +-- + +----------------------------------------------------------------------------------- +-- Alter TABLE cris_layout_box2securitygroup ADD alternative_box_id +----------------------------------------------------------------------------------- + +ALTER TABLE cris_layout_box2securitygroup ADD COLUMN alternative_box_id INTEGER; +ALTER TABLE cris_layout_box2securitygroup ADD CONSTRAINT cris_layout_box2securitygroup_box_id2 FOREIGN KEY (alternative_box_id) REFERENCES cris_layout_box (id) ON DELETE SET NULL; \ No newline at end of file diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql new file mode 100644 index 00000000000..6ae50fb29bf --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql @@ -0,0 +1,14 @@ +-- +-- 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/ +-- + +----------------------------------------------------------------------------------- +-- Alter TABLE cris_layout_tab2securitygroup ADD alternative_tab_id +----------------------------------------------------------------------------------- + +ALTER TABLE cris_layout_tab2securitygroup ADD COLUMN alternative_tab_id INTEGER; +ALTER TABLE cris_layout_tab2securitygroup ADD CONSTRAINT cris_layout_tab2securitygroup_tab_id2 FOREIGN KEY (alternative_tab_id) REFERENCES cris_layout_tab (id) ON DELETE SET NULL; \ No newline at end of file diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql new file mode 100644 index 00000000000..38360bb13cd --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql @@ -0,0 +1,14 @@ +-- +-- 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/ +-- + +----------------------------------------------------------------------------------- +-- Alter TABLE cris_layout_box2securitygroup ADD alternative_box_id +----------------------------------------------------------------------------------- + +ALTER TABLE cris_layout_box2securitygroup ADD COLUMN alternative_box_id INTEGER; +ALTER TABLE cris_layout_box2securitygroup ADD CONSTRAINT cris_layout_box2securitygroup_box_id2 FOREIGN KEY (alternative_box_id) REFERENCES cris_layout_box (id) ON DELETE SET NULL; \ No newline at end of file diff --git a/dspace-api/src/test/java/org/dspace/builder/CrisLayoutBoxBuilder.java b/dspace-api/src/test/java/org/dspace/builder/CrisLayoutBoxBuilder.java index cf80c8778dc..59784b192ed 100644 --- a/dspace-api/src/test/java/org/dspace/builder/CrisLayoutBoxBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/CrisLayoutBoxBuilder.java @@ -19,7 +19,9 @@ import org.dspace.content.MetadataField; import org.dspace.core.Context; import org.dspace.discovery.SearchServiceException; +import org.dspace.eperson.Group; import org.dspace.layout.CrisLayoutBox; +import org.dspace.layout.CrisLayoutBox2SecurityGroup; import org.dspace.layout.CrisLayoutBoxTypes; import org.dspace.layout.CrisLayoutField; import org.dspace.layout.LayoutSecurity; @@ -163,6 +165,17 @@ public CrisLayoutBoxBuilder addMetadataSecurityField(MetadataField field) { return this; } + public CrisLayoutBoxBuilder addBox2SecurityGroups(Group group, CrisLayoutBox alternativeBox) throws SQLException { + if (this.box.getBox2SecurityGroups() == null) { + this.box.setBox2SecurityGroups(new HashSet<>()); + } + this.box.getBox2SecurityGroups().add( + new CrisLayoutBox2SecurityGroup(new CrisLayoutBox2SecurityGroup.CrisLayoutBox2SecurityGroupId(box, group), + box, group, alternativeBox) + ); + return this; + } + public CrisLayoutBoxBuilder withContainer(boolean container) { this.box.setContainer(container); return this; diff --git a/dspace-api/src/test/java/org/dspace/builder/CrisLayoutTabBuilder.java b/dspace-api/src/test/java/org/dspace/builder/CrisLayoutTabBuilder.java index 312d04bb4ad..4736324f4d3 100644 --- a/dspace-api/src/test/java/org/dspace/builder/CrisLayoutTabBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/CrisLayoutTabBuilder.java @@ -20,10 +20,12 @@ import org.dspace.content.MetadataField; import org.dspace.core.Context; import org.dspace.discovery.SearchServiceException; +import org.dspace.eperson.Group; import org.dspace.layout.CrisLayoutBox; import org.dspace.layout.CrisLayoutCell; import org.dspace.layout.CrisLayoutRow; import org.dspace.layout.CrisLayoutTab; +import org.dspace.layout.CrisLayoutTab2SecurityGroup; import org.dspace.layout.LayoutSecurity; import org.dspace.layout.service.CrisLayoutTabService; @@ -207,4 +209,15 @@ public CrisLayoutTabBuilder addMetadatasecurity(MetadataField metadataField) { this.tab.getMetadataSecurityFields().add(metadataField); return this; } + + public CrisLayoutTabBuilder addTab2SecurityGroups(Group group, CrisLayoutTab alternativeTab) { + if (this.tab.getTab2SecurityGroups() == null) { + this.tab.setTab2SecurityGroups(new HashSet<>()); + } + this.tab.getTab2SecurityGroups().add( + new CrisLayoutTab2SecurityGroup(new CrisLayoutTab2SecurityGroup.CrisLayoutTab2SecurityGroupId(tab, group), + tab, group, alternativeTab) + ); + return this; + } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/CrisLayoutTabConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/CrisLayoutTabConverter.java index d15a04cf010..2c94bd8a8f3 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/CrisLayoutTabConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/CrisLayoutTabConverter.java @@ -9,6 +9,7 @@ import java.sql.SQLException; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -27,11 +28,14 @@ import org.dspace.core.Context; import org.dspace.core.exception.SQLRuntimeException; import org.dspace.layout.CrisLayoutBox; +import org.dspace.layout.CrisLayoutBox2SecurityGroup; import org.dspace.layout.CrisLayoutCell; import org.dspace.layout.CrisLayoutRow; import org.dspace.layout.CrisLayoutTab; +import org.dspace.layout.CrisLayoutTab2SecurityGroup; import org.dspace.layout.LayoutSecurity; import org.dspace.layout.service.CrisLayoutBoxService; +import org.dspace.layout.service.CrisLayoutTabService; import org.dspace.services.RequestService; import org.dspace.util.UUIDUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -61,18 +65,47 @@ public class CrisLayoutTabConverter implements DSpaceConverter convertTab(tab, projection)) + .orElseGet(CrisLayoutTabRest::new); + } + + private boolean hasAccess(Item item, CrisLayoutTab tab) { + Context context = ContextUtil.obtainCurrentRequestContext(); + return crisLayoutTabService.hasAccess(context, tab, item); + } + + private CrisLayoutTab findAlternativeTab(CrisLayoutTab tab) { + return tab.getTab2SecurityGroups() + .stream() + .map(CrisLayoutTab2SecurityGroup::getAlternativeTab) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); + } + + private CrisLayoutTabRest convertTab(CrisLayoutTab tab, Projection projection) { CrisLayoutTabRest rest = new CrisLayoutTabRest(); - rest.setId(model.getID()); - rest.setEntityType(model.getEntity().getLabel()); - rest.setCustomFilter(model.getCustomFilter()); - rest.setShortname(model.getShortName()); - rest.setHeader(model.getHeader()); - rest.setPriority(model.getPriority()); - rest.setSecurity(model.getSecurity()); - rest.setRows(convertRows(getScopeItem(), model.getRows(), projection)); - rest.setLeading(model.isLeading()); + rest.setId(tab.getID()); + rest.setEntityType(tab.getEntity().getLabel()); + rest.setCustomFilter(tab.getCustomFilter()); + rest.setShortname(tab.getShortName()); + rest.setHeader(tab.getHeader()); + rest.setPriority(tab.getPriority()); + rest.setSecurity(tab.getSecurity()); + rest.setRows(convertRows(getScopeItem(), tab.getRows(), projection)); + rest.setLeading(tab.isLeading()); return rest; } @@ -124,15 +157,43 @@ private CrisLayoutCellRest convertCell(Item item, CrisLayoutCell cell, Projectio private List convertBoxes(Item item, List boxes, Projection projection) { return boxes.stream() - .filter(box -> item == null || hasAccess(item, box)) - .map(box -> boxConverter.convert(box, projection)) - .collect(Collectors.toList()); + .map(box -> getCrisLayoutBox(item, box)) + .filter(Objects::nonNull) + .map(box -> boxConverter.convert(box, projection)) + .collect(Collectors.toList()); + } + + private CrisLayoutBox getCrisLayoutBox(Item item, CrisLayoutBox box) { + + if (item == null) { + return box; + } + + return Optional.of(box) + .filter(b -> hasAccess(item, b) && hasContent(item, b)) + .orElseGet(() -> + Optional.ofNullable(findAlternativeBox(box)) + .filter(altBox -> hasContent(item, altBox)) + .orElse(null)); } private boolean hasAccess(Item item, CrisLayoutBox box) { Context context = ContextUtil.obtainCurrentRequestContext(); - return crisLayoutBoxService.hasContent(context, box, item) - && crisLayoutBoxService.hasAccess(context, box, item); + return crisLayoutBoxService.hasAccess(context, box, item); + } + + private boolean hasContent(Item item, CrisLayoutBox box) { + Context context = ContextUtil.obtainCurrentRequestContext(); + return crisLayoutBoxService.hasContent(context, box, item); + } + + private CrisLayoutBox findAlternativeBox(CrisLayoutBox box) { + return box.getBox2SecurityGroups() + .stream() + .map(CrisLayoutBox2SecurityGroup::getAlternativeBox) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); } private CrisLayoutRow toRowModel(Context context, CrisLayoutRowRest rowRest) { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/LayoutSecurityIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/LayoutSecurityIT.java index 5d293ce2fbc..aa5705beb86 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/LayoutSecurityIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/LayoutSecurityIT.java @@ -53,6 +53,7 @@ import org.dspace.eperson.service.GroupService; import org.dspace.eperson.service.RegistrationDataService; import org.dspace.layout.CrisLayoutBox; +import org.dspace.layout.CrisLayoutBox2SecurityGroup; import org.dspace.layout.LayoutSecurity; import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; import org.hamcrest.Matchers; @@ -491,14 +492,20 @@ public void customDataTestWithOneGroup() throws Exception { .build(); // Create Group with member userA - Set groups = new HashSet<>(); + Set box2SecurityGroups = new HashSet<>(); Group testGroup = GroupBuilder.createGroup(context) .withName("testGroup") .addMember(userA) .build(); - groups.add(testGroup); - box1.setGroupSecurityFields(groups); + new CrisLayoutBox2SecurityGroup( + new CrisLayoutBox2SecurityGroup.CrisLayoutBox2SecurityGroupId(box1, testGroup), + box1, testGroup, null); + + box2SecurityGroups.add(new CrisLayoutBox2SecurityGroup( + new CrisLayoutBox2SecurityGroup.CrisLayoutBox2SecurityGroupId(box1, testGroup), + box1, testGroup, null)); + box1.setBox2SecurityGroups(box2SecurityGroups); CrisLayoutFieldBuilder.createMetadataField(context, abs, 0, 0) .withLabel("LABEL ABS") @@ -577,7 +584,7 @@ public void customDataTestWithMultipleGroup() throws Exception { .build(); // Create Group with member userA - Set boxGroups = new HashSet<>(); + Set boxGroups = new HashSet<>(); Group testGroup = GroupBuilder.createGroup(context) .withName("testGroup") @@ -589,9 +596,14 @@ public void customDataTestWithMultipleGroup() throws Exception { .addMember(userB) .build(); - boxGroups.add(testGroup); - boxGroups.add(testGroup1); - box1.setGroupSecurityFields(boxGroups); + boxGroups.add(new CrisLayoutBox2SecurityGroup( + new CrisLayoutBox2SecurityGroup.CrisLayoutBox2SecurityGroupId(box1, testGroup), + box1, testGroup, null)); + boxGroups.add(new CrisLayoutBox2SecurityGroup( + new CrisLayoutBox2SecurityGroup.CrisLayoutBox2SecurityGroupId(box1, testGroup1), + box1, testGroup, null)); + + box1.setBox2SecurityGroups(boxGroups); CrisLayoutFieldBuilder.createMetadataField(context, abs, 0, 0) .withLabel("LABEL ABS") diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/layout/CrisLayoutTabRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/layout/CrisLayoutTabRestRepositoryIT.java index d536f547d0b..aaa3fb1c62b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/layout/CrisLayoutTabRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/layout/CrisLayoutTabRestRepositoryIT.java @@ -77,6 +77,7 @@ import org.dspace.content.service.RelationshipService; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; +import org.dspace.eperson.service.GroupService; import org.dspace.layout.CrisLayoutBox; import org.dspace.layout.CrisLayoutBoxTypes; import org.dspace.layout.CrisLayoutCell; @@ -122,6 +123,9 @@ public class CrisLayoutTabRestRepositoryIT extends AbstractControllerIntegration @Autowired protected RelationshipService relationshipService; + @Autowired + protected GroupService groupService; + private final String METADATASECURITY_URL = "http://localhost:8080/api/core/metadatafield/"; /** @@ -2469,6 +2473,213 @@ public void excludeThumbnailNegativeMetadataValueMatcherTabMultiBoxConfiguration .andExpect(jsonPath("$._embedded.tabs[0].rows[1]").doesNotExist()); } + @Test + public void testFindByItemWithAlternativeTabs() throws Exception { + context.turnOffAuthorisationSystem(); + + MetadataSchema schema = mdss.find(context, "person"); + MetadataField firstName = mfss.findByElement(context, schema, "givenName", null); + Group adminGroup = groupService.findByName(context, Group.ADMIN); + // Create new community + Community community = CommunityBuilder.createCommunity(context) + .withName("Test Community") + .withTitle("Title test community") + .build(); + // Create new collection + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("Test Collection") + .build(); + // Create entity Type + EntityTypeBuilder.createEntityTypeBuilder(context, "Publication") + .build(); + EntityType eTypePer = EntityTypeBuilder.createEntityTypeBuilder(context, "Person") + .build(); + // Create new person item + Item item = ItemBuilder.createItem(context, collection) + .withPersonIdentifierFirstName("Danilo") + .withPersonIdentifierLastName("Di Nuzzo") + .withEntityType(eTypePer.getLabel()) + .build(); + + CrisLayoutBox boxOne = CrisLayoutBoxBuilder.createBuilder(context, eTypePer, false, false) + .withShortname("Box shortname 1") + .withSecurity(LayoutSecurity.PUBLIC) + .withContainer(false) + .build(); + + CrisLayoutBox boxTwo = CrisLayoutBoxBuilder.createBuilder(context, eTypePer, false, false) + .withShortname("Box shortname 2") + .withSecurity(LayoutSecurity.PUBLIC) + .withContainer(false) + .build(); + + CrisLayoutFieldBuilder.createMetadataField(context, firstName, 0, 1) + .withLabel("GIVEN NAME") + .withRendering("TEXT") + .withBox(boxOne) + .build(); + + CrisLayoutFieldBuilder.createMetadataField(context, firstName, 0, 1) + .withLabel("GIVEN NAME") + .withRendering("TEXT") + .withBox(boxTwo) + .build(); + + // add boxOne to tabOne + CrisLayoutTab tabOne = + CrisLayoutTabBuilder.createTab(context, eTypePer, 0) + .withShortName("TabOne For Person - priority 0") + .withSecurity(LayoutSecurity.ADMINISTRATOR) + .withHeader("New Tab header") + .addBoxIntoNewRow(boxOne, "rowTwoStyle", "cellOfRowTwoStyle") + .build(); + + // add boxTwo to tabTwo + CrisLayoutTab tabTwo = + CrisLayoutTabBuilder.createTab(context, eTypePer, 0) + .withShortName("Tab2 For Person - priority 0") + .withSecurity(LayoutSecurity.CUSTOM_DATA) + .withHeader("New Tab2 header") + .addBoxIntoNewRow(boxTwo, "rowTwoStyle2", "cellOfRowTwoStyle2") + .addTab2SecurityGroups(adminGroup, tabOne) + .build(); + + context.restoreAuthSystemState(); + + // admin user will see two tabs + getClient(getAuthToken(admin.getEmail(), password)) + .perform(get("/api/layout/tabs/search/findByItem") + .param("uuid", item.getID().toString())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$.page.totalElements", is(2))) + .andExpect(jsonPath("$._embedded.tabs[0].id", is(tabOne.getID()))) + .andExpect(jsonPath("$._embedded.tabs[0].shortname", is("TabOne For Person - priority 0"))) + .andExpect(jsonPath("$._embedded.tabs[0].header", is("New Tab header"))) + .andExpect(jsonPath("$._embedded.tabs[0].security", is(LayoutSecurity.ADMINISTRATOR.getValue()))) + .andExpect(jsonPath("$._embedded.tabs[0].rows", hasSize(1))) + .andExpect(jsonPath("$._embedded.tabs[1].id", is(tabTwo.getID()))) + .andExpect(jsonPath("$._embedded.tabs[1].shortname", is("Tab2 For Person - priority 0"))) + .andExpect(jsonPath("$._embedded.tabs[1].header", is("New Tab2 header"))) + .andExpect(jsonPath("$._embedded.tabs[1].security", is(LayoutSecurity.CUSTOM_DATA.getValue()))) + .andExpect(jsonPath("$._embedded.tabs[1].rows", hasSize(1))); + + // anonymous user will see only alternative tab is tabOne + getClient().perform(get("/api/layout/tabs/search/findByItem") + .param("uuid", item.getID().toString())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$.page.totalElements", is(1))) + .andExpect(jsonPath("$._embedded.tabs[0].id", is(tabOne.getID()))) + .andExpect(jsonPath("$._embedded.tabs[0].shortname", is("TabOne For Person - priority 0"))) + .andExpect(jsonPath("$._embedded.tabs[0].header", is("New Tab header"))) + .andExpect(jsonPath("$._embedded.tabs[0].security", is(LayoutSecurity.ADMINISTRATOR.getValue()))) + .andExpect(jsonPath("$._embedded.tabs[0].rows", hasSize(1))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].style", is("rowTwoStyle"))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells", hasSize(1))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells[0].style", is("cellOfRowTwoStyle"))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells[0].boxes", contains(matchBox(boxOne)))); + } + + @Test + public void testFindByItemWithAlternativeBoxes() throws Exception { + context.turnOffAuthorisationSystem(); + + MetadataSchema schema = mdss.find(context, "person"); + MetadataField firstName = mfss.findByElement(context, schema, "givenName", null); + Group adminGroup = groupService.findByName(context, Group.ADMIN); + // Create new community + Community community = CommunityBuilder.createCommunity(context) + .withName("Test Community") + .withTitle("Title test community") + .build(); + // Create new collection + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("Test Collection") + .build(); + // Create entity Type + EntityTypeBuilder.createEntityTypeBuilder(context, "Publication") + .build(); + EntityType eTypePer = EntityTypeBuilder.createEntityTypeBuilder(context, "Person") + .build(); + // Create new person item + Item item = ItemBuilder.createItem(context, collection) + .withPersonIdentifierFirstName("Danilo") + .withPersonIdentifierLastName("Di Nuzzo") + .withEntityType(eTypePer.getLabel()) + .build(); + + CrisLayoutBox boxOne = CrisLayoutBoxBuilder.createBuilder(context, eTypePer, false, false) + .withShortname("Box shortname 1") + .withSecurity(LayoutSecurity.PUBLIC) + .withContainer(false) + .build(); + + // add boxOne as alternative to boxTwo + CrisLayoutBox boxTwo = CrisLayoutBoxBuilder.createBuilder(context, eTypePer, false, false) + .withShortname("Box shortname 2") + .withSecurity(LayoutSecurity.CUSTOM_DATA) + .withContainer(false) + .addBox2SecurityGroups(adminGroup, boxOne) + .build(); + + CrisLayoutFieldBuilder.createMetadataField(context, firstName, 0, 1) + .withLabel("GIVEN NAME") + .withRendering("TEXT") + .withBox(boxOne) + .build(); + + CrisLayoutFieldBuilder.createMetadataField(context, firstName, 0, 1) + .withLabel("GIVEN NAME") + .withRendering("TEXT") + .withBox(boxTwo) + .build(); + + // add boxTwo to tab + CrisLayoutTab tab = CrisLayoutTabBuilder.createTab(context, eTypePer, 0) + .withShortName("TabOne For Person - priority 0") + .withSecurity(LayoutSecurity.PUBLIC) + .withHeader("New Tab header") + .withLeading(true) + .addBoxIntoNewRow(boxTwo, "rowTwoStyle", "cellOfRowTwoStyle") + .build(); + + context.restoreAuthSystemState(); + + // admin user will see boxTwo + getClient(getAuthToken(admin.getEmail(), password)) + .perform(get("/api/layout/tabs/search/findByItem") + .param("uuid", item.getID().toString())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.tabs[0].id", is(tab.getID()))) + .andExpect(jsonPath("$._embedded.tabs[0].shortname", is("TabOne For Person - priority 0"))) + .andExpect(jsonPath("$._embedded.tabs[0].header", is("New Tab header"))) + .andExpect(jsonPath("$._embedded.tabs[0].leading", is(true))) + .andExpect(jsonPath("$._embedded.tabs[0].security", is(LayoutSecurity.PUBLIC.getValue()))) + .andExpect(jsonPath("$._embedded.tabs[0].rows", hasSize(1))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].style", is("rowTwoStyle"))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells", hasSize(1))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells[0].style", is("cellOfRowTwoStyle"))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells[0].boxes", contains(matchBox(boxTwo)))); + + // anonymous user will see boxOne + getClient().perform(get("/api/layout/tabs/search/findByItem") + .param("uuid", item.getID().toString())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.tabs[0].id", is(tab.getID()))) + .andExpect(jsonPath("$._embedded.tabs[0].shortname", is("TabOne For Person - priority 0"))) + .andExpect(jsonPath("$._embedded.tabs[0].header", is("New Tab header"))) + .andExpect(jsonPath("$._embedded.tabs[0].leading", is(true))) + .andExpect(jsonPath("$._embedded.tabs[0].security", is(LayoutSecurity.PUBLIC.getValue()))) + .andExpect(jsonPath("$._embedded.tabs[0].rows", hasSize(1))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].style", is("rowTwoStyle"))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells", hasSize(1))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells[0].style", is("cellOfRowTwoStyle"))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells[0].boxes", contains(matchBox(boxOne)))); + } + private CrisLayoutTabRest parseJson(String name) throws Exception { return new ObjectMapper().readValue(getFileInputStream(name), CrisLayoutTabRest.class); } diff --git a/dspace/config/hibernate.cfg.xml b/dspace/config/hibernate.cfg.xml index 8597bedbc34..9a3cdd967f0 100644 --- a/dspace/config/hibernate.cfg.xml +++ b/dspace/config/hibernate.cfg.xml @@ -109,6 +109,8 @@ + + diff --git a/dspace/etc/conftool/cris-layout-configuration.xls b/dspace/etc/conftool/cris-layout-configuration.xls index 6a7d9daf8e8083166a0cd7ac64511b5f4c3be1a6..d9c9ab9f0090d4baf78b6269a88f18a9ddc03cc4 100644 GIT binary patch delta 1685 zcma)5ZD?C%6h7zX`>Qv%OJbHb4Y6z`tFukA&aUh1mzf(I&bcZSic1$Q6kDe*g$;ANiM%Xdxg`ECHkF#)S)a$5MdoI&)~W1oV0QBt%HLD zsUw{F{MMpzI6W|UXfc}`9!zngmFw6)5N&=l3vSp{iK>mFHJcX}UgS0xEGf~50GyHw zt|7GS_SIPdcFIj+n@6eu=-Azzc;2@r|*7h zON);kT~~BDMv`>jX;a^|meLvs1oY`U8dW&Rn4fYvhTU z(?YOI^X7Fu3D0SWUZf+D`?#0<&fdKr`bx)`I*#OQf$ue6j8+ilX@-+BE% z=Xsu{mQAT;)8vK`QQ0)E7#@Ew2>j?E+1--{Crs`UXc8FhIe8`xR_N)-N2utA`sW1N zfH69BBH7*7o$OA77j93HZ1?#FuLw71<=w9g^qx+G3@+MlcMtY`Eu0qNJauw#p!Zeb zYW^l~98L}lp3YZu!D|b`Xcjt-4@6=wr@;lE%c$BoTeErK+!MlPhyRsmj!LLdrtL#$ zYWLQf0gfmw&Q7v&?{Dqn=Bt)Xp+Tma!24mPW4BHc$5(ID=oE771Fls&?v`V3%zdgYI3 zn-h&nPTq_MBXhp$9#TC}Z(nKl&&Yx!)RA%Oiv#oS9t5YWg*Ig#N8f zmeh76DsYr`CxW3xQ$dkf)MgQj>f`icqT$JX-a=Fvl);R%pux(Uy;`Kn#^c&sp zpKX+Xuotw8wTvrkvzHC_{ujiv(fI&xT|YI?H64ggc; zJcHy=^-Aiup1;kKEB~ARo)1#yS_gf9tr{O9{dce1I1|J{=9{Raxm-W*S@XS0!WkNy zud3bIS~LaT!|kolinjF8!h`rQU0?a^>o9ztWbwjX0<5Sy7~_16rJzLp8)T;uottnCS{&K&e>U3{v7R zIV0<_Z$d%|8)?^XE!hityy%sGbQMk%a*<5-lpl8#KD4dy;Vc(kZ!iC>=%R_-pN~`h zN+A36QG8o+mMF_a16ZG}Ifn1Ngdb<8uHt2>rX*xrkH?sdFig1S$?Td%Lw4#*JY1Ps zUB}^1Hn3G^i?%`+FEe%H#6e%?&`k`Qo21ypt)WGvklvk5-NZA9$Fg^Bp-J~24H9cs From ba9ee87c5aa218c29e758830fe193ec0b6d3b01a Mon Sep 17 00:00:00 2001 From: "aliaksei.bykau" Date: Sun, 7 Jan 2024 15:29:09 +0100 Subject: [PATCH 21/82] [DSC-1458] now ItemEnhancer avoid to update item when it is not strictly necessary --- .../dspace/content/enhancer/ItemEnhancer.java | 2 + .../impl/RelatedEntityItemEnhancer.java | 66 +++++++++++++++++-- .../service/impl/ItemEnhancerServiceImpl.java | 21 ++++-- 3 files changed, 77 insertions(+), 12 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/ItemEnhancer.java b/dspace-api/src/main/java/org/dspace/content/enhancer/ItemEnhancer.java index f0e3d9d1564..a3784146c5a 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/ItemEnhancer.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/ItemEnhancer.java @@ -47,4 +47,6 @@ public interface ItemEnhancer { * @param item the item to enhance */ void enhance(Context context, Item item); + + boolean needUpdate(Context context, Item item); } diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java index a6c97cc84e6..1eb85afe817 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java @@ -59,22 +59,78 @@ public boolean canEnhance(Context context, Item item) { @Override public void enhance(Context context, Item item) { try { - cleanObsoleteVirtualFields(context, item); - updateVirtualFieldsPlaces(context, item); - performEnhancement(context, item); + boolean isMetadataDeleted = cleanObsoleteVirtualFields(context, item); + if (isMetadataDeleted) { + updateVirtualFieldsPlaces(context, item); + } + if (needUpdate(context, item)) { + performEnhancement(context, item); + } } catch (SQLException e) { LOGGER.error("An error occurs enhancing item with id {}: {}", item.getID(), e.getMessage(), e); throw new SQLRuntimeException(e); } } - private void cleanObsoleteVirtualFields(Context context, Item item) throws SQLException { + @Override + public boolean needUpdate(Context context, Item item) { + List metadataValuesToDelete = getObsoleteVirtualFields(item); + boolean isNeedUpdateMetadata = false; + + if (!noEnhanceableMetadata(context, item)) { + for (MetadataValue metadataValue : getEnhanceableMetadataValue(item)) { + if (wasValueAlreadyUsedForEnhancement(item, metadataValue)) { + continue; + } + + Item relatedItem = findRelatedEntityItem(context, metadataValue); + if (relatedItem == null) { + isNeedUpdateMetadata = true; + break; + } + + List relatedItemMetadataValues = + getMetadataValues(relatedItem, relatedItemMetadataField); + if (relatedItemMetadataValues.isEmpty()) { + isNeedUpdateMetadata = true; + break; + } + for (MetadataValue relatedItemMetadataValue : relatedItemMetadataValues) { + if (!isContainingMetadata(item, relatedItemMetadataValue.getValue())) { + isNeedUpdateMetadata = true; + break; + } + } + + } + } + + return !metadataValuesToDelete.isEmpty() || isNeedUpdateMetadata; + } + + private boolean isContainingMetadata(Item item, String value) { + return itemService.getMetadata(item, VIRTUAL_METADATA_SCHEMA, VIRTUAL_METADATA_ELEMENT, + getVirtualQualifier(), null, true).stream() + .anyMatch(metadataValue -> metadataValue.getValue().equals(value)); + } + + + /** + * Clean obsolete virtual fields. + * + * @param context the DSpace Context + * @param item the item to check + * @return true if some metadata is deleted, false if no metadata was deleted + */ + private boolean cleanObsoleteVirtualFields(Context context, Item item) throws SQLException { List metadataValuesToDelete = getObsoleteVirtualFields(item); if (!metadataValuesToDelete.isEmpty()) { itemService.removeMetadataValues(context, item, metadataValuesToDelete); + return true; + } else { + return false; } - } private void updateVirtualFieldsPlaces(Context context, Item item) { diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/service/impl/ItemEnhancerServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/enhancer/service/impl/ItemEnhancerServiceImpl.java index 90f9181a5e3..bc040eb1f7b 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/service/impl/ItemEnhancerServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/service/impl/ItemEnhancerServiceImpl.java @@ -42,13 +42,20 @@ public class ItemEnhancerServiceImpl implements ItemEnhancerService { @Override public void enhance(Context context, Item item) { + boolean isUpdateNeeded = false; + + for (ItemEnhancer itemEnhancer : itemEnhancers) { + if (itemEnhancer.canEnhance(context, item)) { + if (itemEnhancer.needUpdate(context,item)) { + itemEnhancer.enhance(context, item); + isUpdateNeeded = true; + } + } + } - itemEnhancers.stream() - .filter(itemEnhancer -> itemEnhancer.canEnhance(context, item)) - .forEach(itemEnhancer -> itemEnhancer.enhance(context, item)); - - updateItem(context, item); - + if (isUpdateNeeded) { + updateItem(context, item); + } } @Override @@ -68,7 +75,7 @@ private void cleanUpVirtualFields(Context context, Item item) { } try { - itemService.removeMetadataValues(context, item, ListUtils.union(virtualFields, virtualSourceFields)); + itemService.removeMetadataValues(context, item, metadataValuesToRemove); } catch (SQLException e) { throw new SQLRuntimeException(e); } From 38d851a2cb886d2bfea43eee6a56861f96639c5a Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 28 Dec 2023 16:22:48 +0100 Subject: [PATCH 22/82] [DSC-963] Fixes testResources generation --- dspace-server-webapp/pom.xml | 68 +++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 3a755eaff93..1f21df10c62 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -28,6 +28,66 @@ + + org.codehaus.mojo + properties-maven-plugin + 1.1.0 + + + initialize + + read-project-properties + + + + ${root.basedir}/dspace/config/dspace.cfg + ${root.basedir}/dspace/config/local.cfg + + true + + + + + + maven-resources-plugin + + + testEnvironment + process-resources + + testResources + + + + + ${basedir}/src/test/resources + true + + + + + + webappFiltering + process-resources + + resources + + + + + ${basedir}/src/main/resources + + **/*.properties + **/static/** + **/spring/** + + true + + + + + + org.apache.maven.plugins maven-jar-plugin @@ -333,7 +393,7 @@ com.flipkart.zjsonpatch zjsonpatch - 0.4.6 + 0.4.14 @@ -353,7 +413,7 @@ org.webjars.bowergithub.jquery jquery-dist - 3.6.0 + 3.7.0 @@ -367,7 +427,7 @@ org.webjars.bowergithub.medialize uri.js - 1.19.10 + 1.19.11 @@ -590,7 +650,7 @@ org.exparity hamcrest-date - 2.0.7 + 2.0.8 test From 9b86cb750a61efd71ad7a0dc1465f24d74f8a28b Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 28 Dec 2023 17:09:57 +0100 Subject: [PATCH 23/82] [DSC-963] Fixes server unpacking --- dspace/modules/server/pom.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index b61a3515195..a91491a89f3 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -12,6 +12,7 @@ + cris-2023.02.00 ${basedir}/../../.. @@ -67,6 +68,26 @@ + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack + prepare-package + + unpack-dependencies + + + runtime + org.dspace + dspace-server-webapp + **/static/**,**/*.properties + ${project.build.directory}/additions + + + + ${basedir}/../../.. From 4b0d938c5df314f535801d0e174ebf23be287546 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Mon, 8 Jan 2024 16:38:50 +0100 Subject: [PATCH 27/82] [DSC-963] Fixes Checkstyle issues --- .../service/OrcidProfileSectionFactoryServiceIT.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/orcid/service/OrcidProfileSectionFactoryServiceIT.java b/dspace-api/src/test/java/org/dspace/orcid/service/OrcidProfileSectionFactoryServiceIT.java index 60122ba5dbd..fa0b47133a7 100644 --- a/dspace-api/src/test/java/org/dspace/orcid/service/OrcidProfileSectionFactoryServiceIT.java +++ b/dspace-api/src/test/java/org/dspace/orcid/service/OrcidProfileSectionFactoryServiceIT.java @@ -298,7 +298,7 @@ public void testDisambiguationFromOrgUnitHierarchyOnEmploymentCreation() { assertThat(organization.getDisambiguatedOrganization().getDisambiguatedOrganizationIdentifier(), is("12345")); assertThat(organization.getDisambiguatedOrganization().getDisambiguationSource(), is("RINGGOLD")); } - + @Test public void testDisambiguationFromOrgUnitHierarchyOnEmploymentCreationWithAncestor() { @@ -310,14 +310,13 @@ public void testDisambiguationFromOrgUnitHierarchyOnEmploymentCreationWithAncest .withOrgUnitLocality("Milan") .withOrgUnitRinggoldIdentifier("12345") .build(); - + Item orgUnitFather = ItemBuilder.createItem(context, orgUnits) .withTitle("4Science without rin") .withOrgUnitCountry("IT") .withOrgUnitLocality("Milan") .withParentOrganization("4Science with rin", orgUnitGranfather.getID().toString()) .build(); - Item orgUnit = ItemBuilder.createItem(context, orgUnits) .withTitle("4Science") @@ -363,7 +362,7 @@ public void testDisambiguationFromOrgUnitHierarchyOnEmploymentCreationWithAncest assertThat(organization.getDisambiguatedOrganization().getDisambiguatedOrganizationIdentifier(), is("12345")); assertThat(organization.getDisambiguatedOrganization().getDisambiguationSource(), is("RINGGOLD")); } - + @Test public void testDisambiguationFromOrgUnitHierarchyOnEmploymentCreationWithNoId() { @@ -417,8 +416,7 @@ public void testDisambiguationFromOrgUnitHierarchyOnEmploymentCreationWithNoId() assertThat(organization.getAddress().getCity(), is("Milan")); assertThat(organization.getDisambiguatedOrganization(), nullValue()); } - - + @Test public void testQualificationCreation() { From 975e0d4de824239b52791cefe1c6108c8f310e0f Mon Sep 17 00:00:00 2001 From: "aliaksei.bykau" Date: Fri, 22 Dec 2023 08:18:43 +0100 Subject: [PATCH 28/82] [DSC-1435] improve for email template for subscriptions_content and email about subscribed statistics update. --- .../main/java/org/dspace/subscriptions/ContentGenerator.java | 2 +- .../java/org/dspace/subscriptions/StatisticsGenerator.java | 4 +++- dspace/config/emails/subscriptions_content | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/subscriptions/ContentGenerator.java b/dspace-api/src/main/java/org/dspace/subscriptions/ContentGenerator.java index 80e329ff63f..65f1ae9dcf9 100644 --- a/dspace-api/src/main/java/org/dspace/subscriptions/ContentGenerator.java +++ b/dspace-api/src/main/java/org/dspace/subscriptions/ContentGenerator.java @@ -85,7 +85,7 @@ private String generateBodyMail(String type, List subscription for (SubscriptionItem item : subscriptionItems) { out.write("\n".getBytes(UTF_8)); out.write("List of new content for the\n".getBytes(UTF_8)); - out.write((type + " " + item.getName() + " - " + item.getUrl() + "\n") + out.write((type + " \"" + item.getName() + "\" - " + item.getUrl() + "\n") .getBytes(UTF_8)); for (Entry entry : item.getItemUrlsByItemName().entrySet()) { diff --git a/dspace-api/src/main/java/org/dspace/subscriptions/StatisticsGenerator.java b/dspace-api/src/main/java/org/dspace/subscriptions/StatisticsGenerator.java index 842ff9aa0e8..43ff6b71d4b 100644 --- a/dspace-api/src/main/java/org/dspace/subscriptions/StatisticsGenerator.java +++ b/dspace-api/src/main/java/org/dspace/subscriptions/StatisticsGenerator.java @@ -51,8 +51,10 @@ public void notifyForSubscriptions(Context c, EPerson ePerson, List String name = configurationService.getProperty("dspace.name"); File attachment = generateExcel(crisMetricsList, c); email.addAttachment(attachment, "subscriptions.xlsx"); + email.setSubject(name + ": Statistics of records which you are subscribed"); email.setContent("intro", - "This automatic email is sent by " + name + " based on the subscribed statistics updates."); + "This automatic email is sent by " + name + " based on the subscribed statistics updates.\n\n" + + "See additional details in the file attached."); email.send(); } } catch (Exception ex) { diff --git a/dspace/config/emails/subscriptions_content b/dspace/config/emails/subscriptions_content index e76c8c311a4..9d2d255c06d 100644 --- a/dspace/config/emails/subscriptions_content +++ b/dspace/config/emails/subscriptions_content @@ -4,6 +4,7 @@ ## {1} Collections updates block ## {2} Communities updates block ## {3} Entity updates block +#set($subject = "${config.get('dspace.name')}: Statistics of updates on subscribed items") This email is sent from ${config.get('dspace.name')} based on the chosen subscription preferences. You can manage your subscription preferences from ${params[0]} From bd801b0dfcd52f50ff75f5d94f5840a99861367d Mon Sep 17 00:00:00 2001 From: Vlad Nouski Date: Wed, 10 Jan 2024 14:51:23 +0100 Subject: [PATCH 29/82] [DSC-1469] feature: edit dc subject label for journal form --- dspace/config/submission-forms.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace/config/submission-forms.xml b/dspace/config/submission-forms.xml index 45137c1ab15..df04a4ac7e3 100644 --- a/dspace/config/submission-forms.xml +++ b/dspace/config/submission-forms.xml @@ -511,7 +511,7 @@ dc subject - + tag true From d50aa9d87fa0f79e1a7768624a89c9283ea83811 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Thu, 11 Jan 2024 22:06:25 +0100 Subject: [PATCH 30/82] DSC-1455 simplify the uuid iterator implementation retrieving just the uuid from the database initial query --- .../mediafilter/MediaFilterServiceImpl.java | 15 +-- .../dao/impl/RequestItemDAOImpl.java | 2 +- .../content/dao/impl/BitstreamDAOImpl.java | 35 +++--- .../dspace/content/dao/impl/ItemDAOImpl.java | 105 +++++++++++------- .../dao/impl/MetadataValueDAOImpl.java | 4 +- .../org/dspace/core/AbstractHibernateDAO.java | 15 ++- .../java/org/dspace/core/UUIDIterator.java | 76 ++----------- .../main/java/org/dspace/curate/Curator.java | 23 +--- 8 files changed, 126 insertions(+), 149 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java index 961cbaa7d0e..3f898bd7d9f 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java @@ -37,7 +37,6 @@ import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.SelfNamedPlugin; -import org.dspace.core.UUIDIterator; import org.dspace.eperson.Group; import org.dspace.eperson.service.GroupService; import org.dspace.scripts.handler.DSpaceRunnableHandler; @@ -135,17 +134,13 @@ public void applyFiltersCommunity(Context context, Community community) throws Exception { //only apply filters if community not in skip-list if (!inSkipList(community.getHandle())) { List subcommunities = community.getSubcommunities(); - List collections = community.getCollections(); - - UUIDIterator communityIterator = new UUIDIterator<>(context, subcommunities, Community.class); - UUIDIterator collectionIterator = new UUIDIterator<>(context, collections, Collection.class); - - while (communityIterator.hasNext()) { - applyFiltersCommunity(context, communityIterator.next()); + for (Community subcommunity : subcommunities) { + applyFiltersCommunity(context, subcommunity); } - while (collectionIterator.hasNext()) { - applyFiltersCollection(context, collectionIterator.next()); + List collections = community.getCollections(); + for (Collection collection : collections) { + applyFiltersCollection(context, collection); } } } diff --git a/dspace-api/src/main/java/org/dspace/app/requestitem/dao/impl/RequestItemDAOImpl.java b/dspace-api/src/main/java/org/dspace/app/requestitem/dao/impl/RequestItemDAOImpl.java index a09a2bf250e..008174ded88 100644 --- a/dspace-api/src/main/java/org/dspace/app/requestitem/dao/impl/RequestItemDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/requestitem/dao/impl/RequestItemDAOImpl.java @@ -46,6 +46,6 @@ public RequestItem findByToken(Context context, String token) throws SQLExceptio public Iterator findByItem(Context context, Item item) throws SQLException { Query query = createQuery(context, "FROM RequestItem WHERE item_id= :uuid"); query.setParameter("uuid", item.getID()); - return iterate(context, query, RequestItem.class); + return iterate(query); } } diff --git a/dspace-api/src/main/java/org/dspace/content/dao/impl/BitstreamDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/BitstreamDAOImpl.java index a4f61f0c643..a3347a40ab9 100644 --- a/dspace-api/src/main/java/org/dspace/content/dao/impl/BitstreamDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/dao/impl/BitstreamDAOImpl.java @@ -27,6 +27,7 @@ import org.dspace.core.AbstractHibernateDSODAO; import org.dspace.core.Constants; import org.dspace.core.Context; +import org.dspace.core.UUIDIterator; /** * Hibernate implementation of the Database Access Object interface class for the Bitstream object. @@ -77,7 +78,7 @@ public List findBitstreamsWithNoRecentChecksum(Context context) throw @Override public Iterator findByCommunity(Context context, Community community) throws SQLException { - Query query = createQuery(context, "select b from Bitstream b " + + Query query = createQuery(context, "select b.id from Bitstream b " + "join b.bundles bitBundles " + "join bitBundles.items item " + "join item.collections itemColl " + @@ -85,40 +86,43 @@ public Iterator findByCommunity(Context context, Community community) "WHERE :community IN community"); query.setParameter("community", community); - - return iterate(context, query, Bitstream.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Bitstream.class, this); } @Override public Iterator findByCollection(Context context, Collection collection) throws SQLException { - Query query = createQuery(context, "select b from Bitstream b " + + Query query = createQuery(context, "select b.id from Bitstream b " + "join b.bundles bitBundles " + "join bitBundles.items item " + "join item.collections c " + "WHERE :collection IN c"); query.setParameter("collection", collection); - - return iterate(context, query, Bitstream.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Bitstream.class, this); } @Override public Iterator findByItem(Context context, Item item) throws SQLException { - Query query = createQuery(context, "select b from Bitstream b " + + Query query = createQuery(context, "select b.id from Bitstream b " + "join b.bundles bitBundles " + "join bitBundles.items item " + "WHERE :item IN item"); query.setParameter("item", item); - - return iterate(context, query, Bitstream.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Bitstream.class, this); } @Override public Iterator findShowableByItem(Context context, UUID itemId, String bundleName) throws SQLException { Query query = createQuery( context, - "select b from Bitstream b " + + "select b.id from Bitstream b " + "join b.bundles bitBundle " + "join bitBundle.items item " + "WHERE item.id = :itemId " + @@ -150,15 +154,18 @@ public Iterator findShowableByItem(Context context, UUID itemId, Stri query.setParameter("itemId", itemId); query.setParameter("bundleName", bundleName); - - return iterate(context, query, Bitstream.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Bitstream.class, this); } @Override public Iterator findByStoreNumber(Context context, Integer storeNumber) throws SQLException { - Query query = createQuery(context, "select b from Bitstream b where b.storeNumber = :storeNumber"); + Query query = createQuery(context, "select b.id from Bitstream b where b.storeNumber = :storeNumber"); query.setParameter("storeNumber", storeNumber); - return iterate(context, query, Bitstream.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Bitstream.class, this); } @Override diff --git a/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java index 443268cbbb7..3b12c68dced 100644 --- a/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java @@ -29,6 +29,7 @@ import org.dspace.content.dao.ItemDAO; import org.dspace.core.AbstractHibernateDSODAO; import org.dspace.core.Context; +import org.dspace.core.UUIDIterator; import org.dspace.eperson.EPerson; import org.hibernate.Criteria; import org.hibernate.criterion.Criterion; @@ -56,28 +57,34 @@ protected ItemDAOImpl() { @Override public Iterator findAll(Context context, boolean archived) throws SQLException { - Query query = createQuery(context, "FROM Item WHERE inArchive=:in_archive ORDER BY id"); + Query query = createQuery(context, "SELECT i.id FROM Item i WHERE inArchive=:in_archive ORDER BY id"); query.setParameter("in_archive", archived); - return iterate(context, query, Item.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Item.class, this); } @Override public Iterator findAll(Context context, boolean archived, int limit, int offset) throws SQLException { - Query query = createQuery(context, "FROM Item WHERE inArchive=:in_archive ORDER BY id"); + Query query = createQuery(context, "SELECT i.id FROM Item i WHERE inArchive=:in_archive ORDER BY id"); query.setParameter("in_archive", archived); query.setFirstResult(offset); query.setMaxResults(limit); - return iterate(context, query, Item.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Item.class, this); } @Override public Iterator findAll(Context context, boolean archived, boolean withdrawn) throws SQLException { Query query = createQuery(context, - "FROM Item WHERE inArchive=:in_archive or withdrawn=:withdrawn ORDER BY id"); + "SELECT i.id FROM Item i WHERE inArchive=:in_archive or withdrawn=:withdrawn ORDER BY id"); query.setParameter("in_archive", archived); query.setParameter("withdrawn", withdrawn); - return iterate(context, query, Item.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Item.class, this); } @Override @@ -86,12 +93,14 @@ public Iterator findAllRegularItems(Context context) throws SQLException { // It does not include workspace, workflow or template items. Query query = createQuery( context, - "SELECT i FROM Item as i " + + "SELECT i.id FROM Item as i " + "LEFT JOIN Version as v ON i = v.item " + "WHERE i.inArchive=true or i.withdrawn=true or (i.inArchive=false and v.id IS NOT NULL) " + "ORDER BY i.id" ); - return iterate(context, query, Item.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Item.class, this); } @Override @@ -99,7 +108,7 @@ public Iterator findAll(Context context, boolean archived, boolean withdrawn, boolean discoverable, Date lastModified) throws SQLException { StringBuilder queryStr = new StringBuilder(); - queryStr.append("SELECT i FROM Item i"); + queryStr.append("SELECT i.id FROM Item i"); queryStr.append(" WHERE (inArchive = :in_archive OR withdrawn = :withdrawn)"); queryStr.append(" AND discoverable = :discoverable"); @@ -115,16 +124,20 @@ public Iterator findAll(Context context, boolean archived, if (lastModified != null) { query.setParameter("last_modified", lastModified, TemporalType.TIMESTAMP); } - return iterate(context, query, Item.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Item.class, this); } @Override public Iterator findBySubmitter(Context context, EPerson eperson) throws SQLException { Query query = createQuery(context, - "FROM Item WHERE inArchive=:in_archive and submitter=:submitter ORDER BY id"); + "SELECT i.id FROM Item i WHERE inArchive=:in_archive and submitter=:submitter ORDER BY id"); query.setParameter("in_archive", true); query.setParameter("submitter", eperson); - return iterate(context, query, Item.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Item.class, this); } @Override @@ -133,16 +146,18 @@ public Iterator findBySubmitter(Context context, EPerson eperson, boolean if (!retrieveAllItems) { return findBySubmitter(context, eperson); } - Query query = createQuery(context, "FROM Item WHERE submitter=:submitter ORDER BY id"); + Query query = createQuery(context, "SELECT i.id FROM Item i WHERE submitter=:submitter ORDER BY id"); query.setParameter("submitter", eperson); - return iterate(context, query, Item.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Item.class, this); } @Override public Iterator findBySubmitter(Context context, EPerson eperson, MetadataField metadataField, int limit) throws SQLException { StringBuilder query = new StringBuilder(); - query.append("SELECT item FROM Item as item "); + query.append("SELECT item.id FROM Item as item "); addMetadataLeftJoin(query, Item.class.getSimpleName().toLowerCase(), Collections.singletonList(metadataField)); query.append(" WHERE item.inArchive = :in_archive"); query.append(" AND item.submitter =:submitter"); @@ -154,13 +169,15 @@ public Iterator findBySubmitter(Context context, EPerson eperson, Metadata hibernateQuery.setParameter("in_archive", true); hibernateQuery.setParameter("submitter", eperson); hibernateQuery.setMaxResults(limit); - return iterate(context, hibernateQuery, Item.class); + @SuppressWarnings("unchecked") + List uuids = hibernateQuery.getResultList(); + return new UUIDIterator(context, uuids, Item.class, this); } @Override public Iterator findByMetadataField(Context context, MetadataField metadataField, String value, boolean inArchive) throws SQLException { - String hqlQueryString = "SELECT item FROM Item as item join item.metadata metadatavalue " + + String hqlQueryString = "SELECT item.id FROM Item as item join item.metadata metadatavalue " + "WHERE item.inArchive=:in_archive AND metadatavalue.metadataField = :metadata_field"; if (value != null) { hqlQueryString += " AND STR(metadatavalue.value) = :text_value"; @@ -172,13 +189,15 @@ public Iterator findByMetadataField(Context context, MetadataField metadat if (value != null) { query.setParameter("text_value", value); } - return iterate(context, query, Item.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Item.class, this); } @Override public Iterator findByMetadataField(Context context, MetadataField metadataField, String value) throws SQLException { - String hqlQueryString = "SELECT item FROM Item as item join item.metadata metadatavalue " + + String hqlQueryString = "SELECT item.id FROM Item as item join item.metadata metadatavalue " + "WHERE metadatavalue.metadataField = :metadata_field"; if (value != null) { hqlQueryString += " AND STR(metadatavalue.value) = :text_value"; @@ -189,7 +208,9 @@ public Iterator findByMetadataField(Context context, MetadataField metadat if (value != null) { query.setParameter("text_value", value); } - return iterate(context, query, Item.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Item.class, this); } enum OP { @@ -310,20 +331,22 @@ public Iterator findByMetadataQuery(Context context, List findByAuthorityValue(Context context, MetadataField metadataField, String authority, boolean inArchive) throws SQLException { Query query = createQuery(context, - "SELECT item FROM Item as item join item.metadata metadatavalue " + + "SELECT item.id FROM Item as item join item.metadata metadatavalue " + "WHERE item.inArchive=:in_archive AND metadatavalue.metadataField = :metadata_field AND " + "metadatavalue.authority = :authority ORDER BY item.id"); query.setParameter("in_archive", inArchive); query.setParameter("metadata_field", metadataField); query.setParameter("authority", authority); - return iterate(context, query, Item.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Item.class, this); } @Override public Iterator findArchivedByCollection(Context context, Collection collection, Integer limit, Integer offset) throws SQLException { Query query = createQuery(context, - "select i from Item i join i.collections c " + + "select i.id from Item i join i.collections c " + "WHERE :collection IN c AND i.inArchive=:in_archive ORDER BY i.id"); query.setParameter("collection", collection); query.setParameter("in_archive", true); @@ -333,7 +356,9 @@ public Iterator findArchivedByCollection(Context context, Collection colle if (limit != null) { query.setMaxResults(limit); } - return iterate(context, query, Item.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Item.class, this); } @Override @@ -368,17 +393,18 @@ public int countArchivedByCollectionExcludingOwning(Context context, Collection @Override public Iterator findAllByCollection(Context context, Collection collection) throws SQLException { Query query = createQuery(context, - "select i from Item i join i.collections c WHERE :collection IN c ORDER BY i.id"); + "select i.id from Item i join i.collections c WHERE :collection IN c ORDER BY i.id"); query.setParameter("collection", collection); - - return iterate(context, query, Item.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Item.class, this); } @Override public Iterator findAllByCollection(Context context, Collection collection, Integer limit, Integer offset) throws SQLException { Query query = createQuery(context, - "select i from Item i join i.collections c WHERE :collection IN c ORDER BY i.id"); + "select i.id from Item i join i.collections c WHERE :collection IN c ORDER BY i.id"); query.setParameter("collection", collection); if (offset != null) { @@ -387,8 +413,9 @@ public Iterator findAllByCollection(Context context, Collection collection if (limit != null) { query.setMaxResults(limit); } - - return iterate(context, query, Item.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Item.class, this); } @Override @@ -424,9 +451,12 @@ public int countItems(Context context, List collections, boolean inc public Iterator findByLastModifiedSince(Context context, Date since) throws SQLException { Query query = createQuery(context, - "SELECT i FROM Item i WHERE last_modified > :last_modified ORDER BY id"); + "SELECT i.id FROM Item i WHERE last_modified > :last_modified ORDER BY id"); query.setParameter("last_modified", since, TemporalType.TIMESTAMP); - return iterate(context, query, Item.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Item.class, this); + } @Override @@ -462,22 +492,21 @@ public Iterator findByLikeAuthorityValue(Context context, String likeAuthority, Boolean inArchive) throws SQLException { String allItems = Objects.isNull(inArchive) ? "" : " item.inArchive=:in_archive AND "; Query query = createQuery(context, - "SELECT DISTINCT item FROM Item as item join item.metadata metadatavalue " + "SELECT DISTINCT item.id FROM Item as item join item.metadata metadatavalue " + "WHERE" + allItems + " metadatavalue.authority like :authority ORDER BY item.id"); if (Objects.nonNull(inArchive)) { query.setParameter("in_archive", inArchive); } query.setParameter("authority", likeAuthority); - return iterate(context, query, Item.class); + @SuppressWarnings("unchecked") + List uuids = query.getResultList(); + return new UUIDIterator(context, uuids, Item.class, this); } @Override public Iterator findByIds(Context context, List ids) throws SQLException { - Query query = createQuery(context, - "SELECT item " + "FROM Item as item WHERE item.id IN (:ids)"); - query.setParameter("ids", ids); - return iterate(context, query, Item.class); + return new UUIDIterator(context, ids, Item.class, this); } } diff --git a/dspace-api/src/main/java/org/dspace/content/dao/impl/MetadataValueDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/MetadataValueDAOImpl.java index 2450db5bd19..f37ced9ab7d 100644 --- a/dspace-api/src/main/java/org/dspace/content/dao/impl/MetadataValueDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/dao/impl/MetadataValueDAOImpl.java @@ -58,7 +58,7 @@ public Iterator findItemValuesByFieldAndValue(Context context, Query query = createQuery(context, queryString); query.setParameter("metadata_field_id", metadataField.getID()); query.setParameter("text_value", value); - return iterate(context, query, MetadataValue.class); + return iterate(query); } @Override @@ -69,7 +69,7 @@ public Iterator findByValueLike(Context context, String value) th Query query = createQuery(context, queryString); query.setParameter("searchString", value); - return iterate(context, query, MetadataValue.class); + return iterate(query); } @Override diff --git a/dspace-api/src/main/java/org/dspace/core/AbstractHibernateDAO.java b/dspace-api/src/main/java/org/dspace/core/AbstractHibernateDAO.java index c217eed6ab9..c18f256d956 100644 --- a/dspace-api/src/main/java/org/dspace/core/AbstractHibernateDAO.java +++ b/dspace-api/src/main/java/org/dspace/core/AbstractHibernateDAO.java @@ -26,6 +26,7 @@ import javax.persistence.criteria.Path; import javax.persistence.criteria.Root; +import com.google.common.collect.AbstractIterator; import org.apache.commons.collections.CollectionUtils; import org.hibernate.Session; @@ -341,14 +342,22 @@ public T uniqueResult(Query query) { * @param query * The query for which an Iterator will be made * @return The Iterator for the results of this query - * @throws SQLException */ - public Iterator iterate(Context ctx, Query query, Class entityType) throws SQLException { + public Iterator iterate(Query query) { @SuppressWarnings("unchecked") org.hibernate.query.Query hquery = query.unwrap(org.hibernate.query.Query.class); Stream stream = hquery.stream(); Iterator iter = stream.iterator(); - return new UUIDIterator(ctx, iter, entityType); + return new AbstractIterator () { + @Override + protected T computeNext() { + return iter.hasNext() ? iter.next() : endOfData(); + } + @Override + public void finalize() { + stream.close(); + } + }; } /** diff --git a/dspace-api/src/main/java/org/dspace/core/UUIDIterator.java b/dspace-api/src/main/java/org/dspace/core/UUIDIterator.java index f14ece67742..679f623eb85 100644 --- a/dspace-api/src/main/java/org/dspace/core/UUIDIterator.java +++ b/dspace-api/src/main/java/org/dspace/core/UUIDIterator.java @@ -8,20 +8,15 @@ package org.dspace.core; import java.sql.SQLException; -import java.util.Collection; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.UUID; import com.google.common.collect.AbstractIterator; import org.dspace.content.DSpaceObject; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.DSpaceObjectService; +import org.dspace.core.exception.SQLRuntimeException; import org.springframework.beans.factory.annotation.Autowired; - - /** * Iterator implementation which allows to iterate over items and commit while * iterating. Using a list of UUID the iterator doesn't get invalidated after a @@ -30,77 +25,30 @@ * @author stefano.maffei at 4science.com * @param class type */ -public class UUIDIterator extends AbstractIterator { - - private Class entityTypeClass; - - private List cachedUUIDs = new LinkedList<>(); - - private Iterator uuidIterator; +public class UUIDIterator extends AbstractIterator { + private Class clazz; - private Iterator iterator; + private Iterator iterator; - @SuppressWarnings("rawtypes") @Autowired - private DSpaceObjectService dsoService; + private AbstractHibernateDSODAO dao; private Context ctx; - private boolean isSupportedUUIDIterator; - - public UUIDIterator(Context ctx, Iterator i, Class entityTypeClass) throws SQLException { + public UUIDIterator(Context ctx, List uuids, Class clazz, AbstractHibernateDSODAO dao) + throws SQLException { this.ctx = ctx; - - this.entityTypeClass = entityTypeClass; - isSupportedUUIDIterator = DSpaceObject.class.isAssignableFrom(this.entityTypeClass); - - if (isSupportedUUIDIterator) { - while (i.hasNext()) { - DSpaceObject dso = (DSpaceObject) i.next(); - if (dsoService == null) { - dsoService = ContentServiceFactory.getInstance().getDSpaceObjectService(dso); - } - cachedUUIDs.add(dso.getID()); - } - uuidIterator = cachedUUIDs.iterator(); - } else { - iterator = i; - } - - } - - public UUIDIterator(Context ctx, Collection collection, Class entityTypeClass) throws SQLException { - this.ctx = ctx; - - this.entityTypeClass = entityTypeClass; - isSupportedUUIDIterator = DSpaceObject.class.isAssignableFrom(this.entityTypeClass); - - if (isSupportedUUIDIterator) { - for (T obj : collection) { - DSpaceObject dso = (DSpaceObject) obj; - if (dsoService == null) { - dsoService = ContentServiceFactory.getInstance().getDSpaceObjectService(dso); - } - cachedUUIDs.add(dso.getID()); - } - uuidIterator = cachedUUIDs.iterator(); - } else { - throw new UnsupportedOperationException("Cannot generate iterator for this collection"); - } - + this.clazz = clazz; + this.dao = dao; + this.iterator = uuids.iterator(); } - @SuppressWarnings("unchecked") @Override protected T computeNext() { try { - if (isSupportedUUIDIterator) { - return uuidIterator.hasNext() ? (T) dsoService.find(ctx, uuidIterator.next()) : endOfData(); - } else { - return iterator.hasNext() ? (T) iterator.next() : endOfData(); - } + return iterator.hasNext() ? dao.findByID(ctx, clazz, iterator.next()) : endOfData(); } catch (SQLException e) { - throw new RuntimeException(e); + throw new SQLRuntimeException(e); } } diff --git a/dspace-api/src/main/java/org/dspace/curate/Curator.java b/dspace-api/src/main/java/org/dspace/curate/Curator.java index dc160c336d3..b8b174a1261 100644 --- a/dspace-api/src/main/java/org/dspace/curate/Curator.java +++ b/dspace-api/src/main/java/org/dspace/curate/Curator.java @@ -28,7 +28,6 @@ import org.dspace.content.service.ItemService; import org.dspace.core.Constants; import org.dspace.core.Context; -import org.dspace.core.UUIDIterator; import org.dspace.core.factory.CoreServiceFactory; import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.handle.service.HandleService; @@ -464,10 +463,8 @@ protected boolean doSite(TaskRunner tr, Site site) throws IOException { //Then, perform this task for all Top-Level Communities in the Site // (this will recursively perform task for all objects in DSpace) - Iterator iterator = new UUIDIterator(ctx, communityService.findAllTop(ctx), - Community.class); - while (iterator.hasNext()) { - if (!doCommunity(tr, iterator.next())) { + for (Community subcomm : communityService.findAllTop(ctx)) { + if (!doCommunity(tr, subcomm)) { return false; } } @@ -488,24 +485,16 @@ protected boolean doSite(TaskRunner tr, Site site) throws IOException { * @throws SQLException */ protected boolean doCommunity(TaskRunner tr, Community comm) throws IOException, SQLException { - UUIDIterator subComIter = new UUIDIterator(curationContext(), comm.getSubcommunities(), - Community.class); - UUIDIterator collectionsIter = new UUIDIterator(curationContext(), - comm.getCollections(), - Collection.class); - if (!tr.run(comm)) { return false; } - - while (subComIter.hasNext()) { - if (!doCommunity(tr, subComIter.next())) { + for (Community subcomm : comm.getSubcommunities()) { + if (!doCommunity(tr, subcomm)) { return false; } } - - while (collectionsIter.hasNext()) { - if (!doCollection(tr, collectionsIter.next())) { + for (Collection coll : comm.getCollections()) { + if (!doCollection(tr, coll)) { return false; } } From ced78d38f670d7857af36b807e2f4e67c3825a93 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Fri, 12 Jan 2024 19:09:12 +0100 Subject: [PATCH 31/82] DS-1455 fix lazy initialization exception after commit iterating over communities/collections --- dspace-api/src/main/java/org/dspace/curate/Curator.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/curate/Curator.java b/dspace-api/src/main/java/org/dspace/curate/Curator.java index b8b174a1261..737f2e2277e 100644 --- a/dspace-api/src/main/java/org/dspace/curate/Curator.java +++ b/dspace-api/src/main/java/org/dspace/curate/Curator.java @@ -498,6 +498,8 @@ protected boolean doCommunity(TaskRunner tr, Community comm) throws IOException, return false; } } + Context context = curationContext(); + context.uncacheEntity(comm); return true; } @@ -524,6 +526,7 @@ protected boolean doCollection(TaskRunner tr, Collection coll) throws IOExceptio return false; } } + context.uncacheEntity(coll); } catch (SQLException sqlE) { throw new IOException(sqlE.getMessage(), sqlE); } @@ -542,6 +545,7 @@ protected void visit(DSpaceObject dso) throws IOException, SQLException { Context curCtx = curationContext(); if (curCtx != null && txScope.equals(TxScope.OBJECT)) { curCtx.commit(); + curCtx.reloadEntity(dso); } } From 8c1fa8a019fb974fa40471da094b88d4e8f998e9 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Fri, 12 Jan 2024 19:51:19 +0100 Subject: [PATCH 32/82] DSC-1455 improve handling of not existings ID and test that findByIds consider distinct uuid --- .../main/java/org/dspace/content/ItemServiceImpl.java | 2 +- .../src/main/java/org/dspace/core/UUIDIterator.java | 11 ++++++++++- .../org/dspace/app/rest/ItemRestRepositoryIT.java | 2 ++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java index c61d6e8c8c9..6b3ef003edc 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -1844,7 +1844,7 @@ public boolean isItemListedForUser(Context context, Item item) { @Override public Iterator findByIds(Context context, List ids) throws SQLException { return itemDAO.findByIds(context, - ids.stream().map(uuid -> UUID.fromString(uuid)).collect(Collectors.toList())); + ids.stream().map(uuid -> UUID.fromString(uuid)).distinct().collect(Collectors.toList())); } @Override diff --git a/dspace-api/src/main/java/org/dspace/core/UUIDIterator.java b/dspace-api/src/main/java/org/dspace/core/UUIDIterator.java index 679f623eb85..7cd2616ff6e 100644 --- a/dspace-api/src/main/java/org/dspace/core/UUIDIterator.java +++ b/dspace-api/src/main/java/org/dspace/core/UUIDIterator.java @@ -46,7 +46,16 @@ public UUIDIterator(Context ctx, List uuids, Class clazz, AbstractHiber @Override protected T computeNext() { try { - return iterator.hasNext() ? dao.findByID(ctx, clazz, iterator.next()) : endOfData(); + if (iterator.hasNext()) { + T item = dao.findByID(ctx, clazz, iterator.next()); + if (item != null) { + return item; + } else { + return computeNext(); + } + } else { + return endOfData(); + } } catch (SQLException e) { throw new SQLRuntimeException(e); } 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 7e0edf08bac..681967931f0 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 @@ -415,6 +415,8 @@ public void findAllByIdTest() throws Exception { getClient(token).perform(get("/api/core/items/search/findAllById") .param("id", publicItem1.getID().toString(), + publicItem1.getID().toString(), + UUID.randomUUID().toString(), publicItem2.getID().toString(), UUID.randomUUID().toString() )) From dd699c233948706684916398117ab1d3405d802c Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Mon, 15 Jan 2024 10:47:32 +0100 Subject: [PATCH 33/82] DSC-1455 improve description of expected result --- .../test/java/org/dspace/app/rest/ItemRestRepositoryIT.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 681967931f0..f3877445895 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 @@ -411,7 +411,8 @@ public void findAllByIdTest() throws Exception { context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); - // We want to test that only and exclusively existing items are returned. + // We want to test that only and exclusively existing items are returned + // and each item is returned just one time getClient(token).perform(get("/api/core/items/search/findAllById") .param("id", publicItem1.getID().toString(), From db1d7663b6d5b409d2825ff26612152fbb863305 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Mon, 15 Jan 2024 11:49:47 +0100 Subject: [PATCH 34/82] DSC-1458 add and update javadoc to reflect the requested behavior. Add IT to verify the implementation --- .../dspace/content/enhancer/ItemEnhancer.java | 9 ++ .../enhancer/service/ItemEnhancerService.java | 4 +- .../service/ItemEnhancerServiceIT.java | 106 ++++++++++++++++++ 3 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 dspace-api/src/test/java/org/dspace/content/service/ItemEnhancerServiceIT.java diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/ItemEnhancer.java b/dspace-api/src/main/java/org/dspace/content/enhancer/ItemEnhancer.java index a3784146c5a..70ebf8c5a15 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/ItemEnhancer.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/ItemEnhancer.java @@ -48,5 +48,14 @@ public interface ItemEnhancer { */ void enhance(Context context, Item item); + /** + * Check if the item needs to be re-evaluated. An item needs to be re-evaluated if the result of the evaluation is + * different than its current state. We delegate this check to the individual item enhancer as it can be smarter in + * performing this check than the service as it knows its internal logic + * + * @param context the DSpace Context + * @param item the item to check + * @return true, if the item must be updated + */ boolean needUpdate(Context context, Item item); } diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/service/ItemEnhancerService.java b/dspace-api/src/main/java/org/dspace/content/enhancer/service/ItemEnhancerService.java index 5b3b419bfa8..133b6f74e8b 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/service/ItemEnhancerService.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/service/ItemEnhancerService.java @@ -28,8 +28,8 @@ public interface ItemEnhancerService { void enhance(Context context, Item item); /** - * Remove all the already calculated virtual metadata fields from the given item - * and perform a new enhancement. + * Force the computation of the enhanced values and, only if the result is different than the current state of the + * item update it. * * @param context the DSpace Context * @param item the item to enhance diff --git a/dspace-api/src/test/java/org/dspace/content/service/ItemEnhancerServiceIT.java b/dspace-api/src/test/java/org/dspace/content/service/ItemEnhancerServiceIT.java new file mode 100644 index 00000000000..76ef29bdb3b --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/content/service/ItemEnhancerServiceIT.java @@ -0,0 +1,106 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.content.service; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import org.apache.logging.log4j.Logger; +import org.dspace.AbstractIntegrationTestWithDatabase; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.ItemBuilder; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.Item; +import org.dspace.content.MetadataValue; +import org.dspace.content.enhancer.service.ItemEnhancerService; +import org.dspace.content.enhancer.service.impl.ItemEnhancerServiceImpl; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.utils.DSpace; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class ItemEnhancerServiceIT extends AbstractIntegrationTestWithDatabase { + private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemEnhancerServiceIT.class); + + private ItemService itemService = ContentServiceFactory.getInstance().getItemService(); + private ItemService spyItemService = spy(itemService); + private ItemEnhancerServiceImpl itemEnhancerService = (ItemEnhancerServiceImpl) new DSpace() + .getSingletonService(ItemEnhancerService.class); + + Community community; + Collection collPub; + Collection collPerson; + Item person; + Item publication; + + /** + * This method will be run before every test as per @Before. It will + * initialize resources required for the tests. + */ + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + context.turnOffAuthorisationSystem(); + + community = CommunityBuilder.createCommunity(context) + .build(); + collPerson = CollectionBuilder.createCollection(context, community) + .withEntityType("Person") + .build(); + collPub = CollectionBuilder.createCollection(context, community) + .withEntityType("Publication") + .build(); + person = ItemBuilder.createItem(context, collPerson) + .withTitle("Famous Researcher") + .withAffiliation("Some department", null) + .build(); + + publication = ItemBuilder.createItem(context, collPub) + .withTitle("Item to enhance") + .withAuthor(person.getName(), person.getID().toString()) + .build(); + assertMetadataValue(itemService.getMetadataByMetadataString(publication, "cris.virtual.department").get(0), + "cris", "virtual", "department", "Some department", null, 0); + context.restoreAuthSystemState(); + itemEnhancerService.setItemService(spyItemService); + } + + @After + public void after() { + itemEnhancerService.setItemService(itemService); + } + + @Test + public void noUpdateRequiredTest() throws Exception { + context.turnOffAuthorisationSystem(); + itemEnhancerService.enhance(context, publication); + verify(spyItemService, never()).update(any(), any()); + itemEnhancerService.forceEnhancement(context, publication); + verify(spyItemService, never()).update(any(), any()); + context.restoreAuthSystemState(); + } + + + private void assertMetadataValue(MetadataValue metadataValue, String schema, String element, String qualifier, + String value, String authority, int place) { + assertThat(metadataValue.getValue(), equalTo(value)); + assertThat(metadataValue.getMetadataField().getMetadataSchema().getName(), equalTo(schema)); + assertThat(metadataValue.getMetadataField().getElement(), equalTo(element)); + assertThat(metadataValue.getMetadataField().getQualifier(), equalTo(qualifier)); + assertThat(metadataValue.getAuthority(), equalTo(authority)); + assertThat(metadataValue.getPlace(), equalTo(place)); + } +} From de625fc8156e38af5ab7f65798ba15d6481dea3d Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Mon, 15 Jan 2024 13:47:01 +0100 Subject: [PATCH 35/82] DSC-1458 update implementation to support deep mode concept --- .../dspace/content/enhancer/ItemEnhancer.java | 15 +- .../consumer/ItemEnhancerConsumer.java | 2 +- .../impl/RelatedEntityItemEnhancer.java | 195 +++++++++++------- .../enhancer/script/ItemEnhancerScript.java | 8 +- .../ItemEnhancerScriptConfiguration.java | 4 +- .../enhancer/service/ItemEnhancerService.java | 12 +- .../service/impl/ItemEnhancerServiceImpl.java | 47 +---- .../service/ItemEnhancerServiceIT.java | 4 +- 8 files changed, 142 insertions(+), 145 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/ItemEnhancer.java b/dspace-api/src/main/java/org/dspace/content/enhancer/ItemEnhancer.java index 70ebf8c5a15..ee6bdbf1dff 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/ItemEnhancer.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/ItemEnhancer.java @@ -45,17 +45,10 @@ public interface ItemEnhancer { * * @param context the DSpace Context * @param item the item to enhance + * @param deepMode false, if the implementation can assume that only the target + * item as been updated since the eventual previous computation of enhanced metadata + * @return true, if any changes have been performed on the provided item */ - void enhance(Context context, Item item); + boolean enhance(Context context, Item item, boolean deepMode); - /** - * Check if the item needs to be re-evaluated. An item needs to be re-evaluated if the result of the evaluation is - * different than its current state. We delegate this check to the individual item enhancer as it can be smarter in - * performing this check than the service as it knows its internal logic - * - * @param context the DSpace Context - * @param item the item to check - * @return true, if the item must be updated - */ - boolean needUpdate(Context context, Item item); } diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumer.java b/dspace-api/src/main/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumer.java index 5a2ae2975ef..c526537bf5a 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumer.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumer.java @@ -61,7 +61,7 @@ public void consume(Context context, Event event) throws Exception { context.turnOffAuthorisationSystem(); try { - itemEnhancerService.enhance(context, item); + itemEnhancerService.enhance(context, item, false); } finally { context.restoreAuthSystemState(); } diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java index 1eb85afe817..db0fc6573f0 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java @@ -18,9 +18,11 @@ import java.util.function.Consumer; import java.util.function.Predicate; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.dspace.content.Item; import org.dspace.content.MetadataValue; +import org.dspace.content.authority.Choices; +import org.dspace.content.dto.MetadataValueDTO; import org.dspace.content.enhancer.AbstractItemEnhancer; import org.dspace.content.enhancer.ItemEnhancer; import org.dspace.content.service.ItemService; @@ -57,88 +59,148 @@ public boolean canEnhance(Context context, Item item) { } @Override - public void enhance(Context context, Item item) { - try { - boolean isMetadataDeleted = cleanObsoleteVirtualFields(context, item); - if (isMetadataDeleted) { - updateVirtualFieldsPlaces(context, item); + public boolean enhance(Context context, Item item, boolean deepMode) { + boolean result = false; + if (!deepMode) { + try { + result = cleanObsoleteVirtualFields(context, item); + result = result || updateVirtualFieldsPlaces(context, item); + result = result || performEnhancement(context, item); + } catch (SQLException e) { + LOGGER.error("An error occurs enhancing item with id {}: {}", item.getID(), e.getMessage(), e); + throw new SQLRuntimeException(e); } - if (needUpdate(context, item)) { - performEnhancement(context, item); + } else { + List currMetadataValues = getCurrentVirtualMetadata(context, item); + List toBeMetadataValues = getToBeVirtualMetadata(context, item); + if (!equivalent(currMetadataValues, toBeMetadataValues)) { + try { + itemService.removeMetadataValues(context, item, currMetadataValues); + addMetadata(context, item, toBeMetadataValues); + } catch (SQLException e) { + throw new SQLRuntimeException(e); + } + result = true; } - } catch (SQLException e) { - LOGGER.error("An error occurs enhancing item with id {}: {}", item.getID(), e.getMessage(), e); - throw new SQLRuntimeException(e); } + return result; } - @Override - public boolean needUpdate(Context context, Item item) { - List metadataValuesToDelete = getObsoleteVirtualFields(item); - boolean isNeedUpdateMetadata = false; + private void addMetadata(Context context, Item item, List toBeMetadataValues) + throws SQLException { + for (MetadataValueDTO dto : toBeMetadataValues) { + itemService.addMetadata(context, item, dto.getSchema(), dto.getElement(), dto.getQualifier(), null, + dto.getValue(), dto.getAuthority(), dto.getConfidence()); + } + } - if (!noEnhanceableMetadata(context, item)) { - for (MetadataValue metadataValue : getEnhanceableMetadataValue(item)) { - if (wasValueAlreadyUsedForEnhancement(item, metadataValue)) { - continue; + private boolean equivalent(List currMetadataValues, List toBeMetadataValues) { + if (currMetadataValues.size() != toBeMetadataValues.size()) { + return false; + } else { + for (int idx = 0; idx < currMetadataValues.size(); idx++) { + if (!equivalent(currMetadataValues.get(idx), toBeMetadataValues.get(idx))) { + return false; } + } + } + return true; + } - Item relatedItem = findRelatedEntityItem(context, metadataValue); - if (relatedItem == null) { - isNeedUpdateMetadata = true; - break; - } + private boolean equivalent(MetadataValue metadataValue, MetadataValueDTO metadataValueDTO) { + return StringUtils.equals(metadataValue.getMetadataField().getMetadataSchema().getName(), + metadataValueDTO.getSchema()) + && StringUtils.equals(metadataValue.getMetadataField().getElement(), metadataValueDTO.getElement()) + && StringUtils.equals(metadataValue.getMetadataField().getQualifier(), metadataValueDTO.getQualifier()) + && StringUtils.equals(metadataValue.getValue(), metadataValueDTO.getValue()) + && StringUtils.equals(metadataValue.getAuthority(), metadataValueDTO.getAuthority()); + } - List relatedItemMetadataValues = - getMetadataValues(relatedItem, relatedItemMetadataField); - if (relatedItemMetadataValues.isEmpty()) { - isNeedUpdateMetadata = true; - break; - } - for (MetadataValue relatedItemMetadataValue : relatedItemMetadataValues) { - if (!isContainingMetadata(item, relatedItemMetadataValue.getValue())) { - isNeedUpdateMetadata = true; - break; - } - } + private List getToBeVirtualMetadata(Context context, Item item) { + List tobeVirtualMetadata = new ArrayList<>(); + List virtualSourceFields = getEnhanceableMetadataValue(item); + for (MetadataValue virtualSourceField : virtualSourceFields) { + MetadataValueDTO mv = new MetadataValueDTO(); + mv.setSchema(VIRTUAL_METADATA_SCHEMA); + mv.setElement(VIRTUAL_SOURCE_METADATA_ELEMENT); + mv.setQualifier(getVirtualQualifier()); + String authority = virtualSourceField.getAuthority(); + Item relatedItem = null; + if (StringUtils.isNotBlank(authority)) { + mv.setValue(authority); + relatedItem = findRelatedEntityItem(context, virtualSourceField); + } else { + mv.setValue(PLACEHOLDER_PARENT_METADATA_VALUE); + relatedItem = findRelatedEntityItem(context, virtualSourceField); + } + tobeVirtualMetadata.add(mv); + if (relatedItem == null) { + MetadataValueDTO mvRelated = new MetadataValueDTO(); + mvRelated.setSchema(VIRTUAL_METADATA_SCHEMA); + mvRelated.setElement(VIRTUAL_METADATA_ELEMENT); + mvRelated.setQualifier(getVirtualQualifier()); + mvRelated.setValue(PLACEHOLDER_PARENT_METADATA_VALUE); + tobeVirtualMetadata.add(mvRelated); + continue; + } + List relatedItemMetadataValues = getMetadataValues(relatedItem, relatedItemMetadataField); + if (relatedItemMetadataValues.isEmpty()) { + MetadataValueDTO mvRelated = new MetadataValueDTO(); + mvRelated.setSchema(VIRTUAL_METADATA_SCHEMA); + mvRelated.setElement(VIRTUAL_METADATA_ELEMENT); + mvRelated.setQualifier(getVirtualQualifier()); + mvRelated.setValue(PLACEHOLDER_PARENT_METADATA_VALUE); + tobeVirtualMetadata.add(mvRelated); + continue; + } + for (MetadataValue relatedItemMetadataValue : relatedItemMetadataValues) { + MetadataValueDTO mvRelated = new MetadataValueDTO(); + mvRelated.setSchema(VIRTUAL_METADATA_SCHEMA); + mvRelated.setElement(VIRTUAL_METADATA_ELEMENT); + mvRelated.setQualifier(getVirtualQualifier()); + mvRelated.setValue(relatedItemMetadataValue.getValue()); + String authorityRelated = relatedItemMetadataValue.getAuthority(); + if (StringUtils.isNotBlank(authorityRelated)) { + mvRelated.setAuthority(authorityRelated); + mvRelated.setConfidence(Choices.CF_ACCEPTED); + } + tobeVirtualMetadata.add(mvRelated); } } - - return !metadataValuesToDelete.isEmpty() || isNeedUpdateMetadata; + return tobeVirtualMetadata; } - private boolean isContainingMetadata(Item item, String value) { - return itemService.getMetadata(item, VIRTUAL_METADATA_SCHEMA, VIRTUAL_METADATA_ELEMENT, - getVirtualQualifier(), null, true).stream() - .anyMatch(metadataValue -> metadataValue.getValue().equals(value)); + private List getCurrentVirtualMetadata(Context context, Item item) { + List currentVirtualMetadata = new ArrayList<>(); + List virtualSourceFields = getVirtualSourceFields(item); + for (MetadataValue virtualSourceField : virtualSourceFields) { + currentVirtualMetadata.add(virtualSourceField); + getRelatedVirtualField(item, virtualSourceField).ifPresent(currentVirtualMetadata::add); + } + return currentVirtualMetadata; } - - /** - * Clean obsolete virtual fields. - * - * @param context the DSpace Context - * @param item the item to check - * @return true if some metadata is deleted, false if no metadata was deleted - */ private boolean cleanObsoleteVirtualFields(Context context, Item item) throws SQLException { - + boolean result = false; List metadataValuesToDelete = getObsoleteVirtualFields(item); if (!metadataValuesToDelete.isEmpty()) { itemService.removeMetadataValues(context, item, metadataValuesToDelete); - return true; - } else { - return false; + result = true; } + return result; } - private void updateVirtualFieldsPlaces(Context context, Item item) { + private boolean updateVirtualFieldsPlaces(Context context, Item item) { + boolean result = false; List virtualSourceFields = getVirtualSourceFields(item); for (MetadataValue virtualSourceField : virtualSourceFields) { - metadataWithPlaceToUpdate(item, virtualSourceField) - .ifPresent(updatePlaces(item, virtualSourceField)); + if (metadataWithPlaceToUpdate(item, virtualSourceField).isPresent()) { + updatePlaces(item, virtualSourceField); + result = true; + } } + return result; } private Optional metadataWithPlaceToUpdate(Item item, MetadataValue virtualSourceField) { @@ -192,10 +254,10 @@ private Optional getRelatedVirtualField(Item item, MetadataValue .findFirst(); } - private void performEnhancement(Context context, Item item) throws SQLException { - + private boolean performEnhancement(Context context, Item item) throws SQLException { + boolean result = false; if (noEnhanceableMetadata(context, item)) { - return; + return false; } for (MetadataValue metadataValue : getEnhanceableMetadataValue(item)) { @@ -207,7 +269,7 @@ private void performEnhancement(Context context, Item item) throws SQLException Item relatedItem = findRelatedEntityItem(context, metadataValue); if (relatedItem == null) { addVirtualField(context, item, PLACEHOLDER_PARENT_METADATA_VALUE); - addVirtualSourceField(context, item, PLACEHOLDER_PARENT_METADATA_VALUE); + addVirtualSourceField(context, item, metadataValue); continue; } @@ -221,9 +283,9 @@ private void performEnhancement(Context context, Item item) throws SQLException addVirtualField(context, item, relatedItemMetadataValue.getValue()); addVirtualSourceField(context, item, metadataValue); } - + result = true; } - + return result; } private boolean noEnhanceableMetadata(Context context, Item item) { @@ -234,13 +296,8 @@ private boolean noEnhanceableMetadata(Context context, Item item) { } private boolean validAuthority(Context context, MetadataValue metadataValue) { - - // FIXME: we could find a more efficient way, here we are doing twice the same action - // to understand if the enhanced item has at least an item whose references should be put in virtual fields. Item relatedItem = findRelatedEntityItem(context, metadataValue); - return Objects.nonNull(relatedItem) && - CollectionUtils.isNotEmpty( - getMetadataValues(relatedItem, relatedItemMetadataField)); + return Objects.nonNull(relatedItem); } private List getEnhanceableMetadataValue(Item item) { diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerScript.java b/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerScript.java index 2c4d1f20346..3100920dc17 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerScript.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerScript.java @@ -82,14 +82,8 @@ private Iterator findItemsToEnhance() { private void enhanceItem(Item item) { - if (force) { - itemEnhancerService.forceEnhancement(context, item); - } else { - itemEnhancerService.enhance(context, item); - } - + itemEnhancerService.enhance(context, item, force); uncacheItem(item); - } private void uncacheItem(Item item) { diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerScriptConfiguration.java index 3822695969f..17377f67a3d 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerScriptConfiguration.java @@ -41,7 +41,9 @@ public Options getOptions() { if (options == null) { Options options = new Options(); - options.addOption("f", "force", false, "force the recalculation of all the virtual fields"); + options.addOption("f", "force", false, + "force the usage of the deep mode" + + " (always compute the enhanced metadata to verify if the item need an update)"); options.getOption("f").setType(boolean.class); options.getOption("f").setRequired(false); diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/service/ItemEnhancerService.java b/dspace-api/src/main/java/org/dspace/content/enhancer/service/ItemEnhancerService.java index 133b6f74e8b..08170448e68 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/service/ItemEnhancerService.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/service/ItemEnhancerService.java @@ -24,15 +24,9 @@ public interface ItemEnhancerService { * * @param context the DSpace Context * @param item the item to enhance + * @param deepMode false, if the implementation can assume that only the target + * item as been updated since the eventual previous computation of enhanced metadata */ - void enhance(Context context, Item item); + void enhance(Context context, Item item, boolean deepMode); - /** - * Force the computation of the enhanced values and, only if the result is different than the current state of the - * item update it. - * - * @param context the DSpace Context - * @param item the item to enhance - */ - void forceEnhancement(Context context, Item item); } diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/service/impl/ItemEnhancerServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/enhancer/service/impl/ItemEnhancerServiceImpl.java index bc040eb1f7b..578f04305b8 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/service/impl/ItemEnhancerServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/service/impl/ItemEnhancerServiceImpl.java @@ -7,23 +7,15 @@ */ package org.dspace.content.enhancer.service.impl; -import static org.dspace.content.Item.ANY; -import static org.dspace.content.enhancer.ItemEnhancer.VIRTUAL_METADATA_ELEMENT; -import static org.dspace.content.enhancer.ItemEnhancer.VIRTUAL_METADATA_SCHEMA; -import static org.dspace.content.enhancer.ItemEnhancer.VIRTUAL_SOURCE_METADATA_ELEMENT; - import java.sql.SQLException; import java.util.List; -import org.apache.commons.collections4.ListUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Item; -import org.dspace.content.MetadataValue; import org.dspace.content.enhancer.ItemEnhancer; import org.dspace.content.enhancer.service.ItemEnhancerService; import org.dspace.content.service.ItemService; import org.dspace.core.Context; -import org.dspace.core.exception.SQLRuntimeException; import org.springframework.beans.factory.annotation.Autowired; /** @@ -41,15 +33,12 @@ public class ItemEnhancerServiceImpl implements ItemEnhancerService { private ItemService itemService; @Override - public void enhance(Context context, Item item) { + public void enhance(Context context, Item item, boolean deepMode) { boolean isUpdateNeeded = false; for (ItemEnhancer itemEnhancer : itemEnhancers) { if (itemEnhancer.canEnhance(context, item)) { - if (itemEnhancer.needUpdate(context,item)) { - itemEnhancer.enhance(context, item); - isUpdateNeeded = true; - } + isUpdateNeeded = isUpdateNeeded || itemEnhancer.enhance(context, item, deepMode); } } @@ -58,30 +47,6 @@ public void enhance(Context context, Item item) { } } - @Override - public void forceEnhancement(Context context, Item item) { - cleanUpVirtualFields(context, item); - enhance(context, item); - } - - private void cleanUpVirtualFields(Context context, Item item) { - - List virtualFields = getVirtualFields(item); - List virtualSourceFields = getVirtualSourceFields(item); - List metadataValuesToRemove = ListUtils.union(virtualFields, virtualSourceFields); - - if (metadataValuesToRemove.isEmpty()) { - return; - } - - try { - itemService.removeMetadataValues(context, item, metadataValuesToRemove); - } catch (SQLException e) { - throw new SQLRuntimeException(e); - } - - } - private void updateItem(Context context, Item item) { try { itemService.update(context, item); @@ -90,14 +55,6 @@ private void updateItem(Context context, Item item) { } } - private List getVirtualFields(Item item) { - return itemService.getMetadata(item, VIRTUAL_METADATA_SCHEMA, VIRTUAL_METADATA_ELEMENT, ANY, ANY); - } - - private List getVirtualSourceFields(Item item) { - return itemService.getMetadata(item, VIRTUAL_METADATA_SCHEMA, VIRTUAL_SOURCE_METADATA_ELEMENT, ANY, ANY); - } - public List getItemEnhancers() { return itemEnhancers; } diff --git a/dspace-api/src/test/java/org/dspace/content/service/ItemEnhancerServiceIT.java b/dspace-api/src/test/java/org/dspace/content/service/ItemEnhancerServiceIT.java index 76ef29bdb3b..d766b956528 100644 --- a/dspace-api/src/test/java/org/dspace/content/service/ItemEnhancerServiceIT.java +++ b/dspace-api/src/test/java/org/dspace/content/service/ItemEnhancerServiceIT.java @@ -86,9 +86,9 @@ public void after() { @Test public void noUpdateRequiredTest() throws Exception { context.turnOffAuthorisationSystem(); - itemEnhancerService.enhance(context, publication); + itemEnhancerService.enhance(context, publication, false); verify(spyItemService, never()).update(any(), any()); - itemEnhancerService.forceEnhancement(context, publication); + itemEnhancerService.enhance(context, publication, true); verify(spyItemService, never()).update(any(), any()); context.restoreAuthSystemState(); } From 3640ccf87085b4df565d7103a9d9e0d70777d8df Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Mon, 15 Jan 2024 14:08:33 +0100 Subject: [PATCH 36/82] DSC-1458 store the placeholder for source metadata without authority --- .../enhancer/impl/RelatedEntityItemEnhancer.java | 6 +++++- .../enhancer/consumer/ItemEnhancerConsumerIT.java | 10 ++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java index db0fc6573f0..23c01d940fb 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java @@ -355,7 +355,11 @@ private void addVirtualField(Context context, Item item, String value) throws SQ } private void addVirtualSourceField(Context context, Item item, MetadataValue sourceValue) throws SQLException { - addVirtualSourceField(context, item, sourceValue.getAuthority()); + if (StringUtils.isNotBlank(sourceValue.getAuthority())) { + addVirtualSourceField(context, item, sourceValue.getAuthority()); + } else { + addVirtualSourceField(context, item, PLACEHOLDER_PARENT_METADATA_VALUE); + } } private void addVirtualSourceField(Context context, Item item, String sourceValueAuthority) throws SQLException { diff --git a/dspace-api/src/test/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumerIT.java b/dspace-api/src/test/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumerIT.java index eee35a81d04..6e152ed7b0d 100644 --- a/dspace-api/src/test/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumerIT.java +++ b/dspace-api/src/test/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumerIT.java @@ -31,6 +31,7 @@ import org.dspace.content.WorkspaceItem; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.ItemService; +import org.dspace.core.CrisConstants; import org.dspace.core.ReloadableEntity; import org.junit.Before; import org.junit.Test; @@ -80,9 +81,12 @@ public void testSingleMetadataValueEnhancement() throws Exception { publication = commitAndReload(publication); List metadataValues = publication.getMetadata(); - assertThat(metadataValues, hasSize(9)); + assertThat(metadataValues, hasSize(11)); assertThat(metadataValues, hasItem(with("cris.virtual.department", "4Science"))); assertThat(metadataValues, hasItem(with("cris.virtualsource.department", personId))); + assertThat(metadataValues, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); + assertThat(metadataValues, hasItem(with("cris.virtualsource.author-orcid", personId))); + MetadataValue virtualField = getFirstMetadataValue(publication, "cris.virtual.department"); MetadataValue virtualSourceField = getFirstMetadataValue(publication, "cris.virtualsource.department"); @@ -94,10 +98,12 @@ public void testSingleMetadataValueEnhancement() throws Exception { publication = commitAndReload(publication); metadataValues = publication.getMetadata(); - assertThat(metadataValues, hasSize(10)); + assertThat(metadataValues, hasSize(12)); assertThat(metadataValues, hasItem(with("dc.contributor.author", "Walter White", personId, 600))); assertThat(metadataValues, hasItem(with("cris.virtual.department", "4Science"))); assertThat(metadataValues, hasItem(with("cris.virtualsource.department", personId))); + assertThat(metadataValues, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); + assertThat(metadataValues, hasItem(with("cris.virtualsource.author-orcid", personId))); assertThat(virtualField, equalTo(getFirstMetadataValue(publication, "cris.virtual.department"))); assertThat(virtualSourceField, equalTo(getFirstMetadataValue(publication, "cris.virtualsource.department"))); From 1b1fbacbd901238427ecb5b30aea41777b2ddcf6 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Mon, 15 Jan 2024 22:15:06 +0100 Subject: [PATCH 37/82] DSC-1455 fix lazy initialization iterating over the hierarchy with the object scope --- .../src/main/java/org/dspace/curate/Curator.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/curate/Curator.java b/dspace-api/src/main/java/org/dspace/curate/Curator.java index 737f2e2277e..670c9bcbe43 100644 --- a/dspace-api/src/main/java/org/dspace/curate/Curator.java +++ b/dspace-api/src/main/java/org/dspace/curate/Curator.java @@ -464,6 +464,8 @@ protected boolean doSite(TaskRunner tr, Site site) throws IOException { //Then, perform this task for all Top-Level Communities in the Site // (this will recursively perform task for all objects in DSpace) for (Community subcomm : communityService.findAllTop(ctx)) { + // force a reload of the community in case a commit was performed + subcomm = ctx.reloadEntity(subcomm); if (!doCommunity(tr, subcomm)) { return false; } @@ -488,17 +490,22 @@ protected boolean doCommunity(TaskRunner tr, Community comm) throws IOException, if (!tr.run(comm)) { return false; } + Context context = curationContext(); + // force a reload in case we are committing after each object + comm = context.reloadEntity(comm); for (Community subcomm : comm.getSubcommunities()) { if (!doCommunity(tr, subcomm)) { return false; } } + // force a reload in case we are committing after each object + comm = context.reloadEntity(comm); for (Collection coll : comm.getCollections()) { + context.reloadEntity(coll); if (!doCollection(tr, coll)) { return false; } } - Context context = curationContext(); context.uncacheEntity(comm); return true; } @@ -545,7 +552,6 @@ protected void visit(DSpaceObject dso) throws IOException, SQLException { Context curCtx = curationContext(); if (curCtx != null && txScope.equals(TxScope.OBJECT)) { curCtx.commit(); - curCtx.reloadEntity(dso); } } From bba03237fecee3cfccc1678bc722bcca0969cd51 Mon Sep 17 00:00:00 2001 From: "aliaksei.bykau" Date: Mon, 20 Nov 2023 08:59:19 +0100 Subject: [PATCH 38/82] [DSC-1355] removed config for dc.relation.ispartof from authority.cfg --- dspace/config/modules/authority.cfg | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dspace/config/modules/authority.cfg b/dspace/config/modules/authority.cfg index 037cf01e5d7..e1814c1669c 100644 --- a/dspace/config/modules/authority.cfg +++ b/dspace/config/modules/authority.cfg @@ -271,10 +271,6 @@ choices.plugin.dc.identifier.issn = ZDBAuthority choices.presentation.dc.identifier.issn = suggest authority.controlled.dc.identifier.issn = true -choices.plugin.dc.relation.ispartof = SherpaAuthority -choices.presentation.dc.relation.ispartof = suggest -authority.controlled.dc.relation.ispartof = true - authority.controlled.dc.type = true choices.plugin.dc.type = ControlledVocabularyAuthority From e4f3cf4e8c5df10fb38010522ffaa43684c47e38 Mon Sep 17 00:00:00 2001 From: "aliaksei.bykau" Date: Fri, 5 Jan 2024 09:17:05 +0100 Subject: [PATCH 39/82] [DSC-1457] Updated logic of MetadataImporter, RegistryUpdater and RegistryLoader. Now you can load all registries with one script. --- .../dspace/administer/MetadataImporter.java | 37 +++++++++++++++++-- .../org/dspace/administer/RegistryLoader.java | 10 ++++- .../dspace/storage/rdbms/RegistryUpdater.java | 5 ++- dspace/config/dspace.cfg | 2 + 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java b/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java index 2677cb20501..501d86af45f 100644 --- a/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java +++ b/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java @@ -7,8 +7,13 @@ */ package org.dspace.administer; +import java.io.File; import java.io.IOException; import java.sql.SQLException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPath; @@ -100,18 +105,39 @@ public static void main(String[] args) Options options = new Options(); options.addOption("f", "file", true, "source xml file for DC fields"); options.addOption("u", "update", false, "update an existing schema"); + options.addOption("h", "help", false, "help message"); CommandLine line = parser.parse(options, args); - if (line.hasOption('f')) { + if (line.hasOption('h')) { + usage(); + System.exit(1); + } else if (line.hasOption('f')) { String file = line.getOptionValue('f'); boolean forceUpdate = line.hasOption('u'); loadRegistry(file, forceUpdate); } else { - usage(); - System.exit(1); + boolean forceUpdate = line.hasOption('u'); + for (String file : getAllRegistryFiles()) { + loadRegistry(file, forceUpdate); + } } } + public static List getAllRegistryFiles() { + File folder = new File("config/registries"); + + if (folder.exists() && folder.isDirectory()) { + File[] files = folder.listFiles((dir, name) -> name.toLowerCase().endsWith(".xml")); + + if (files != null) { + return Arrays.stream(files) + .map(file -> "config/registries/" + file.getName()) + .collect(Collectors.toList()); + } + } + return Collections.emptyList(); + } + /** * Load the data from the specified file path into the database * @@ -285,7 +311,10 @@ private static void loadType(Context context, Node node) public static void usage() { String usage = "Use this class with the following option:\n" + " -f : specify which xml source file " + - "contains the DC fields to import.\n"; + "contains the DC fields to import.\n" + + "If you use the script without the -f parameter, then all" + + " registries will be loaded from the config/registries folder\n"; + System.out.println(usage); } } diff --git a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java index bbf320a0d5e..ba156cafc89 100644 --- a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java +++ b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java @@ -33,6 +33,8 @@ import org.w3c.dom.NodeList; import org.xml.sax.SAXException; + + /** * Loads the bitstream format and Dublin Core type registries into the database. * Intended for use as a command-line tool. @@ -84,7 +86,13 @@ public static void main(String[] argv) throws Exception { RegistryLoader.loadBitstreamFormats(context, argv[1]); } else if (argv[0].equalsIgnoreCase("-metadata")) { // Call MetadataImporter, as it handles Metadata schema updates - MetadataImporter.loadRegistry(argv[1], true); + if (argv.length == 1) { + for (String file : MetadataImporter.getAllRegistryFiles()) { + MetadataImporter.loadRegistry(file, true); + } + } else { + MetadataImporter.loadRegistry(argv[1], true); + } } else { System.err.println(usage); } diff --git a/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java b/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java index 7debf3ba449..6a1d71b9e65 100644 --- a/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java +++ b/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java @@ -69,8 +69,9 @@ private void updateRegistries() { + "registries" + File.separator; // Load updates to Bitstream format registry (if any) - log.info("Updating Bitstream Format Registry based on {}bitstream-formats.xml", base); - RegistryLoader.loadBitstreamFormats(context, base + "bitstream-formats.xml"); + String bitstreamFormat = config.getProperty("registry.bitstream-formats.load"); + log.info("Updating Bitstream Format Registry based on {}{}", base, bitstreamFormat); + RegistryLoader.loadBitstreamFormats(context, base + bitstreamFormat); // Load updates to Metadata schema registries (if any) log.info("Updating Metadata Registries based on metadata type configs in {}", base); diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 05ebdd66067..dcba8455fdd 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1012,6 +1012,8 @@ registry.metadata.load = dspace-types.xml registry.metadata.load = iiif-types.xml registry.metadata.load = bitstream-types.xml +registry.bitstream-formats.load = bitstream-formats.xml + #---------------------------------------------------------------# #-----------------UI-Related CONFIGURATIONS---------------------# #---------------------------------------------------------------# From 37cb89a28212a76412192f8841f6c4144987c35d Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Wed, 17 Jan 2024 22:56:26 +0100 Subject: [PATCH 40/82] DSC-1458 fix test and implementation related to the removal of a metadata --- .../impl/RelatedEntityItemEnhancer.java | 26 +++++++-------- .../service/impl/ItemEnhancerServiceImpl.java | 2 +- .../consumer/ItemEnhancerConsumerIT.java | 33 +++++++++++++++---- 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java index 23c01d940fb..1eee6daeee1 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java @@ -15,7 +15,6 @@ import java.util.Objects; import java.util.Optional; import java.util.UUID; -import java.util.function.Consumer; import java.util.function.Predicate; import org.apache.commons.lang3.StringUtils; @@ -64,8 +63,8 @@ public boolean enhance(Context context, Item item, boolean deepMode) { if (!deepMode) { try { result = cleanObsoleteVirtualFields(context, item); - result = result || updateVirtualFieldsPlaces(context, item); - result = result || performEnhancement(context, item); + result = updateVirtualFieldsPlaces(context, item) || result; + result = performEnhancement(context, item) || result; } catch (SQLException e) { LOGGER.error("An error occurs enhancing item with id {}: {}", item.getID(), e.getMessage(), e); throw new SQLRuntimeException(e); @@ -131,7 +130,6 @@ private List getToBeVirtualMetadata(Context context, Item item relatedItem = findRelatedEntityItem(context, virtualSourceField); } else { mv.setValue(PLACEHOLDER_PARENT_METADATA_VALUE); - relatedItem = findRelatedEntityItem(context, virtualSourceField); } tobeVirtualMetadata.add(mv); if (relatedItem == null) { @@ -195,8 +193,9 @@ private boolean updateVirtualFieldsPlaces(Context context, Item item) { boolean result = false; List virtualSourceFields = getVirtualSourceFields(item); for (MetadataValue virtualSourceField : virtualSourceFields) { - if (metadataWithPlaceToUpdate(item, virtualSourceField).isPresent()) { - updatePlaces(item, virtualSourceField); + Optional metadataWithPlaceToUpdate = metadataWithPlaceToUpdate(item, virtualSourceField); + if (metadataWithPlaceToUpdate.isPresent()) { + updatePlaces(item, metadataWithPlaceToUpdate.get(), virtualSourceField); result = true; } } @@ -213,12 +212,10 @@ private Predicate hasToUpdatePlace(MetadataValue virtualSourceFie return metadataValue -> metadataValue.getPlace() != virtualSourceField.getPlace(); } - private Consumer updatePlaces(Item item, MetadataValue virtualSourceField) { - return mv -> { - virtualSourceField.setPlace(mv.getPlace()); - getRelatedVirtualField(item, mv) - .ifPresent(relatedMv -> relatedMv.setPlace(mv.getPlace())); - }; + private void updatePlaces(Item item, MetadataValue mv, MetadataValue virtualSourceField) { + virtualSourceField.setPlace(mv.getPlace()); + getRelatedVirtualField(item, mv) + .ifPresent(relatedMv -> relatedMv.setPlace(mv.getPlace())); } private Optional findEnhanceableValue(MetadataValue virtualSourceField, Item item) { @@ -233,7 +230,7 @@ private List getObsoleteVirtualFields(Item item) { List virtualSourceFields = getVirtualSourceFields(item); for (MetadataValue virtualSourceField : virtualSourceFields) { - if (!isPlaceholder(virtualSourceField) && isRelatedSourceNoMorePresent(item, virtualSourceField)) { + if (isRelatedSourceNoMorePresent(item, virtualSourceField)) { obsoleteVirtualFields.add(virtualSourceField); getRelatedVirtualField(item, virtualSourceField).ifPresent(obsoleteVirtualFields::add); } @@ -321,7 +318,8 @@ private boolean isPlaceholderAtPlace(List metadataValues, int pla } private boolean hasAuthorityEqualsTo(MetadataValue metadataValue, String authority) { - return Objects.equals(metadataValue.getAuthority(), authority); + return Objects.equals(metadataValue.getAuthority(), authority) + || Objects.equals(PLACEHOLDER_PARENT_METADATA_VALUE, authority); } private Item findRelatedEntityItem(Context context, MetadataValue metadataValue) { diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/service/impl/ItemEnhancerServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/enhancer/service/impl/ItemEnhancerServiceImpl.java index 578f04305b8..e751a431ac3 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/service/impl/ItemEnhancerServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/service/impl/ItemEnhancerServiceImpl.java @@ -38,7 +38,7 @@ public void enhance(Context context, Item item, boolean deepMode) { for (ItemEnhancer itemEnhancer : itemEnhancers) { if (itemEnhancer.canEnhance(context, item)) { - isUpdateNeeded = isUpdateNeeded || itemEnhancer.enhance(context, item, deepMode); + isUpdateNeeded = itemEnhancer.enhance(context, item, deepMode) || isUpdateNeeded; } } diff --git a/dspace-api/src/test/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumerIT.java b/dspace-api/src/test/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumerIT.java index 6e152ed7b0d..176f055a446 100644 --- a/dspace-api/src/test/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumerIT.java +++ b/dspace-api/src/test/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumerIT.java @@ -31,7 +31,6 @@ import org.dspace.content.WorkspaceItem; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.ItemService; -import org.dspace.core.CrisConstants; import org.dspace.core.ReloadableEntity; import org.junit.Before; import org.junit.Test; @@ -142,7 +141,7 @@ public void testManyMetadataValuesEnhancement() throws Exception { publication = commitAndReload(publication); List values = publication.getMetadata(); - assertThat(values, hasSize(18)); + assertThat(values, hasSize(26)); assertThat(values, hasItem(with("dc.contributor.author", "Red Smith"))); assertThat(values, hasItem(with("dc.contributor.author", "Walter White", person1.getID().toString(), 1, 600))); assertThat(values, hasItem(with("dc.contributor.author", "John Smith", person2.getID().toString(), 2, 600))); @@ -155,9 +154,18 @@ public void testManyMetadataValuesEnhancement() throws Exception { assertThat(values, hasItem(with("cris.virtualsource.department", person2.getID().toString(), 2))); assertThat(values, hasItem(with("cris.virtual.department", "University of Rome", 3))); assertThat(values, hasItem(with("cris.virtualsource.department", person3.getID().toString(), 3))); - + assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 0))); + assertThat(values, hasItem(with("cris.virtualsource.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 0))); + assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 1))); + assertThat(values, hasItem(with("cris.virtualsource.author-orcid", person1.getID().toString(), 1))); + assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 2))); + assertThat(values, hasItem(with("cris.virtualsource.author-orcid", person2.getID().toString(), 2))); + assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 3))); + assertThat(values, hasItem(with("cris.virtualsource.author-orcid", person3.getID().toString(), 3))); assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(4)); assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(4)); + assertThat(getMetadataValues(publication, "cris.virtual.author-orcid"), hasSize(4)); + assertThat(getMetadataValues(publication, "cris.virtualsource.author-orcid"), hasSize(4)); } @@ -195,7 +203,7 @@ public void testEnhancementAfterMetadataAddition() throws Exception { publication = commitAndReload(publication); metadataValues = publication.getMetadata(); - assertThat(metadataValues, hasSize(9)); + assertThat(metadataValues, hasSize(11)); assertThat(metadataValues, hasItem(with("dc.contributor.author", "Walter White", personId, 600))); assertThat(metadataValues, hasItem(with("cris.virtual.department", "4Science"))); assertThat(metadataValues, hasItem(with("cris.virtualsource.department", personId))); @@ -234,7 +242,7 @@ public void testEnhancementWithMetadataRemoval() throws Exception { publication = commitAndReload(publication); List values = publication.getMetadata(); - assertThat(values, hasSize(15)); + assertThat(values, hasSize(21)); assertThat(values, hasItem(with("dc.contributor.author", "Walter White", person1.getID().toString(), 0, 600))); assertThat(values, hasItem(with("dc.contributor.author", "John Smith", person2.getID().toString(), 1, 600))); assertThat(values, hasItem(with("dc.contributor.author", "Jesse Pinkman", person3.getID().toString(), 2, 600))); @@ -244,6 +252,12 @@ public void testEnhancementWithMetadataRemoval() throws Exception { assertThat(values, hasItem(with("cris.virtualsource.department", person2.getID().toString(), 1))); assertThat(values, hasItem(with("cris.virtual.department", "University of Rome", 2))); assertThat(values, hasItem(with("cris.virtualsource.department", person3.getID().toString(), 2))); + assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); + assertThat(values, hasItem(with("cris.virtualsource.author-orcid", person1.getID().toString()))); + assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); + assertThat(values, hasItem(with("cris.virtualsource.author-orcid", person2.getID().toString(), 1))); + assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); + assertThat(values, hasItem(with("cris.virtualsource.author-orcid", person3.getID().toString(), 2))); assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(3)); assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(3)); @@ -257,16 +271,21 @@ public void testEnhancementWithMetadataRemoval() throws Exception { publication = commitAndReload(publication); values = publication.getMetadata(); - assertThat(values, hasSize(12)); + assertThat(values, hasSize(16)); assertThat(values, hasItem(with("dc.contributor.author", "Walter White", person1.getID().toString(), 0, 600))); assertThat(values, hasItem(with("dc.contributor.author", "Jesse Pinkman", person3.getID().toString(), 1, 600))); assertThat(values, hasItem(with("cris.virtual.department", "4Science"))); assertThat(values, hasItem(with("cris.virtualsource.department", person1.getID().toString()))); assertThat(values, hasItem(with("cris.virtual.department", "University of Rome", 1))); assertThat(values, hasItem(with("cris.virtualsource.department", person3.getID().toString(), 1))); - + assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 0))); + assertThat(values, hasItem(with("cris.virtualsource.author-orcid", person1.getID().toString(), 0))); + assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 1))); + assertThat(values, hasItem(with("cris.virtualsource.author-orcid", person3.getID().toString(), 1))); assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(2)); assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(2)); + assertThat(getMetadataValues(publication, "cris.virtual.author-orcid"), hasSize(2)); + assertThat(getMetadataValues(publication, "cris.virtualsource.author-orcid"), hasSize(2)); } From 0e88cf56b4a154a1b4f6075fa05fef5654b62007 Mon Sep 17 00:00:00 2001 From: Mattia Vianelli Date: Thu, 18 Jan 2024 17:24:52 +0100 Subject: [PATCH 41/82] DSC-1488 Added test for NPE in CrisLayoutBoxServiceImpl --- .../impl/CrisLayoutBoxServiceImplTest.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dspace-api/src/test/java/org/dspace/layout/service/impl/CrisLayoutBoxServiceImplTest.java b/dspace-api/src/test/java/org/dspace/layout/service/impl/CrisLayoutBoxServiceImplTest.java index fd61aeb0a76..67872fb5233 100644 --- a/dspace-api/src/test/java/org/dspace/layout/service/impl/CrisLayoutBoxServiceImplTest.java +++ b/dspace-api/src/test/java/org/dspace/layout/service/impl/CrisLayoutBoxServiceImplTest.java @@ -291,6 +291,25 @@ public void testIiifBoxHasNoContentWithMetadataUndefined() { assertFalse(crisLayoutBoxService.hasContent(context, box, item)); } + @Test + public void testSingleMetadataboxBitstreamWithoutField() { + + CrisLayoutBox singleBitstreamBox = new CrisLayoutBox(); + singleBitstreamBox.setShortname("File"); + singleBitstreamBox.setType(null); + Item item = item(); + + CrisLayoutFieldBitstream fieldBitstream = new CrisLayoutFieldBitstream(); + fieldBitstream.setBundle("ORIGINAL"); + fieldBitstream.setMetadataValue(null); + fieldBitstream.setMetadataField(null); + fieldBitstream.setRendering("attachment"); + + singleBitstreamBox.addLayoutField(fieldBitstream); + + assertThat(crisLayoutBoxService.hasContent(context, singleBitstreamBox, item), is(true)); + } + private CrisLayoutBox crisLayoutMetadataBox(String shortname, MetadataField... metadataFields) { return crisLayoutBox(shortname, CrisLayoutBoxTypes.METADATA.name(), metadataFields); } From 6404c730371af4421ede693a277a3479e8d8b457 Mon Sep 17 00:00:00 2001 From: Mattia Vianelli Date: Thu, 18 Jan 2024 18:01:25 +0100 Subject: [PATCH 42/82] DSC-1488 Test fix --- .../layout/service/impl/CrisLayoutBoxServiceImplTest.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/test/java/org/dspace/layout/service/impl/CrisLayoutBoxServiceImplTest.java b/dspace-api/src/test/java/org/dspace/layout/service/impl/CrisLayoutBoxServiceImplTest.java index 67872fb5233..ce539cda67a 100644 --- a/dspace-api/src/test/java/org/dspace/layout/service/impl/CrisLayoutBoxServiceImplTest.java +++ b/dspace-api/src/test/java/org/dspace/layout/service/impl/CrisLayoutBoxServiceImplTest.java @@ -292,12 +292,14 @@ public void testIiifBoxHasNoContentWithMetadataUndefined() { } @Test - public void testSingleMetadataboxBitstreamWithoutField() { + public void testSingleMetadataboxBitstreamWithoutField() throws SQLException { CrisLayoutBox singleBitstreamBox = new CrisLayoutBox(); singleBitstreamBox.setShortname("File"); singleBitstreamBox.setType(null); + Item item = item(); + Bitstream bitstream = mock(Bitstream.class); CrisLayoutFieldBitstream fieldBitstream = new CrisLayoutFieldBitstream(); fieldBitstream.setBundle("ORIGINAL"); @@ -307,7 +309,11 @@ public void testSingleMetadataboxBitstreamWithoutField() { singleBitstreamBox.addLayoutField(fieldBitstream); + when(bitstreamService.findShowableByItem(context, item.getID(), "ORIGINAL", Map.of())) + .thenReturn(List.of(bitstream)); + assertThat(crisLayoutBoxService.hasContent(context, singleBitstreamBox, item), is(true)); + } private CrisLayoutBox crisLayoutMetadataBox(String shortname, MetadataField... metadataFields) { From 71c8a1931a6039aa2c059a521c3a2594aaafe24f Mon Sep 17 00:00:00 2001 From: Mattia Vianelli Date: Thu, 18 Jan 2024 18:02:09 +0100 Subject: [PATCH 43/82] DSC-1488 Added fix for NPE in CrisLayoutBoxServiceImpl --- .../layout/service/impl/CrisLayoutBoxServiceImpl.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutBoxServiceImpl.java b/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutBoxServiceImpl.java index 269dd660185..acd5d38c0b4 100644 --- a/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutBoxServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutBoxServiceImpl.java @@ -208,7 +208,13 @@ private boolean isMetadataFieldPresent(DSpaceObject item, MetadataField metadata } private boolean isBitstreamPresent(Context context, Item item, CrisLayoutFieldBitstream field) { - Map filters = Map.of(field.getMetadataField().toString('.'), field.getMetadataValue()); + + Map filters = Map.of(); + + if (field.getMetadataField() != null) { + filters = Map.of(field.getMetadataField().toString('.'), field.getMetadataValue()); + } + try { return bitstreamService.findShowableByItem(context, item.getID(), field.getBundle(), filters).size() > 0; } catch (SQLException e) { From 76c42ee8d659f269d0d10d4808663b077ddb0579 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Thu, 18 Jan 2024 18:52:16 +0100 Subject: [PATCH 44/82] DSC-1458 fix misbehaviours in case an author or external authors appear multiple time the metadata --- .../impl/RelatedEntityItemEnhancer.java | 67 ++++++++++++++----- 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java index 1eee6daeee1..f17e36ee90a 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java @@ -15,7 +15,6 @@ import java.util.Objects; import java.util.Optional; import java.util.UUID; -import java.util.function.Predicate; import org.apache.commons.lang3.StringUtils; import org.dspace.content.Item; @@ -192,8 +191,10 @@ private boolean cleanObsoleteVirtualFields(Context context, Item item) throws SQ private boolean updateVirtualFieldsPlaces(Context context, Item item) { boolean result = false; List virtualSourceFields = getVirtualSourceFields(item); + List enhanceableMetadataValue = getEnhanceableMetadataValue(item); for (MetadataValue virtualSourceField : virtualSourceFields) { - Optional metadataWithPlaceToUpdate = metadataWithPlaceToUpdate(item, virtualSourceField); + Optional metadataWithPlaceToUpdate = metadataWithPlaceToUpdate(item, + enhanceableMetadataValue, virtualSourceField); if (metadataWithPlaceToUpdate.isPresent()) { updatePlaces(item, metadataWithPlaceToUpdate.get(), virtualSourceField); result = true; @@ -202,14 +203,14 @@ private boolean updateVirtualFieldsPlaces(Context context, Item item) { return result; } - private Optional metadataWithPlaceToUpdate(Item item, MetadataValue virtualSourceField) { - return findEnhanceableValue(virtualSourceField, item) - .filter(hasToUpdatePlace(virtualSourceField)) - .stream().findFirst(); + private Optional metadataWithPlaceToUpdate(Item item, List enhanceableMetadataValue, + MetadataValue virtualSourceField) { + return findMetadataValueToUpdatePlace(enhanceableMetadataValue, virtualSourceField, + item); } - private Predicate hasToUpdatePlace(MetadataValue virtualSourceField) { - return metadataValue -> metadataValue.getPlace() != virtualSourceField.getPlace(); + private boolean hasToUpdatePlace(MetadataValue metadataValue, MetadataValue virtualSourceField) { + return metadataValue.getPlace() != virtualSourceField.getPlace(); } private void updatePlaces(Item item, MetadataValue mv, MetadataValue virtualSourceField) { @@ -218,10 +219,23 @@ private void updatePlaces(Item item, MetadataValue mv, MetadataValue virtualSour .ifPresent(relatedMv -> relatedMv.setPlace(mv.getPlace())); } - private Optional findEnhanceableValue(MetadataValue virtualSourceField, Item item) { - return getEnhanceableMetadataValue(item).stream() - .filter(metadataValue -> hasAuthorityEqualsTo(metadataValue, virtualSourceField.getValue())) - .findFirst(); + private Optional findMetadataValueToUpdatePlace(List enhanceableMetadataValue, + MetadataValue virtualSourceField, Item item) { + Optional exactMatch = enhanceableMetadataValue.stream() + .filter(metadataValue -> hasAuthorityEqualsTo(metadataValue, + virtualSourceField.getValue()) && !hasToUpdatePlace(metadataValue, virtualSourceField)) + .findFirst(); + if (exactMatch.isPresent()) { + enhanceableMetadataValue.remove(exactMatch.get()); + return Optional.empty(); + } else { + Optional authorityOnlyMatch = enhanceableMetadataValue.stream() + .filter(metadataValue -> hasAuthorityEqualsTo(metadataValue, + virtualSourceField.getValue()) && hasToUpdatePlace(metadataValue, virtualSourceField)) + .findFirst(); + enhanceableMetadataValue.remove(authorityOnlyMatch.get()); + return authorityOnlyMatch; + } } private List getObsoleteVirtualFields(Item item) { @@ -229,8 +243,9 @@ private List getObsoleteVirtualFields(Item item) { List obsoleteVirtualFields = new ArrayList<>(); List virtualSourceFields = getVirtualSourceFields(item); + List enhanceableMetadata = getEnhanceableMetadataValue(item); for (MetadataValue virtualSourceField : virtualSourceFields) { - if (isRelatedSourceNoMorePresent(item, virtualSourceField)) { + if (isRelatedSourceNoMorePresent(item, enhanceableMetadata, virtualSourceField)) { obsoleteVirtualFields.add(virtualSourceField); getRelatedVirtualField(item, virtualSourceField).ifPresent(obsoleteVirtualFields::add); } @@ -240,9 +255,26 @@ private List getObsoleteVirtualFields(Item item) { } - private boolean isRelatedSourceNoMorePresent(Item item, MetadataValue virtualSourceField) { - return getEnhanceableMetadataValue(item).stream() - .noneMatch(metadataValue -> hasAuthorityEqualsTo(metadataValue, virtualSourceField.getValue())); + /** + * This method will look in the enhanceableMetadata if the source metadata is still present. If so, it will remove + * form the list as it would not be used to validate other potential duplicate source metadata + * + * @param item + * @param enhanceableMetadata + * @param virtualSourceField + * @return true if the metadata containing a source of enhancement is still present in the list of the metadata to + * use to enhance the item + */ + private boolean isRelatedSourceNoMorePresent(Item item, List enhanceableMetadata, + MetadataValue virtualSourceField) { + Optional mv = enhanceableMetadata.stream() + .filter(metadataValue -> hasAuthorityEqualsTo(metadataValue, virtualSourceField.getValue())) + .findFirst(); + if (mv.isPresent()) { + enhanceableMetadata.remove(mv.get()); + return false; + } + return true; } private Optional getRelatedVirtualField(Item item, MetadataValue virtualSourceField) { @@ -319,7 +351,8 @@ private boolean isPlaceholderAtPlace(List metadataValues, int pla private boolean hasAuthorityEqualsTo(MetadataValue metadataValue, String authority) { return Objects.equals(metadataValue.getAuthority(), authority) - || Objects.equals(PLACEHOLDER_PARENT_METADATA_VALUE, authority); + || (StringUtils.isBlank(metadataValue.getAuthority()) + && Objects.equals(PLACEHOLDER_PARENT_METADATA_VALUE, authority)); } private Item findRelatedEntityItem(Context context, MetadataValue metadataValue) { From cdcd95951f6d5edf227aacdaff9a08c269692b67 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Fri, 19 Jan 2024 10:47:44 +0100 Subject: [PATCH 45/82] DSC-1458 fix test to reflect the fact that enhanced metadata are always included also for external authors --- .../app/bulkimport/service/BulkImportWorkbookBuilderIT.java | 4 ++-- .../src/test/java/org/dspace/harvest/OAIHarvesterIT.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/app/bulkimport/service/BulkImportWorkbookBuilderIT.java b/dspace-api/src/test/java/org/dspace/app/bulkimport/service/BulkImportWorkbookBuilderIT.java index a7006b6a8a1..9de061db8f9 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkimport/service/BulkImportWorkbookBuilderIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkimport/service/BulkImportWorkbookBuilderIT.java @@ -209,7 +209,7 @@ public void testWorkbookBuildingFromItemDtos() throws Exception { Item firstItem = getItemFromMessage(handler.getInfoMessages().get(7)); assertThat(firstItem, notNullValue()); - assertThat(firstItem.getMetadata(), hasSize(14)); + assertThat(firstItem.getMetadata(), hasSize(16)); assertThat(firstItem.getMetadata(), hasItems( with("dc.title", "Test Publication"), with("dc.date.issued", "2020/02/15"), @@ -227,7 +227,7 @@ public void testWorkbookBuildingFromItemDtos() throws Exception { Item secondItem = getItemFromMessage(handler.getInfoMessages().get(10)); assertThat(secondItem, notNullValue()); - assertThat(secondItem.getMetadata(), hasSize(14)); + assertThat(secondItem.getMetadata(), hasSize(16)); assertThat(secondItem.getMetadata(), hasItems( with("dc.title", "Second Publication"), with("dc.date.issued", "2022/02/15"), diff --git a/dspace-api/src/test/java/org/dspace/harvest/OAIHarvesterIT.java b/dspace-api/src/test/java/org/dspace/harvest/OAIHarvesterIT.java index e58788104aa..c180b77dc26 100644 --- a/dspace-api/src/test/java/org/dspace/harvest/OAIHarvesterIT.java +++ b/dspace-api/src/test/java/org/dspace/harvest/OAIHarvesterIT.java @@ -769,7 +769,7 @@ public void testRunHarvestWithPublicationAndThenPerson() throws Exception { Item publication = publications.get(0); List values = publication.getMetadata(); - assertThat(values, hasSize(17)); + assertThat(values, hasSize(19)); assertThat(values, hasItems(with("dc.title", "Test Publication"))); assertThat(values, hasItems(with("dc.type", "Controlled Vocabulary for Resource Type Genres::text"))); @@ -859,7 +859,7 @@ public void testRunHarvestWithPersonAndThenPublication() throws Exception { Item person = findItemByOaiID("oai:test-harvest:Persons/123", personCollection); List values = person.getMetadata(); - assertThat(values, hasSize(12)); + assertThat(values, hasSize(14)); assertThat(values, hasItems(with("dc.title", "Manghi, Paolo"))); assertThat(values, hasItems(with("cris.sourceId", "test-harvest::123"))); assertThat(values, hasItems(with("dspace.entity.type", "Person"))); From 4e39c59b31f2e1c9a60138a9ccf6e66751ba582f Mon Sep 17 00:00:00 2001 From: mohamed eskander Date: Thu, 7 Dec 2023 12:03:20 +0200 Subject: [PATCH 46/82] [DSC-1386] Added support to create template item via struct builder --- .../org/dspace/administer/StructBuilder.java | 102 +++++++++++++++++- .../dspace/administer/StructBuilderIT.java | 29 +++++ dspace/config/sample-structure.xml | 70 ++++++++++++ 3 files changed, 200 insertions(+), 1 deletion(-) 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 cb65604a262..c08566dc5b0 100644 --- a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java +++ b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java @@ -9,6 +9,7 @@ import static org.dspace.content.Item.ANY; import static org.dspace.content.MetadataSchemaEnum.CRIS; +import static org.dspace.content.authority.Choices.CF_UNSET; import static org.dspace.content.service.DSpaceObjectService.MD_COPYRIGHT_TEXT; import static org.dspace.content.service.DSpaceObjectService.MD_INTRODUCTORY_TEXT; import static org.dspace.content.service.DSpaceObjectService.MD_LICENSE; @@ -49,12 +50,14 @@ import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.Item; +import org.dspace.content.MetadataField; import org.dspace.content.MetadataFieldName; import org.dspace.content.MetadataSchemaEnum; import org.dspace.content.MetadataValue; 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.core.Context; import org.dspace.core.CrisConstants; import org.dspace.eperson.factory.EPersonServiceFactory; @@ -122,7 +125,8 @@ public class StructBuilder { = EPersonServiceFactory.getInstance().getEPersonService(); protected static final HandleService handleService = HandleServiceFactory.getInstance().getHandleService(); - + protected static final ItemService itemService + = ContentServiceFactory.getInstance().getItemService(); /** * Default constructor */ @@ -407,6 +411,9 @@ private static Element exportACollection(Collection collection) { Element element = new Element("collection"); element.setAttribute("identifier", collection.getHandle()); element.addContent(new Element("name").setText(collection.getName())); + + buildTemplateItem(collection, element); + element.addContent(new Element("description") .setText(collectionService.getMetadataFirstValue(collection, MetadataSchemaEnum.DC.getName(), "description", "abstract", Item.ANY))); @@ -833,6 +840,8 @@ private static Element[] handleCollections(Context context, collectionService.setMetadataSingleValue(context, collection, MD_SHORT_DESCRIPTION, Item.ANY, " "); + handleTemplateItem(context, collection, tn); + // import the rest of the metadata for (Map.Entry entry : collectionMap.entrySet()) { NodeList nl = (NodeList) xPath.compile(entry.getKey()).evaluate(tn, XPathConstants.NODESET); @@ -854,6 +863,8 @@ private static Element[] handleCollections(Context context, String fieldValue; + buildTemplateItem(collection, element); + fieldValue = collectionService.getMetadataFirstValue(collection, CollectionService.MD_SHORT_DESCRIPTION, Item.ANY); if (fieldValue != null) { @@ -930,4 +941,93 @@ private static Element[] handleCollections(Context context, return elements; } + + private static void handleTemplateItem(Context context, Collection collection, Node tn) + throws XPathExpressionException, SQLException, AuthorizeException { + + XPath xPath = XPathFactory.newInstance().newXPath(); + Node node = (Node) xPath.compile("templateItem").evaluate(tn, XPathConstants.NODE); + + if (node == null) { + return; + } + + Item templateItem = itemService.createTemplateItem(context, collection); + + NodeList metadataNodes = (NodeList) xPath.compile("metadata").evaluate(node, XPathConstants.NODESET); + + for (int i = 0; i < metadataNodes.getLength(); i++) { + Node metadataNode = metadataNodes.item(i); + MetadataFieldName metadataFieldName = buildMetadataFieldName(metadataNode); + + Node valueAttribute = (Node) xPath.compile("value").evaluate(metadataNode, XPathConstants.NODE); + Node authorityAttribute = (Node) xPath.compile("authority").evaluate(metadataNode, XPathConstants.NODE); + Node confidenceAttribute = (Node) xPath.compile("confidence").evaluate(metadataNode, XPathConstants.NODE); + + String authority = null; + int confidence = CF_UNSET; + + if (authorityAttribute != null) { + authority = authorityAttribute.getTextContent(); + confidence = confidenceAttribute != null ? Integer.parseInt(confidenceAttribute.getTextContent()) : 600; + } + + itemService.addMetadata(context, templateItem, metadataFieldName.schema, metadataFieldName.element, + metadataFieldName.qualifier, ANY, valueAttribute.getTextContent(), authority, confidence); + itemService.update(context, templateItem); + } + } + + private static MetadataFieldName buildMetadataFieldName(Node node) { + Node schemaAttribute = node.getAttributes().getNamedItem("schema"); + Node elementAttribute = node.getAttributes().getNamedItem("element"); + Node qualifierAttribute = node.getAttributes().getNamedItem("qualifier"); + + if (qualifierAttribute == null) { + return new MetadataFieldName(schemaAttribute.getTextContent(), elementAttribute.getTextContent()); + } else { + return new MetadataFieldName(schemaAttribute.getTextContent(), + elementAttribute.getTextContent(), qualifierAttribute.getTextContent()); + } + } + + private static void buildTemplateItem(Collection collection, Element element) { + + try { + Item templateItem = collection.getTemplateItem(); + + if (templateItem == null) { + return; + } + + Element templateItemElement = new Element("templateItem"); + + for (MetadataValue metadataValue : templateItem.getMetadata()) { + MetadataField metadataField = metadataValue.getMetadataField(); + Element metadata = new Element("metadata"); + metadata.setAttribute("schema", metadataField.getMetadataSchema().getName()); + metadata.setAttribute("element", metadataField.getElement()); + + if (metadataField.getQualifier() != null) { + metadata.setAttribute("qualifier", metadataField.getQualifier()); + } + + metadata.addContent(new Element("value").setText(metadataValue.getValue())); + + if (metadataValue.getAuthority() != null) { + metadata.addContent(new Element("authority").setText(metadataValue.getAuthority())); + metadata.addContent(new Element("confidence").setText( + String.valueOf(metadataValue.getConfidence()) + )); + } + + templateItemElement.addContent(metadata); + } + + element.addContent(templateItemElement); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } 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 a113aa0438d..e69fc85b497 100644 --- a/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java +++ b/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java @@ -17,6 +17,7 @@ import java.nio.charset.StandardCharsets; import java.sql.SQLException; import java.util.Iterator; +import java.util.UUID; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; @@ -27,10 +28,12 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; import org.dspace.content.Community; +import org.dspace.content.Item; import org.dspace.content.MetadataSchemaEnum; 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.handle.Handle; import org.junit.AfterClass; import org.junit.Before; @@ -61,6 +64,8 @@ public class StructBuilderIT = ContentServiceFactory.getInstance().getCommunityService(); private static final CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService(); + private static final ItemService itemService + = ContentServiceFactory.getInstance().getItemService(); public StructBuilderIT() { } @@ -114,6 +119,21 @@ public void setUp() throws SQLException, AuthorizeException, IOException { " Another sidebar\n" + " \n" + " Collection 0.0.0\n" + + " \n" + + " \n" + + " template item\n" + + " \n" + + " \n" + + " Walter White\n" + + " " + UUID.randomUUID() + "\n" + + " 600\n" + + " \n" + + " \n" + + " Donald, Smith\n" + + " " + UUID.randomUUID() + "\n" + + " 400\n" + + " \n" + + " \n" + " A collection\n" + " Our next guest needs no introduction\n" + " 1776\n" + @@ -149,6 +169,11 @@ public void setUp() throws SQLException, AuthorizeException, IOException { " \n" + " \n" + " Collection 0.0\n" + + " \n" + + " \n" + + " template item\n" + + " \n" + + " \n" + " \n" + " \n" + " \n" + @@ -301,6 +326,10 @@ public void testExportStructure() MetadataSchemaEnum.DC.getName(), "title", null, null, "Collection 0.0"); + Item item = itemService.createTemplateItem(context, collection0_0); + itemService.addMetadata(context, item, MetadataSchemaEnum.DC.getName(), "title", null, + Item.ANY, "template item", null, -1); + // Export the current structure. System.out.println("exportStructure"); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); diff --git a/dspace/config/sample-structure.xml b/dspace/config/sample-structure.xml index 89c577bdcfa..1d9095841e0 100644 --- a/dspace/config/sample-structure.xml +++ b/dspace/config/sample-structure.xml @@ -10,6 +10,13 @@ Person Person person + + + + + + + @@ -21,6 +28,13 @@ Project Project project + + + + + + + @@ -32,6 +46,13 @@ Funding Funding funding + + + + + + + @@ -43,6 +64,13 @@ OrgUnit OrgUnit orgunit + + + + + + + @@ -54,6 +82,13 @@ Journal Journal journal + + + + + + + @@ -65,6 +100,13 @@ Publication Publication publication + + + + + + + @@ -76,6 +118,13 @@ Patent Patent patent + + + + + + + @@ -86,6 +135,13 @@ Dataset or other products Product + + + + + + + @@ -97,6 +153,13 @@ Event Event event + + + + + + + @@ -108,6 +171,13 @@ Equipment Equipment equipment + + + + + + + From ba6cdbf1f78a6f099b30336aa8f3bfe534207011 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Sat, 20 Jan 2024 15:51:06 +0100 Subject: [PATCH 47/82] DURACOM-225 add IT for the media filter script --- .../dspace/app/mediafilter/MediaFilterIT.java | 237 ++++++++++++++++++ .../org/dspace/builder/BitstreamBuilder.java | 14 ++ 2 files changed, 251 insertions(+) create mode 100644 dspace-api/src/test/java/org/dspace/app/mediafilter/MediaFilterIT.java diff --git a/dspace-api/src/test/java/org/dspace/app/mediafilter/MediaFilterIT.java b/dspace-api/src/test/java/org/dspace/app/mediafilter/MediaFilterIT.java new file mode 100644 index 00000000000..aef2476fdc4 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/app/mediafilter/MediaFilterIT.java @@ -0,0 +1,237 @@ +/** + * 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.mediafilter; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.InputStream; +import java.sql.SQLException; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.dspace.AbstractIntegrationTestWithDatabase; +import org.dspace.authorize.AuthorizeException; +import org.dspace.builder.BitstreamBuilder; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.ItemBuilder; +import org.dspace.content.Bitstream; +import org.dspace.content.Bundle; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.DSpaceObject; +import org.dspace.content.Item; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.BitstreamService; +import org.dspace.content.service.ItemService; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests of {@link MediaFilterScript}. + * + * @author Andrea Bollini + */ +public class MediaFilterIT extends AbstractIntegrationTestWithDatabase { + + private ItemService itemService = ContentServiceFactory.getInstance().getItemService(); + private BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); + protected Community topComm1; + protected Community topComm2; + protected Community childComm1_1; + protected Community childComm1_2; + protected Collection col1_1; + protected Collection col1_2; + protected Collection col1_1_1; + protected Collection col1_1_2; + protected Collection col1_2_1; + protected Collection col1_2_2; + protected Collection col2_1; + protected Item item1_1_a; + protected Item item1_1_b; + protected Item item1_2_a; + protected Item item1_2_b; + protected Item item1_1_1_a; + protected Item item1_1_1_b; + protected Item item1_1_2_a; + protected Item item1_1_2_b; + protected Item item1_2_1_a; + protected Item item1_2_1_b; + protected Item item1_2_2_a; + protected Item item1_2_2_b; + protected Item item2_1_a; + protected Item item2_1_b; + + @Before + public void setup() throws IOException, SQLException, AuthorizeException { + context.turnOffAuthorisationSystem(); + topComm1 = CommunityBuilder.createCommunity(context).withName("Parent Community1").build(); + topComm2 = CommunityBuilder.createCommunity(context).withName("Parent Community2").build(); + childComm1_1 = CommunityBuilder.createCommunity(context).withName("Child Community1_1") + .addParentCommunity(context, topComm1).build(); + childComm1_2 = CommunityBuilder.createCommunity(context).withName("Child Community1_2") + .addParentCommunity(context, topComm1).build(); + col1_1 = CollectionBuilder.createCollection(context, topComm1).withName("Collection 1_1").build(); + col1_2 = CollectionBuilder.createCollection(context, topComm1).withName("Collection 1_2").build(); + col1_1_1 = CollectionBuilder.createCollection(context, childComm1_1).withName("Collection 1_1_1").build(); + col1_1_2 = CollectionBuilder.createCollection(context, childComm1_1).withName("Collection 1_1_2").build(); + col1_2_1 = CollectionBuilder.createCollection(context, childComm1_2).withName("Collection 1_1_1").build(); + col1_2_2 = CollectionBuilder.createCollection(context, childComm1_2).withName("Collection 1_2").build(); + col2_1 = CollectionBuilder.createCollection(context, topComm2).withName("Collection 2_1").build(); + + // Create two items in each collection, one with the test.csv file and one with the test.txt file + item1_1_a = ItemBuilder.createItem(context, col1_1).withTitle("Item 1_1_a").withIssueDate("2017-10-17").build(); + item1_1_b = ItemBuilder.createItem(context, col1_1).withTitle("Item 1_1_b").withIssueDate("2017-10-17").build(); + item1_1_1_a = ItemBuilder.createItem(context, col1_1_1).withTitle("Item 1_1_1_a").withIssueDate("2017-10-17") + .build(); + item1_1_1_b = ItemBuilder.createItem(context, col1_1_1).withTitle("Item 1_1_1_b").withIssueDate("2017-10-17") + .build(); + item1_1_2_a = ItemBuilder.createItem(context, col1_1_2).withTitle("Item 1_1_2_a").withIssueDate("2017-10-17") + .build(); + item1_1_2_b = ItemBuilder.createItem(context, col1_1_2).withTitle("Item 1_1_2_b").withIssueDate("2017-10-17") + .build(); + item1_2_a = ItemBuilder.createItem(context, col1_2).withTitle("Item 1_2_a").withIssueDate("2017-10-17").build(); + item1_2_b = ItemBuilder.createItem(context, col1_2).withTitle("Item 1_2_b").withIssueDate("2017-10-17").build(); + item1_2_1_a = ItemBuilder.createItem(context, col1_2_1).withTitle("Item 1_2_1_a").withIssueDate("2017-10-17") + .build(); + item1_2_1_b = ItemBuilder.createItem(context, col1_2_1).withTitle("Item 1_2_1_b").withIssueDate("2017-10-17") + .build(); + item1_2_2_a = ItemBuilder.createItem(context, col1_2_2).withTitle("Item 1_2_2_a").withIssueDate("2017-10-17") + .build(); + item1_2_2_b = ItemBuilder.createItem(context, col1_2_2).withTitle("Item 1_2_2_b").withIssueDate("2017-10-17") + .build(); + item2_1_a = ItemBuilder.createItem(context, col2_1).withTitle("Item 2_1_a").withIssueDate("2017-10-17").build(); + item2_1_b = ItemBuilder.createItem(context, col2_1).withTitle("Item 2_1_b").withIssueDate("2017-10-17").build(); + addBitstream(item1_1_a, "test.csv"); + addBitstream(item1_1_b, "test.txt"); + addBitstream(item1_2_a, "test.csv"); + addBitstream(item1_2_b, "test.txt"); + addBitstream(item1_1_1_a, "test.csv"); + addBitstream(item1_1_1_b, "test.txt"); + addBitstream(item1_1_2_a, "test.csv"); + addBitstream(item1_1_2_b, "test.txt"); + addBitstream(item1_2_1_a, "test.csv"); + addBitstream(item1_2_1_b, "test.txt"); + addBitstream(item1_2_2_a, "test.csv"); + addBitstream(item1_2_2_b, "test.txt"); + addBitstream(item2_1_a, "test.csv"); + addBitstream(item2_1_b, "test.txt"); + context.restoreAuthSystemState(); + } + + private void addBitstream(Item item, String filename) throws SQLException, AuthorizeException, IOException { + BitstreamBuilder.createBitstream(context, item, getClass().getResourceAsStream(filename)).withName(filename) + .guessFormat().build(); + } + + @Test + public void mediaFilterScriptAllItemsTest() throws Exception { + performMediaFilterScript(null); + Iterator items = itemService.findAll(context); + while (items.hasNext()) { + Item item = items.next(); + checkItemHasBeenProcessed(item); + } + } + + @Test + public void mediaFilterScriptIdentifiersTest() throws Exception { + // process the item 1_1_a and verify that no other items has been processed using the "closer" one + performMediaFilterScript(item1_1_a); + checkItemHasBeenProcessed(item1_1_a); + checkItemHasBeenNotProcessed(item1_1_b); + // process the collection 1_1_1 and verify that items in another collection has not been processed + performMediaFilterScript(col1_1_1); + checkItemHasBeenProcessed(item1_1_1_a); + checkItemHasBeenProcessed(item1_1_1_b); + checkItemHasBeenNotProcessed(item1_1_2_a); + checkItemHasBeenNotProcessed(item1_1_2_b); + // process a top community with only collections + performMediaFilterScript(topComm2); + checkItemHasBeenProcessed(item2_1_a); + checkItemHasBeenProcessed(item2_1_b); + // verify that the other items have not been processed yet + checkItemHasBeenNotProcessed(item1_1_b); + checkItemHasBeenNotProcessed(item1_2_a); + checkItemHasBeenNotProcessed(item1_2_b); + checkItemHasBeenNotProcessed(item1_1_2_a); + checkItemHasBeenNotProcessed(item1_1_2_b); + checkItemHasBeenNotProcessed(item1_2_1_a); + checkItemHasBeenNotProcessed(item1_2_1_b); + checkItemHasBeenNotProcessed(item1_2_2_a); + checkItemHasBeenNotProcessed(item1_2_2_b); + // process a more structured community and verify that all the items at all levels are processed + performMediaFilterScript(topComm1); + // items that were already processed should stay processed + checkItemHasBeenProcessed(item1_1_a); + checkItemHasBeenProcessed(item1_1_1_a); + checkItemHasBeenProcessed(item1_1_1_b); + // residual items should have been processed as well now + checkItemHasBeenProcessed(item1_1_b); + checkItemHasBeenProcessed(item1_2_a); + checkItemHasBeenProcessed(item1_2_b); + checkItemHasBeenProcessed(item1_1_2_a); + checkItemHasBeenProcessed(item1_1_2_b); + checkItemHasBeenProcessed(item1_2_1_a); + checkItemHasBeenProcessed(item1_2_1_b); + checkItemHasBeenProcessed(item1_2_2_a); + checkItemHasBeenProcessed(item1_2_2_b); + } + + private void checkItemHasBeenNotProcessed(Item item) throws IOException, SQLException, AuthorizeException { + List textBundles = item.getBundles("TEXT"); + assertTrue("The item " + item.getName() + " should NOT have the TEXT bundle", textBundles.size() == 0); + } + + private void checkItemHasBeenProcessed(Item item) throws IOException, SQLException, AuthorizeException { + String expectedFileName = StringUtils.endsWith(item.getName(), "_a") ? "test.csv.txt" : "test.txt.txt"; + String expectedContent = StringUtils.endsWith(item.getName(), "_a") ? "data3,3" : "quick brown fox"; + List textBundles = item.getBundles("TEXT"); + assertTrue("The item " + item.getName() + " has NOT the TEXT bundle", textBundles.size() == 1); + List bitstreams = textBundles.get(0).getBitstreams(); + assertTrue("The item " + item.getName() + " has NOT exactly 1 bitstream in the TEXT bundle", + bitstreams.size() == 1); + assertTrue("The text bistream in the " + item.getName() + " is NOT named properly [" + expectedFileName + "]", + StringUtils.equals(bitstreams.get(0).getName(), expectedFileName)); + assertTrue("The text bistream in the " + item.getName() + " doesn't contain the proper content [" + + expectedContent + "]", StringUtils.contains(getContent(bitstreams.get(0)), expectedContent)); + } + + private CharSequence getContent(Bitstream bitstream) throws IOException, SQLException, AuthorizeException { + try (InputStream input = bitstreamService.retrieve(context, bitstream)) { + return IOUtils.toString(input, "UTF-8"); + } + } + + private void performMediaFilterScript(DSpaceObject dso) throws Exception { + if (dso != null) { + runDSpaceScript("filter-media", "-i", dso.getHandle()); + } else { + runDSpaceScript("filter-media"); + } + // reload our items to see the changes + item1_1_a = context.reloadEntity(item1_1_a); + item1_1_b = context.reloadEntity(item1_1_b); + item1_2_a = context.reloadEntity(item1_2_a); + item1_2_b = context.reloadEntity(item1_2_b); + item1_1_1_a = context.reloadEntity(item1_1_1_a); + item1_1_1_b = context.reloadEntity(item1_1_1_b); + item1_1_2_a = context.reloadEntity(item1_1_2_a); + item1_1_2_b = context.reloadEntity(item1_1_2_b); + item1_2_1_a = context.reloadEntity(item1_2_1_a); + item1_2_1_b = context.reloadEntity(item1_2_1_b); + item1_2_2_a = context.reloadEntity(item1_2_2_a); + item1_2_2_b = context.reloadEntity(item1_2_2_b); + item2_1_a = context.reloadEntity(item2_1_a); + item2_1_b = context.reloadEntity(item2_1_b); + + } +} diff --git a/dspace-api/src/test/java/org/dspace/builder/BitstreamBuilder.java b/dspace-api/src/test/java/org/dspace/builder/BitstreamBuilder.java index a4775c4b835..dbcf3a7972b 100644 --- a/dspace-api/src/test/java/org/dspace/builder/BitstreamBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/BitstreamBuilder.java @@ -20,6 +20,7 @@ import org.dspace.content.MetadataField; import org.dspace.content.MetadataValue; import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.BitstreamFormatService; import org.dspace.content.service.DSpaceObjectService; import org.dspace.content.service.MetadataValueService; import org.dspace.core.Constants; @@ -167,6 +168,19 @@ public BitstreamBuilder withMimeType(String mimeType) throws SQLException { return this; } + /** + * Guess the bitstream format as during the submission via the + * {@link BitstreamFormatService#guessFormat(Context, Bitstream)} + * + * @return the BitstreamBuilder with the format set according to + * {@link BitstreamFormatService#guessFormat(Context, Bitstream)} + * @throws SQLException + */ + public BitstreamBuilder guessFormat() throws SQLException { + bitstream.setFormat(context, bitstreamFormatService.guessFormat(context, bitstream)); + return this; + } + public BitstreamBuilder withFormat(String format) throws SQLException { bitstreamService.addMetadata(context, bitstream, "dc", "format", null, null, format); From c706135fa22b97a554c0b4ef7aea60a8db8138d1 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Sat, 20 Jan 2024 17:51:33 +0100 Subject: [PATCH 48/82] DSC-1496 fix lazy initialization traversing comms/colls --- .../app/mediafilter/MediaFilterServiceImpl.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java index 3f898bd7d9f..974dc784bd4 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java @@ -132,12 +132,18 @@ public void applyFiltersAllItems(Context context) throws Exception { @Override public void applyFiltersCommunity(Context context, Community community) throws Exception { //only apply filters if community not in skip-list + // ensure that the community is attached to the current hibernate session + // as we are committing after each item (handles, sub-communties and + // collections are lazy attributes) + community = context.reloadEntity(community); if (!inSkipList(community.getHandle())) { List subcommunities = community.getSubcommunities(); for (Community subcommunity : subcommunities) { applyFiltersCommunity(context, subcommunity); } - + // ensure that the community is attached to the current hibernate session + // as we are committing after each item + community = context.reloadEntity(community); List collections = community.getCollections(); for (Collection collection : collections) { applyFiltersCollection(context, collection); @@ -148,6 +154,9 @@ public void applyFiltersCommunity(Context context, Community community) @Override public void applyFiltersCollection(Context context, Collection collection) throws Exception { + // ensure that the collection is attached to the current hibernate session + // as we are committing after each item (handles are lazy attributes) + collection = context.reloadEntity(collection); //only apply filters if collection not in skip-list if (!inSkipList(collection.getHandle())) { Iterator itemIterator = itemService.findAllByCollection(context, collection); From 1f1f6f4e84c51373b910b07f7c30fd7c82ff2cf9 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Tue, 23 Jan 2024 12:19:13 +0100 Subject: [PATCH 49/82] [DSC-963] Addresses changes of the PR#8797 --- dspace-server-webapp/pom.xml | 13 +- .../GenericAuthorizationFeatureIT.java | 547 +++++------------- .../resources/application-test.properties | 1 + dspace/modules/server-boot/pom.xml | 2 +- dspace/modules/server/pom.xml | 2 +- 5 files changed, 171 insertions(+), 394 deletions(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index fe47ad87ef3..0a0b394d576 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -76,10 +76,21 @@ ${basedir}/src/main/resources - **/*.properties + **/*application*.properties + **/*dspace*.properties true + + ${basedir}/src/main/resources + + **/*application*.properties + **/*dspace*.properties + + + **/*.properties + + ${basedir}/src/main/resources diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/GenericAuthorizationFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/GenericAuthorizationFeatureIT.java index 7872067a31e..0ac7eea4250 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/GenericAuthorizationFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/GenericAuthorizationFeatureIT.java @@ -37,6 +37,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.ResultActions; /** * Test for the following authorization features: @@ -52,6 +53,8 @@ */ public class GenericAuthorizationFeatureIT extends AbstractControllerIntegrationTest { + private static final int SIZE = 100; + @Autowired ConfigurationService configurationService; @@ -208,215 +211,163 @@ private void testAdminsHavePermissionsAllDso(String feature) throws Exception { String siteId = ContentServiceFactory.getInstance().getSiteService().findSite(context).getID().toString(); // Verify the general admin has this feature on the site - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/sites/" + siteId)) + getAuthorizationFeatures(adminToken, "http://localhost/api/core/sites/" + siteId) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin doesn’t have this feature on the site - getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/sites/" + siteId)) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/sites/" + siteId) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify the general admin has this feature on community A - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityA.getID())) + getAuthorizationFeatures(adminToken,"http://localhost/api/core/communities/" + communityA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on community A - getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityA.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/communities/" + communityA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on community AA - getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityAA.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/communities/" + communityAA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin doesn’t have this feature on community A - getClient(collectionXAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityA.getID())) + getAuthorizationFeatures(collectionXAdminToken, "http://localhost/api/core/communities/" + communityA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify community A admin doesn’t have this feature on community B - getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityB.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/communities/" + communityB.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify the general admin has this feature on collection X - getClient(adminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionX.getID())) + getAuthorizationFeatures(adminToken, "http://localhost/api/core/collections/" + collectionX.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on collection X - getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionX.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/collections/" + collectionX.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on collection X - getClient(collectionXAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionX.getID())) + getAuthorizationFeatures(collectionXAdminToken, "http://localhost/api/core/collections/" + collectionX.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin doesn’t have this feature on collection X - getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionX.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/collections/" + collectionX.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X admin doesn’t have this feature on collection Y - getClient(collectionXAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionY.getID())) + getAuthorizationFeatures(collectionXAdminToken, "http://localhost/api/core/collections/" + collectionY.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify the general admin has this feature on item 1 - getClient(adminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(adminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on item 1 - getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on item 1 - getClient(collectionXAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(collectionXAdminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin has this feature on item 1 - getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin doesn’t have this feature on item 2 - getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item2.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/items/" + item2.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify the general admin has this feature on the bundle in item 1 - getClient(adminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(adminToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on the bundle in item 1 - getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on the bundle in item 1 - getClient(collectionXAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(collectionXAdminToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin has this feature on the bundle in item 1 - getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin doesn’t have this feature on the bundle in item 2 - getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle2.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/bundles/" + bundle2.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify the general admin has this feature on the bitstream in item 1 - getClient(adminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) + getAuthorizationFeatures(adminToken, "http://localhost/api/core/bitstreams/" + bitstream1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on the bitstream in item 1 - getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/bitstreams/" + bitstream1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on the bitstream in item 1 - getClient(collectionXAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) + getAuthorizationFeatures(collectionXAdminToken, "http://localhost/api/core/bitstreams/" + bitstream1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin has this feature on the bitstream in item 1 - getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/bitstreams/" + bitstream1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin doesn’t have this feature on the bitstream in item 2 - getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bitstreams/" + bitstream2.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/bitstreams/" + bitstream2.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -429,41 +380,31 @@ private void testAdminsHavePermissionsItem(String feature) throws Exception { String item1AdminToken = getAuthToken(item1Admin.getEmail(), password); // Verify the general admin has this feature on item 1 - getClient(adminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(adminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on item 1 - getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on item 1 - getClient(collectionXAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(collectionXAdminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin has this feature on item 1 - getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin doesn’t have this feature on item 2 - getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item2.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/items/" + item2.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -478,73 +419,55 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify community A write has this feature on community A if the boolean parameter is true // (or doesn’t have access otherwise) if (hasDSOAccess) { - getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityA.getID())) + getAuthorizationFeatures(communityAWriterToken, "http://localhost/api/core/communities/" + communityA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); } else { - getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityA.getID())) + getAuthorizationFeatures(communityAWriterToken, "http://localhost/api/core/communities/" + communityA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); } // Verify community A write doesn’t have this feature on community AA - getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityAA.getID())) + getAuthorizationFeatures(communityAWriterToken, "http://localhost/api/core/communities/" + communityAA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify community A write doesn’t have this feature on collection X - getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionX.getID())) + getAuthorizationFeatures(communityAWriterToken, "http://localhost/api/core/collections/" + collectionX.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify community A write doesn’t have this feature on item 1 - getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(communityAWriterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify community A write doesn’t have this feature on the bundle in item 1 - getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(communityAWriterToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify community A write doesn’t have this feature on the bitstream in item 1 - getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(communityAWriterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X write doesn’t have this feature on community A - getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityA.getID())) + getAuthorizationFeatures(collectionXWriterToken, "http://localhost/api/core/communities/" + communityA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X write doesn’t have this feature on community AA - getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityAA.getID())) + getAuthorizationFeatures(collectionXWriterToken, "http://localhost/api/core/communities/" + communityAA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -552,65 +475,49 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify collection X write has this feature on collection X if the boolean parameter is true // (or doesn’t have access otherwise) if (hasDSOAccess) { - getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionX.getID())) + getAuthorizationFeatures(collectionXWriterToken, "http://localhost/api/core/collections/" + collectionX.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); } else { - getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionX.getID())) + getAuthorizationFeatures(collectionXWriterToken, "http://localhost/api/core/collections/" + collectionX.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); } // Verify collection X write doesn’t have this feature on item 1 - getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(collectionXWriterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X write doesn’t have this feature on the bundle in item 1 - getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(collectionXWriterToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X write doesn’t have this feature on the bitstream in item 1 - getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) + getAuthorizationFeatures(collectionXWriterToken, "http://localhost/api/core/bitstreams/" + bitstream1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify item 1 write doesn’t have this feature on community A - getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityA.getID())) + getAuthorizationFeatures(item1WriterToken, "http://localhost/api/core/communities/" + communityA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify item 1 write doesn’t have this feature on community AA - getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityAA.getID())) + getAuthorizationFeatures(item1WriterToken, "http://localhost/api/core/communities/" + communityAA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify item 1 write doesn’t have this feature on collection X - getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionX.getID())) + getAuthorizationFeatures(item1WriterToken, "http://localhost/api/core/collections/" + collectionX.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -618,57 +525,43 @@ private void testWriteUsersHavePermissionsAllDso(String feature, boolean hasDSOA // Verify item 1 write has this feature on item 1 if the boolean parameter is true // (or doesn’t have access otherwise) if (hasDSOAccess) { - getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(item1WriterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); } else { - getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(item1WriterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); } // Verify item 1 write doesn’t have this feature on the bundle in item 1 - getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(item1WriterToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify item 1 write doesn’t have this feature on the bitstream in item 1 - getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) + getAuthorizationFeatures(item1WriterToken, "http://localhost/api/core/bitstreams/" + bitstream1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify community A write doesn’t have this feature on community B - getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityB.getID())) + getAuthorizationFeatures(communityAWriterToken, "http://localhost/api/core/communities/" + communityB.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X write doesn’t have this feature on collection Y - getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionY.getID())) + getAuthorizationFeatures(collectionXWriterToken, "http://localhost/api/core/collections/" + collectionY.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify item 1 write doesn’t have this feature on item 2 - getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item2.getID())) + getAuthorizationFeatures(item1WriterToken, "http://localhost/api/core/items/" + item2.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -680,17 +573,13 @@ private void testWriteUsersHavePermissionsItem(String feature, boolean hasDSOAcc String item1WriterToken = getAuthToken(item1Writer.getEmail(), password); // Verify community A write doesn’t have this feature on item 1 - getClient(communityAWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(communityAWriterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X write doesn’t have this feature on item 1 - getClient(collectionXWriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(collectionXWriterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -698,25 +587,19 @@ private void testWriteUsersHavePermissionsItem(String feature, boolean hasDSOAcc // Verify item 1 write has this feature on item 1 if the boolean parameter is true // (or doesn’t have access otherwise) if (hasDSOAccess) { - getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(item1WriterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); } else { - getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(item1WriterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); } // Verify item 1 write doesn’t have this feature on item 2 - getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item2.getID())) + getAuthorizationFeatures(item1WriterToken, "http://localhost/api/core/items/" + item2.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -754,41 +637,31 @@ public void testCanMoveAdmin() throws Exception { final String feature = "canMove"; // Verify the general admin has this feature on item 1 - getClient(adminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(adminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on item 1 - getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on item 1 - getClient(collectionXAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(collectionXAdminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin doesn’t have this feature on item 1 - getClient(item1AdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify community A admin doesn’t have this feature on item 2 - getClient(communityAAdminToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item2.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/items/" + item2.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -805,9 +678,7 @@ public void testCanMoveAdmin() throws Exception { context.restoreAuthSystemState(); // verify item 1 write has this feature on item 1 - getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(item1WriterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canMove')]") .exists()); @@ -828,9 +699,7 @@ public void testCanMoveWriter() throws Exception { String item1WriterToken = getAuthToken(item1Writer.getEmail(), password); // verify item 1 write has this feature on item 1 - getClient(item1WriterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(item1WriterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canMove')]") .exists()); @@ -866,31 +735,25 @@ public void testCanDeleteAdmin() throws Exception { final String feature = "canDelete"; // Verify the general admin doesn’t have this feature on the site - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/sites/" + siteId)) + getAuthorizationFeatures(adminToken, "http://localhost/api/core/sites/" + siteId) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify the general admin has this feature on community A - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityA.getID())) + getAuthorizationFeatures(adminToken, "http://localhost/api/core/communities/" + communityA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on community A - getClient(communityAAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityA.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/communities/" + communityA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on community AA - getClient(communityAAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityAA.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/communities/" + communityAA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); @@ -909,174 +772,139 @@ public void testCanDeleteAdmin() throws Exception { .build(); context.restoreAuthSystemState(); String communityAAAdminToken = getAuthToken(communityAAAdmin.getEmail(), password); - getClient(communityAAAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityAA.getID())) + getAuthorizationFeatures(communityAAAdminToken, "http://localhost/api/core/communities/" + communityAA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X admin doesn’t have this feature on community A - getClient(collectionXAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityA.getID())) + getAuthorizationFeatures(collectionXAdminToken, "http://localhost/api/core/communities/" + communityA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify community A admin doesn’t have this feature on community B - getClient(communityAAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityB.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/communities/" + communityB.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify the general admin has this feature on collection X - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionX.getID())) + getAuthorizationFeatures(adminToken, "http://localhost/api/core/collections/" + collectionX.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on collection X - getClient(communityAAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionX.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/collections/" + collectionX.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin doesn’t have this feature on collection X - getClient(collectionXAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionX.getID())) + getAuthorizationFeatures(collectionXAdminToken, "http://localhost/api/core/collections/" + collectionX.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify item 1 admin doesn’t have this feature on collection X - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionX.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/collections/" + collectionX.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X admin doesn’t have this feature on collection Y - getClient(collectionXAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionY.getID())) + getAuthorizationFeatures(collectionXAdminToken, "http://localhost/api/core/collections/" + collectionY.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify the general admin has this feature on item 1 - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(adminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on item 1 - getClient(communityAAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(communityAAdminToken,"http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on item 1 - getClient(collectionXAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(collectionXAdminToken,"http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin doesn’t have this feature on item 1 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify item 1 admin doesn’t have this feature on item 2 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item2.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/items/" + item2.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify the general admin has this feature on the bundle in item 1 - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(adminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on the bundle in item 1 - getClient(communityAAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on the bundle in item 1 - getClient(collectionXAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(collectionXAdminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin has this feature on the bundle in item 1 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin doesn’t have this feature on the bundle in item 2 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle2.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/bundles/" + bundle2.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify the general admin has this feature on the bitstream in item 1 - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) + getAuthorizationFeatures(adminToken, "http://localhost/api/core/bitstreams/" + bitstream1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on the bitstream in item 1 - getClient(communityAAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/bitstreams/" + bitstream1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on the bitstream in item 1 - getClient(collectionXAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) + getAuthorizationFeatures(collectionXAdminToken, "http://localhost/api/core/bitstreams/" + bitstream1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin has this feature on the bitstream in item 1 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/bitstreams/" + bitstream1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin doesn’t have this feature on the bitstream in item 2 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bitstreams/" + bitstream2.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/bitstreams/" + bitstream2.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1103,9 +931,7 @@ public void testCanDeleteAdminParent() throws Exception { context.restoreAuthSystemState(); String communityAAAdminToken = getAuthToken(communityAAAdmin.getEmail(), password); //verify the community AA admin has this feature on community AA - getClient(communityAAAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityAA.getID())) + getAuthorizationFeatures(communityAAAdminToken, "http://localhost/api/core/communities/" + communityAA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); @@ -1119,9 +945,7 @@ public void testCanDeleteAdminParent() throws Exception { .build(); context.restoreAuthSystemState(); // verify collection X admin has this feature on collection X - getClient(collectionXAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionX.getID())) + getAuthorizationFeatures(collectionXAdminToken, "http://localhost/api/core/collections/" + collectionX.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); @@ -1135,8 +959,7 @@ public void testCanDeleteAdminParent() throws Exception { .build(); context.restoreAuthSystemState(); // verify item 1 admin has this feature on item 1 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); @@ -1166,16 +989,12 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String communityADeleterToken = getAuthToken(communityADeleter.getEmail(), password); // Verify the user has this feature on community A - getClient(communityADeleterToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityA.getID())) + getAuthorizationFeatures(communityADeleterToken, "http://localhost/api/core/communities/" + communityA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify this user doesn’t have this feature on community AA - getClient(communityADeleterToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityAA.getID())) + getAuthorizationFeatures(communityADeleterToken, "http://localhost/api/core/communities/" + communityAA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1196,23 +1015,17 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String communityARemoverToken = getAuthToken(communityARemover.getEmail(), password); // Verify the user has this feature on community AA - getClient(communityARemoverToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityAA.getID())) + getAuthorizationFeatures(communityARemoverToken, "http://localhost/api/core/communities/" + communityAA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify this user doesn’t have this feature on community A - getClient(communityARemoverToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityA.getID())) + getAuthorizationFeatures(communityARemoverToken, "http://localhost/api/core/communities/" + communityA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify this user doesn’t have this feature on collection X - getClient(communityARemoverToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionX.getID())) + getAuthorizationFeatures(communityARemoverToken, "http://localhost/api/core/collections/" + collectionX.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1232,23 +1045,17 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String communityAARemoverToken = getAuthToken(communityAARemover.getEmail(), password); // Verify the user has this feature on collection X - getClient(communityAARemoverToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionX.getID())) + getAuthorizationFeatures(communityAARemoverToken, "http://localhost/api/core/collections/" + collectionX.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify this user doesn’t have this feature on community AA - getClient(communityAARemoverToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/communities/" + communityAA.getID())) + getAuthorizationFeatures(communityAARemoverToken, "http://localhost/api/core/communities/" + communityAA.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify this user doesn’t have this feature on item 1 - getClient(communityAARemoverToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(communityAARemoverToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1268,9 +1075,7 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String collectionXRemoverToken = getAuthToken(collectionXRemover.getEmail(), password); // Verify the user doesn’t have this feature on item 1 - getClient(collectionXRemoverToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(collectionXRemoverToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1290,8 +1095,7 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String item1DeleterToken = getAuthToken(item1Deleter.getEmail(), password); // Verify the user doesn’t have this feature on item 1 - getClient(item1DeleterToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(item1DeleterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1316,23 +1120,17 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String collectionXRemoverItem1DeleterToken = getAuthToken(collectionXRemoverItem1Deleter.getEmail(), password); // Verify the user has this feature on item 1 - getClient(collectionXRemoverItem1DeleterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(collectionXRemoverItem1DeleterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify this user doesn’t have this feature on collection X - getClient(collectionXRemoverItem1DeleterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/collections/" + collectionX.getID())) + getAuthorizationFeatures(collectionXRemoverItem1DeleterToken, "http://localhost/api/core/collections/" + collectionX.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify this user doesn’t have this feature on the bundle in item 1 - getClient(collectionXRemoverItem1DeleterToken).perform( - get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(collectionXRemoverItem1DeleterToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1352,20 +1150,17 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String item1RemoverToken = getAuthToken(item1Remover.getEmail(), password); // Verify the user has this feature on the bundle in item 1 - getClient(item1RemoverToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(item1RemoverToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify this user doesn’t have this feature on item 1 - getClient(item1RemoverToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(item1RemoverToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify this user doesn’t have this feature on the bitstream in item 1 - getClient(item1RemoverToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) + getAuthorizationFeatures(item1RemoverToken, "http://localhost/api/core/bitstreams/" + bitstream1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1385,9 +1180,7 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String bundle1RemoverToken = getAuthToken(bundle1Remover.getEmail(), password); // Verify the user doesn’t have this feature on the bitstream in item 1 - getClient(bundle1RemoverToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) + getAuthorizationFeatures(bundle1RemoverToken, "http://localhost/api/core/bitstreams/" + bitstream1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1413,9 +1206,7 @@ public void testCanDeleteMinimalPermissions() throws Exception { context.restoreAuthSystemState(); String bundle1item1RemoverToken = getAuthToken(bundle1item1Remover.getEmail(), password); // Verify the user has this feature on the bitstream in item 1 - getClient(bundle1item1RemoverToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bitstreams/" + bitstream1.getID())) + getAuthorizationFeatures(bundle1item1RemoverToken, "http://localhost/api/core/bitstreams/" + bitstream1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1430,39 +1221,31 @@ public void testCanReorderBitstreamsAdmin() throws Exception { final String feature = "canReorderBitstreams"; // Verify the general admin has this feature on the bundle in item 1 - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(adminToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on the bundle in item 1 - getClient(communityAAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on the bundle in item 1 - getClient(collectionXAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(collectionXAdminToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin has this feature on the bundle in item 1 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin doesn’t have this feature on the bundle in item 2 - getClient(communityAAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle2.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/bundles/" + bundle2.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1476,31 +1259,24 @@ public void testCanReorderBitstreamsWriter() throws Exception { final String feature = "canReorderBitstreams"; // Verify community A write doesn’t have this feature on the bundle in item 1 - getClient(communityAWriterToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(communityAWriterToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X write doesn’t have this feature on the bundle in item 1 - getClient(collectionXWriterToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(collectionXWriterToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify item 1 write doesn’t have this feature on the bundle in item 1 - getClient(item1WriterToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(item1WriterToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Create a new user, grant WRITE permissions on the bundle in item 1 to this user // Verify the user has this feature on the bundle in item 1 - getClient(communityAWriterToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(communityAWriterToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1515,39 +1291,31 @@ public void testCanCreateBitstreamAdmin() throws Exception { final String feature = "canCreateBitstream"; // Verify the general admin has this feature on the bundle in item 1 - getClient(adminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(adminToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin has this feature on the bundle in item 1 - getClient(communityAAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify collection X admin has this feature on the bundle in item 1 - getClient(collectionXAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(collectionXAdminToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify item 1 admin has this feature on the bundle in item 1 - getClient(item1AdminToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(item1AdminToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); // Verify community A admin doesn’t have this feature on the bundle in item 2 - getClient(communityAAdminToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle2.getID())) + getAuthorizationFeatures(communityAAdminToken, "http://localhost/api/core/bundles/" + bundle2.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1561,24 +1329,19 @@ public void testCanCreateBitstreamWriter() throws Exception { final String feature = "canCreateBitstream"; // Verify community A write doesn’t have this feature on the bundle in item 1 - getClient(communityAWriterToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(communityAWriterToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X write doesn’t have this feature on the bundle in item 1 - getClient(collectionXWriterToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(collectionXWriterToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify item 1 write doesn’t have this feature on the bundle in item 1 - getClient(item1WriterToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(item1WriterToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1598,8 +1361,7 @@ public void testCanCreateBitstreamWriter() throws Exception { context.restoreAuthSystemState(); String bundle1WriterToken = getAuthToken(bundle1Writer.getEmail(), password); // Verify the user doesn’t have this feature on the bundle in item 1 - getClient(bundle1WriterToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(bundle1WriterToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1619,8 +1381,7 @@ public void testCanCreateBitstreamWriter() throws Exception { context.restoreAuthSystemState(); String bundle1AdderToken = getAuthToken(bundle1Adder.getEmail(), password); // Verify the user doesn’t have this feature on the bundle in item 1 - getClient(bundle1AdderToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(bundle1AdderToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1656,9 +1417,7 @@ public void testCanCreateBitstreamWriter() throws Exception { context.restoreAuthSystemState(); String bundle1WriterAdderToken = getAuthToken(bundle1WriterAdder.getEmail(), password); // Verify the user has this feature on the bundle in item 1 - getClient(bundle1WriterAdderToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/bundles/" + bundle1.getID())) + getAuthorizationFeatures(bundle1WriterAdderToken, "http://localhost/api/core/bundles/" + bundle1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); @@ -1677,24 +1436,19 @@ public void testCanCreateBundleWriter() throws Exception { final String feature = "canCreateBundle"; // Verify community A write doesn’t have this feature on item 1 - getClient(communityAWriterToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(communityAWriterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify collection X write doesn’t have this feature on item 1 - getClient(collectionXWriterToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(collectionXWriterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); // Verify item 1 write doesn’t have this feature on item 1 - getClient(item1WriterToken).perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(item1WriterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").doesNotExist()); @@ -1719,11 +1473,22 @@ public void testCanCreateBundleWriter() throws Exception { context.restoreAuthSystemState(); String item1AdderWriterToken = getAuthToken(item1AdderWriter.getEmail(), password); // Verify the user has this feature on item 1 - getClient(item1AdderWriterToken) - .perform(get("/api/authz/authorizations/search/object?size=1000&embed=feature&uri=" - + "http://localhost/api/core/items/" + item1.getID())) + getAuthorizationFeatures(item1AdderWriterToken, "http://localhost/api/core/items/" + item1.getID()) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='" + feature + "')]").exists()); } + + private ResultActions getAuthorizationFeatures(String adminToken, String uri) throws Exception { + return getAuthorizationFeatures(adminToken, uri, SIZE); + } + + private ResultActions getAuthorizationFeatures(String adminToken, String uri, int size) throws Exception { + return getClient(adminToken) + .perform( + get( + "/api/authz/authorizations/search/object?size=" + size + "&embed=feature&uri=" + uri + ) + ); + } } \ No newline at end of file diff --git a/dspace-server-webapp/src/test/resources/application-test.properties b/dspace-server-webapp/src/test/resources/application-test.properties index e92e1166e35..bd9e2ea4a17 100644 --- a/dspace-server-webapp/src/test/resources/application-test.properties +++ b/dspace-server-webapp/src/test/resources/application-test.properties @@ -16,4 +16,5 @@ ## This file is found on classpath at src/test/resources/log4j2-test.xml logging.config = classpath:log4j2-test.xml +# Our integration tests expect application to be deployed at the root path (/) server.servlet.context-path=/ \ No newline at end of file diff --git a/dspace/modules/server-boot/pom.xml b/dspace/modules/server-boot/pom.xml index 96cad508db7..ee23c8ee7bc 100644 --- a/dspace/modules/server-boot/pom.xml +++ b/dspace/modules/server-boot/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.dspace server-boot - DSpace Server Webapp:: Boot + DSpace Server Webapp:: Executable JAR + + From 9cae03593a0918d599ede39f27582c9df1f9ad11 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 25 Jan 2024 11:09:34 +0100 Subject: [PATCH 52/82] [DSC-1503] Fix checkstyle --- .../main/java/org/dspace/content/logic/InCollectionFilter.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/logic/InCollectionFilter.java b/dspace-api/src/main/java/org/dspace/content/logic/InCollectionFilter.java index 6fdc53460f8..c7697ce82fa 100644 --- a/dspace-api/src/main/java/org/dspace/content/logic/InCollectionFilter.java +++ b/dspace-api/src/main/java/org/dspace/content/logic/InCollectionFilter.java @@ -17,7 +17,6 @@ import org.dspace.content.Collection; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; -import org.dspace.content.logic.LogicalStatementException; import org.dspace.content.service.CollectionService; import org.dspace.content.service.ItemService; import org.dspace.core.Context; @@ -43,7 +42,7 @@ public class InCollectionFilter implements Filter { private String name; private Map parameters = new HashMap<>(); private static Logger log = LogManager.getLogger(InCollectionFilter.class); - + /** * Get parameters set by spring configuration in item-filters.xml * These could be any kind of map that the extending condition class needs for evaluation From 9d5fa139452b78b33c9858111a30e68dca85ff9b Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Sun, 28 Jan 2024 18:04:05 +0100 Subject: [PATCH 53/82] DSC-1515 allows to configure a sync ExecutorService mainly for testing purpose --- .../src/test/data/dspaceFolder/config/local.cfg | 5 ++++- dspace-services/pom.xml | 12 ++++++++++++ .../dspace/services/events/SystemEventService.java | 7 ++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/test/data/dspaceFolder/config/local.cfg b/dspace-api/src/test/data/dspaceFolder/config/local.cfg index 3618d405f7f..2f3a64218db 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/local.cfg +++ b/dspace-api/src/test/data/dspaceFolder/config/local.cfg @@ -223,4 +223,7 @@ orcid.synchronization-enabled = true # These settings ensure "dspace.object.owner" field are indexed by Authority Control choices.plugin.dspace.object.owner = EPersonAuthority choices.presentation.dspace.object.owner = suggest -authority.controlled.dspace.object.owner = true \ No newline at end of file +authority.controlled.dspace.object.owner = true + +# force the event system to work synchronously during test +system-event.thread.size = 0 \ No newline at end of file diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 867ac1dc1a7..39e2ccf36f3 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -154,5 +154,17 @@ ${spring-boot.version} + + com.google.guava + guava + + + + org.checkerframework + checker-qual + + + + diff --git a/dspace-services/src/main/java/org/dspace/services/events/SystemEventService.java b/dspace-services/src/main/java/org/dspace/services/events/SystemEventService.java index 5a4f118fcdc..322b0dc8be2 100644 --- a/dspace-services/src/main/java/org/dspace/services/events/SystemEventService.java +++ b/dspace-services/src/main/java/org/dspace/services/events/SystemEventService.java @@ -15,6 +15,7 @@ import java.util.function.Supplier; import javax.annotation.PreDestroy; +import com.google.common.util.concurrent.MoreExecutors; import org.apache.commons.lang3.ArrayUtils; import org.dspace.services.ConfigurationService; import org.dspace.services.EventService; @@ -107,7 +108,11 @@ private void initExecutor() { } ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); int threadSize = configurationService.getIntProperty("system-event.thread.size", DEFAULT_THREAD_SIZE); - this.executorService = Executors.newFixedThreadPool(threadSize); + if (threadSize == 0) { + this.executorService = MoreExecutors.newDirectExecutorService(); + } else { + this.executorService = Executors.newFixedThreadPool(threadSize); + } } /* (non-Javadoc) From 7b466631b3b8620bec8721d3b7de9f85a79401d6 Mon Sep 17 00:00:00 2001 From: mohamed eskander Date: Mon, 29 Jan 2024 12:59:52 +0200 Subject: [PATCH 54/82] [DSC-1377] added support for alternative tab/box for tabpolicy/boxpolicy sheets of layout config --- .../java/org/dspace/layout/CrisLayoutBox.java | 29 +-- .../layout/CrisLayoutBox2SecurityGroup.java | 124 ++++++++++ .../java/org/dspace/layout/CrisLayoutTab.java | 29 +-- .../layout/CrisLayoutTab2SecurityGroup.java | 124 ++++++++++ .../service/CrisLayoutToolValidator.java | 2 + .../impl/CrisLayoutToolConverterImpl.java | 32 ++- .../impl/CrisLayoutToolParserImpl.java | 127 ++++++++++- .../service/CrisLayoutTabAccessService.java | 3 +- .../layout/service/CrisLayoutTabService.java | 2 + .../impl/CrisLayoutTabAccessServiceImpl.java | 16 +- .../impl/CrisLayoutTabServiceImpl.java | 9 + .../cris-layout-configuration-template.xls | Bin 686080 -> 686080 bytes ...ab_id_to_cris_layout_tab2securitygroup.sql | 14 ++ ...ox_id_to_cris_layout_box2securitygroup.sql | 14 ++ ...ab_id_to_cris_layout_tab2securitygroup.sql | 14 ++ ...ox_id_to_cris_layout_box2securitygroup.sql | 14 ++ ...ab_id_to_cris_layout_tab2securitygroup.sql | 14 ++ ...ox_id_to_cris_layout_box2securitygroup.sql | 14 ++ .../dspace/builder/CrisLayoutBoxBuilder.java | 13 ++ .../dspace/builder/CrisLayoutTabBuilder.java | 13 ++ .../converter/CrisLayoutTabConverter.java | 89 ++++++-- .../org/dspace/app/rest/LayoutSecurityIT.java | 26 ++- .../layout/CrisLayoutTabRestRepositoryIT.java | 211 ++++++++++++++++++ dspace/config/hibernate.cfg.xml | 2 + .../conftool/cris-layout-configuration.xls | Bin 126464 -> 126464 bytes 25 files changed, 852 insertions(+), 83 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/layout/CrisLayoutBox2SecurityGroup.java create mode 100644 dspace-api/src/main/java/org/dspace/layout/CrisLayoutTab2SecurityGroup.java create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql diff --git a/dspace-api/src/main/java/org/dspace/layout/CrisLayoutBox.java b/dspace-api/src/main/java/org/dspace/layout/CrisLayoutBox.java index 47b426b135c..9fb9a725c5b 100644 --- a/dspace-api/src/main/java/org/dspace/layout/CrisLayoutBox.java +++ b/dspace-api/src/main/java/org/dspace/layout/CrisLayoutBox.java @@ -11,6 +11,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import javax.persistence.Cacheable; import javax.persistence.CascadeType; import javax.persistence.Column; @@ -79,13 +80,8 @@ public class CrisLayoutBox implements ReloadableEntity { ) private Set metadataSecurityFields = new HashSet<>(); - @ManyToMany(fetch = FetchType.LAZY) - @JoinTable( - name = "cris_layout_box2securitygroup", - joinColumns = {@JoinColumn(name = "box_id")}, - inverseJoinColumns = {@JoinColumn(name = "group_id")} - ) - private Set groupSecurityFields = new HashSet<>(); + @OneToMany(fetch = FetchType.LAZY, mappedBy = "box", cascade = CascadeType.ALL, orphanRemoval = true) + private Set box2SecurityGroups = new HashSet<>(); @OneToMany(fetch = FetchType.LAZY, mappedBy = "box", cascade = CascadeType.ALL) @OrderBy(value = "row, cell, priority") @@ -288,20 +284,19 @@ public void setContainer(Boolean container) { this.container = container; } - public void setGroupSecurityFields(Set groupSecurityFields) { - this.groupSecurityFields = groupSecurityFields; - } - - public void addGroupSecurityFields(Set groupSecurityFields) { - this.groupSecurityFields.addAll(groupSecurityFields); + public Set getGroupSecurityFields() { + return box2SecurityGroups.stream() + .map(crisLayoutBox2SecurityGroup -> + crisLayoutBox2SecurityGroup.getGroup()) + .collect(Collectors.toSet()); } - public void addGroupSecurityFields(Group group) { - this.groupSecurityFields.add(group); + public Set getBox2SecurityGroups() { + return box2SecurityGroups; } - public Set getGroupSecurityFields() { - return groupSecurityFields; + public void setBox2SecurityGroups(Set box2SecurityGroups) { + this.box2SecurityGroups = box2SecurityGroups; } @Override diff --git a/dspace-api/src/main/java/org/dspace/layout/CrisLayoutBox2SecurityGroup.java b/dspace-api/src/main/java/org/dspace/layout/CrisLayoutBox2SecurityGroup.java new file mode 100644 index 00000000000..d0ee1cd5841 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/layout/CrisLayoutBox2SecurityGroup.java @@ -0,0 +1,124 @@ +/** + * 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.layout; + +import java.io.Serializable; +import javax.persistence.CascadeType; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MapsId; +import javax.persistence.Table; + +import org.dspace.eperson.Group; + +@Entity +@Table(name = "cris_layout_box2securitygroup") +public class CrisLayoutBox2SecurityGroup implements Serializable { + + @Embeddable + public static class CrisLayoutBox2SecurityGroupId implements Serializable { + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "box_id") + private CrisLayoutBox boxId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "group_id") + private Group groupId; + + public CrisLayoutBox2SecurityGroupId() { + + } + + public CrisLayoutBox2SecurityGroupId(CrisLayoutBox boxId, Group groupId) { + this.boxId = boxId; + this.groupId = groupId; + } + + public CrisLayoutBox getBoxId() { + return boxId; + } + + public void setBoxId(CrisLayoutBox boxId) { + this.boxId = boxId; + } + + public Group getGroupId() { + return groupId; + } + + public void setGroupId(Group groupId) { + this.groupId = groupId; + } + } + + @EmbeddedId + private CrisLayoutBox2SecurityGroupId id; + + @ManyToOne(fetch = FetchType.LAZY) + @MapsId("boxId") + @JoinColumn(name = "box_id", insertable = false, updatable = false) + private CrisLayoutBox box; + + @ManyToOne(fetch = FetchType.LAZY) + @MapsId("groupId") + @JoinColumn(name = "group_id", insertable = false, updatable = false) + private Group group; + + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn(name = "alternative_box_id", nullable = true) + private CrisLayoutBox alternativeBox; + + public CrisLayoutBox2SecurityGroup() { + + } + + public CrisLayoutBox2SecurityGroup(CrisLayoutBox2SecurityGroupId id, + CrisLayoutBox box, Group group, + CrisLayoutBox alternativeBox) { + this.id = id; + this.box = box; + this.group = group; + this.alternativeBox = alternativeBox; + } + + public CrisLayoutBox2SecurityGroupId getId() { + return id; + } + + public void setId(CrisLayoutBox2SecurityGroupId id) { + this.id = id; + } + + public CrisLayoutBox getBox() { + return box; + } + + public void setBox(CrisLayoutBox box) { + this.box = box; + } + + public Group getGroup() { + return group; + } + + public void setGroup(Group group) { + this.group = group; + } + + public CrisLayoutBox getAlternativeBox() { + return alternativeBox; + } + + public void setAlternativeBox(CrisLayoutBox alternativeBox) { + this.alternativeBox = alternativeBox; + } +} diff --git a/dspace-api/src/main/java/org/dspace/layout/CrisLayoutTab.java b/dspace-api/src/main/java/org/dspace/layout/CrisLayoutTab.java index 48bd0dc5611..9c0f4ef1e2b 100644 --- a/dspace-api/src/main/java/org/dspace/layout/CrisLayoutTab.java +++ b/dspace-api/src/main/java/org/dspace/layout/CrisLayoutTab.java @@ -87,14 +87,8 @@ public class CrisLayoutTab implements ReloadableEntity { @JoinColumn(name = "tab_id") }, inverseJoinColumns = { @JoinColumn(name = "metadata_field_id") }) private Set metadataSecurityFields = new HashSet<>(); - @ManyToMany(fetch = FetchType.LAZY) - @JoinTable( - name = "cris_layout_tab2securitygroup", - joinColumns = {@JoinColumn(name = "tab_id")}, - inverseJoinColumns = {@JoinColumn(name = "group_id")} - ) - private Set groupSecurityFields = new HashSet<>(); - + @OneToMany(fetch = FetchType.LAZY, mappedBy = "tab", cascade = CascadeType.ALL) + private Set tab2SecurityGroups = new HashSet<>(); @Column(name = "is_leading") private Boolean leading; @@ -230,20 +224,19 @@ public List getBoxes() { .collect(Collectors.toList()); } - public void setGroupSecurityFields(Set groupSecurityFields) { - this.groupSecurityFields = groupSecurityFields; - } - - public void addGroupSecurityFields(Set groupSecurityFields) { - this.groupSecurityFields.addAll(groupSecurityFields); + public Set getGroupSecurityFields() { + return tab2SecurityGroups.stream() + .map(crisLayoutTab2SecurityGroup -> + crisLayoutTab2SecurityGroup.getGroup()) + .collect(Collectors.toSet()); } - public void addGroupSecurityFields(Group group) { - this.groupSecurityFields.add(group); + public Set getTab2SecurityGroups() { + return tab2SecurityGroups; } - public Set getGroupSecurityFields() { - return groupSecurityFields; + public void setTab2SecurityGroups(Set tab2SecurityGroups) { + this.tab2SecurityGroups = tab2SecurityGroups; } @Override diff --git a/dspace-api/src/main/java/org/dspace/layout/CrisLayoutTab2SecurityGroup.java b/dspace-api/src/main/java/org/dspace/layout/CrisLayoutTab2SecurityGroup.java new file mode 100644 index 00000000000..f41b3ec53e8 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/layout/CrisLayoutTab2SecurityGroup.java @@ -0,0 +1,124 @@ +/** + * 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.layout; + +import java.io.Serializable; +import javax.persistence.CascadeType; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MapsId; +import javax.persistence.Table; + +import org.dspace.eperson.Group; + +@Entity +@Table(name = "cris_layout_tab2securitygroup") +public class CrisLayoutTab2SecurityGroup implements Serializable { + + @Embeddable + public static class CrisLayoutTab2SecurityGroupId implements Serializable { + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "tab_id") + private CrisLayoutTab tabId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "group_id") + private Group groupId; + + public CrisLayoutTab2SecurityGroupId() { + + } + + public CrisLayoutTab2SecurityGroupId(CrisLayoutTab tabId, Group groupId) { + this.tabId = tabId; + this.groupId = groupId; + } + + public CrisLayoutTab getTabId() { + return tabId; + } + + public void setTabId(CrisLayoutTab tabId) { + this.tabId = tabId; + } + + public Group getGroupId() { + return groupId; + } + + public void setGroupId(Group groupId) { + this.groupId = groupId; + } + } + + @EmbeddedId + private CrisLayoutTab2SecurityGroupId id; + + @ManyToOne(fetch = FetchType.LAZY) + @MapsId("tabId") + @JoinColumn(name = "tab_id", insertable = false, updatable = false) + private CrisLayoutTab tab; + + @ManyToOne(fetch = FetchType.LAZY) + @MapsId("groupId") + @JoinColumn(name = "group_id", insertable = false, updatable = false) + private Group group; + + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn(name = "alternative_tab_id") + private CrisLayoutTab alternativeTab; + + public CrisLayoutTab2SecurityGroup() { + + } + + public CrisLayoutTab2SecurityGroup(CrisLayoutTab2SecurityGroupId id, + CrisLayoutTab tab, Group group, + CrisLayoutTab alternativeTab) { + this.id = id; + this.tab = tab; + this.group = group; + this.alternativeTab = alternativeTab; + } + + public CrisLayoutTab2SecurityGroupId getId() { + return id; + } + + public void setId(CrisLayoutTab2SecurityGroupId id) { + this.id = id; + } + + public CrisLayoutTab getTab() { + return tab; + } + + public void setTab(CrisLayoutTab tab) { + this.tab = tab; + } + + public Group getGroup() { + return group; + } + + public void setGroup(Group group) { + this.group = group; + } + + public CrisLayoutTab getAlternativeTab() { + return alternativeTab; + } + + public void setAlternativeTab(CrisLayoutTab alternativeTab) { + this.alternativeTab = alternativeTab; + } +} diff --git a/dspace-api/src/main/java/org/dspace/layout/script/service/CrisLayoutToolValidator.java b/dspace-api/src/main/java/org/dspace/layout/script/service/CrisLayoutToolValidator.java index c1a9cff5dbb..74302960cff 100644 --- a/dspace-api/src/main/java/org/dspace/layout/script/service/CrisLayoutToolValidator.java +++ b/dspace-api/src/main/java/org/dspace/layout/script/service/CrisLayoutToolValidator.java @@ -108,6 +108,8 @@ public interface CrisLayoutToolValidator { String GROUP_COLUMN = "GROUP"; + String ALTERNATIVE_TO_COLUMN = "ALTERNATIVE_TO"; + String METADATA_TYPE = "METADATA"; String BITSTREAM_TYPE = "BITSTREAM"; diff --git a/dspace-api/src/main/java/org/dspace/layout/script/service/impl/CrisLayoutToolConverterImpl.java b/dspace-api/src/main/java/org/dspace/layout/script/service/impl/CrisLayoutToolConverterImpl.java index 1aec1c34937..52ba3ddedc1 100644 --- a/dspace-api/src/main/java/org/dspace/layout/script/service/impl/CrisLayoutToolConverterImpl.java +++ b/dspace-api/src/main/java/org/dspace/layout/script/service/impl/CrisLayoutToolConverterImpl.java @@ -31,13 +31,14 @@ import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; import org.dspace.content.MetadataField; -import org.dspace.eperson.Group; import org.dspace.layout.CrisLayoutBox; +import org.dspace.layout.CrisLayoutBox2SecurityGroup; import org.dspace.layout.CrisLayoutCell; import org.dspace.layout.CrisLayoutField; import org.dspace.layout.CrisLayoutFieldBitstream; import org.dspace.layout.CrisLayoutMetric2Box; import org.dspace.layout.CrisLayoutTab; +import org.dspace.layout.CrisLayoutTab2SecurityGroup; import org.dspace.layout.CrisMetadataGroup; import org.dspace.layout.LayoutSecurity; import org.dspace.layout.script.service.CrisLayoutToolConverter; @@ -247,9 +248,9 @@ private void buildTabPolicy(Workbook workbook, CrisLayoutTab tab) { buildTabPolicyMetadataSecurityFieldRow(sheet, tab, metadataField) ); - tab.getGroupSecurityFields() - .forEach(group -> - buildTabPolicyGroupSecurityFieldRow(sheet, tab, group) + tab.getTab2SecurityGroups() + .forEach(tab2SecurityGroup -> + buildTabPolicyGroupSecurityFieldRow(sheet, tab, tab2SecurityGroup) ); } @@ -259,14 +260,18 @@ private void buildTabPolicyMetadataSecurityFieldRow(Sheet sheet, CrisLayoutTab t createCell(row, 1, tab.getShortName()); createCell(row, 2, metadataField.toString('.')); createCell(row, 3, ""); + createCell(row, 4, ""); } - private void buildTabPolicyGroupSecurityFieldRow(Sheet sheet, CrisLayoutTab tab, Group group) { + private void buildTabPolicyGroupSecurityFieldRow(Sheet sheet, CrisLayoutTab tab, + CrisLayoutTab2SecurityGroup tab2SecurityGroup) { + CrisLayoutTab alternativeTab = tab2SecurityGroup.getAlternativeTab(); Row row = sheet.createRow(sheet.getLastRowNum() + 1); createCell(row, 0, tab.getEntity().getLabel()); createCell(row, 1, tab.getShortName()); createCell(row, 2, ""); - createCell(row, 3, group.getName()); + createCell(row, 3, tab2SecurityGroup.getGroup().getName()); + createCell(row, 4, alternativeTab == null ? "" : alternativeTab.getShortName()); } private void buildBoxPolicy(Workbook workbook, List boxes) { @@ -277,9 +282,9 @@ private void buildBoxPolicy(Workbook workbook, List boxes) { buildBoxPolicyMetadataSecurityFieldRow(sheet, box, metadataField) ); - box.getGroupSecurityFields() - .forEach(group -> - buildBoxPolicyGroupSecurityFieldRow(sheet, box, group) + box.getBox2SecurityGroups() + .forEach(box2SecurityGroup -> + buildBoxPolicyGroupSecurityFieldRow(sheet, box, box2SecurityGroup) ); }); } @@ -290,14 +295,19 @@ private void buildBoxPolicyMetadataSecurityFieldRow(Sheet sheet, CrisLayoutBox b createCell(row, 1, box.getShortname()); createCell(row, 2, metadataField.toString('.')); createCell(row, 3, ""); + createCell(row, 4, ""); } - private void buildBoxPolicyGroupSecurityFieldRow(Sheet sheet, CrisLayoutBox box, Group group) { + private void buildBoxPolicyGroupSecurityFieldRow(Sheet sheet, CrisLayoutBox box, + CrisLayoutBox2SecurityGroup box2SecurityGroup) { + + CrisLayoutBox alternativeBox = box2SecurityGroup.getAlternativeBox(); Row row = sheet.createRow(sheet.getLastRowNum() + 1); createCell(row, 0, box.getCell().getRow().getTab().getEntity().getLabel()); createCell(row, 1, box.getShortname()); createCell(row, 2, ""); - createCell(row, 3, group.getName()); + createCell(row, 3, box2SecurityGroup.getGroup().getName()); + createCell(row, 4, alternativeBox == null ? "" : alternativeBox.getShortname()); } private String convertToString(boolean value) { diff --git a/dspace-api/src/main/java/org/dspace/layout/script/service/impl/CrisLayoutToolParserImpl.java b/dspace-api/src/main/java/org/dspace/layout/script/service/impl/CrisLayoutToolParserImpl.java index 9c368c2785b..a4f5fec248e 100644 --- a/dspace-api/src/main/java/org/dspace/layout/script/service/impl/CrisLayoutToolParserImpl.java +++ b/dspace-api/src/main/java/org/dspace/layout/script/service/impl/CrisLayoutToolParserImpl.java @@ -7,6 +7,7 @@ */ package org.dspace.layout.script.service.impl; +import static org.dspace.layout.script.service.CrisLayoutToolValidator.ALTERNATIVE_TO_COLUMN; import static org.dspace.layout.script.service.CrisLayoutToolValidator.BITSTREAM_TYPE; import static org.dspace.layout.script.service.CrisLayoutToolValidator.BOX2METADATA_SHEET; import static org.dspace.layout.script.service.CrisLayoutToolValidator.BOX2METRICS_SHEET; @@ -53,6 +54,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -76,6 +78,7 @@ import org.dspace.eperson.Group; import org.dspace.eperson.service.GroupService; import org.dspace.layout.CrisLayoutBox; +import org.dspace.layout.CrisLayoutBox2SecurityGroup; import org.dspace.layout.CrisLayoutBoxTypes; import org.dspace.layout.CrisLayoutCell; import org.dspace.layout.CrisLayoutField; @@ -84,6 +87,7 @@ import org.dspace.layout.CrisLayoutMetric2Box; import org.dspace.layout.CrisLayoutRow; import org.dspace.layout.CrisLayoutTab; +import org.dspace.layout.CrisLayoutTab2SecurityGroup; import org.dspace.layout.CrisMetadataGroup; import org.dspace.layout.LayoutSecurity; import org.dspace.layout.script.service.CrisLayoutToolParser; @@ -110,9 +114,21 @@ public class CrisLayoutToolParserImpl implements CrisLayoutToolParser { @Override public List parse(Context context, Workbook workbook) { Sheet tabSheet = getSheetByName(workbook, TAB_SHEET); - return WorkbookUtils.getNotEmptyRowsSkippingHeader(tabSheet).stream() - .map(row -> buildTab(context, row)) - .collect(Collectors.toList()); + List tabs = + WorkbookUtils.getNotEmptyRowsSkippingHeader(tabSheet).stream() + .map(row -> buildTab(context, row)) + .collect(Collectors.toList()); + + tabs.forEach(tab -> { + tab.setTab2SecurityGroups(buildTab2SecurityGroups(context, + workbook, TAB_POLICY_SHEET, tab.getEntity().getLabel(), tab.getShortName(), tab, tabs)); + + tab.getBoxes().forEach(box -> + box.setBox2SecurityGroups(buildBox2SecurityGroups(context, + workbook, BOX_POLICY_SHEET, box.getEntitytype().getLabel(), box.getShortname(), box, tabs))); + }); + + return tabs; } private CrisLayoutTab buildTab(Context context, Row tabRow) { @@ -136,8 +152,6 @@ private CrisLayoutTab buildTab(Context context, Row tabRow) { buildTabRows(context, workbook, entityType, name).forEach(tab::addRow); tab.setMetadataSecurityFields(buildMetadataSecurityField(context, workbook, TAB_POLICY_SHEET, entityType, name)); - tab.setGroupSecurityFields(buildGroupSecurityField(context, workbook, - TAB_POLICY_SHEET, entityType, name)); return tab; } @@ -218,8 +232,6 @@ private CrisLayoutBox buildBox(Context context, Sheet boxSheet, String entityTyp box.setStyle(getCellValue(boxRow, STYLE_COLUMN)); box.setMetadataSecurityFields(buildMetadataSecurityField(context, workbook, BOX_POLICY_SHEET, entityType, boxName)); - box.setGroupSecurityFields(buildGroupSecurityField(context, workbook, - BOX_POLICY_SHEET, entityType, boxName)); if (boxType.equals(CrisLayoutBoxTypes.METADATA.name())) { buildCrisLayoutFields(context, workbook, entityType, boxName).forEach(box::addLayoutField); @@ -376,6 +388,107 @@ private Set buildGroupSecurityField(Context context, Workbook workbook, .collect(Collectors.toSet()); } + private Set buildBox2SecurityGroups(Context context, Workbook workbook, + String sheetName, String entity, String name, + CrisLayoutBox crisLayoutBox, + List tabs) { + Sheet sheet = getSheetByName(workbook, sheetName); + Set box2SecurityGroups = new HashSet<>(); + + getRowsByEntityAndColumnValue(sheet, entity, SHORTNAME_COLUMN, name) + .forEach(row -> { + String groupName = getCellValue(row, GROUP_COLUMN); + String alternativeBox = getCellValue(row, ALTERNATIVE_TO_COLUMN); + + if (StringUtils.isNotBlank(groupName)) { + Group group = getGroupField(context, groupName); + if (group != null) { + box2SecurityGroups.add( + buildBox2SecurityGroup(group, crisLayoutBox, entity, alternativeBox, tabs) + ); + } + } + }); + + return box2SecurityGroups; + } + + private CrisLayoutBox2SecurityGroup buildBox2SecurityGroup(Group group, CrisLayoutBox box, + String entity, + String alternativeBox, List tabs) { + + CrisLayoutBox2SecurityGroup.CrisLayoutBox2SecurityGroupId box2SecurityGroupId = + new CrisLayoutBox2SecurityGroup.CrisLayoutBox2SecurityGroupId(box, group); + + return new CrisLayoutBox2SecurityGroup(box2SecurityGroupId, box, group, + findAlternativeBox(alternativeBox, entity, tabs)); + } + + private CrisLayoutBox findAlternativeBox(String alternativeBox, String entityType, List tabs) { + + if (alternativeBox == null) { + return null; + } + + return tabs.stream() + .flatMap(tab -> tab.getBoxes().stream()) + .filter(crisLayoutBox -> crisLayoutBox.getShortname().equals(alternativeBox) && + crisLayoutBox.getEntitytype().getLabel().equals(entityType)) + .findFirst() + .orElseThrow(() -> new RuntimeException("Alternative box not found for shortname: " + + alternativeBox + ", entityType: " + entityType)); + } + + private Set buildTab2SecurityGroups(Context context, Workbook workbook, + String sheetName, String entity, String name, + CrisLayoutTab crisLayoutTab, + List tabs) { + Sheet sheet = getSheetByName(workbook, sheetName); + Set tab2SecurityGroups = new HashSet<>(); + + getRowsByEntityAndColumnValue(sheet, entity, SHORTNAME_COLUMN, name) + .forEach(row -> { + String groupName = getCellValue(row, GROUP_COLUMN); + String alternativeTab = getCellValue(row, ALTERNATIVE_TO_COLUMN); + + if (StringUtils.isNotBlank(groupName)) { + Group group = getGroupField(context, groupName); + if (group != null) { + tab2SecurityGroups.add( + buildTab2SecurityGroup(group, crisLayoutTab, entity, alternativeTab, tabs) + ); + } + } + }); + + return tab2SecurityGroups; + } + + private CrisLayoutTab2SecurityGroup buildTab2SecurityGroup(Group group, CrisLayoutTab tab, + String entity, + String alternativeTab, List tabs) { + + CrisLayoutTab2SecurityGroup.CrisLayoutTab2SecurityGroupId tab2SecurityGroupId = + new CrisLayoutTab2SecurityGroup.CrisLayoutTab2SecurityGroupId(tab, group); + + return new CrisLayoutTab2SecurityGroup(tab2SecurityGroupId, tab, group, + findAlternativeTab(alternativeTab, entity, tabs)); + } + + private CrisLayoutTab findAlternativeTab(String alternativeTab, String entityType, List tabs) { + + if (alternativeTab == null) { + return null; + } + + return tabs.stream() + .filter(crisLayoutTab -> crisLayoutTab.getShortName().equals(alternativeTab) && + crisLayoutTab.getEntity().getLabel().equals(entityType)) + .findFirst() + .orElseThrow(() -> new RuntimeException("Alternative tab not found for shortname: " + + alternativeTab + ", entityType: " + entityType)); + } + private Stream getRowsByEntityAndColumnValue(Sheet sheet, String entity, String columnName, String value) { return WorkbookUtils.getNotEmptyRowsSkippingHeader(sheet).stream() .filter(row -> value.equals(getCellValue(row, columnName))) diff --git a/dspace-api/src/main/java/org/dspace/layout/service/CrisLayoutTabAccessService.java b/dspace-api/src/main/java/org/dspace/layout/service/CrisLayoutTabAccessService.java index 12d7d08084e..2679d34865d 100644 --- a/dspace-api/src/main/java/org/dspace/layout/service/CrisLayoutTabAccessService.java +++ b/dspace-api/src/main/java/org/dspace/layout/service/CrisLayoutTabAccessService.java @@ -32,6 +32,5 @@ public interface CrisLayoutTabAccessService { * @return true if access has to be granded, false otherwise * @throws SQLException in case of error during database access */ - boolean hasAccess(Context context, EPerson user, CrisLayoutTab tab, Item item) - throws SQLException; + boolean hasAccess(Context context, EPerson user, CrisLayoutTab tab, Item item); } diff --git a/dspace-api/src/main/java/org/dspace/layout/service/CrisLayoutTabService.java b/dspace-api/src/main/java/org/dspace/layout/service/CrisLayoutTabService.java index 7224b8f2630..919dc7eb431 100644 --- a/dspace-api/src/main/java/org/dspace/layout/service/CrisLayoutTabService.java +++ b/dspace-api/src/main/java/org/dspace/layout/service/CrisLayoutTabService.java @@ -152,4 +152,6 @@ public List getMetadataField(Context context, Integer tabId, Inte * @throws SQLException An exception that provides information on a database errors. */ public List findByItem(Context context, String itemUuid) throws SQLException; + + public boolean hasAccess(Context context, CrisLayoutTab tab, Item item); } diff --git a/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutTabAccessServiceImpl.java b/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutTabAccessServiceImpl.java index 71e20fd883f..331c5df679b 100644 --- a/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutTabAccessServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutTabAccessServiceImpl.java @@ -31,12 +31,14 @@ public CrisLayoutTabAccessServiceImpl(LayoutSecurityService layoutSecurityServic } @Override - public boolean hasAccess(Context context, EPerson user, CrisLayoutTab tab, Item item) throws SQLException { - return layoutSecurityService.hasAccess(LayoutSecurity.valueOf(tab.getSecurity()), - context, - user, - tab.getMetadataSecurityFields(), - tab.getGroupSecurityFields(), - item); + public boolean hasAccess(Context context, EPerson user, CrisLayoutTab tab, Item item) { + try { + return layoutSecurityService.hasAccess( + LayoutSecurity.valueOf(tab.getSecurity()), context, user, tab.getMetadataSecurityFields(), + tab.getGroupSecurityFields(), item + ); + } catch (SQLException e) { + throw new RuntimeException(e); + } } } diff --git a/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutTabServiceImpl.java b/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutTabServiceImpl.java index 2ce7ee7ac4f..980305e67d6 100644 --- a/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutTabServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutTabServiceImpl.java @@ -29,6 +29,7 @@ import org.dspace.core.Context; import org.dspace.layout.CrisLayoutTab; import org.dspace.layout.dao.CrisLayoutTabDAO; +import org.dspace.layout.service.CrisLayoutTabAccessService; import org.dspace.layout.service.CrisLayoutTabService; import org.dspace.services.ConfigurationService; import org.springframework.beans.factory.annotation.Autowired; @@ -54,6 +55,9 @@ public class CrisLayoutTabServiceImpl implements CrisLayoutTabService { private SubmissionConfigReader submissionConfigReader; + @Autowired + CrisLayoutTabAccessService crisLayoutTabAccessService; + @PostConstruct private void setup() throws SubmissionConfigReaderException { submissionConfigReader = new SubmissionConfigReader(); @@ -217,6 +221,11 @@ public List findByItem(Context context, String itemUuid) throws S return layoutTabs; } + @Override + public boolean hasAccess(Context context, CrisLayoutTab tab, Item item) { + return crisLayoutTabAccessService.hasAccess(context, context.getCurrentUser(), tab, item); + } + private String getSubmissionDefinitionName(Item item) { if (submissionConfigReader == null || item.getOwningCollection() == null) { return ""; diff --git a/dspace-api/src/main/resources/org/dspace/layout/script/service/impl/cris-layout-configuration-template.xls b/dspace-api/src/main/resources/org/dspace/layout/script/service/impl/cris-layout-configuration-template.xls index a921219abfad173dde9253ce4368468386eda6c7..2eddc88c677749d144d96421e076cd3a8938de3d 100644 GIT binary patch delta 1476 zcmb7@OKcNY6o${eH>9Br91=(!Gc`@36xd{#5v=U$55$N6OF9z zzyEp8+`_Z2!n3aB1K$2y9>6~d^4Y`lph1_P&`sEXc=q%>c;HGy#bIFxwupka>D;Lp ziGDctMLzp^HlO|S#O%qr+&ly!)j=m7Kzl!)o|ry@asbTDB-c~ox!IZNBVUvFDqa!M z>xjdNig+?VJ(Hu{^Uwf?k|Zh=J~f>hY&lNiH+Q%tVmOJY>98jHe9gSI#~Am`F!u}o z89H_uEB+xCz|nT^pB(w*!0yS+rz2C76UJZK_l)f_%7I1}8whD=1vm>{;P}6~!C?s( z^qcW}V>2rbeJ2a?F#Ht@ikG3N)b7?rG!Y531oUdO3^K%x5k19YH{VNA2slYy;DYl; zu_?>hCGomD8e4NaIjG{}8YXi|q+5~)2g8m_1r=5`C#47Xey^s47iUS;ocq8Bk89YE zZ?I(i)N#bNCnKd`b9I<{d}n%tQ$iI7^$o`Dc!%9JVm`P#mTauLm8LsgfpRCknSdL0oI6_&E_1{=PQPYb*j7p+T$A zd(Yszm5`a$w;fL5(B@P=QW)sEf@IjTx*X|R=CGiZ}7Tdy5dXB{T1M6LGO>@n~7@S;(w&d?Yio z?iybTnl)F0VxLTHGO?u}|Gk|dtDS|#vW!oixmb3OFR^9w{;2ZHe*`Y9WE6br&6SM$ wN9T<>^+QQb!^b!zoSM*!`*LcV!k_OSzR-J@iI^*77INxo#>(d3dDShw1(10Fod5s; delta 1377 zcmZ`&O=weD6h8O9{J#9WK9d(?ZJTClJ8h#)nrf}>ECdT;78arCPKi2>g~k>etur94 zKegyW(JN#j8M>;on1y%8n8PFgeCg>%YJver11_JO~T}5HRD8$5caP8EQB#~h# z{9Mc*&KL8)93ML|J~0CkXx>1P0H8dJ(c>eBkk(pBoU&E5UQH>1YGz5X;e{r3I6&%Mi`C(0*l@9#JCrgR4yFpxuo@9pb z7zw4#Yluv$QE$+Vs)ZZe&?)yC-8}hpmD5?>adv?V&dWd6<~h44!l^{^h1c<*vL9es zs8xj5Cq*ICEcSIxu9$C3Os2=#qWCA>C|D%^-Mkx_m3b%G1=*0@!KRw9&@wg#z;F=~ zRm5REG~0;b(*RmtQZh^VLltrS0$OZDt7YImSCn`!M6}wJNB1Z-#yz-CRGxS@meydSt#VtXH`&Nz#pv5uh9MO^7}Owb z2fbD>Vi%g=Kboh`(eS%~r6>&W{$XD4Xp*9SFmwYSBb+UdKz+PiOs_cqIW zc43L*QOt!US8iUvTeHt(_phn{?v`9z)nw_?65lxcbaPuc#8k|s@eP`s*m*vkdnMk^#=JbSm Nim@K^|Dx(uz5#Vw{(}Gj diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql new file mode 100644 index 00000000000..6ae50fb29bf --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql @@ -0,0 +1,14 @@ +-- +-- 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/ +-- + +----------------------------------------------------------------------------------- +-- Alter TABLE cris_layout_tab2securitygroup ADD alternative_tab_id +----------------------------------------------------------------------------------- + +ALTER TABLE cris_layout_tab2securitygroup ADD COLUMN alternative_tab_id INTEGER; +ALTER TABLE cris_layout_tab2securitygroup ADD CONSTRAINT cris_layout_tab2securitygroup_tab_id2 FOREIGN KEY (alternative_tab_id) REFERENCES cris_layout_tab (id) ON DELETE SET NULL; \ No newline at end of file diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql new file mode 100644 index 00000000000..38360bb13cd --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql @@ -0,0 +1,14 @@ +-- +-- 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/ +-- + +----------------------------------------------------------------------------------- +-- Alter TABLE cris_layout_box2securitygroup ADD alternative_box_id +----------------------------------------------------------------------------------- + +ALTER TABLE cris_layout_box2securitygroup ADD COLUMN alternative_box_id INTEGER; +ALTER TABLE cris_layout_box2securitygroup ADD CONSTRAINT cris_layout_box2securitygroup_box_id2 FOREIGN KEY (alternative_box_id) REFERENCES cris_layout_box (id) ON DELETE SET NULL; \ No newline at end of file diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql new file mode 100644 index 00000000000..6ae50fb29bf --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql @@ -0,0 +1,14 @@ +-- +-- 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/ +-- + +----------------------------------------------------------------------------------- +-- Alter TABLE cris_layout_tab2securitygroup ADD alternative_tab_id +----------------------------------------------------------------------------------- + +ALTER TABLE cris_layout_tab2securitygroup ADD COLUMN alternative_tab_id INTEGER; +ALTER TABLE cris_layout_tab2securitygroup ADD CONSTRAINT cris_layout_tab2securitygroup_tab_id2 FOREIGN KEY (alternative_tab_id) REFERENCES cris_layout_tab (id) ON DELETE SET NULL; \ No newline at end of file diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql new file mode 100644 index 00000000000..38360bb13cd --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql @@ -0,0 +1,14 @@ +-- +-- 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/ +-- + +----------------------------------------------------------------------------------- +-- Alter TABLE cris_layout_box2securitygroup ADD alternative_box_id +----------------------------------------------------------------------------------- + +ALTER TABLE cris_layout_box2securitygroup ADD COLUMN alternative_box_id INTEGER; +ALTER TABLE cris_layout_box2securitygroup ADD CONSTRAINT cris_layout_box2securitygroup_box_id2 FOREIGN KEY (alternative_box_id) REFERENCES cris_layout_box (id) ON DELETE SET NULL; \ No newline at end of file diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql new file mode 100644 index 00000000000..6ae50fb29bf --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.12.12__add_alternative_tab_id_to_cris_layout_tab2securitygroup.sql @@ -0,0 +1,14 @@ +-- +-- 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/ +-- + +----------------------------------------------------------------------------------- +-- Alter TABLE cris_layout_tab2securitygroup ADD alternative_tab_id +----------------------------------------------------------------------------------- + +ALTER TABLE cris_layout_tab2securitygroup ADD COLUMN alternative_tab_id INTEGER; +ALTER TABLE cris_layout_tab2securitygroup ADD CONSTRAINT cris_layout_tab2securitygroup_tab_id2 FOREIGN KEY (alternative_tab_id) REFERENCES cris_layout_tab (id) ON DELETE SET NULL; \ No newline at end of file diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql new file mode 100644 index 00000000000..38360bb13cd --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2023.12.13__add_alternative_box_id_to_cris_layout_box2securitygroup.sql @@ -0,0 +1,14 @@ +-- +-- 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/ +-- + +----------------------------------------------------------------------------------- +-- Alter TABLE cris_layout_box2securitygroup ADD alternative_box_id +----------------------------------------------------------------------------------- + +ALTER TABLE cris_layout_box2securitygroup ADD COLUMN alternative_box_id INTEGER; +ALTER TABLE cris_layout_box2securitygroup ADD CONSTRAINT cris_layout_box2securitygroup_box_id2 FOREIGN KEY (alternative_box_id) REFERENCES cris_layout_box (id) ON DELETE SET NULL; \ No newline at end of file diff --git a/dspace-api/src/test/java/org/dspace/builder/CrisLayoutBoxBuilder.java b/dspace-api/src/test/java/org/dspace/builder/CrisLayoutBoxBuilder.java index cf80c8778dc..59784b192ed 100644 --- a/dspace-api/src/test/java/org/dspace/builder/CrisLayoutBoxBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/CrisLayoutBoxBuilder.java @@ -19,7 +19,9 @@ import org.dspace.content.MetadataField; import org.dspace.core.Context; import org.dspace.discovery.SearchServiceException; +import org.dspace.eperson.Group; import org.dspace.layout.CrisLayoutBox; +import org.dspace.layout.CrisLayoutBox2SecurityGroup; import org.dspace.layout.CrisLayoutBoxTypes; import org.dspace.layout.CrisLayoutField; import org.dspace.layout.LayoutSecurity; @@ -163,6 +165,17 @@ public CrisLayoutBoxBuilder addMetadataSecurityField(MetadataField field) { return this; } + public CrisLayoutBoxBuilder addBox2SecurityGroups(Group group, CrisLayoutBox alternativeBox) throws SQLException { + if (this.box.getBox2SecurityGroups() == null) { + this.box.setBox2SecurityGroups(new HashSet<>()); + } + this.box.getBox2SecurityGroups().add( + new CrisLayoutBox2SecurityGroup(new CrisLayoutBox2SecurityGroup.CrisLayoutBox2SecurityGroupId(box, group), + box, group, alternativeBox) + ); + return this; + } + public CrisLayoutBoxBuilder withContainer(boolean container) { this.box.setContainer(container); return this; diff --git a/dspace-api/src/test/java/org/dspace/builder/CrisLayoutTabBuilder.java b/dspace-api/src/test/java/org/dspace/builder/CrisLayoutTabBuilder.java index 312d04bb4ad..4736324f4d3 100644 --- a/dspace-api/src/test/java/org/dspace/builder/CrisLayoutTabBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/CrisLayoutTabBuilder.java @@ -20,10 +20,12 @@ import org.dspace.content.MetadataField; import org.dspace.core.Context; import org.dspace.discovery.SearchServiceException; +import org.dspace.eperson.Group; import org.dspace.layout.CrisLayoutBox; import org.dspace.layout.CrisLayoutCell; import org.dspace.layout.CrisLayoutRow; import org.dspace.layout.CrisLayoutTab; +import org.dspace.layout.CrisLayoutTab2SecurityGroup; import org.dspace.layout.LayoutSecurity; import org.dspace.layout.service.CrisLayoutTabService; @@ -207,4 +209,15 @@ public CrisLayoutTabBuilder addMetadatasecurity(MetadataField metadataField) { this.tab.getMetadataSecurityFields().add(metadataField); return this; } + + public CrisLayoutTabBuilder addTab2SecurityGroups(Group group, CrisLayoutTab alternativeTab) { + if (this.tab.getTab2SecurityGroups() == null) { + this.tab.setTab2SecurityGroups(new HashSet<>()); + } + this.tab.getTab2SecurityGroups().add( + new CrisLayoutTab2SecurityGroup(new CrisLayoutTab2SecurityGroup.CrisLayoutTab2SecurityGroupId(tab, group), + tab, group, alternativeTab) + ); + return this; + } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/CrisLayoutTabConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/CrisLayoutTabConverter.java index d15a04cf010..2c94bd8a8f3 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/CrisLayoutTabConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/CrisLayoutTabConverter.java @@ -9,6 +9,7 @@ import java.sql.SQLException; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -27,11 +28,14 @@ import org.dspace.core.Context; import org.dspace.core.exception.SQLRuntimeException; import org.dspace.layout.CrisLayoutBox; +import org.dspace.layout.CrisLayoutBox2SecurityGroup; import org.dspace.layout.CrisLayoutCell; import org.dspace.layout.CrisLayoutRow; import org.dspace.layout.CrisLayoutTab; +import org.dspace.layout.CrisLayoutTab2SecurityGroup; import org.dspace.layout.LayoutSecurity; import org.dspace.layout.service.CrisLayoutBoxService; +import org.dspace.layout.service.CrisLayoutTabService; import org.dspace.services.RequestService; import org.dspace.util.UUIDUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -61,18 +65,47 @@ public class CrisLayoutTabConverter implements DSpaceConverter convertTab(tab, projection)) + .orElseGet(CrisLayoutTabRest::new); + } + + private boolean hasAccess(Item item, CrisLayoutTab tab) { + Context context = ContextUtil.obtainCurrentRequestContext(); + return crisLayoutTabService.hasAccess(context, tab, item); + } + + private CrisLayoutTab findAlternativeTab(CrisLayoutTab tab) { + return tab.getTab2SecurityGroups() + .stream() + .map(CrisLayoutTab2SecurityGroup::getAlternativeTab) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); + } + + private CrisLayoutTabRest convertTab(CrisLayoutTab tab, Projection projection) { CrisLayoutTabRest rest = new CrisLayoutTabRest(); - rest.setId(model.getID()); - rest.setEntityType(model.getEntity().getLabel()); - rest.setCustomFilter(model.getCustomFilter()); - rest.setShortname(model.getShortName()); - rest.setHeader(model.getHeader()); - rest.setPriority(model.getPriority()); - rest.setSecurity(model.getSecurity()); - rest.setRows(convertRows(getScopeItem(), model.getRows(), projection)); - rest.setLeading(model.isLeading()); + rest.setId(tab.getID()); + rest.setEntityType(tab.getEntity().getLabel()); + rest.setCustomFilter(tab.getCustomFilter()); + rest.setShortname(tab.getShortName()); + rest.setHeader(tab.getHeader()); + rest.setPriority(tab.getPriority()); + rest.setSecurity(tab.getSecurity()); + rest.setRows(convertRows(getScopeItem(), tab.getRows(), projection)); + rest.setLeading(tab.isLeading()); return rest; } @@ -124,15 +157,43 @@ private CrisLayoutCellRest convertCell(Item item, CrisLayoutCell cell, Projectio private List convertBoxes(Item item, List boxes, Projection projection) { return boxes.stream() - .filter(box -> item == null || hasAccess(item, box)) - .map(box -> boxConverter.convert(box, projection)) - .collect(Collectors.toList()); + .map(box -> getCrisLayoutBox(item, box)) + .filter(Objects::nonNull) + .map(box -> boxConverter.convert(box, projection)) + .collect(Collectors.toList()); + } + + private CrisLayoutBox getCrisLayoutBox(Item item, CrisLayoutBox box) { + + if (item == null) { + return box; + } + + return Optional.of(box) + .filter(b -> hasAccess(item, b) && hasContent(item, b)) + .orElseGet(() -> + Optional.ofNullable(findAlternativeBox(box)) + .filter(altBox -> hasContent(item, altBox)) + .orElse(null)); } private boolean hasAccess(Item item, CrisLayoutBox box) { Context context = ContextUtil.obtainCurrentRequestContext(); - return crisLayoutBoxService.hasContent(context, box, item) - && crisLayoutBoxService.hasAccess(context, box, item); + return crisLayoutBoxService.hasAccess(context, box, item); + } + + private boolean hasContent(Item item, CrisLayoutBox box) { + Context context = ContextUtil.obtainCurrentRequestContext(); + return crisLayoutBoxService.hasContent(context, box, item); + } + + private CrisLayoutBox findAlternativeBox(CrisLayoutBox box) { + return box.getBox2SecurityGroups() + .stream() + .map(CrisLayoutBox2SecurityGroup::getAlternativeBox) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); } private CrisLayoutRow toRowModel(Context context, CrisLayoutRowRest rowRest) { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/LayoutSecurityIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/LayoutSecurityIT.java index 5d293ce2fbc..aa5705beb86 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/LayoutSecurityIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/LayoutSecurityIT.java @@ -53,6 +53,7 @@ import org.dspace.eperson.service.GroupService; import org.dspace.eperson.service.RegistrationDataService; import org.dspace.layout.CrisLayoutBox; +import org.dspace.layout.CrisLayoutBox2SecurityGroup; import org.dspace.layout.LayoutSecurity; import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; import org.hamcrest.Matchers; @@ -491,14 +492,20 @@ public void customDataTestWithOneGroup() throws Exception { .build(); // Create Group with member userA - Set groups = new HashSet<>(); + Set box2SecurityGroups = new HashSet<>(); Group testGroup = GroupBuilder.createGroup(context) .withName("testGroup") .addMember(userA) .build(); - groups.add(testGroup); - box1.setGroupSecurityFields(groups); + new CrisLayoutBox2SecurityGroup( + new CrisLayoutBox2SecurityGroup.CrisLayoutBox2SecurityGroupId(box1, testGroup), + box1, testGroup, null); + + box2SecurityGroups.add(new CrisLayoutBox2SecurityGroup( + new CrisLayoutBox2SecurityGroup.CrisLayoutBox2SecurityGroupId(box1, testGroup), + box1, testGroup, null)); + box1.setBox2SecurityGroups(box2SecurityGroups); CrisLayoutFieldBuilder.createMetadataField(context, abs, 0, 0) .withLabel("LABEL ABS") @@ -577,7 +584,7 @@ public void customDataTestWithMultipleGroup() throws Exception { .build(); // Create Group with member userA - Set boxGroups = new HashSet<>(); + Set boxGroups = new HashSet<>(); Group testGroup = GroupBuilder.createGroup(context) .withName("testGroup") @@ -589,9 +596,14 @@ public void customDataTestWithMultipleGroup() throws Exception { .addMember(userB) .build(); - boxGroups.add(testGroup); - boxGroups.add(testGroup1); - box1.setGroupSecurityFields(boxGroups); + boxGroups.add(new CrisLayoutBox2SecurityGroup( + new CrisLayoutBox2SecurityGroup.CrisLayoutBox2SecurityGroupId(box1, testGroup), + box1, testGroup, null)); + boxGroups.add(new CrisLayoutBox2SecurityGroup( + new CrisLayoutBox2SecurityGroup.CrisLayoutBox2SecurityGroupId(box1, testGroup1), + box1, testGroup, null)); + + box1.setBox2SecurityGroups(boxGroups); CrisLayoutFieldBuilder.createMetadataField(context, abs, 0, 0) .withLabel("LABEL ABS") diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/layout/CrisLayoutTabRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/layout/CrisLayoutTabRestRepositoryIT.java index d536f547d0b..aaa3fb1c62b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/layout/CrisLayoutTabRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/layout/CrisLayoutTabRestRepositoryIT.java @@ -77,6 +77,7 @@ import org.dspace.content.service.RelationshipService; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; +import org.dspace.eperson.service.GroupService; import org.dspace.layout.CrisLayoutBox; import org.dspace.layout.CrisLayoutBoxTypes; import org.dspace.layout.CrisLayoutCell; @@ -122,6 +123,9 @@ public class CrisLayoutTabRestRepositoryIT extends AbstractControllerIntegration @Autowired protected RelationshipService relationshipService; + @Autowired + protected GroupService groupService; + private final String METADATASECURITY_URL = "http://localhost:8080/api/core/metadatafield/"; /** @@ -2469,6 +2473,213 @@ public void excludeThumbnailNegativeMetadataValueMatcherTabMultiBoxConfiguration .andExpect(jsonPath("$._embedded.tabs[0].rows[1]").doesNotExist()); } + @Test + public void testFindByItemWithAlternativeTabs() throws Exception { + context.turnOffAuthorisationSystem(); + + MetadataSchema schema = mdss.find(context, "person"); + MetadataField firstName = mfss.findByElement(context, schema, "givenName", null); + Group adminGroup = groupService.findByName(context, Group.ADMIN); + // Create new community + Community community = CommunityBuilder.createCommunity(context) + .withName("Test Community") + .withTitle("Title test community") + .build(); + // Create new collection + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("Test Collection") + .build(); + // Create entity Type + EntityTypeBuilder.createEntityTypeBuilder(context, "Publication") + .build(); + EntityType eTypePer = EntityTypeBuilder.createEntityTypeBuilder(context, "Person") + .build(); + // Create new person item + Item item = ItemBuilder.createItem(context, collection) + .withPersonIdentifierFirstName("Danilo") + .withPersonIdentifierLastName("Di Nuzzo") + .withEntityType(eTypePer.getLabel()) + .build(); + + CrisLayoutBox boxOne = CrisLayoutBoxBuilder.createBuilder(context, eTypePer, false, false) + .withShortname("Box shortname 1") + .withSecurity(LayoutSecurity.PUBLIC) + .withContainer(false) + .build(); + + CrisLayoutBox boxTwo = CrisLayoutBoxBuilder.createBuilder(context, eTypePer, false, false) + .withShortname("Box shortname 2") + .withSecurity(LayoutSecurity.PUBLIC) + .withContainer(false) + .build(); + + CrisLayoutFieldBuilder.createMetadataField(context, firstName, 0, 1) + .withLabel("GIVEN NAME") + .withRendering("TEXT") + .withBox(boxOne) + .build(); + + CrisLayoutFieldBuilder.createMetadataField(context, firstName, 0, 1) + .withLabel("GIVEN NAME") + .withRendering("TEXT") + .withBox(boxTwo) + .build(); + + // add boxOne to tabOne + CrisLayoutTab tabOne = + CrisLayoutTabBuilder.createTab(context, eTypePer, 0) + .withShortName("TabOne For Person - priority 0") + .withSecurity(LayoutSecurity.ADMINISTRATOR) + .withHeader("New Tab header") + .addBoxIntoNewRow(boxOne, "rowTwoStyle", "cellOfRowTwoStyle") + .build(); + + // add boxTwo to tabTwo + CrisLayoutTab tabTwo = + CrisLayoutTabBuilder.createTab(context, eTypePer, 0) + .withShortName("Tab2 For Person - priority 0") + .withSecurity(LayoutSecurity.CUSTOM_DATA) + .withHeader("New Tab2 header") + .addBoxIntoNewRow(boxTwo, "rowTwoStyle2", "cellOfRowTwoStyle2") + .addTab2SecurityGroups(adminGroup, tabOne) + .build(); + + context.restoreAuthSystemState(); + + // admin user will see two tabs + getClient(getAuthToken(admin.getEmail(), password)) + .perform(get("/api/layout/tabs/search/findByItem") + .param("uuid", item.getID().toString())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$.page.totalElements", is(2))) + .andExpect(jsonPath("$._embedded.tabs[0].id", is(tabOne.getID()))) + .andExpect(jsonPath("$._embedded.tabs[0].shortname", is("TabOne For Person - priority 0"))) + .andExpect(jsonPath("$._embedded.tabs[0].header", is("New Tab header"))) + .andExpect(jsonPath("$._embedded.tabs[0].security", is(LayoutSecurity.ADMINISTRATOR.getValue()))) + .andExpect(jsonPath("$._embedded.tabs[0].rows", hasSize(1))) + .andExpect(jsonPath("$._embedded.tabs[1].id", is(tabTwo.getID()))) + .andExpect(jsonPath("$._embedded.tabs[1].shortname", is("Tab2 For Person - priority 0"))) + .andExpect(jsonPath("$._embedded.tabs[1].header", is("New Tab2 header"))) + .andExpect(jsonPath("$._embedded.tabs[1].security", is(LayoutSecurity.CUSTOM_DATA.getValue()))) + .andExpect(jsonPath("$._embedded.tabs[1].rows", hasSize(1))); + + // anonymous user will see only alternative tab is tabOne + getClient().perform(get("/api/layout/tabs/search/findByItem") + .param("uuid", item.getID().toString())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$.page.totalElements", is(1))) + .andExpect(jsonPath("$._embedded.tabs[0].id", is(tabOne.getID()))) + .andExpect(jsonPath("$._embedded.tabs[0].shortname", is("TabOne For Person - priority 0"))) + .andExpect(jsonPath("$._embedded.tabs[0].header", is("New Tab header"))) + .andExpect(jsonPath("$._embedded.tabs[0].security", is(LayoutSecurity.ADMINISTRATOR.getValue()))) + .andExpect(jsonPath("$._embedded.tabs[0].rows", hasSize(1))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].style", is("rowTwoStyle"))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells", hasSize(1))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells[0].style", is("cellOfRowTwoStyle"))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells[0].boxes", contains(matchBox(boxOne)))); + } + + @Test + public void testFindByItemWithAlternativeBoxes() throws Exception { + context.turnOffAuthorisationSystem(); + + MetadataSchema schema = mdss.find(context, "person"); + MetadataField firstName = mfss.findByElement(context, schema, "givenName", null); + Group adminGroup = groupService.findByName(context, Group.ADMIN); + // Create new community + Community community = CommunityBuilder.createCommunity(context) + .withName("Test Community") + .withTitle("Title test community") + .build(); + // Create new collection + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("Test Collection") + .build(); + // Create entity Type + EntityTypeBuilder.createEntityTypeBuilder(context, "Publication") + .build(); + EntityType eTypePer = EntityTypeBuilder.createEntityTypeBuilder(context, "Person") + .build(); + // Create new person item + Item item = ItemBuilder.createItem(context, collection) + .withPersonIdentifierFirstName("Danilo") + .withPersonIdentifierLastName("Di Nuzzo") + .withEntityType(eTypePer.getLabel()) + .build(); + + CrisLayoutBox boxOne = CrisLayoutBoxBuilder.createBuilder(context, eTypePer, false, false) + .withShortname("Box shortname 1") + .withSecurity(LayoutSecurity.PUBLIC) + .withContainer(false) + .build(); + + // add boxOne as alternative to boxTwo + CrisLayoutBox boxTwo = CrisLayoutBoxBuilder.createBuilder(context, eTypePer, false, false) + .withShortname("Box shortname 2") + .withSecurity(LayoutSecurity.CUSTOM_DATA) + .withContainer(false) + .addBox2SecurityGroups(adminGroup, boxOne) + .build(); + + CrisLayoutFieldBuilder.createMetadataField(context, firstName, 0, 1) + .withLabel("GIVEN NAME") + .withRendering("TEXT") + .withBox(boxOne) + .build(); + + CrisLayoutFieldBuilder.createMetadataField(context, firstName, 0, 1) + .withLabel("GIVEN NAME") + .withRendering("TEXT") + .withBox(boxTwo) + .build(); + + // add boxTwo to tab + CrisLayoutTab tab = CrisLayoutTabBuilder.createTab(context, eTypePer, 0) + .withShortName("TabOne For Person - priority 0") + .withSecurity(LayoutSecurity.PUBLIC) + .withHeader("New Tab header") + .withLeading(true) + .addBoxIntoNewRow(boxTwo, "rowTwoStyle", "cellOfRowTwoStyle") + .build(); + + context.restoreAuthSystemState(); + + // admin user will see boxTwo + getClient(getAuthToken(admin.getEmail(), password)) + .perform(get("/api/layout/tabs/search/findByItem") + .param("uuid", item.getID().toString())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.tabs[0].id", is(tab.getID()))) + .andExpect(jsonPath("$._embedded.tabs[0].shortname", is("TabOne For Person - priority 0"))) + .andExpect(jsonPath("$._embedded.tabs[0].header", is("New Tab header"))) + .andExpect(jsonPath("$._embedded.tabs[0].leading", is(true))) + .andExpect(jsonPath("$._embedded.tabs[0].security", is(LayoutSecurity.PUBLIC.getValue()))) + .andExpect(jsonPath("$._embedded.tabs[0].rows", hasSize(1))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].style", is("rowTwoStyle"))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells", hasSize(1))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells[0].style", is("cellOfRowTwoStyle"))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells[0].boxes", contains(matchBox(boxTwo)))); + + // anonymous user will see boxOne + getClient().perform(get("/api/layout/tabs/search/findByItem") + .param("uuid", item.getID().toString())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.tabs[0].id", is(tab.getID()))) + .andExpect(jsonPath("$._embedded.tabs[0].shortname", is("TabOne For Person - priority 0"))) + .andExpect(jsonPath("$._embedded.tabs[0].header", is("New Tab header"))) + .andExpect(jsonPath("$._embedded.tabs[0].leading", is(true))) + .andExpect(jsonPath("$._embedded.tabs[0].security", is(LayoutSecurity.PUBLIC.getValue()))) + .andExpect(jsonPath("$._embedded.tabs[0].rows", hasSize(1))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].style", is("rowTwoStyle"))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells", hasSize(1))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells[0].style", is("cellOfRowTwoStyle"))) + .andExpect(jsonPath("$._embedded.tabs[0].rows[0].cells[0].boxes", contains(matchBox(boxOne)))); + } + private CrisLayoutTabRest parseJson(String name) throws Exception { return new ObjectMapper().readValue(getFileInputStream(name), CrisLayoutTabRest.class); } diff --git a/dspace/config/hibernate.cfg.xml b/dspace/config/hibernate.cfg.xml index 8597bedbc34..9a3cdd967f0 100644 --- a/dspace/config/hibernate.cfg.xml +++ b/dspace/config/hibernate.cfg.xml @@ -109,6 +109,8 @@ + + diff --git a/dspace/etc/conftool/cris-layout-configuration.xls b/dspace/etc/conftool/cris-layout-configuration.xls index 6a7d9daf8e8083166a0cd7ac64511b5f4c3be1a6..d9c9ab9f0090d4baf78b6269a88f18a9ddc03cc4 100644 GIT binary patch delta 1685 zcma)5ZD?C%6h7zX`>Qv%OJbHb4Y6z`tFukA&aUh1mzf(I&bcZSic1$Q6kDe*g$;ANiM%Xdxg`ECHkF#)S)a$5MdoI&)~W1oV0QBt%HLD zsUw{F{MMpzI6W|UXfc}`9!zngmFw6)5N&=l3vSp{iK>mFHJcX}UgS0xEGf~50GyHw zt|7GS_SIPdcFIj+n@6eu=-Azzc;2@r|*7h zON);kT~~BDMv`>jX;a^|meLvs1oY`U8dW&Rn4fYvhTU z(?YOI^X7Fu3D0SWUZf+D`?#0<&fdKr`bx)`I*#OQf$ue6j8+ilX@-+BE% z=Xsu{mQAT;)8vK`QQ0)E7#@Ew2>j?E+1--{Crs`UXc8FhIe8`xR_N)-N2utA`sW1N zfH69BBH7*7o$OA77j93HZ1?#FuLw71<=w9g^qx+G3@+MlcMtY`Eu0qNJauw#p!Zeb zYW^l~98L}lp3YZu!D|b`Xcjt-4@6=wr@;lE%c$BoTeErK+!MlPhyRsmj!LLdrtL#$ zYWLQf0gfmw&Q7v&?{Dqn=Bt)Xp+Tma!24mPW4BHc$5(ID=oE771Fls&?v`V3%zdgYI3 zn-h&nPTq_MBXhp$9#TC}Z(nKl&&Yx!)RA%Oiv#oS9t5YWg*Ig#N8f zmeh76DsYr`CxW3xQ$dkf)MgQj>f`icqT$JX-a=Fvl);R%pux(Uy;`Kn#^c&sp zpKX+Xuotw8wTvrkvzHC_{ujiv(fI&xT|YI?H64ggc; zJcHy=^-Aiup1;kKEB~ARo)1#yS_gf9tr{O9{dce1I1|J{=9{Raxm-W*S@XS0!WkNy zud3bIS~LaT!|kolinjF8!h`rQU0?a^>o9ztWbwjX0<5Sy7~_16rJzLp8)T;uottnCS{&K&e>U3{v7R zIV0<_Z$d%|8)?^XE!hityy%sGbQMk%a*<5-lpl8#KD4dy;Vc(kZ!iC>=%R_-pN~`h zN+A36QG8o+mMF_a16ZG}Ifn1Ngdb<8uHt2>rX*xrkH?sdFig1S$?Td%Lw4#*JY1Ps zUB}^1Hn3G^i?%`+FEe%H#6e%?&`k`Qo21ypt)WGvklvk5-NZA9$Fg^Bp-J~24H9cs From 5f6e1fe653960543a2a1fcd374cd087637b20b1a Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Fri, 2 Feb 2024 16:38:23 +0100 Subject: [PATCH 55/82] [DSC-963] Fixes version numbers --- dspace/modules/server-boot/pom.xml | 2 +- dspace/modules/server/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace/modules/server-boot/pom.xml b/dspace/modules/server-boot/pom.xml index ee23c8ee7bc..497fab3e6a1 100644 --- a/dspace/modules/server-boot/pom.xml +++ b/dspace/modules/server-boot/pom.xml @@ -11,7 +11,7 @@ modules org.dspace - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index 6992da0a9fb..faa4aa8cb20 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -12,7 +12,7 @@ - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT ${basedir}/../../.. From dea151e39f2a1c2b21346ac0aeebc6fb26384278 Mon Sep 17 00:00:00 2001 From: mohamed eskander Date: Fri, 2 Feb 2024 18:07:11 +0200 Subject: [PATCH 56/82] [DSC-1386] refactoring --- dspace/config/sample-structure.xml | 140 ++++++++++++++--------------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/dspace/config/sample-structure.xml b/dspace/config/sample-structure.xml index 1d9095841e0..bde920dea38 100644 --- a/dspace/config/sample-structure.xml +++ b/dspace/config/sample-structure.xml @@ -10,13 +10,13 @@ Person Person person - - - - - - - + + + + + + + @@ -28,13 +28,13 @@ Project Project project - - - - - - - + + + + + + + @@ -46,13 +46,13 @@ Funding Funding funding - - - - - - - + + + + + + + @@ -64,13 +64,13 @@ OrgUnit OrgUnit orgunit - - - - - - - + + + + + + + @@ -82,13 +82,13 @@ Journal Journal journal - - - - - - - + + + + + + + @@ -100,13 +100,13 @@ Publication Publication publication - - - - - - - + + + + + + + @@ -118,13 +118,13 @@ Patent Patent patent - - - - - - - + + + + + + + @@ -135,13 +135,13 @@ Dataset or other products Product - - - - - - - + + + + + + + @@ -153,13 +153,13 @@ Event Event event - - - - - - - + + + + + + + @@ -171,13 +171,13 @@ Equipment Equipment equipment - - - - - - - + + + + + + + From 2cb2076f8cfe8badcfb97ea3d6b230154f54e65e Mon Sep 17 00:00:00 2001 From: mohamed eskander Date: Fri, 2 Feb 2024 18:09:35 +0200 Subject: [PATCH 57/82] [DSC-1386] refactoring --- dspace/config/sample-structure.xml | 140 ++++++++++++++--------------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/dspace/config/sample-structure.xml b/dspace/config/sample-structure.xml index 1d9095841e0..bde920dea38 100644 --- a/dspace/config/sample-structure.xml +++ b/dspace/config/sample-structure.xml @@ -10,13 +10,13 @@ Person Person person - - - - - - - + + + + + + + @@ -28,13 +28,13 @@ Project Project project - - - - - - - + + + + + + + @@ -46,13 +46,13 @@ Funding Funding funding - - - - - - - + + + + + + + @@ -64,13 +64,13 @@ OrgUnit OrgUnit orgunit - - - - - - - + + + + + + + @@ -82,13 +82,13 @@ Journal Journal journal - - - - - - - + + + + + + + @@ -100,13 +100,13 @@ Publication Publication publication - - - - - - - + + + + + + + @@ -118,13 +118,13 @@ Patent Patent patent - - - - - - - + + + + + + + @@ -135,13 +135,13 @@ Dataset or other products Product - - - - - - - + + + + + + + @@ -153,13 +153,13 @@ Event Event event - - - - - - - + + + + + + + @@ -171,13 +171,13 @@ Equipment Equipment equipment - - - - - - - + + + + + + + From 55da2ca05691b31858032bee343099caadbcea4d Mon Sep 17 00:00:00 2001 From: mohamed eskander Date: Mon, 5 Feb 2024 14:00:42 +0200 Subject: [PATCH 58/82] [DSC-1497] refactoring and adding more ITs --- .../content/authority/ItemAuthority.java | 8 +- .../org/dspace/app/rest/ItemAuthorityIT.java | 82 +------------- .../org/dspace/app/rest/OrcidAuthorityIT.java | 107 ++++++++++++++++++ .../org/dspace/app/rest/ZDBAuthorityIT.java | 17 ++- .../rest/matcher/ItemAuthorityMatcher.java | 17 ++- dspace/config/modules/authority.cfg | 9 +- 6 files changed, 153 insertions(+), 87 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/authority/ItemAuthority.java b/dspace-api/src/main/java/org/dspace/content/authority/ItemAuthority.java index 83f049abe8f..13ed3398fae 100644 --- a/dspace-api/src/main/java/org/dspace/content/authority/ItemAuthority.java +++ b/dspace-api/src/main/java/org/dspace/content/authority/ItemAuthority.java @@ -79,7 +79,7 @@ public class ItemAuthority implements ChoiceAuthority, LinkableEntityAuthority { // map of field key to presentation type protected Map externalSource = new HashMap(); - private String source; + public static final String DEFAULT = "local"; // punt! this is a poor implementation.. @Override @@ -176,7 +176,7 @@ private List getChoiceListFromQueryResults(SolrDocumentList results, Str Map extras = ItemAuthorityUtils.buildExtra(getPluginInstanceName(), doc); return new Choice((String) doc.getFieldValue("search.resourceid"), title, - title, extras, getSource()); + title, extras, DEFAULT); }).collect(Collectors.toList()); } @@ -299,9 +299,9 @@ private Context getContext() { return context != null ? context : new Context(); } - public String getSource() { + protected String getSource() { return configurationService.getProperty( - "cris.ItemAuthority." + authorityName + ".source", "local"); + "cris.ItemAuthority." + authorityName + ".source", DEFAULT); } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemAuthorityIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemAuthorityIT.java index d57d579308b..fe675261275 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemAuthorityIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemAuthorityIT.java @@ -29,6 +29,7 @@ import org.dspace.builder.ItemBuilder; import org.dspace.content.Collection; import org.dspace.content.Item; +import org.dspace.content.authority.ItemAuthority; import org.dspace.content.authority.service.ChoiceAuthorityService; import org.dspace.core.service.PluginService; import org.dspace.eperson.EPerson; @@ -265,7 +266,7 @@ public void singleItemAuthorityWithoutOrgUnitTest() throws Exception { Map.of("data-oairecerif_author_affiliation", "", "oairecerif_author_affiliation", "", "data-person_identifier_orcid", "", "person_identifier_orcid", ""), - "local") + ItemAuthority.DEFAULT) ))) .andExpect(jsonPath("$.page.totalElements", Matchers.is(1))); } @@ -889,81 +890,6 @@ public void authorCoarseAuthorityTests() throws Exception { person4Id, "Cortese, Claudio", "Cortese, Claudio", "vocabularyEntry")))); } - @Test - public void orcidAuthoritySourceReferenceTest() throws Exception { - context.turnOffAuthorisationSystem(); - - configurationService.setProperty("plugin.named.org.dspace.content.authority.ChoiceAuthority", - new String[] { - "org.dspace.content.authority.OrcidAuthority=AuthorAuthority" - }); - - configurationService.setProperty("solr.authority.server", "${solr.server}/authority"); - configurationService.setProperty("choices.plugin.dc.contributor.author", "AuthorAuthority"); - configurationService.setProperty("choices.presentation.dc.contributor.author", "suggest"); - configurationService.setProperty("authority.controlled.dc.contributor.author", "true"); - - // set authority source reference - configurationService.setProperty("cris.ItemAuthority.AuthorAuthority.source", "ORCID"); - - // These clears have to happen so that the config is actually reloaded in those classes. This is needed for - // the properties that we're altering above and this is only used within the tests - pluginService.clearNamedPluginClasses(); - choiceAuthorityService.clearCache(); - - parentCommunity = CommunityBuilder.createCommunity(context).build(); - Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).build(); - - Item orgUnit_1 = ItemBuilder.createItem(context, col1) - .withTitle("OrgUnit_1") - .withEntityType("orgunit") - .build(); - - Item orgUnit_2 = ItemBuilder.createItem(context, col1) - .withTitle("OrgUnit_2") - .withEntityType("orgunit") - .build(); - - Item author_1 = ItemBuilder.createItem(context, col1) - .withTitle("Author 1") - .withPersonMainAffiliation(orgUnit_1.getName(), orgUnit_1.getID().toString()) - .withEntityType("person") - .build(); - - Item author_2 = ItemBuilder.createItem(context, col1) - .withTitle("Author 2") - .withPersonMainAffiliation(orgUnit_2.getName(), orgUnit_2.getID().toString()) - .withEntityType("person") - .build(); - - context.restoreAuthSystemState(); - - String token = getAuthToken(eperson.getEmail(), password); - getClient(token).perform(get("/api/submission/vocabularies/AuthorAuthority/entries") - .param("metadata", "dc.contributor.author") - .param("collection", col1.getID().toString()) - .param("filter", "author")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$._embedded.entries", Matchers.containsInAnyOrder( - // filled with AuthorAuthority extra metadata generator - ItemAuthorityMatcher.matchItemAuthorityWithOtherInformations(author_1.getID().toString(), - "Author 1", "Author 1", "vocabularyEntry", - Map.of("data-oairecerif_author_affiliation", "OrgUnit_1::" + orgUnit_1.getID(), - "oairecerif_author_affiliation", "OrgUnit_1::" + orgUnit_1.getID(), - "data-person_identifier_orcid", "", - "person_identifier_orcid", ""), - "ORCID"), - ItemAuthorityMatcher.matchItemAuthorityWithOtherInformations(author_2.getID().toString(), - "Author 2", "Author 2", "vocabularyEntry", - Map.of("data-oairecerif_author_affiliation", "OrgUnit_2::" + orgUnit_2.getID(), - "oairecerif_author_affiliation", "OrgUnit_2::" + orgUnit_2.getID(), - "data-person_identifier_orcid", "", - "person_identifier_orcid", ""), - "ORCID") - ))) - .andExpect(jsonPath("$.page.totalElements", Matchers.is(2))); - } - @Test public void itemAuthoritySourceReferenceTest() throws Exception { context.turnOffAuthorisationSystem(); @@ -1012,8 +938,8 @@ public void itemAuthoritySourceReferenceTest() throws Exception { .andExpect(status().isOk()).andExpect(jsonPath("$.page.totalElements", Matchers.is(1))) .andExpect(jsonPath("$._embedded.entries", Matchers.containsInAnyOrder(ItemAuthorityMatcher.matchItemAuthorityWithOtherInformations( - person1.getID().toString(), "Person 1", "Person 1", "vocabularyEntry", Map.of(), "ORCID")))); - + person1.getID().toString(), "Person 1", "Person 1", "vocabularyEntry", Map.of(), + ItemAuthority.DEFAULT)))); } @Override diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/OrcidAuthorityIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/OrcidAuthorityIT.java index 62cc6b11c32..a2889994f9a 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/OrcidAuthorityIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/OrcidAuthorityIT.java @@ -31,6 +31,7 @@ import org.dspace.builder.ItemBuilder; import org.dspace.content.Collection; import org.dspace.content.Item; +import org.dspace.content.authority.ItemAuthority; import org.dspace.content.authority.OrcidAuthority; import org.dspace.orcid.client.OrcidClient; import org.dspace.orcid.client.OrcidConfiguration; @@ -712,6 +713,101 @@ public void testWithAffiliationExtra() throws Exception { verifyNoMoreInteractions(orcidClientMock); } + @Test + public void testSourceReference() throws Exception { + + List orcidSearchResults = List.of( + expandedResult("Author", "From Orcid 1", "0000-1111-2222-3333"), + expandedResult("AUTHOR", "FROM ORCID 2", "0000-2222-3333-4444")); + + String expectedQuery = "(given-names:author+OR+family-name:author+OR+other-names:author)"; + + when(orcidClientMock.expandedSearch(READ_PUBLIC_TOKEN, expectedQuery, 0, 20)) + .thenReturn(expandedSearch(2, orcidSearchResults)); + + String token = getAuthToken(eperson.getEmail(), password); + getClient(token).perform(get("/api/submission/vocabularies/AuthorAuthority/entries") + .param("filter", "author")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._embedded.entries", containsInAnyOrder( + orcidEntry("From Orcid 1 Author", REFERENCE, "0000-1111-2222-3333", getSource()), + orcidEntry("From Orcid 2 Author", REFERENCE, "0000-2222-3333-4444", getSource())))) + .andExpect(jsonPath("$.page.size", Matchers.is(20))) + .andExpect(jsonPath("$.page.totalPages", Matchers.is(1))) + .andExpect(jsonPath("$.page.totalElements", Matchers.is(2))); + + verify(orcidClientMock).getReadPublicAccessToken(); + verify(orcidClientMock).expandedSearch(READ_PUBLIC_TOKEN, expectedQuery, 0, 20); + verifyNoMoreInteractions(orcidClientMock); + } + + @Test + public void testMultipleSourcesReferences() throws Exception { + context.turnOffAuthorisationSystem(); + + List orcidSearchResults = List.of( + expandedResult("Author", "From Orcid 1", "0000-1111-2222-3333"), + expandedResult("AUTHOR", "FROM ORCID 2", "0000-2222-3333-4444")); + + String expectedQuery = "(given-names:author+OR+family-name:author+OR+other-names:author)"; + + when(orcidClientMock.expandedSearch(READ_PUBLIC_TOKEN, expectedQuery, 0, 18)) + .thenReturn(expandedSearch(2, orcidSearchResults)); + + parentCommunity = CommunityBuilder.createCommunity(context).build(); + Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).build(); + + Item orgUnit_1 = ItemBuilder.createItem(context, col1) + .withTitle("OrgUnit_1") + .withEntityType("orgunit") + .build(); + + Item orgUnit_2 = ItemBuilder.createItem(context, col1) + .withTitle("OrgUnit_2") + .withEntityType("orgunit") + .build(); + + Item author_1 = ItemBuilder.createItem(context, col1) + .withTitle("Author 1") + .withPersonMainAffiliation(orgUnit_1.getName(), orgUnit_1.getID().toString()) + .withEntityType("person") + .build(); + + Item author_2 = ItemBuilder.createItem(context, col1) + .withTitle("Author 2") + .withPersonMainAffiliation(orgUnit_2.getName(), orgUnit_2.getID().toString()) + .withEntityType("person") + .build(); + + context.restoreAuthSystemState(); + + String token = getAuthToken(eperson.getEmail(), password); + getClient(token).perform(get("/api/submission/vocabularies/AuthorAuthority/entries") + .param("filter", "author")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._embedded.entries", Matchers.containsInAnyOrder( + // source should be local + ItemAuthorityMatcher.matchItemAuthorityWithOtherInformations(author_1.getID().toString(), + "Author 1", "Author 1", "vocabularyEntry", + Map.of("data-oairecerif_author_affiliation", "OrgUnit_1::" + orgUnit_1.getID(), + "oairecerif_author_affiliation", "OrgUnit_1::" + orgUnit_1.getID(), + "data-person_identifier_orcid", "", + "person_identifier_orcid", ""), + ItemAuthority.DEFAULT), + ItemAuthorityMatcher.matchItemAuthorityWithOtherInformations(author_2.getID().toString(), + "Author 2", "Author 2", "vocabularyEntry", + Map.of("data-oairecerif_author_affiliation", "OrgUnit_2::" + orgUnit_2.getID(), + "oairecerif_author_affiliation", "OrgUnit_2::" + orgUnit_2.getID(), + "data-person_identifier_orcid", "", + "person_identifier_orcid", ""), + ItemAuthority.DEFAULT), + // source should be orcid as configured + orcidEntry("From Orcid 1 Author", REFERENCE, "0000-1111-2222-3333", getSource()), + orcidEntry("From Orcid 2 Author", REFERENCE, "0000-2222-3333-4444", getSource()) + ))) + .andExpect(jsonPath("$.page.totalElements", Matchers.is(4))); + } + private ExpandedSearch buildExpandedSearchFromSublist(List totalResults, int start, int rows) { int total = totalResults.size(); if (start > total) { @@ -783,6 +879,12 @@ private Matcher orcidEntry(String title, String authorityPrefix, title, "vocabularyEntry", ORCID_INFO, orcid); } + private Matcher orcidEntry(String title, String authorityPrefix, String orcid, String source) { + String authority = authorityPrefix + "ORCID::" + orcid; + return ItemAuthorityMatcher.matchItemAuthorityWithOtherInformations(authority, title, + title, "vocabularyEntry", ORCID_INFO, orcid, source); + } + private Matcher orcidEntryWithAffiliation(String title, String authorityPrefix, String orcid, String affiliation) { String authority = authorityPrefix + "ORCID::" + orcid; @@ -800,4 +902,9 @@ private Matcher orcidEntryWithAffiliation(String title, String a private String id(Item item) { return item.getID().toString(); } + + private String getSource() { + return configurationService.getProperty( + "cris.ItemAuthority.AuthorAuthority.source", ItemAuthority.DEFAULT); + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ZDBAuthorityIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ZDBAuthorityIT.java index f2e77d86cec..c73832c1907 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ZDBAuthorityIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ZDBAuthorityIT.java @@ -13,8 +13,11 @@ import org.dspace.app.rest.matcher.ItemAuthorityMatcher; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.content.authority.ItemAuthority; +import org.dspace.services.ConfigurationService; import org.hamcrest.Matchers; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; /** * This class handles ZDBAuthority related IT. @@ -23,6 +26,9 @@ */ public class ZDBAuthorityIT extends AbstractControllerIntegrationTest { + @Autowired + private ConfigurationService configurationService; + @Test public void zdbAuthorityTest() throws Exception { String token = getAuthToken(eperson.getEmail(), password); @@ -35,21 +41,21 @@ public void zdbAuthorityTest() throws Exception { "will be generated::zdb::1447228-4", "Acta mathematica et informatica", "Acta mathematica et informatica", "vocabularyEntry", "data-dc_relation_ispartof", "Acta mathematica et informatica::will be generated::zdb::1447228-4", - "data-dc_relation_issn", ""), + "data-dc_relation_issn", "", getSource()), ItemAuthorityMatcher.matchItemAuthorityWithTwoMetadataInOtherInformations( "will be generated::zdb::1194912-0", "Acta mathematica Universitatis Ostraviensis", "Acta mathematica Universitatis Ostraviensis", "vocabularyEntry", "data-dc_relation_ispartof", "Acta mathematica Universitatis Ostraviensis::will be generated::zdb::1194912-0", - "data-dc_relation_issn", "1211-4774"), + "data-dc_relation_issn", "1211-4774", getSource()), ItemAuthorityMatcher.matchItemAuthorityWithTwoMetadataInOtherInformations( "will be generated::zdb::2618143-5", "Acta mathematica Universitatis Ostraviensis", "Acta mathematica Universitatis Ostraviensis", "vocabularyEntry", "data-dc_relation_ispartof", "Acta mathematica Universitatis Ostraviensis::will be generated::zdb::2618143-5", - "data-dc_relation_issn", "")))) + "data-dc_relation_issn", "", getSource())))) .andExpect(jsonPath("$.page.totalElements", Matchers.is(3))); } @@ -67,4 +73,9 @@ public void zdbAuthorityUnauthorizedTest() throws Exception { .param("filter", "Mathematica AND informatica")) .andExpect(status().isUnauthorized()); } + + private String getSource() { + return configurationService.getProperty( + "cris.ItemAuthority.ZDBAuthority.source", ItemAuthority.DEFAULT); + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ItemAuthorityMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ItemAuthorityMatcher.java index f69591201b0..ed29e408135 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ItemAuthorityMatcher.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ItemAuthorityMatcher.java @@ -48,6 +48,20 @@ public static Matcher matchItemAuthorityWithOtherInformations(St ); } + public static Matcher matchItemAuthorityWithOtherInformations(String authority, String display, + String value, String type, String otherInfMetadata, String metadataValue, String source) { + return allOf( + hasJsonPath("$.authority", is(authority)), + hasJsonPath("$.display", is(display)), + hasJsonPath("$.value", is(value)), + hasJsonPath("$.type", is(type)), + hasJsonPath("$.source", is(source)), + hasJsonPath("$.otherInformation", aMapWithSize(2)), + hasJsonPath("$.otherInformation['" + otherInfMetadata + "']", is(metadataValue)), + hasJsonPath("$.otherInformation['" + "data-" + otherInfMetadata + "']", is(metadataValue)) + ); + } + public static Matcher matchItemAuthorityWithOtherInformations(String authority, String display, String value, String type, Map otherInformation) { return allOf( @@ -73,12 +87,13 @@ public static Matcher matchItemAuthorityWithOtherInformations(St public static Matcher matchItemAuthorityWithTwoMetadataInOtherInformations(String authority, String display, String value, String type, String firstOtherMetadata, String firstOtherValue, - String secondOtherMetadata, String secondOtherValue) { + String secondOtherMetadata, String secondOtherValue, String source) { return allOf( hasJsonPath("$.authority", is(authority)), hasJsonPath("$.display", is(display)), hasJsonPath("$.value", is(value)), hasJsonPath("$.type", is(type)), + hasJsonPath("$.source", is(source)), hasJsonPath("$.otherInformation", aMapWithSize(2)), allOf ( hasJsonPath("$.otherInformation." + firstOtherMetadata, is(firstOtherValue)), diff --git a/dspace/config/modules/authority.cfg b/dspace/config/modules/authority.cfg index 8d1598c10ec..4ae4c43f139 100644 --- a/dspace/config/modules/authority.cfg +++ b/dspace/config/modules/authority.cfg @@ -114,7 +114,14 @@ cris.ItemAuthority.EquipmentAuthority.entityType = Equipment #define source for each authority -#cris.ItemAuthority.AuthorAuthority.source = ORCID +cris.ItemAuthority.AuthorAuthority.source = orcid +cris.ItemAuthority.EditorAuthority.source = orcid +cris.ItemAuthority.AuthorStrictMatchAuthority.source = orcid +cris.ItemAuthority.AuthorCoarseMatchAuthority.source = orcid +cris.ItemAuthority.OrgUnitAuthority.source = ror +cris.ItemAuthority.FundingAuthority.source = openaire +cris.ItemAuthority.ZDBAuthority.source = zdb +cris.ItemAuthority.SherpaAuthority.source = sherpa ## OrcidAuthority Extras configuration # From 0e16cb5a3b07109c7d507ab63d76aa243759f92a Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 5 Feb 2024 17:33:31 +0100 Subject: [PATCH 59/82] [DSC-1526] Add test to check the bug --- .../org/dspace/app/rest/CorrectionStepIT.java | 98 ++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CorrectionStepIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CorrectionStepIT.java index 34665592823..685036833f0 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CorrectionStepIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CorrectionStepIT.java @@ -126,7 +126,8 @@ public void setup() throws Exception { .withEntityType("Publication") .withWorkflowGroup("editor", admin) .withSubmitterGroup(eperson) - .withSubmissionDefinition("traditional-with-correction") + .withSubmissionDefinition("traditional") + .withCorrectionSubmissionDefinition("traditional-with-correction") .build(); date = "2020-02-20"; @@ -274,6 +275,101 @@ public void checkCorrection() throws Exception { } + @Test + public void checkCorrectionWithDuplicates() throws Exception { + + String tokenSubmitter = getAuthToken(eperson.getEmail(), password); + + //create a correction item + getClient(tokenSubmitter).perform(post("/api/submission/workspaceitems") + .param("owningCollection", collection.getID().toString()) + .param("relationship", "isCorrectionOfItem") + .param("item", itemToBeCorrected.getID().toString()) + .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) + .andExpect(status().isCreated()) + .andDo(result -> workspaceItemIdRef.set(read(result.getResponse().getContentAsString(), "$.id"))); + + List relationshipList = relationshipService.findByItem(context, itemToBeCorrected); + assert relationshipList.size() > 0; + Item correctedItem = relationshipList.get(0).getLeftItem(); + WorkspaceItem newWorkspaceItem = workspaceItemService.findByItem(context,correctedItem); + + //make a change on the title + Map value = new HashMap(); + final String newDate = "2020-02-21"; + value.put("value", newDate); + List operations = new ArrayList(); + operations.add(new ReplaceOperation("/sections/traditionalpageone/dc.date.issued/0", value)); + String patchBody = getPatchContent(operations); + getClient(tokenSubmitter).perform(patch("/api/submission/workspaceitems/" + newWorkspaceItem.getID()) + .content(patchBody) + .contentType("application/json-patch+json")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.errors").doesNotExist()); + + final String newTitle = "New Title"; + value.put("value", newTitle); + operations = new ArrayList(); + operations.add(new ReplaceOperation("/sections/traditionalpageone/dc.title/0", value)); + patchBody = getPatchContent(operations); + getClient(tokenSubmitter).perform(patch("/api/submission/workspaceitems/" + newWorkspaceItem.getID()) + .content(patchBody) + .contentType("application/json-patch+json")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.errors").doesNotExist()); + + //remove subject + operations = new ArrayList(); + operations.add(new RemoveOperation("/sections/traditionalpagetwo/dc.subject/0")); + patchBody = getPatchContent(operations); + getClient(tokenSubmitter).perform(patch("/api/submission/workspaceitems/" + newWorkspaceItem.getID()) + .content(patchBody) + .contentType("application/json-patch+json")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.errors").doesNotExist()); + + //add an asbtract description + Map addValue = new HashMap(); + final String newDescription = "New Description"; + addValue.put("value", newDescription); + operations = new ArrayList(); + operations.add(new AddOperation("/sections/traditionalpagetwo/dc.description.abstract", List.of(addValue))); + patchBody = getPatchContent(operations); + getClient(tokenSubmitter).perform(patch("/api/submission/workspaceitems/" + newWorkspaceItem.getID()) + .content(patchBody) + .contentType("application/json-patch+json")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.errors").doesNotExist()); + + getClient(tokenSubmitter).perform(get("/api/submission/workspaceitems/" + newWorkspaceItem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.sections.correction.metadata").doesNotExist()); + + AtomicReference workflowItemIdRef = new AtomicReference(); + + getClient(tokenSubmitter).perform(post("/api/workflow/workflowitems") + .content("/api/submission/workspaceitems/" + newWorkspaceItem.getID()) + .contentType(textUriContentType)) + .andExpect(status().isCreated()) + .andDo(result -> workflowItemIdRef.set(read(result.getResponse().getContentAsString(), "$.id"))); + + String tokenAdmin = getAuthToken(admin.getEmail(), password); + + //check if the correction is present + final String extraEntry = "ExtraEntry"; + getClient(tokenAdmin).perform(get("/api/workflow/workflowitems/" + workflowItemIdRef.get())) + //The status has to be 200 OK + .andExpect(status().isOk()) + //The array of browse index should have a size equals to 4 + .andExpect(jsonPath("$.sections.correction.metadata", hasSize(equalTo(4)))) + .andExpect(jsonPath("$.sections.correction.empty", is(false))) + .andExpect(jsonPath("$.sections.correction.metadata",hasItem(matchMetadataCorrection(newTitle)))) + .andExpect(jsonPath("$.sections.correction.metadata",hasItem(matchMetadataCorrection(newDate)))) + .andExpect(jsonPath("$.sections.correction.metadata",hasItem(matchMetadataCorrection(newDescription)))) + .andExpect(jsonPath("$.sections.correction.metadata",hasItem(matchMetadataCorrection(extraEntry)))); + + } + @Test public void checkEmptyCorrection() throws Exception { String tokenSubmitter = getAuthToken(eperson.getEmail(), password); From 2740523fe89176d2819fd9606fa812fedc630de8 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Mon, 5 Feb 2024 18:47:19 +0100 Subject: [PATCH 60/82] Remove duplicate field introduced by automatic git-merge --- dspace/config/submission-forms.xml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/dspace/config/submission-forms.xml b/dspace/config/submission-forms.xml index e2c51d43b55..45137c1ab15 100644 --- a/dspace/config/submission-forms.xml +++ b/dspace/config/submission-forms.xml @@ -47,17 +47,6 @@ - - - bitstream - hide - - dropdown - false - - - -

From 90bda15fc62d82e7b6d0f8d635203dbf791c0126 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Tue, 6 Feb 2024 08:55:03 +0100 Subject: [PATCH 61/82] DSC-1458 fix test to consider virtual metadata --- .../service/BulkImportWorkbookBuilderIT.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/app/bulkimport/service/BulkImportWorkbookBuilderIT.java b/dspace-api/src/test/java/org/dspace/app/bulkimport/service/BulkImportWorkbookBuilderIT.java index 9de061db8f9..bef8ca45c09 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkimport/service/BulkImportWorkbookBuilderIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkimport/service/BulkImportWorkbookBuilderIT.java @@ -209,7 +209,7 @@ public void testWorkbookBuildingFromItemDtos() throws Exception { Item firstItem = getItemFromMessage(handler.getInfoMessages().get(7)); assertThat(firstItem, notNullValue()); - assertThat(firstItem.getMetadata(), hasSize(16)); + assertThat(firstItem.getMetadata(), hasSize(18)); assertThat(firstItem.getMetadata(), hasItems( with("dc.title", "Test Publication"), with("dc.date.issued", "2020/02/15"), @@ -219,7 +219,9 @@ public void testWorkbookBuildingFromItemDtos() throws Exception { with("dc.subject", "Java", 1), with("dc.subject", "DSpace", 2), with("dc.contributor.author", "White, Walter", authorId, 600), - with("oairecerif.author.affiliation", PLACEHOLDER_PARENT_METADATA_VALUE))); + with("oairecerif.author.affiliation", PLACEHOLDER_PARENT_METADATA_VALUE), + with("cris.virtual.department", PLACEHOLDER_PARENT_METADATA_VALUE), + with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); assertThat(getItemBitstreamsByBundle(firstItem, "ORIGINAL"), contains( bitstreamWith("Bitstream 1", "First bitstream content"), @@ -227,7 +229,7 @@ public void testWorkbookBuildingFromItemDtos() throws Exception { Item secondItem = getItemFromMessage(handler.getInfoMessages().get(10)); assertThat(secondItem, notNullValue()); - assertThat(secondItem.getMetadata(), hasSize(16)); + assertThat(secondItem.getMetadata(), hasSize(22)); assertThat(secondItem.getMetadata(), hasItems( with("dc.title", "Second Publication"), with("dc.date.issued", "2022/02/15"), @@ -237,7 +239,11 @@ public void testWorkbookBuildingFromItemDtos() throws Exception { with("dc.contributor.author", "Jesse Pinkman", jesse.getID().toString(), 600), with("dc.contributor.author", "Test User", testUser.getID().toString(), 1, 600), with("oairecerif.author.affiliation", PLACEHOLDER_PARENT_METADATA_VALUE), - with("oairecerif.author.affiliation", "Company", 1) + with("oairecerif.author.affiliation", "Company", 1), + with("cris.virtual.department", PLACEHOLDER_PARENT_METADATA_VALUE), + with("cris.virtual.department", PLACEHOLDER_PARENT_METADATA_VALUE), + with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE), + with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE) )); assertThat(getItemBitstreamsByBundle(secondItem, "ORIGINAL"), contains( From b86c0faccb63e50afb5017aa7b7aa27670deb2e2 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Tue, 6 Feb 2024 09:04:43 +0100 Subject: [PATCH 62/82] DSC-1458 fix test to consider virtual metadata --- .../test/java/org/dspace/harvest/OAIHarvesterIT.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/harvest/OAIHarvesterIT.java b/dspace-api/src/test/java/org/dspace/harvest/OAIHarvesterIT.java index c180b77dc26..b305ccc1806 100644 --- a/dspace-api/src/test/java/org/dspace/harvest/OAIHarvesterIT.java +++ b/dspace-api/src/test/java/org/dspace/harvest/OAIHarvesterIT.java @@ -769,7 +769,7 @@ public void testRunHarvestWithPublicationAndThenPerson() throws Exception { Item publication = publications.get(0); List values = publication.getMetadata(); - assertThat(values, hasSize(19)); + assertThat(values, hasSize(21)); assertThat(values, hasItems(with("dc.title", "Test Publication"))); assertThat(values, hasItems(with("dc.type", "Controlled Vocabulary for Resource Type Genres::text"))); @@ -780,6 +780,8 @@ public void testRunHarvestWithPublicationAndThenPerson() throws Exception { assertThat(values, hasItems(with("oaire.citation.endPage", "180"))); assertThat(values, hasItems(with("dc.identifier.doi", "10.1007/978-3-642-35233-1_18"))); assertThat(values, hasItems(with("oairecerif.author.affiliation", PLACEHOLDER_PARENT_METADATA_VALUE))); + assertThat(values, hasItems(with("cris.virtual.department", PLACEHOLDER_PARENT_METADATA_VALUE))); + assertThat(values, hasItems(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); assertThat(values, hasItems(with("cris.sourceId", "test-harvest::3"))); assertThat(values, hasItems(with("dspace.entity.type", "Publication"))); @@ -859,7 +861,7 @@ public void testRunHarvestWithPersonAndThenPublication() throws Exception { Item person = findItemByOaiID("oai:test-harvest:Persons/123", personCollection); List values = person.getMetadata(); - assertThat(values, hasSize(14)); + assertThat(values, hasSize(12)); assertThat(values, hasItems(with("dc.title", "Manghi, Paolo"))); assertThat(values, hasItems(with("cris.sourceId", "test-harvest::123"))); assertThat(values, hasItems(with("dspace.entity.type", "Person"))); @@ -872,7 +874,7 @@ public void testRunHarvestWithPersonAndThenPublication() throws Exception { Item publication = findItemByOaiID("oai:test-harvest:Publications/3", collection); values = publication.getMetadata(); - assertThat(values, hasSize(19)); + assertThat(values, hasSize(21)); assertThat(values, hasItems(with("dc.title", "Test Publication"))); assertThat(values, hasItems(with("dc.type", "Controlled Vocabulary for Resource Type Genres::text"))); @@ -885,6 +887,8 @@ public void testRunHarvestWithPersonAndThenPublication() throws Exception { assertThat(values, hasItems(with("oairecerif.author.affiliation", PLACEHOLDER_PARENT_METADATA_VALUE))); assertThat(values, hasItems(with("cris.sourceId", "test-harvest::3"))); assertThat(values, hasItems(with("dspace.entity.type", "Publication"))); + assertThat(values, hasItems(with("cris.virtual.department", PLACEHOLDER_PARENT_METADATA_VALUE))); + assertThat(values, hasItems(with("cris.virtualsource.department", UUIDUtils.toString(person.getID())))); assertThat(values, hasItems(with("cris.virtual.author-orcid", "0000-0002-9079-5932"))); assertThat(values, hasItems(with("cris.virtualsource.author-orcid", UUIDUtils.toString(person.getID())))); From 62309bab1fb9164c051b659fcbf1b6f5b144a578 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Tue, 6 Feb 2024 09:45:10 +0100 Subject: [PATCH 63/82] fix obvious typo --- dspace/config/spring/api/csl-citation.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace/config/spring/api/csl-citation.xml b/dspace/config/spring/api/csl-citation.xml index 47186c214ca..78bf43835f0 100644 --- a/dspace/config/spring/api/csl-citation.xml +++ b/dspace/config/spring/api/csl-citation.xml @@ -8,7 +8,7 @@ - + From 20c67d4289faa7f9c87634271918a5358a38376d Mon Sep 17 00:00:00 2001 From: Mattia Vianelli Date: Tue, 6 Feb 2024 09:50:39 +0100 Subject: [PATCH 64/82] DSC-1526 We now have a check for which if the workspace item is a correction request we check if it is submission correction or a simple submission --- .../app/util/SubmissionConfigReader.java | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java index 2f591b6e7a8..02d7429b411 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java @@ -30,7 +30,11 @@ import org.dspace.core.Context; import org.dspace.discovery.SearchServiceException; import org.dspace.handle.factory.HandleServiceFactory; +import org.dspace.services.RequestService; import org.dspace.services.factory.DSpaceServicesFactory; +import org.dspace.versioning.ItemCorrectionService; +import org.dspace.web.ContextUtil; +import org.springframework.beans.factory.annotation.Autowired; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; @@ -58,6 +62,13 @@ */ public class SubmissionConfigReader { + + @Autowired + private ItemCorrectionService itemCorrectionService; + + @Autowired + RequestService requestService; + /** * The ID of the default collection. Will never be the ID of a named * collection @@ -430,6 +441,21 @@ private void doNodes(Node n) throws SAXException, SearchServiceException, Submis } } + + private boolean isCorrectionItem(Item item) { + Context context = ContextUtil.obtainCurrentRequestContext(); + ItemCorrectionService itemCorrectionService = + DSpaceServicesFactory.getInstance().getServiceManager() + .getServicesByType(ItemCorrectionService.class) + .get(0); + try { + return itemCorrectionService.checkIfIsCorrectionItem(context, item); + } catch (Exception ex) { + log.error("An error occurs checking if the given item is a correction item.", ex); + return false; + } + } + /** * Process the submission-map section of the XML file. Each element looks * like: Extract @@ -764,6 +790,13 @@ public SubmissionConfig getSubmissionConfigByInProgressSubmission(InProgressSubm return getSubmissionConfigByName(submissionDefinition); } - return getSubmissionConfigByCollection(object.getCollection()); + if (isCorrectionItem(object.getItem())) { + return getCorrectionSubmissionConfigByCollection(object.getCollection()); + } else { + return getSubmissionConfigByCollection(object.getCollection()); + } + } + + } \ No newline at end of file From 786ca86252b7e0fff92918ad0587455dd0b305ef Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Tue, 6 Feb 2024 20:04:25 +0100 Subject: [PATCH 65/82] DSC-1458 fix IT failures due to detached entity --- .../dspace/app/rest/layout/CrisLayoutTabRestRepositoryIT.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/layout/CrisLayoutTabRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/layout/CrisLayoutTabRestRepositoryIT.java index aaa3fb1c62b..48516a840da 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/layout/CrisLayoutTabRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/layout/CrisLayoutTabRestRepositoryIT.java @@ -2146,6 +2146,8 @@ public void findByItemTabsWithHiddenRelationshipsTest() throws Exception { context.turnOffAuthorisationSystem(); + // reload the collection as we need to create an additional item in it + col1 = context.reloadEntity(col1); Item publication1 = ItemBuilder.createItem(context, col1) .withTitle("Title Of Item") From 482584827bca6ec97ea36420bd7ed14c1fc9da5f Mon Sep 17 00:00:00 2001 From: Mattia Vianelli Date: Wed, 7 Feb 2024 10:15:25 +0100 Subject: [PATCH 66/82] DSC-1526 Improved code and fixed checkstyle --- .../app/util/SubmissionConfigReader.java | 42 +++++++++---------- .../service/impl/ValidationServiceImpl.java | 3 +- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java index 02d7429b411..57e6a3fafce 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java @@ -33,7 +33,6 @@ import org.dspace.services.RequestService; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.versioning.ItemCorrectionService; -import org.dspace.web.ContextUtil; import org.springframework.beans.factory.annotation.Autowired; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; @@ -63,9 +62,6 @@ public class SubmissionConfigReader { - @Autowired - private ItemCorrectionService itemCorrectionService; - @Autowired RequestService requestService; @@ -129,6 +125,16 @@ public class SubmissionConfigReader { protected static final CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService(); + /** + * itemCorrectionService instance, needed to retrieve the handle correctly + * item correction actions + * + */ + protected static final ItemCorrectionService itemCorrectionService = + DSpaceServicesFactory.getInstance().getServiceManager() + .getServicesByType(ItemCorrectionService.class) + .get(0); + /** * Load Submission Configuration from the * item-submission.xml configuration file @@ -442,19 +448,7 @@ private void doNodes(Node n) throws SAXException, SearchServiceException, Submis } - private boolean isCorrectionItem(Item item) { - Context context = ContextUtil.obtainCurrentRequestContext(); - ItemCorrectionService itemCorrectionService = - DSpaceServicesFactory.getInstance().getServiceManager() - .getServicesByType(ItemCorrectionService.class) - .get(0); - try { - return itemCorrectionService.checkIfIsCorrectionItem(context, item); - } catch (Exception ex) { - log.error("An error occurs checking if the given item is a correction item.", ex); - return false; - } - } + /** * Process the submission-map section of the XML file. Each element looks @@ -784,19 +778,25 @@ public List getCollectionsBySubmissionConfig(Context context, String return results; } - public SubmissionConfig getSubmissionConfigByInProgressSubmission(InProgressSubmission object) { + public SubmissionConfig getSubmissionConfigByInProgressSubmission(InProgressSubmission object, Context context) { if (object instanceof EditItem) { String submissionDefinition = ((EditItem) object).getMode().getSubmissionDefinition(); return getSubmissionConfigByName(submissionDefinition); } - if (isCorrectionItem(object.getItem())) { + if (isCorrectionItem(object.getItem(), context)) { return getCorrectionSubmissionConfigByCollection(object.getCollection()); } else { return getSubmissionConfigByCollection(object.getCollection()); } - } - + private boolean isCorrectionItem(Item item, Context context) { + try { + return itemCorrectionService.checkIfIsCorrectionItem(context, item); + } catch (Exception ex) { + log.error("An error occurs checking if the given item is a correction item.", ex); + return false; + } + } } \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/validation/service/impl/ValidationServiceImpl.java b/dspace-api/src/main/java/org/dspace/validation/service/impl/ValidationServiceImpl.java index b4c9b4bc4c1..65bd0bf1945 100644 --- a/dspace-api/src/main/java/org/dspace/validation/service/impl/ValidationServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/validation/service/impl/ValidationServiceImpl.java @@ -55,7 +55,8 @@ private void setup() throws SubmissionConfigReaderException { @Override public List validate(Context context, InProgressSubmission obj) { - SubmissionConfig submissionConfig = submissionConfigReader.getSubmissionConfigByInProgressSubmission(obj); + SubmissionConfig submissionConfig = submissionConfigReader + .getSubmissionConfigByInProgressSubmission(obj, context); List errors = new ArrayList(); From cec6d1a1518c841d9d3b286cc64c1d8f60f797e1 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Wed, 7 Feb 2024 19:20:52 +0100 Subject: [PATCH 67/82] Revert fix of the abstrct typo as it wasn't --- dspace/config/spring/api/csl-citation.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace/config/spring/api/csl-citation.xml b/dspace/config/spring/api/csl-citation.xml index 78bf43835f0..ce98c5a30f0 100644 --- a/dspace/config/spring/api/csl-citation.xml +++ b/dspace/config/spring/api/csl-citation.xml @@ -8,7 +8,9 @@ - + + From 98f58abe40c2bfc2cc2b7f7d2ef25e7c645a9d5c Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 11 Jan 2024 11:01:34 +0100 Subject: [PATCH 68/82] [DQ-26] Adds data-quality addon configuration --- .../service/impl/SolrDedupServiceImpl.java | 8 +- .../app/deduplication/utils/DedupUtils.java | 61 +++++---- .../utils/DuplicateInfoList.java | 34 ----- .../app/deduplication/utils/IDedupUtils.java | 79 +++++++++++ .../service/DeduplicationService.java | 11 +- .../DetectPotentialDuplicateValidator.java | 4 +- .../entities/merge-relationship-types.xml | 126 ++++++++++++++++++ dspace-server-webapp/pom.xml | 17 +++ .../DetectDuplicateAddPatchOperation.java | 12 +- .../step/DetectPotentialDuplicateStep.java | 8 +- dspace/config/dspace.cfg | 10 ++ .../entities/merge-relationship-types.xml | 126 ++++++++++++++++++ dspace/config/modules/authority.cfg | 11 ++ dspace/config/modules/rest.cfg | 4 + dspace/config/registries/dspace-types.xml | 8 ++ dspace/modules/additions/pom.xml | 49 ++++--- pom.xml | 21 +++ 17 files changed, 495 insertions(+), 94 deletions(-) delete mode 100644 dspace-api/src/main/java/org/dspace/app/deduplication/utils/DuplicateInfoList.java create mode 100644 dspace-api/src/main/java/org/dspace/app/deduplication/utils/IDedupUtils.java create mode 100644 dspace-api/src/test/data/dspaceFolder/config/entities/merge-relationship-types.xml create mode 100644 dspace/config/entities/merge-relationship-types.xml diff --git a/dspace-api/src/main/java/org/dspace/app/deduplication/service/impl/SolrDedupServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/deduplication/service/impl/SolrDedupServiceImpl.java index cc5c0f2bc86..e12f1100be1 100644 --- a/dspace-api/src/main/java/org/dspace/app/deduplication/service/impl/SolrDedupServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/deduplication/service/impl/SolrDedupServiceImpl.java @@ -43,8 +43,8 @@ import org.dspace.app.deduplication.service.DedupService; import org.dspace.app.deduplication.service.SearchDeduplication; import org.dspace.app.deduplication.service.SolrDedupServiceIndexPlugin; -import org.dspace.app.deduplication.utils.DedupUtils; import org.dspace.app.deduplication.utils.DuplicateItemInfo; +import org.dspace.app.deduplication.utils.IDedupUtils; import org.dspace.app.deduplication.utils.Signature; import org.dspace.app.util.Util; import org.dspace.authorize.AuthorizeException; @@ -174,7 +174,7 @@ public class SolrDedupServiceImpl implements DedupService { protected VersioningService versioningService; @Autowired(required = true) - protected DedupUtils dedupUtils; + protected IDedupUtils dedupUtils; /*** * Deduplication status @@ -750,8 +750,8 @@ private void setDuplicateDecision(Context context, Item item, UUID duplicatedIte private List findDuplicationWithDecisions(Context context, Item item) { try { return dedupUtils.getAdminDuplicateByIdAndType(context, item.getID(), item.getType()).stream() - .filter(duplication -> isNotEmpty(duplication.getDecisionTypes())) - .collect(Collectors.toList()); + .filter(duplication -> isNotEmpty(duplication.getDecisionTypes())) + .collect(Collectors.toList()); } catch (SQLException | SearchServiceException e) { throw new RuntimeException(e); } diff --git a/dspace-api/src/main/java/org/dspace/app/deduplication/utils/DedupUtils.java b/dspace-api/src/main/java/org/dspace/app/deduplication/utils/DedupUtils.java index 97bf4a33465..b4c29d8780c 100644 --- a/dspace-api/src/main/java/org/dspace/app/deduplication/utils/DedupUtils.java +++ b/dspace-api/src/main/java/org/dspace/app/deduplication/utils/DedupUtils.java @@ -47,12 +47,15 @@ import org.dspace.services.ConfigurationService; import org.dspace.util.ItemUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; /** * Utility class used to search for duplicates inside the dedup solr core. * */ -public class DedupUtils { + +@Service +public class DedupUtils implements IDedupUtils { private static Logger log = LogManager.getLogger(DedupUtils.class); @@ -64,11 +67,14 @@ public class DedupUtils { @Autowired(required = true) protected ConfigurationService configurationService; - public DuplicateInfoList findSignatureWithDuplicate(Context context, String signatureType, int resourceType, - int limit, int offset, int rule) throws SearchServiceException, SQLException { + @Override + public Collection findSignatureWithDuplicate(Context context, String signatureType, int resourceType, + int limit, int offset, int rule) + throws SearchServiceException, SQLException { return findPotentialMatch(context, signatureType, resourceType, limit, offset, rule); } + @Override public Map countSignaturesWithDuplicates(String query, int resourceTypeId) throws SearchServiceException { Map results = new HashMap(); @@ -113,6 +119,7 @@ public Map countSignaturesWithDuplicates(String query, int reso return results; } + @Override public Map countSuggestedDuplicate(String query, int resourceTypeId) throws SearchServiceException { Map results = new HashMap(); @@ -241,8 +248,9 @@ private boolean hasStoredDecision(UUID firstItemID, UUID secondItemID, Duplicate return !response.getResults().isEmpty(); } + @Override public boolean matchExist(Context context, UUID itemID, UUID targetItemID, Integer resourceType, - String signatureType, Boolean isInWorkflow) throws SQLException, SearchServiceException { + String signatureType, Boolean isInWorkflow) throws SQLException, SearchServiceException { boolean exist = false; List potentialDuplicates = findDuplicate(context, itemID, resourceType, null, isInWorkflow); for (DuplicateItemInfo match : potentialDuplicates) { @@ -256,6 +264,7 @@ public boolean matchExist(Context context, UUID itemID, UUID targetItemID, Integ } + @Override public boolean rejectAdminDups(Context context, UUID firstId, UUID secondId, Integer type) throws SQLException, AuthorizeException { if (firstId == secondId) { @@ -309,6 +318,7 @@ public boolean rejectAdminDups(Context context, UUID firstId, UUID secondId, Int * @throws AuthorizeException * @throws SearchServiceException */ + @Override public boolean rejectAdminDups(Context context, UUID itemID, String signatureType, int resourceType) throws SQLException, AuthorizeException, SearchServiceException { @@ -336,6 +346,7 @@ public boolean rejectAdminDups(Context context, UUID itemID, String signatureTyp } + @Override public void rejectAdminDups(Context context, List items, String signatureID) throws SQLException, AuthorizeException, SearchServiceException { for (DSpaceObject item : items) { @@ -343,8 +354,9 @@ public void rejectAdminDups(Context context, List items, String si } } + @Override public void verify(Context context, int dedupId, UUID firstId, UUID secondId, int type, boolean toFix, String note, - boolean check) throws SQLException, AuthorizeException { + boolean check) throws SQLException, AuthorizeException { UUID[] sortedIds = new UUID[] { firstId, secondId }; Arrays.sort(sortedIds); firstId = sortedIds[0]; @@ -417,8 +429,9 @@ private Deduplication retrieveDuplicationRow(Context context, UUID firstId, UUID return row; } + @Override public void setDuplicateDecision(Context context, UUID firstId, UUID secondId, Integer type, - DuplicateDecisionObjectRest decisionObject) + DuplicateDecisionObjectRest decisionObject) throws AuthorizeException, SQLException, SearchServiceException { if (hasAuthorization(context, firstId, secondId)) { @@ -478,6 +491,7 @@ public void setDuplicateDecision(Context context, UUID firstId, UUID secondId, I } } + @Override public boolean validateDecision(DuplicateDecisionObjectRest decisionObject) { boolean valid = false; @@ -500,8 +514,9 @@ public boolean validateDecision(DuplicateDecisionObjectRest decisionObject) { return valid; } + @Override public boolean rejectDups(Context context, UUID firstId, UUID secondId, Integer type, boolean notDupl, String note, - boolean check) throws SQLException { + boolean check) throws SQLException { UUID[] sortedIds = new UUID[] { firstId, secondId }; Arrays.sort(sortedIds); Deduplication row = null; @@ -547,11 +562,9 @@ public boolean rejectDups(Context context, UUID firstId, UUID secondId, Integer return false; } - private DuplicateInfoList findPotentialMatch(Context context, String signatureType, int resourceType, int start, + private List findPotentialMatch(Context context, String signatureType, int resourceType, int start, int rows, int rule) throws SearchServiceException, SQLException { - DuplicateInfoList dil = new DuplicateInfoList(); - if (StringUtils.isNotEmpty(signatureType)) { if (!StringUtils.contains(signatureType, "_signature")) { signatureType += "_signature"; @@ -594,7 +607,7 @@ private DuplicateInfoList findPotentialMatch(Context context, String signatureTy FacetField facetField = responseFacet.getFacetField(signatureType); - List result = new ArrayList(); + List result = new ArrayList<>(); int index = 0; for (Count facetHit : facetField.getValues()) { @@ -653,10 +666,7 @@ private DuplicateInfoList findPotentialMatch(Context context, String signatureTy } index++; } - - dil.setDsi(result); - dil.setSize(facetField.getValues().size()); - return dil; + return result; } private DuplicateSignatureInfo findPotentialMatchByID(Context context, String signatureType, int resourceType, @@ -699,38 +709,45 @@ private DuplicateSignatureInfo findPotentialMatchByID(Context context, String si return dsi; } + @Override public DedupService getDedupService() { return dedupService; } + @Override public void setDedupService(DedupService dedupService) { this.dedupService = dedupService; } + @Override public void commit() { dedupService.commit(); } + @Override public List getDuplicateByIDandType(Context context, UUID itemID, int typeID, - boolean isInWorkflow) throws SQLException, SearchServiceException { + boolean isInWorkflow) + throws SQLException, SearchServiceException { return getDuplicateByIdAndTypeAndSignatureType(context, itemID, typeID, null, isInWorkflow); } + @Override public List getDuplicateByIdAndTypeAndSignatureType(Context context, UUID itemID, int typeID, - String signatureType, boolean isInWorkflow) throws SQLException, SearchServiceException { + String signatureType, boolean isInWorkflow) + throws SQLException, SearchServiceException { return findDuplicate(context, itemID, typeID, signatureType, isInWorkflow); } + @Override public List getAdminDuplicateByIdAndType(Context context, UUID itemID, int typeID) throws SQLException, SearchServiceException { return findDuplicate(context, itemID, typeID, null, null); } - public DuplicateInfoList findSuggestedDuplicate(Context context, int resourceType, int start, int rows) + @Override + public List findSuggestedDuplicate(Context context, int resourceType, int start, int rows) throws SearchServiceException, SQLException { - DuplicateInfoList dil = new DuplicateInfoList(); - SolrQuery solrQueryInternal = new SolrQuery(); solrQueryInternal.setQuery(SolrDedupServiceImpl.SUBQUERY_NOT_IN_REJECTED); @@ -774,8 +791,6 @@ public DuplicateInfoList findSuggestedDuplicate(Context context, int resourceTyp index++; } - dil.setDsi(result); - dil.setSize(solrDocumentList.getNumFound()); - return dil; + return result; } } diff --git a/dspace-api/src/main/java/org/dspace/app/deduplication/utils/DuplicateInfoList.java b/dspace-api/src/main/java/org/dspace/app/deduplication/utils/DuplicateInfoList.java deleted file mode 100644 index 3935944ffa7..00000000000 --- a/dspace-api/src/main/java/org/dspace/app/deduplication/utils/DuplicateInfoList.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.deduplication.utils; - -import java.util.List; - -public class DuplicateInfoList { - - private long size; - - private List dsi; - - public long getSize() { - return size; - } - - public void setSize(long size) { - this.size = size; - } - - public List getDsi() { - return dsi; - } - - public void setDsi(List dsi) { - this.dsi = dsi; - } - -} \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/app/deduplication/utils/IDedupUtils.java b/dspace-api/src/main/java/org/dspace/app/deduplication/utils/IDedupUtils.java new file mode 100644 index 00000000000..774735eaac3 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/deduplication/utils/IDedupUtils.java @@ -0,0 +1,79 @@ +/** + * 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.deduplication.utils; + +import java.sql.SQLException; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.dspace.app.deduplication.model.DuplicateDecisionObjectRest; +import org.dspace.app.deduplication.service.DedupService; +import org.dspace.authorize.AuthorizeException; +import org.dspace.content.DSpaceObject; +import org.dspace.core.Context; +import org.dspace.discovery.SearchServiceException; + +/** + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public interface IDedupUtils { + Collection findSignatureWithDuplicate(Context context, String signatureType, int resourceType, + int limit, int offset, int rule) throws SearchServiceException, + SQLException; + + Map countSignaturesWithDuplicates(String query, int resourceTypeId) + throws SearchServiceException; + + Map countSuggestedDuplicate(String query, int resourceTypeId) + throws SearchServiceException; + + boolean matchExist(Context context, UUID itemID, UUID targetItemID, Integer resourceType, + String signatureType, Boolean isInWorkflow) throws SQLException, SearchServiceException; + + boolean rejectAdminDups(Context context, UUID firstId, UUID secondId, Integer type) + throws SQLException, AuthorizeException; + + boolean rejectAdminDups(Context context, UUID itemID, String signatureType, int resourceType) + throws SQLException, AuthorizeException, SearchServiceException; + + void rejectAdminDups(Context context, List items, String signatureID) + throws SQLException, AuthorizeException, SearchServiceException; + + void verify(Context context, int dedupId, UUID firstId, UUID secondId, int type, boolean toFix, String note, + boolean check) throws SQLException, AuthorizeException; + + void setDuplicateDecision(Context context, UUID firstId, UUID secondId, Integer type, + DuplicateDecisionObjectRest decisionObject) + throws AuthorizeException, SQLException, SearchServiceException; + + boolean validateDecision(DuplicateDecisionObjectRest decisionObject); + + boolean rejectDups(Context context, UUID firstId, UUID secondId, Integer type, boolean notDupl, String note, + boolean check) throws SQLException; + + DedupService getDedupService(); + + void setDedupService(DedupService dedupService); + + void commit(); + + List getDuplicateByIDandType(Context context, UUID itemID, int typeID, + boolean isInWorkflow) throws SQLException, SearchServiceException; + + List getDuplicateByIdAndTypeAndSignatureType(Context context, UUID itemID, int typeID, + String signatureType, boolean isInWorkflow) + throws SQLException, SearchServiceException; + + List getAdminDuplicateByIdAndType(Context context, UUID itemID, int typeID) + throws SQLException, SearchServiceException; + + Collection findSuggestedDuplicate(Context context, int resourceType, int start, int rows) + throws SearchServiceException, SQLException; +} diff --git a/dspace-api/src/main/java/org/dspace/deduplication/service/DeduplicationService.java b/dspace-api/src/main/java/org/dspace/deduplication/service/DeduplicationService.java index ab36dc46b4c..b2826998ccc 100644 --- a/dspace-api/src/main/java/org/dspace/deduplication/service/DeduplicationService.java +++ b/dspace-api/src/main/java/org/dspace/deduplication/service/DeduplicationService.java @@ -15,6 +15,7 @@ import org.dspace.deduplication.Deduplication; public interface DeduplicationService { + /** * Create a new Deduplication object * @@ -23,7 +24,7 @@ public interface DeduplicationService { * @throws SQLException An exception that provides information on a database * access error or other errors. */ - public Deduplication create(Context context, Deduplication dedup) throws SQLException; + Deduplication create(Context context, Deduplication dedup) throws SQLException; /*** * Return all deduplication objects @@ -35,7 +36,7 @@ public interface DeduplicationService { * @throws SQLException An exception that provides information on a database * access error or other errors. */ - public List findAll(Context context, int pageSize, int offset) throws SQLException; + List findAll(Context context, int pageSize, int offset) throws SQLException; /** * Count all accounts. @@ -55,11 +56,11 @@ public interface DeduplicationService { * @throws SQLException An exception that provides information on a database * access error or other errors. */ - public void update(Context context, Deduplication dedup) throws SQLException; + void update(Context context, Deduplication dedup) throws SQLException; - public List getDeduplicationByFirstAndSecond(Context context, UUID firstId, UUID secondId) + List getDeduplicationByFirstAndSecond(Context context, UUID firstId, UUID secondId) throws SQLException; - public Deduplication uniqueDeduplicationByFirstAndSecond(Context context, UUID firstId, UUID secondId) + Deduplication uniqueDeduplicationByFirstAndSecond(Context context, UUID firstId, UUID secondId) throws SQLException; } diff --git a/dspace-api/src/main/java/org/dspace/validation/DetectPotentialDuplicateValidator.java b/dspace-api/src/main/java/org/dspace/validation/DetectPotentialDuplicateValidator.java index 8a9a7aba10b..4b4e237a377 100644 --- a/dspace-api/src/main/java/org/dspace/validation/DetectPotentialDuplicateValidator.java +++ b/dspace-api/src/main/java/org/dspace/validation/DetectPotentialDuplicateValidator.java @@ -21,8 +21,8 @@ import org.dspace.app.deduplication.model.DuplicateDecisionType; import org.dspace.app.deduplication.model.DuplicateDecisionValue; -import org.dspace.app.deduplication.utils.DedupUtils; import org.dspace.app.deduplication.utils.DuplicateItemInfo; +import org.dspace.app.deduplication.utils.IDedupUtils; import org.dspace.app.util.SubmissionStepConfig; import org.dspace.content.DSpaceObject; import org.dspace.content.InProgressSubmission; @@ -47,7 +47,7 @@ public class DetectPotentialDuplicateValidator implements SubmissionStepValidato private static final String ERROR_VALIDATION_DUPLICATION = "error.validation.detect-duplicate"; @Autowired - private DedupUtils dedupUtils; + private IDedupUtils dedupUtils; @Autowired private ItemService itemService; diff --git a/dspace-api/src/test/data/dspaceFolder/config/entities/merge-relationship-types.xml b/dspace-api/src/test/data/dspaceFolder/config/entities/merge-relationship-types.xml new file mode 100644 index 00000000000..8db94731954 --- /dev/null +++ b/dspace-api/src/test/data/dspaceFolder/config/entities/merge-relationship-types.xml @@ -0,0 +1,126 @@ + + + + + + Project + Project + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + Person + Person + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + Funding + Funding + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + OrgUnit + OrgUnit + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + Journal + Journal + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + Publication + Publication + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + Product + Product + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + Patent + Patent + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + Event + Event + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + Equipment + Equipment + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + \ No newline at end of file diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 0a0b394d576..c83aa538698 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -350,6 +350,23 @@ + + + addon-dataquality + + + dq.on + + + + + it.4science.dspace + addon-dataquality + ${addon-dataquality.version} + jar + + + diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/DetectDuplicateAddPatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/DetectDuplicateAddPatchOperation.java index 4561a8a9c80..819bba0c142 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/DetectDuplicateAddPatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/DetectDuplicateAddPatchOperation.java @@ -12,7 +12,7 @@ import org.dspace.app.deduplication.model.DuplicateDecisionObjectRest; import org.dspace.app.deduplication.model.DuplicateDecisionType; -import org.dspace.app.deduplication.utils.DedupUtils; +import org.dspace.app.deduplication.utils.IDedupUtils; import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.patch.LateObjectEvaluator; import org.dspace.content.InProgressSubmission; @@ -43,7 +43,7 @@ void add(Context context, HttpServletRequest currentRequest, InProgressSubmissio String.format("The specified path '%s' is not valid", getAbsolutePath(path))); } - DedupUtils dedupUtils = new DSpace().getServiceManager().getServiceByName("dedupUtils", DedupUtils.class); + IDedupUtils IDedupUtils = new DSpace().getServiceManager().getServiceByName("dedupUtils", IDedupUtils.class); DuplicateDecisionObjectRest decisionObject = evaluateSingleObject((LateObjectEvaluator) value); UUID currentItemID = source.getItem().getID(); @@ -98,7 +98,7 @@ void add(Context context, HttpServletRequest currentRequest, InProgressSubmissio // generate UnprocessableEntityException if decisionObject is invalid try { - if (!dedupUtils.validateDecision(decisionObject)) { + if (!IDedupUtils.validateDecision(decisionObject)) { throw new UnprocessableEntityException( String.format("The specified decision %s is not valid", decisionObject.getValue())); } @@ -106,13 +106,13 @@ void add(Context context, HttpServletRequest currentRequest, InProgressSubmissio throw new UnprocessableEntityException(String.format("The specified decision %s is not valid", subPath)); } - if (!dedupUtils.matchExist(context, currentItemID, duplicateItemID, resourceType, null, isInWorkflow)) { + if (!IDedupUtils.matchExist(context, currentItemID, duplicateItemID, resourceType, null, isInWorkflow)) { throw new UnprocessableEntityException( String.format("Cannot find any duplicate match related to Item %s", duplicateItemID)); } - dedupUtils.setDuplicateDecision(context, source.getItem().getID(), duplicateItemID, source.getItem().getType(), - decisionObject); + IDedupUtils.setDuplicateDecision(context, source.getItem().getID(), duplicateItemID, source.getItem().getType(), + decisionObject); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/DetectPotentialDuplicateStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/DetectPotentialDuplicateStep.java index d7ad62153bc..90f72afe7f0 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/DetectPotentialDuplicateStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/DetectPotentialDuplicateStep.java @@ -15,8 +15,8 @@ import javax.servlet.http.HttpServletRequest; import org.dspace.app.deduplication.model.DuplicateDecisionType; -import org.dspace.app.deduplication.utils.DedupUtils; import org.dspace.app.deduplication.utils.DuplicateItemInfo; +import org.dspace.app.deduplication.utils.IDedupUtils; import org.dspace.app.rest.converter.factory.ConverterServiceFactoryImpl; import org.dspace.app.rest.model.MetadataValueRest; import org.dspace.app.rest.model.patch.Operation; @@ -54,14 +54,14 @@ public class DetectPotentialDuplicateStep extends AbstractProcessingStep { public DataDetectDuplicate getData(SubmissionService submissionService, InProgressSubmission obj, SubmissionStepConfig config) throws Exception { - DedupUtils dedupUtils = new DSpace().getServiceManager().getServiceByName("dedupUtils", DedupUtils.class); + IDedupUtils IDedupUtils = new DSpace().getServiceManager().getServiceByName("dedupUtils", IDedupUtils.class); UUID itemID = obj.getItem().getID(); int typeID = obj.getItem().getType(); boolean check = !(obj instanceof WorkspaceItem); - List potentialDuplicates = dedupUtils.getDuplicateByIDandType(getContext(), itemID, typeID, - check); + List potentialDuplicates = IDedupUtils.getDuplicateByIDandType(getContext(), itemID, typeID, + check); Map matches = processPotentialDuplicates(itemID, check, potentialDuplicates); DataDetectDuplicate result = new DataDetectDuplicate(); diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 05ebdd66067..477d14e207e 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1926,6 +1926,16 @@ bulk-export.limit.notLoggedIn = 0 # has 2 threads on which schedule events system-event.thread.size = 2 +#------------------------------------------------------------------# +#------------DEDUPLICATION / DATAQUALITY CONFIGURATIONS------------# +#------------------------------------------------------------------# +# # +# Configurations for the Deduplication / DataQuality features # +# # +#------------------------------------------------------------------# +# metadata here listed will be excluded by merge tool logic +merge.excluded-metadata = dc.description.provenance + # Load default module configs # ---------------------------- # To exclude a module configuration, simply comment out its "include" statement. diff --git a/dspace/config/entities/merge-relationship-types.xml b/dspace/config/entities/merge-relationship-types.xml new file mode 100644 index 00000000000..8db94731954 --- /dev/null +++ b/dspace/config/entities/merge-relationship-types.xml @@ -0,0 +1,126 @@ + + + + + + Project + Project + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + Person + Person + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + Funding + Funding + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + OrgUnit + OrgUnit + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + Journal + Journal + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + Publication + Publication + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + Product + Product + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + Patent + Patent + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + Event + Event + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + Equipment + Equipment + isMergedFromItem + isMergedInItem + + 0 + + + 0 + + + + \ No newline at end of file diff --git a/dspace/config/modules/authority.cfg b/dspace/config/modules/authority.cfg index 037cf01e5d7..ecc9e858df8 100644 --- a/dspace/config/modules/authority.cfg +++ b/dspace/config/modules/authority.cfg @@ -280,3 +280,14 @@ choices.plugin.dc.type = ControlledVocabularyAuthority # DSpace-CRIS stores by default the authority of controlled vocabularies vocabulary.plugin.authority.store = true + +#------------------------------------------------------------------# +#------------DEDUPLICATION / DATAQUALITY CONFIGURATIONS------------# +#------------------------------------------------------------------# +org.dspace.content.authority.ItemAuthority = OrgUnitAuthority +# AuthorStrictMatchAuthority configuration +cris.ItemAuthority.AuthorStrictMatchAuthority.forceInternalName = false +# AuthorCoarseMatchAuthority configuration +cris.ItemAuthority.AuthorCoarseMatchAuthority.forceInternalName = false +choices.plugin.green.override.dc.contributor.author = AuthorStrictMatchAuthority +choices.plugin.orange.override.dc.contributor.author = AuthorCoarseMatchAuthority \ No newline at end of file diff --git a/dspace/config/modules/rest.cfg b/dspace/config/modules/rest.cfg index ab28cd13cc2..faf7b248046 100644 --- a/dspace/config/modules/rest.cfg +++ b/dspace/config/modules/rest.cfg @@ -95,6 +95,10 @@ rest.properties.exposed = identifiers.item-status.register-doi rest.properties.exposed = authentication-password.domain.valid rest.properties.exposed = request.item.type rest.properties.exposed = handle.canonical.prefix +#------------------------------------------------------------------# +#------------DEDUPLICATION / DATAQUALITY CONFIGURATIONS------------# +#------------------------------------------------------------------# +rest.properties.exposed = merge.excluded-metadata #---------------------------------------------------------------# # These configs are used by the deprecated REST (v4-6) module # diff --git a/dspace/config/registries/dspace-types.xml b/dspace/config/registries/dspace-types.xml index 861dc67a816..2cc899dd0cd 100644 --- a/dspace/config/registries/dspace-types.xml +++ b/dspace/config/registries/dspace-types.xml @@ -122,4 +122,12 @@ + + + dspace + merge + target-uri + stores the value of uri of target item + + diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index 7de65e9ca49..f5ae804f8b8 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -249,22 +249,39 @@ - - addon-analytics - - - analytics.on - - - - - it.4science.dspace - addon-analytics-api - ${addon-analytics.version} - jar - - - + + addon-analytics + + + analytics.on + + + + + it.4science.dspace + addon-analytics-api + ${addon-analytics.version} + jar + + + + + + addon-dataquality + + + dq.on + + + + + it.4science.dspace + addon-dataquality + ${addon-dataquality.version} + jar + + + diff --git a/pom.xml b/pom.xml index 09b01d200b5..c7e823636c0 100644 --- a/pom.xml +++ b/pom.xml @@ -65,6 +65,7 @@ [CRIS-7.1-SNAPSHOT,CRIS-8.0-SNAPSHOT) [CRIS-7.0-SNAPSHOT,CRIS-8.0-SNAPSHOT) [CRIS-7.0-SNAPSHOT,CRIS-8.0-SNAPSHOT) + cris-2023.02.00 UTF-8 @@ -939,6 +940,26 @@ + + + addon-dataquality + + false + + + + + it.4science.dspace + addon-dataquality + ${addon-dataquality.version} + jar + + + + + From 8d73d806644711eca5d8a89cb013a8215b78b477 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 8 Feb 2024 17:14:27 +0100 Subject: [PATCH 69/82] [DQ-26] Refactors configurations --- dspace/config/dspace.cfg | 10 ---------- dspace/config/modules/authority.cfg | 13 +------------ dspace/config/modules/deduplication.cfg | 10 ++++++++++ pom.xml | 2 +- 4 files changed, 12 insertions(+), 23 deletions(-) diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 477d14e207e..05ebdd66067 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1926,16 +1926,6 @@ bulk-export.limit.notLoggedIn = 0 # has 2 threads on which schedule events system-event.thread.size = 2 -#------------------------------------------------------------------# -#------------DEDUPLICATION / DATAQUALITY CONFIGURATIONS------------# -#------------------------------------------------------------------# -# # -# Configurations for the Deduplication / DataQuality features # -# # -#------------------------------------------------------------------# -# metadata here listed will be excluded by merge tool logic -merge.excluded-metadata = dc.description.provenance - # Load default module configs # ---------------------------- # To exclude a module configuration, simply comment out its "include" statement. diff --git a/dspace/config/modules/authority.cfg b/dspace/config/modules/authority.cfg index ecc9e858df8..86d0248060e 100644 --- a/dspace/config/modules/authority.cfg +++ b/dspace/config/modules/authority.cfg @@ -279,15 +279,4 @@ authority.controlled.dc.type = true choices.plugin.dc.type = ControlledVocabularyAuthority # DSpace-CRIS stores by default the authority of controlled vocabularies -vocabulary.plugin.authority.store = true - -#------------------------------------------------------------------# -#------------DEDUPLICATION / DATAQUALITY CONFIGURATIONS------------# -#------------------------------------------------------------------# -org.dspace.content.authority.ItemAuthority = OrgUnitAuthority -# AuthorStrictMatchAuthority configuration -cris.ItemAuthority.AuthorStrictMatchAuthority.forceInternalName = false -# AuthorCoarseMatchAuthority configuration -cris.ItemAuthority.AuthorCoarseMatchAuthority.forceInternalName = false -choices.plugin.green.override.dc.contributor.author = AuthorStrictMatchAuthority -choices.plugin.orange.override.dc.contributor.author = AuthorCoarseMatchAuthority \ No newline at end of file +vocabulary.plugin.authority.store = true \ No newline at end of file diff --git a/dspace/config/modules/deduplication.cfg b/dspace/config/modules/deduplication.cfg index 71aa5c8840d..c2f42dc2902 100644 --- a/dspace/config/modules/deduplication.cfg +++ b/dspace/config/modules/deduplication.cfg @@ -27,3 +27,13 @@ deduplication.tool.duplicatechecker.ignorewithdrawn = true # only reported section don't check submitter suggestion duplicate deduplication.tool.duplicatechecker.ignore.submitter.suggestion = true + +#------------------------------------------------------------------# +#------------DEDUPLICATION / DATAQUALITY CONFIGURATIONS------------# +#------------------------------------------------------------------# +# # +# Configurations for the Deduplication / DataQuality features # +# # +#------------------------------------------------------------------# +# metadata here listed will be excluded by merge tool logic +merge.excluded-metadata = dc.description.provenance diff --git a/pom.xml b/pom.xml index c7e823636c0..8ec60b3a99d 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ [CRIS-7.1-SNAPSHOT,CRIS-8.0-SNAPSHOT) [CRIS-7.0-SNAPSHOT,CRIS-8.0-SNAPSHOT) [CRIS-7.0-SNAPSHOT,CRIS-8.0-SNAPSHOT) - cris-2023.02.00 + [CRIS-2023.02-SNAPSHOT,CRIS-2023.03-SNAPSHOT) UTF-8 From e439ad996c2a3a9c042c8dee0b2207816787ad0c Mon Sep 17 00:00:00 2001 From: Mattia Vianelli Date: Fri, 9 Feb 2024 13:00:23 +0100 Subject: [PATCH 70/82] DSC-1531 Updated modules version to reflect the correct one --- dspace/modules/server-boot/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace/modules/server-boot/pom.xml b/dspace/modules/server-boot/pom.xml index 497fab3e6a1..ee23c8ee7bc 100644 --- a/dspace/modules/server-boot/pom.xml +++ b/dspace/modules/server-boot/pom.xml @@ -11,7 +11,7 @@ modules org.dspace - cris-2024.01.00-SNAPSHOT + cris-2023.02.02-SNAPSHOT .. From f3e266878b1a82b7eb70f2b3bf745080afe43bf4 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Fri, 9 Feb 2024 13:22:52 +0100 Subject: [PATCH 71/82] [DQ-26] Addresses changes for a new dataquality-types file --- dspace/config/dspace.cfg | 1 + .../config/registries/dataquality-types.xml | 19 +++++++++++++++++++ dspace/config/registries/dspace-types.xml | 8 -------- 3 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 dspace/config/registries/dataquality-types.xml diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 05ebdd66067..98a7aba0aa5 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1011,6 +1011,7 @@ registry.metadata.load = openaire4-types.xml registry.metadata.load = dspace-types.xml registry.metadata.load = iiif-types.xml registry.metadata.load = bitstream-types.xml +registry.metadata.load = dataquality-types.xml #---------------------------------------------------------------# #-----------------UI-Related CONFIGURATIONS---------------------# diff --git a/dspace/config/registries/dataquality-types.xml b/dspace/config/registries/dataquality-types.xml new file mode 100644 index 00000000000..2658d701dc2 --- /dev/null +++ b/dspace/config/registries/dataquality-types.xml @@ -0,0 +1,19 @@ + + + + DataQuality Addon metadata types + + + + dq + http://dspace.org/dq + + + + dq + merge + target-uri + stores the value of uri of target item + + + \ No newline at end of file diff --git a/dspace/config/registries/dspace-types.xml b/dspace/config/registries/dspace-types.xml index 2cc899dd0cd..861dc67a816 100644 --- a/dspace/config/registries/dspace-types.xml +++ b/dspace/config/registries/dspace-types.xml @@ -122,12 +122,4 @@ - - - dspace - merge - target-uri - stores the value of uri of target item - - From db85f99920876aa8dcd815d300f81de20d47444c Mon Sep 17 00:00:00 2001 From: "aliaksei.bykau" Date: Fri, 9 Feb 2024 13:55:31 +0100 Subject: [PATCH 72/82] =?UTF-8?q?[CST-13510]=20Update=20the=20registry-loa?= =?UTF-8?q?der=20script=20and=20related=20classes.=20Added=20new=20script?= =?UTF-8?q?=20argument=20=E2=80=9C-all=E2=80=9D=20that=20will=20run=20impo?= =?UTF-8?q?rt=20of=20all=20metadata=20types=20and=20bitstream=20formats.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dspace/administer/MetadataImporter.java | 35 ++++++++++------- .../org/dspace/administer/RegistryLoader.java | 39 ++++++++++++++++--- .../dspace/storage/rdbms/RegistryUpdater.java | 30 +++++++------- 3 files changed, 68 insertions(+), 36 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java b/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java index 501d86af45f..85f084b03cd 100644 --- a/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java +++ b/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java @@ -11,7 +11,6 @@ import java.io.IOException; import java.sql.SQLException; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import javax.xml.parsers.ParserConfigurationException; @@ -35,6 +34,8 @@ import org.dspace.content.service.MetadataFieldService; import org.dspace.content.service.MetadataSchemaService; import org.dspace.core.Context; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -66,10 +67,18 @@ * } */ public class MetadataImporter { + public static final String BASE = DSpaceServicesFactory.getInstance() + .getConfigurationService().getProperty("dspace.dir") + File.separator + "config" + File.separator + + "registries" + File.separator; + public static final String REGISTRY_METADATA_PROPERTY = "registry.metadata.load"; + public static final String REGISTRY_BITSTREAM_FORMAT_PROPERTY = "registry.bitstream-formats.load"; + protected static MetadataSchemaService metadataSchemaService = ContentServiceFactory.getInstance() .getMetadataSchemaService(); protected static MetadataFieldService metadataFieldService = ContentServiceFactory.getInstance() .getMetadataFieldService(); + protected static ConfigurationService configurationService = DSpaceServicesFactory.getInstance() + .getConfigurationService(); /** * logging category @@ -117,25 +126,21 @@ public static void main(String[] args) loadRegistry(file, forceUpdate); } else { boolean forceUpdate = line.hasOption('u'); - for (String file : getAllRegistryFiles()) { + for (String file : getAllRegistryFiles(REGISTRY_METADATA_PROPERTY)) { loadRegistry(file, forceUpdate); } } } - public static List getAllRegistryFiles() { - File folder = new File("config/registries"); - - if (folder.exists() && folder.isDirectory()) { - File[] files = folder.listFiles((dir, name) -> name.toLowerCase().endsWith(".xml")); - - if (files != null) { - return Arrays.stream(files) - .map(file -> "config/registries/" + file.getName()) - .collect(Collectors.toList()); - } - } - return Collections.emptyList(); + /** + * Load all registry file names from config + * + * @param propertyName + * @return list of all registry files + */ + public static List getAllRegistryFiles(String propertyName) { + List files = Arrays.asList(configurationService.getArrayProperty(propertyName)); + return files.stream().map(file -> BASE + file).collect(Collectors.toList()); } /** diff --git a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java index ba156cafc89..7db0cc8d893 100644 --- a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java +++ b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java @@ -70,7 +70,7 @@ private RegistryLoader() { } */ public static void main(String[] argv) throws Exception { String usage = "Usage: " + RegistryLoader.class.getName() - + " (-bitstream | -metadata) registry-file.xml"; + + " (-bitstream | -metadata | -all) registry-file.xml"; Context context = null; @@ -83,16 +83,21 @@ public static void main(String[] argv) throws Exception { // Work out what we're loading if (argv[0].equalsIgnoreCase("-bitstream")) { - RegistryLoader.loadBitstreamFormats(context, argv[1]); + if (argv.length == 1) { + loadAllBitstreamFormats(context); + } else { + RegistryLoader.loadBitstreamFormats(context, argv[1]); + } } else if (argv[0].equalsIgnoreCase("-metadata")) { // Call MetadataImporter, as it handles Metadata schema updates if (argv.length == 1) { - for (String file : MetadataImporter.getAllRegistryFiles()) { - MetadataImporter.loadRegistry(file, true); - } + loadAllRegistry(); } else { MetadataImporter.loadRegistry(argv[1], true); } + } else if (argv[0].equalsIgnoreCase("-all")) { + loadAllBitstreamFormats(context); + loadAllRegistry(); } else { System.err.println(usage); } @@ -119,6 +124,30 @@ public static void main(String[] argv) throws Exception { } } + + /** + * Load all bitstream formats from configuration properties + * + * @param context + * @throws Exception + */ + private static void loadAllBitstreamFormats(Context context) throws Exception { + for (String file : MetadataImporter.getAllRegistryFiles(MetadataImporter.REGISTRY_BITSTREAM_FORMAT_PROPERTY)) { + RegistryLoader.loadBitstreamFormats(context, file); + } + } + + /** + * Load all metadata registry from configuration properties + * + * @throws Exception + */ + private static void loadAllRegistry() throws Exception { + for (String file : MetadataImporter.getAllRegistryFiles(MetadataImporter.REGISTRY_METADATA_PROPERTY)) { + MetadataImporter.loadRegistry(file, true); + } + } + /** * Load Bitstream Format metadata * diff --git a/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java b/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java index 6a1d71b9e65..1d9948690d8 100644 --- a/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java +++ b/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java @@ -7,9 +7,9 @@ */ package org.dspace.storage.rdbms; -import java.io.File; import java.io.IOException; import java.sql.SQLException; +import java.util.List; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPathExpressionException; @@ -20,8 +20,6 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.content.NonUniqueMetadataException; import org.dspace.core.Context; -import org.dspace.services.ConfigurationService; -import org.dspace.services.factory.DSpaceServicesFactory; import org.flywaydb.core.api.callback.Callback; import org.flywaydb.core.api.callback.Event; import org.slf4j.Logger; @@ -58,31 +56,31 @@ public class RegistryUpdater implements Callback { * Method to actually update our registries from latest configuration files. */ private void updateRegistries() { - ConfigurationService config = DSpaceServicesFactory.getInstance().getConfigurationService(); Context context = null; try { context = new Context(); context.turnOffAuthorisationSystem(); - String base = config.getProperty("dspace.dir") - + File.separator + "config" + File.separator - + "registries" + File.separator; - - // Load updates to Bitstream format registry (if any) - String bitstreamFormat = config.getProperty("registry.bitstream-formats.load"); - log.info("Updating Bitstream Format Registry based on {}{}", base, bitstreamFormat); - RegistryLoader.loadBitstreamFormats(context, base + bitstreamFormat); + // Load updates to Bitstream formats registries (if any) + List RegistryMetadataFiles = + MetadataImporter.getAllRegistryFiles(MetadataImporter.REGISTRY_METADATA_PROPERTY); + for (String bitstreamFormat : RegistryMetadataFiles) { + log.info("Updating Bitstream Format Registry based on {}", bitstreamFormat); + RegistryLoader.loadBitstreamFormats(context, bitstreamFormat); + } // Load updates to Metadata schema registries (if any) - log.info("Updating Metadata Registries based on metadata type configs in {}", base); - for (String namespaceFile: config.getArrayProperty("registry.metadata.load")) { + List RegistryBitstreamFormatFiles = + MetadataImporter.getAllRegistryFiles(MetadataImporter.REGISTRY_BITSTREAM_FORMAT_PROPERTY); + log.info("Updating Metadata Registries based on metadata type configs in {}", MetadataImporter.BASE); + for (String namespaceFile: RegistryBitstreamFormatFiles) { log.info("Reading {}", namespaceFile); - MetadataImporter.loadRegistry(base + namespaceFile, true); + MetadataImporter.loadRegistry(namespaceFile, true); } String workflowTypes = "workflow-types.xml"; log.info("Reading {}", workflowTypes); - MetadataImporter.loadRegistry(base + workflowTypes, true); + MetadataImporter.loadRegistry( MetadataImporter.BASE + workflowTypes, true); context.restoreAuthSystemState(); // Commit changes and close context From 13aa12074f6d881adc3d00ec417ee0dfd9836862 Mon Sep 17 00:00:00 2001 From: "aliaksei.bykau" Date: Fri, 9 Feb 2024 13:57:07 +0100 Subject: [PATCH 73/82] [CST-13510] checkstyle --- .../src/main/java/org/dspace/administer/MetadataImporter.java | 2 +- .../src/main/java/org/dspace/administer/RegistryLoader.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java b/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java index 85f084b03cd..7a1aaa78278 100644 --- a/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java +++ b/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java @@ -135,7 +135,7 @@ public static void main(String[] args) /** * Load all registry file names from config * - * @param propertyName + * @param propertyName name of the property that used in config * @return list of all registry files */ public static List getAllRegistryFiles(String propertyName) { diff --git a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java index 7db0cc8d893..37876c587ee 100644 --- a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java +++ b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java @@ -128,7 +128,7 @@ public static void main(String[] argv) throws Exception { /** * Load all bitstream formats from configuration properties * - * @param context + * @param context DSpace context object * @throws Exception */ private static void loadAllBitstreamFormats(Context context) throws Exception { From e666ff85b2aeb2baf0bfc05882310415807bf59e Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Fri, 9 Feb 2024 16:53:25 +0100 Subject: [PATCH 74/82] DSC-1457 fix inverted list of registries (metadata, bitstream) --- .../dspace/storage/rdbms/RegistryUpdater.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java b/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java index 1d9948690d8..d6577dc7e19 100644 --- a/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java +++ b/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java @@ -62,20 +62,20 @@ private void updateRegistries() { context.turnOffAuthorisationSystem(); // Load updates to Bitstream formats registries (if any) - List RegistryMetadataFiles = - MetadataImporter.getAllRegistryFiles(MetadataImporter.REGISTRY_METADATA_PROPERTY); - for (String bitstreamFormat : RegistryMetadataFiles) { + List registryBitstreamFormatFiles = + MetadataImporter.getAllRegistryFiles(MetadataImporter.REGISTRY_BITSTREAM_FORMAT_PROPERTY); + for (String bitstreamFormat : registryBitstreamFormatFiles) { log.info("Updating Bitstream Format Registry based on {}", bitstreamFormat); RegistryLoader.loadBitstreamFormats(context, bitstreamFormat); } // Load updates to Metadata schema registries (if any) - List RegistryBitstreamFormatFiles = - MetadataImporter.getAllRegistryFiles(MetadataImporter.REGISTRY_BITSTREAM_FORMAT_PROPERTY); + List registryMetadataFiles = + MetadataImporter.getAllRegistryFiles(MetadataImporter.REGISTRY_METADATA_PROPERTY); log.info("Updating Metadata Registries based on metadata type configs in {}", MetadataImporter.BASE); - for (String namespaceFile: RegistryBitstreamFormatFiles) { - log.info("Reading {}", namespaceFile); - MetadataImporter.loadRegistry(namespaceFile, true); + for (String metadataFile : registryMetadataFiles) { + log.info("Reading {}", metadataFile); + MetadataImporter.loadRegistry(metadataFile, true); } String workflowTypes = "workflow-types.xml"; From 81035478f3a7eaca3157b50ab4ef9b879b567356 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 9 Feb 2024 17:51:16 +0100 Subject: [PATCH 75/82] [maven-release-plugin] prepare release dspace-cris-2023.02.02 --- dspace-api/pom.xml | 2 +- dspace-iiif/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-rdf/pom.xml | 2 +- dspace-rest/pom.xml | 4 ++-- dspace-server-webapp/pom.xml | 2 +- dspace-services/pom.xml | 2 +- dspace-sword/pom.xml | 2 +- dspace-swordv2/pom.xml | 2 +- dspace/modules/additions/pom.xml | 2 +- dspace/modules/pom.xml | 2 +- dspace/modules/rest/pom.xml | 2 +- dspace/modules/server-boot/pom.xml | 2 +- dspace/modules/server/pom.xml | 2 +- dspace/pom.xml | 2 +- pom.xml | 30 +++++++++++++++--------------- 16 files changed, 31 insertions(+), 31 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 1c6879a5d70..a7d1fa104ed 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 3b56ba53e83..f6846b955fb 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 76718f44ba3..a3ab33551c0 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index eb63a67e457..83af00bc343 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index 257d0b3a91f..a77c960283e 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -3,7 +3,7 @@ org.dspace dspace-rest war - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 DSpace (Deprecated) REST Webapp DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED. Please consider using the REST API in the dspace-server-webapp instead! @@ -12,7 +12,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index c83aa538698..a26174341ef 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -14,7 +14,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 39e2ccf36f3..8b67a90a8ea 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 10a44f3615f..803eba85911 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index e76bfca65b9..1c8103016e5 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index f5ae804f8b8..db2f30166a7 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index 00f691235bc..5a4426dddfd 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 ../../pom.xml diff --git a/dspace/modules/rest/pom.xml b/dspace/modules/rest/pom.xml index 4dfa2939bf9..8b80b402166 100644 --- a/dspace/modules/rest/pom.xml +++ b/dspace/modules/rest/pom.xml @@ -13,7 +13,7 @@ org.dspace modules - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace/modules/server-boot/pom.xml b/dspace/modules/server-boot/pom.xml index ee23c8ee7bc..90ceff73554 100644 --- a/dspace/modules/server-boot/pom.xml +++ b/dspace/modules/server-boot/pom.xml @@ -11,7 +11,7 @@ modules org.dspace - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index 3797e809dca..0d26cdb5fa1 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -7,7 +7,7 @@ modules org.dspace - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 .. diff --git a/dspace/pom.xml b/dspace/pom.xml index 85b98dbb10d..b8cd8ad6c37 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 ../pom.xml diff --git a/pom.xml b/pom.xml index 8ec60b3a99d..c7b81d3eeb3 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 DSpace Parent Project DSpace-CRIS is an open source extension of DSpace (http://www.dspace.org) providing out of box support for the CRIS / RIMS and moder Institution Repository use cases with advanced features and optimized configurations @@ -979,14 +979,14 @@ org.dspace dspace-rest - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 jar classes org.dspace dspace-rest - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 war @@ -1137,62 +1137,62 @@ org.dspace dspace-api - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 org.dspace dspace-api test-jar - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 test org.dspace.modules additions - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 org.dspace dspace-sword - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 org.dspace dspace-swordv2 - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 org.dspace dspace-oai - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 org.dspace dspace-services - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 org.dspace dspace-server-webapp test-jar - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 test org.dspace dspace-rdf - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 org.dspace dspace-iiif - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 org.dspace dspace-server-webapp - cris-2023.02.02-SNAPSHOT + cris-2023.02.02 @@ -2037,7 +2037,7 @@ scm:git:git@github.com:4Science/DSpace.git scm:git:git@github.com:4Science/DSpace.git git@github.com:4Science/DSpace.git - dspace-cris-2023.02.02-SNAPSHOT + dspace-cris-2023.02.02 From af719dfd1dfef0b27b55dd8f7d4dddd4baa72cae Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 9 Feb 2024 17:51:21 +0100 Subject: [PATCH 76/82] [maven-release-plugin] prepare for next development iteration --- dspace-api/pom.xml | 2 +- dspace-iiif/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-rdf/pom.xml | 2 +- dspace-rest/pom.xml | 4 ++-- dspace-server-webapp/pom.xml | 2 +- dspace-services/pom.xml | 2 +- dspace-sword/pom.xml | 2 +- dspace-swordv2/pom.xml | 2 +- dspace/modules/additions/pom.xml | 2 +- dspace/modules/pom.xml | 2 +- dspace/modules/rest/pom.xml | 2 +- dspace/modules/server-boot/pom.xml | 2 +- dspace/modules/server/pom.xml | 2 +- dspace/pom.xml | 2 +- pom.xml | 30 +++++++++++++++--------------- 16 files changed, 31 insertions(+), 31 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index a7d1fa104ed..b97879e1419 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index f6846b955fb..43a4aa8457c 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index a3ab33551c0..eeb37785fde 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index 83af00bc343..834f1a084eb 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT .. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index a77c960283e..96a76b6e0ec 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -3,7 +3,7 @@ org.dspace dspace-rest war - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT DSpace (Deprecated) REST Webapp DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED. Please consider using the REST API in the dspace-server-webapp instead! @@ -12,7 +12,7 @@ org.dspace dspace-parent - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index a26174341ef..0ba8f40d86a 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -14,7 +14,7 @@ org.dspace dspace-parent - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 8b67a90a8ea..792f68b80a5 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT .. diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 803eba85911..a525e6f9978 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 1c8103016e5..1cecf563f37 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index db2f30166a7..3077c65c456 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index 5a4426dddfd..fec235b81fd 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT ../../pom.xml diff --git a/dspace/modules/rest/pom.xml b/dspace/modules/rest/pom.xml index 8b80b402166..95587d4fc7f 100644 --- a/dspace/modules/rest/pom.xml +++ b/dspace/modules/rest/pom.xml @@ -13,7 +13,7 @@ org.dspace modules - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT .. diff --git a/dspace/modules/server-boot/pom.xml b/dspace/modules/server-boot/pom.xml index 90ceff73554..7de670e1788 100644 --- a/dspace/modules/server-boot/pom.xml +++ b/dspace/modules/server-boot/pom.xml @@ -11,7 +11,7 @@ modules org.dspace - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index 0d26cdb5fa1..bf13d202b7b 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -7,7 +7,7 @@ modules org.dspace - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT .. diff --git a/dspace/pom.xml b/dspace/pom.xml index b8cd8ad6c37..826ab408e8e 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index c7b81d3eeb3..6f805739537 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT DSpace Parent Project DSpace-CRIS is an open source extension of DSpace (http://www.dspace.org) providing out of box support for the CRIS / RIMS and moder Institution Repository use cases with advanced features and optimized configurations @@ -979,14 +979,14 @@ org.dspace dspace-rest - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT jar classes org.dspace dspace-rest - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT war @@ -1137,62 +1137,62 @@ org.dspace dspace-api - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT org.dspace dspace-api test-jar - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT test org.dspace.modules additions - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT org.dspace dspace-sword - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT org.dspace dspace-swordv2 - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT org.dspace dspace-oai - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT org.dspace dspace-services - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT org.dspace dspace-server-webapp test-jar - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT test org.dspace dspace-rdf - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT org.dspace dspace-iiif - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT org.dspace dspace-server-webapp - cris-2023.02.02 + cris-2023.02.03-SNAPSHOT @@ -2037,7 +2037,7 @@ scm:git:git@github.com:4Science/DSpace.git scm:git:git@github.com:4Science/DSpace.git git@github.com:4Science/DSpace.git - dspace-cris-2023.02.02 + dspace-cris-2023.02.02-SNAPSHOT From 31fa7ab7abdbb519bb347c5c9dfe1bdf7396b336 Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Tue, 13 Feb 2024 12:30:19 +0100 Subject: [PATCH 77/82] [DSC-1531] fix cris version --- dspace-api/pom.xml | 2 +- dspace-iiif/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-rdf/pom.xml | 2 +- dspace-rest/pom.xml | 4 ++-- dspace-server-webapp/pom.xml | 2 +- dspace-services/pom.xml | 2 +- dspace-sword/pom.xml | 2 +- dspace-swordv2/pom.xml | 2 +- dspace/modules/additions/pom.xml | 2 +- dspace/modules/pom.xml | 2 +- dspace/modules/rest/pom.xml | 2 +- dspace/modules/server-boot/pom.xml | 2 +- dspace/modules/server/pom.xml | 4 ++-- dspace/pom.xml | 2 +- pom.xml | 30 +++++++++++++++--------------- 16 files changed, 32 insertions(+), 32 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 1c6879a5d70..5dbbad211f5 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 3b56ba53e83..64674628de2 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 76718f44ba3..68b9c575e50 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index eb63a67e457..5b3c914a458 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT .. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index 257d0b3a91f..9b38518a389 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -3,7 +3,7 @@ org.dspace dspace-rest war - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT DSpace (Deprecated) REST Webapp DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED. Please consider using the REST API in the dspace-server-webapp instead! @@ -12,7 +12,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 0a0b394d576..8a0aabaa1e4 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -14,7 +14,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 39e2ccf36f3..6d05215790f 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT .. diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 10a44f3615f..2c370fc5d99 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index e76bfca65b9..f23bb27da23 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index 7de65e9ca49..f97a91042f8 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index 00f691235bc..73ecc58fbcb 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT ../../pom.xml diff --git a/dspace/modules/rest/pom.xml b/dspace/modules/rest/pom.xml index 4dfa2939bf9..e9d62053025 100644 --- a/dspace/modules/rest/pom.xml +++ b/dspace/modules/rest/pom.xml @@ -13,7 +13,7 @@ org.dspace modules - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT .. diff --git a/dspace/modules/server-boot/pom.xml b/dspace/modules/server-boot/pom.xml index ee23c8ee7bc..497fab3e6a1 100644 --- a/dspace/modules/server-boot/pom.xml +++ b/dspace/modules/server-boot/pom.xml @@ -11,7 +11,7 @@ modules org.dspace - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index 3797e809dca..faa4aa8cb20 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -7,12 +7,12 @@ modules org.dspace - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT .. - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT ${basedir}/../../.. diff --git a/dspace/pom.xml b/dspace/pom.xml index 85b98dbb10d..fdc82dbc41a 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 09b01d200b5..bb001488480 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT DSpace Parent Project DSpace-CRIS is an open source extension of DSpace (http://www.dspace.org) providing out of box support for the CRIS / RIMS and moder Institution Repository use cases with advanced features and optimized configurations @@ -958,14 +958,14 @@ org.dspace dspace-rest - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT jar classes org.dspace dspace-rest - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT war @@ -1116,62 +1116,62 @@ org.dspace dspace-api - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT org.dspace dspace-api test-jar - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT test org.dspace.modules additions - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT org.dspace dspace-sword - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT org.dspace dspace-swordv2 - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT org.dspace dspace-oai - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT org.dspace dspace-services - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT org.dspace dspace-server-webapp test-jar - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT test org.dspace dspace-rdf - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT org.dspace dspace-iiif - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT org.dspace dspace-server-webapp - cris-2023.02.02-SNAPSHOT + cris-2024.01.00-SNAPSHOT @@ -2016,7 +2016,7 @@ scm:git:git@github.com:4Science/DSpace.git scm:git:git@github.com:4Science/DSpace.git git@github.com:4Science/DSpace.git - dspace-cris-2023.02.02-SNAPSHOT + dspace-cris-2024.01.00-SNAPSHOT From ade2999cb172bbd5c562314b2dcc68732bbddb06 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Mon, 26 Feb 2024 19:01:50 +0100 Subject: [PATCH 78/82] DSC-714 expose the authority in the VocabularyEntryDetails when and only when it should be saved in the metadata --- .../authority/DSpaceControlledVocabulary.java | 1 + .../test/data/dspaceFolder/config/local.cfg | 3 +- .../model/VocabularyEntryDetailsRest.java | 12 + ...aryEntryDetailsChildrenLinkRepository.java | 2 +- ...ularyEntryDetailsParentLinkRepository.java | 2 +- .../VocabularyEntryDetailsRestRepository.java | 4 +- .../dspace/app/rest/utils/AuthorityUtils.java | 32 +- .../srsc-noauthority.xml | 2256 +++++++++++++++++ .../app/rest/VocabularyEntryDetailsIT.java | 98 + .../VocabularyEntryDetailsMatcher.java | 9 + 10 files changed, 2402 insertions(+), 17 deletions(-) create mode 100644 dspace-server-webapp/src/test/data/dspaceFolder/config/controlled-vocabularies/srsc-noauthority.xml diff --git a/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabulary.java b/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabulary.java index cfd8c53f79a..65d0f25c306 100644 --- a/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabulary.java +++ b/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabulary.java @@ -93,6 +93,7 @@ public boolean isPublic() { @Override public boolean storeAuthorityInMetadata() { + init(); return storeAuthority; } diff --git a/dspace-api/src/test/data/dspaceFolder/config/local.cfg b/dspace-api/src/test/data/dspaceFolder/config/local.cfg index 2f3a64218db..b02b9fd1550 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/local.cfg +++ b/dspace-api/src/test/data/dspaceFolder/config/local.cfg @@ -226,4 +226,5 @@ choices.presentation.dspace.object.owner = suggest authority.controlled.dspace.object.owner = true # force the event system to work synchronously during test -system-event.thread.size = 0 \ No newline at end of file +system-event.thread.size = 0 +vocabulary.plugin.srsc-noauthority.authority.store = false \ No newline at end of file diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java index 30e5eb71cbf..94765e4b91d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java @@ -10,6 +10,8 @@ import java.util.Map; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; import org.dspace.app.rest.RestResourceController; /** @@ -28,6 +30,8 @@ public class VocabularyEntryDetailsRest extends BaseObjectRest { public static final String CHILDREN = "children"; private String display; private String value; + @JsonInclude(Include.NON_NULL) + private String authority; private Map otherInformation; private boolean selectable; @JsonIgnore @@ -60,6 +64,14 @@ public void setValue(String value) { this.value = value; } + public void setAuthority(String authority) { + this.authority = authority; + } + + public String getAuthority() { + return authority; + } + public static String getName() { return NAME; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyEntryDetailsChildrenLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyEntryDetailsChildrenLinkRepository.java index d96e43c79aa..4a256cab644 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyEntryDetailsChildrenLinkRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyEntryDetailsChildrenLinkRepository.java @@ -72,7 +72,7 @@ public Page getChildren(@Nullable HttpServletRequest pageable.getPageSize(), context.getCurrentLocale().toString()); for (Choice value : choices.values) { results.add(authorityUtils.convertEntryDetails(fix, value, vocabularyName, authority.isHierarchical(), - utils.obtainProjection())); + authority.storeAuthorityInMetadata(), utils.obtainProjection())); } Page resources = new PageImpl(results, pageable, choices.total); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyEntryDetailsParentLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyEntryDetailsParentLinkRepository.java index 0b91b3ecf68..37b62f857dc 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyEntryDetailsParentLinkRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyEntryDetailsParentLinkRepository.java @@ -67,6 +67,6 @@ public VocabularyEntryDetailsRest getParent(@Nullable HttpServletRequest request throw new NotFoundException(); } return authorityUtils.convertEntryDetails(fix, choice, vocabularyName, authority.isHierarchical(), - utils.obtainProjection()); + authority.storeAuthorityInMetadata(), utils.obtainProjection()); } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyEntryDetailsRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyEntryDetailsRestRepository.java index 31e8d7d3b92..0ddf0bdcae8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyEntryDetailsRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VocabularyEntryDetailsRestRepository.java @@ -85,7 +85,7 @@ public VocabularyEntryDetailsRest findOne(Context context, String name) { fix = true; } VocabularyEntryDetailsRest entryDetails = authorityUtils.convertEntryDetails(fix, choice, vocabularyName, - source.isHierarchical(), utils.obtainProjection()); + source.isHierarchical(), source.storeAuthorityInMetadata(), utils.obtainProjection()); //FIXME hack to deal with an improper use on the angular side of the node id (otherinformation.id) to // build a vocabulary entry details ID if (source instanceof DSpaceControlledVocabulary && !StringUtils.startsWith(vocabularyId, vocabularyName) @@ -114,7 +114,7 @@ public Page findAllTop(@Parameter(value = "vocabular } for (Choice value : choices.values) { results.add(authorityUtils.convertEntryDetails(fix, value, vocabularyId, source.isHierarchical(), - utils.obtainProjection())); + source.storeAuthorityInMetadata(), utils.obtainProjection())); } Page resources = new PageImpl(results, pageable, choices.total); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/AuthorityUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/AuthorityUtils.java index 418f5f8ab6c..2262f07da07 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/AuthorityUtils.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/AuthorityUtils.java @@ -75,21 +75,26 @@ public String getPresentation(String schema, String element, String qualifier) { /** * TODO the authorityName MUST be a part of Choice model * - * @param fix if true mean that we need to deal with a - * DSpaceControlledVocabulary that requires to have the - * vocabulary name in both the authority than in the entry - * id. An entry id with a double vocabulary name would cause issue to angular - * if the vocabulary entry was requested using just one occurrence of the name - * FIXME hack to deal with an improper use on the angular side of the node id - * (otherinformation.id) to build a vocabulary entry details ID - - * @param choice - * @param authorityName - * @param projection the name of the projection to use, or {@code null}. + * @param fix if true mean that we need to deal with a + * DSpaceControlledVocabulary that requires to have the + * vocabulary name in both the authority than in the entry + * id. An entry id with a double vocabulary name would + * cause issue to angular if the vocabulary entry was + * requested using just one occurrence of the name FIXME + * hack to deal with an improper use on the angular side + * of the node id (otherinformation.id) to build a + * vocabulary entry details ID + * @param choice the choice to convert + * @param authorityName the name of the authority to which the choice belongs + * @param isHierarchical true if it is an hierarchical vocabulary + * @param storeAuthority true if the authority is configured to store the + * authority in the metadata + * {@link ChoiceAuthority#storeAuthorityInMetadata()} + * @param projection the name of the projection to use, or {@code null}. * @return */ public VocabularyEntryDetailsRest convertEntryDetails(boolean fix, Choice choice, String authorityName, - boolean isHierarchical, Projection projection) { + boolean isHierarchical, boolean storeAuthority, Projection projection) { if (choice == null) { return null; } @@ -98,6 +103,9 @@ public VocabularyEntryDetailsRest convertEntryDetails(boolean fix, Choice choice if (!fix) { entry.setId(authorityName + ":" + entry.getId()); } + if (storeAuthority) { + entry.setAuthority(choice.authority); + } entry.setInHierarchicalVocabulary(isHierarchical); return entry; } diff --git a/dspace-server-webapp/src/test/data/dspaceFolder/config/controlled-vocabularies/srsc-noauthority.xml b/dspace-server-webapp/src/test/data/dspaceFolder/config/controlled-vocabularies/srsc-noauthority.xml new file mode 100644 index 00000000000..f37dda70f10 --- /dev/null +++ b/dspace-server-webapp/src/test/data/dspaceFolder/config/controlled-vocabularies/srsc-noauthority.xml @@ -0,0 +1,2256 @@ + + + + + + + + + + Religionshistoria + + + Kyrkovetenskap + + + Missionsvetenskap + + + Systematisk teologi + + + Islamologi + + + Tros- och livsåskådningsvetenskap + + + Religionssociologi + + + Religionspsykologi + + + Religionsfilosofi + + + Nya testamentets exegetik + + + Gamla testamentets exegetik + + + Dogmatik med symbolik + + + Religionsvetenskap/Teologi + + + + + + + Logik + + + Praktisk filosofi + + + Teoretisk filosofi + + + Vetenskapsteori + + + Filosofiämnen + + + + + Arkeologi + + + Arkeologi, klassisk + + + Arkeologi, medeltid + + + Arkeologi, nordeuropeisk + + + Arkeologi, utomeuropeisk + + + Afrikansk och jämförande arkeologi + + + Historisk osteologi + + + Arkeologiämnen + + + + + Historia + + + Idé- o lärdomshistoria + + + Ekonomisk historia + + + Kyrkohistoria + + + Teknikhistoria + + + Teknik och kultur + + + Teknik- och industrihistoria + + + Vetenskapshistoria + + + Rättshistoria + + + Medicinens historia + + + Agrarhistoria + + + Bok- och bibliotekshistoria + + + Historieämnen + + + + + Humanekologi + + + Kulturantropologi + + + Etnologi + + + + + Antikens kultur och samhälle + + + Bysantinologi + + + Historiska kulturer + + + Historisk-filosofiska ämnen + + + + + Estetik + + + Filmvetenskap + + + Konstvetenskap + + + Litteraturvetenskap + + + Musikvetenskap + + + Retorik + + + Teatervetenskap + + + Bebyggelseforskning + + + Estetiska ämnen + + + + + + + Svenska språket + + + Samiska + + + Norska språket + + + Danska språket + + + Nordiska språk + + + + + Germanistik + + + Engelska språket + + + Tyska språket + + + Nederländska + + + Övriga germanska språk + + + + + Franska språket + + + Italienska språket + + + Spanska språket + + + Portugisiska + + + Rumänska + + + Romanska språk + + + + + Finska språket + + + Estniska språket + + + Finsk-ugriska språk + + + + + Ryska språket + + + Polska språket + + + Slaviska språk + + + + + Latin + + + Klassisk grekiska + + + Nygrekiska + + + Klassiska språk + + + + + Sanskrit med indoeuropeisk språkforskning + + + Keltiska språk + + + Baltiska språk + + + Östasiatiska språk + + + Turkiska språk + + + Iranska språk + + + Arabiska + + + Kinesiska + + + Egyptologi + + + Indonesiska + + + Afrikanska språk + + + Bantuistik + + + Kaukasiska språk + + + Semitiska språk + + + Altaistik + + + Assyriologi + + + Indologi + + + Japanologi + + + Koreanologi + + + Övriga språk + + + Teckenspråk + + + + + Lingvistik + + + Fonetik + + + Barnspråk + + + Tvåspråkighet + + + Språkteknologi + + + Datorlingvistik + + + Lingvistikämnen + + + Språkvetenskap + + + Övrig humaniora och religionsvetenskap + + + HUMANIORA och RELIGIONSVETENSKAP + + + + + + + Förvaltningsrätt + + + Konstitutionell rätt + + + Socialrätt + + + Offentlig rätt + + + + + Civilprocess + + + Straffprocess + + + Förvaltningsprocess + + + Skiljemannarätt + + + Processrätt + + + Straffrätt + + + Finansrätt + + + + + Allmän avtalsrätt + + + Arbetsrätt + + + Affärsrätt + + + Bolagsrätt + + + Försäkringsrätt + + + Immaterialrätt + + + Sakrätt + + + Skadeståndsrätt + + + Speciell avtalsrätt + + + Civilrätt + + + + + Allmän rättslära + + + EU-rätt + + + Folkrätt + + + Internationell privat- och processrätt + + + Komparativ rätt + + + Marknads- och konkurrensrätt + + + Miljörätt + + + Övrig rätt + + + RÄTTSVETENSKAP/JURIDIK + + + + + + + + + Ämnesdidaktik + + + Vårdpedagogik + + + Musikpedagogik + + + Internationell pedagogik + + + Pedagogik + + + + + Tillämpad psykologi + + + Miljöpsykologi + + + Kognitionsforskning + + + Psykologi + + + + + Socialantropologi + + + Etnografi + + + Socialantrolopologi/etnografi + + + + + Familjeforskning + + + Ungdomsforskning + + + Handikappsforskning + + + Idrottsforskning + + + Socialt arbete + + + + + Kriminalvetenskap + + + Kriminologi + + + + + Rättssociologi + + + Sociologi + + + Demografi + + + + + Freds- och konfliktforskning + + + Freds- och utvecklingsforskning + + + Empirisk konfliktforskning + + + Forskning om Europa + + + Statsvetenskap + + + Pedagogiskt arbete + + + Socialvetenskap + + + + + Företagsekonomi + + + + + Kulturgeografi + + + Ekonomisk geografi + + + Kulturgeografi, ekonomisk geografi + + + + + Ekonometri + + + Nationalekonomi + + + Ekonomi + + + + + + + Informatik + + + Informatik och systemvetenskap + + + ADB + + + Data- och systemvetenskap + + + Databaser + + + Databehandling + + + Informations- och språkteknologi + + + Informationsbehandling + + + Informationsteknologi + + + Informatik, data- och systemvetenskap + + + + + Biostatistik + + + Statistik + + + Statistik, data- och systemvetenskap + + + + + Masskommunikation + + + Medie- och kommunikationsvetenskap + + + Biblioteks- och informationsvetenskap + + + Arbetsmarknadsforskning + + + Europeiskt forskningssamarbete + + + Forskning om offentlig sektor + + + Forskningspolitik + + + Militär underrättelse- och säkerhetstjänst + + + Övrig samhällsvetenskap + + + SAMHÄLLSVETENSKAP + + + + + + + Algebra och geometri + + + Analys + + + Matematisk logik + + + Diskret matematik + + + Algebra, geometri och analys + + + + + Numerisk analys + + + Matematisk statistik + + + Optimeringslära, systemteori + + + Teoretisk datalogi + + + Tillämpad matematik + + + Övrig matematik + + + MATEMATIK + + + + + + + + + Astropartikelfysik + + + Jonfysik + + + Elementarpartikelfysik + + + + + Tungjonsfysik + + + Mellanenergifysik + + + Lågenergifysik + + + Kärnfysik + + + + + Atomfysik + + + Molekylfysik + + + Kemisk fysik + + + Atom- och molekylfysik + + + + + Magnetism + + + Ytor och mellanytor + + + Halvledarfysik + + + Elektronstruktur + + + Supraledning + + + Defekter och diffusion + + + Struktur- och vibrationsfysik + + + Kritiska fenomen (fasövergångar) + + + Vätskefysik + + + Lågtemperaturfysik + + + Makromolekylfysik + + + Mesoskopisk fysik + + + Biologisk fysik + + + Kondenserade materiens fysik + + + + + Astronomi + + + Astropartikelfysik + + + Högenergiastrofysik + + + Solfysik + + + Planetsystemet + + + Galaktisk astronomi + + + Extragalaktisk astronomi + + + Stjärnors bildning och utveckling + + + Kosmologi + + + Astronomi och astrofysik + + + + + Rymdfysik + + + Plasmafysik + + + Fusion + + + Geokosmofysik och plasmafysik + + + + + Optik + + + Geofysik + + + Matematisk fysik + + + Beräkningsfysik + + + Relativitetsteori, gravitation + + + Statistisk fysik + + + Icke-linjär dynamik, kaos + + + Övrig fysik + + + Fysik + + + + + + + Kvantkemi + + + Statistisk mekanik + + + Bioinformatik + + + Teoretisk kemi + + + + + Biofysikalisk kemi + + + Kinetik + + + Spektroskopi + + + Yt- och kolloidkemi + + + Kemisk fysik + + + Fysikalisk kemi + + + + + Separationsmetoder + + + Elektrokemi + + + Spektroskopi + + + Analytisk kemi + + + Molekylär biofysik + + + + + Koordinationskemi + + + Lösningskemi + + + Fasta tillståndets kemi + + + Bio-oorganisk kemi + + + Oorganisk kemi + + + + + Organisk syntes + + + Fysikalisk organisk kemi + + + Bioorganisk kemi + + + Polymerkemi + + + Läkemedelskemi + + + Organisk kemi + + + + + Molekylärbiologi + + + Toxikologi + + + Strukturbiologi + + + Funktionsgenomik + + + Biokemi + + + + + Persistenta organiska föreningar + + + Tungmetaller och övriga metaller + + + Miljötoxikologi + + + Miljökemi + + + Övrig kemi + + + Kemi + + + + + + + Terrestisk ekologi + + + Limnisk ekologi + + + Marin ekologi + + + Etologi och beteendeekologi + + + Terrestisk, limnisk och marin ekologi + + + + + Mikrobiologi + + + Morfologi + + + Systematik och fylogeni + + + Växtfysiologi + + + Zoofysiologi + + + Utvecklingsbiologi + + + Organismbiologi + + + + + Cellbiologi + + + Molekylärbiologi + + + Immunologi + + + Toxikologi + + + Neurobiologi + + + Genetik + + + Cell- och molekylärbiologi + + + + + Bioinformatik + + + Funktionsgenomik + + + Övrig biologi + + + Biologi + + + + + + + Berggrundsgeologi och petrologi + + + Mineralvetenskap + + + Fasta jordens fysik + + + Endogen geovetenskap + + + + + Historisk geologi och paleontologi + + + Kvartärgeologi + + + Naturgeografi + + + Exogen geokemi + + + Sedimentologi + + + Exogen geovetenskap + + + + + Meteorologi + + + Hydrologi + + + Oceanografi + + + Klimatologi + + + Atmosfärs- och hydrosfärsvetenskap + + + Övrig geovetenskap + + + Geovetenskap + + + NATURVETENSKAP + + + + + + + + + Datalogi + + + Programvaruteknik + + + Kognitionsvetenskap + + + Datavetenskap + + + Reglerteknik + + + + + Telekommunikationsteori + + + Teletransmissionsteori + + + Teletrafiksystem + + + Datatransmission + + + Telekommunikation + + + Signalbehandling + + + Bildanalys + + + Datorteknik + + + Systemteknik + + + Övrig informationsteknik + + + Informationsteknik + + + + + Optisk fysik + + + Akustik + + + Materialfysik med ytfysik + + + Plasmafysik med fusion + + + Biofysik + + + Övrig teknisk fysik + + + Teknisk fysik + + + + + Elektroteknik + + + Elektronik + + + Elektrofysik + + + Fotonik + + + Elektronisk mät- och apparatteknik + + + Elkraftteknik + + + Övrig elektroteknik, elektronik och fotonik + + + Elektroteknik, elektronik och fotonik + + + + + + + Processkemi + + + Katalys + + + Molekylära transportprocesser i kemisk processteknik + + + Kemisk energiteknik + + + Cellulosa- och pappersteknik + + + Materialkemi + + + Elektrokemi + + + Yt- och kolloidkemi + + + Kärnkemi + + + Kemisk apparatteknik + + + Kemisk produktionsteknik + + + Kemisk process- och produktionsteknik + + + + + Metallurgisk processteknik + + + Metallurgisk produktionsteknik + + + Metallurgisk process- och produktionsteknik + + + Livsmedelsteknik + + + Övrig kemiteknik + + + Kemiteknik + + + + + Genteknik inkl. funktionsgenomik + + + Strukturbiokemi + + + Biokemisk och bioteknisk processteknik + + + Enzymteknik + + + Immunteknik + + + Bioteknisk separation + + + Bioanalytisk teknik + + + Bioorganisk syntes + + + Växtbioteknik + + + Bioinformatik + + + Övrig bioteknik + + + Bioteknik + + + + + Fastkroppsmekanik + + + Strömningsmekanik + + + Konstruktionsteknik + + + Mekanisk tillverkningsteknik + + + + + Mekanisk energiteknik + + + Termisk energiteknik + + + Mekanisk och termisk energiteknik + + + Farkostteknik + + + Övrig teknisk mekanik + + + Teknisk mekanik + + + + + Funktionella material + + + Konstruktionsmaterial + + + Ytbehandlingsteknik + + + Övrig bearbetning/sammanfogning + + + Övrig teknisk materialvetenskap + + + Teknisk materialvetenskap + + + + + + + Geoteknik + + + Gruvteknik + + + Geoteknik och gruvteknik + + + Byggnadsteknik + + + Byggproduktionsteknik + + + Vattenteknik + + + Lantmäteri + + + + + Arkitektur + + + Bebyggelsevård + + + Arkitektur och bebyggelsevård + + + Övrig samhällsbyggnadsteknik och arkitektur + + + Samhällsbyggnadsteknik och arkitektur + + + + + + + Produktionsteknik + + + Arbetsvetenskap och ergonomi + + + Produktion och arbetsvetenskap + + + Industriell organisation, administration och ekonomi + + + Fysisk planläggning + + + Övrig industriell teknik och ekonomi + + + Industriell teknik och ekonomi + + + + + Medicinsk teknik + + + Miljöteknik + + + Rymdteknik + + + Övriga teknikvetenskaper + + + TEKNIKVETENSKAP + + + + + + + Markfysik + + + Markkemi + + + Markbiologi + + + Jordmånslära + + + Markvetenskap + + + + + Växtförädling + + + Trädgårdsväxtodling + + + Jordbruksväxtodling + + + Skogsskötsel + + + Växt- och skogsskydd + + + Växtproduktion + + + + + Husdjursförädling + + + Husdjurens utfodring och vård + + + Vattenbruk + + + Fiske + + + Animalieproduktion + + + + + Livsmedelsvetenskap + + + Träfiber- och virkeslära + + + Fytokemi inklusive alger och industribioråvaror + + + Produktforskning + + + + + Landskapsarkitektur + + + Vegetationsbyggnad + + + Natur- och landskapsvård + + + Översiktlig planering + + + Landskapsplanering + + + + + Jordbruksteknik + + + Jordbrukets byggnadsteknik + + + Skogsteknik + + + Fjärranalys + + + Areell teknik + + + + + Jordbruksekonomi + + + Skogsekonomi + + + Skogsuppskattning och skogsindelning + + + Informationslära + + + Areell ekonomi + + + SKOGS- och JORDBRUKSVETENSKAP samt LANDSKAPSPLANERING + + + + + + + + + Anestesiologi + + + Intensivvård + + + Katastrofmedicin + + + Traumatologi + + + Anestesiologi och intensivvård + + + + + Endokrin kirurgi + + + Handkirurgi + + + Kirurgi + + + Kärlkirurgi + + + Neurokirurgi + + + Ortopedi + + + Otoneurologi + + + Pediatrisk kirurgi + + + Plastikkirurgi + + + Thoraxkirurgi + + + Transplantationskirurgi + + + Urologi och andrologi + + + Kirurgisk forskning + + + + + Obstetrik och gynekologi + + + Reproduktiv hälsa + + + Reproduktiv och perinatal omvårdnad + + + Obstetrik och kvinnosjukdomar + + + Onkologi + + + + + Audiologi + + + Logopedi och foniatrik + + + Otorhinolaryngologi + + + Kirurgi + + + + + + + Neurovetenskap + + + Neurobiologi + + + Medicinsk cellbiologi + + + Cellbiologi + + + + + Anatomi + + + Biomaterial + + + Histologi + + + Tumörbiologi + + + Morfologi + + + + + Rättsmedicin + + + Molekylär medicin + + + Patologi + + + Morfologi, cellbiologi, patologi + + + + + Dermatologi och venerologi + + + + + Medicinsk genetik + + + Rättsgenetik + + + Molekylärbiologi + + + Molekykär ekogenetik + + + Klinisk genetik + + + + + Barnkardiologi + + + Diabetologi + + + Endokrinologi + + + Gastroenterologi + + + Geriatrik och medicinsk gerontologi + + + Hematologi + + + Kardiologi + + + Kardiovaskulär medicin + + + Lungsjukdomar + + + Molekylär medicin (genetik och patologi) + + + Neurologi + + + Njursjukdomar + + + Palliativ medicin + + + Pediatrisk medicin + + + Prenatal- och perinatalforskning + + + Reumatologi + + + Transfusionsmedicin + + + Invärtesmedicin + + + Dermatologi och venerologi, klinisk genetik, invärtesmedicin + + + + + + + Farmakologi + + + Klinisk farmakologi + + + Toxikologi + + + Farmakologisk forskning + + + + + Experimentell hjärnforskning + + + Försöksdjursvetenskap + + + Klinisk fysiologi + + + Klinisk neurofysiologi + + + Medicinsk informatik + + + Medicinsk teknik + + + Molekylär neurobiologi + + + Neurobiologi + + + Neurofysiologi + + + Näringslära + + + Fysiologi + + + + + Oftalmiatrik + + + Optometri + + + Oftalmologi + + + + + Radiologi + + + Diagnostisk radiologi + + + Radiofysik + + + Strålningsbiologi + + + Radiologisk forskning + + + Fysiologi och farmakologi + + + + + + + Allmänmedicin + + + Arbetsfysiologi + + + Epidemiologi + + + Folkhälsovetenskap + + + Miljömedicin + + + Samhällsmedicin + + + Yrkesmedicin + + + Folkhälsomedicinska forskningsområden + + + Socialmedicin + + + + + + + Allergologi + + + Immungenetik + + + Immunbiologi + + + Klinisk immunologi + + + Tumörimmunologi + + + Immunologi + + + + + Bakteriologi + + + Klinisk bakteriologi + + + Klinisk virologi + + + Medicinsk mikrobiologi + + + Virologi + + + Mikrobiologi + + + Infektionssjukdomar + + + Mikrobiologi, immunologi, infektionssjukdomar + + + + + Biokemi + + + Klinisk kemi + + + Neurokemi + + + Rättskemi + + + Kemi + + + + + Barn- och ungdomspsykiatri + + + Beroendelära + + + Psykiatri + + + MEDICIN + + + + + Biokemi + + + Biomaterial + + + Cariologi + + + Cell- och molekylärbiologi + + + Endodonti + + + Farmakologisk forskning + + + Fysiologi + + + Gerodontologi + + + Kirurgisk forskning + + + Morfologi + + + Odontologisk beteendevetenskap + + + Oral mikrobiologi + + + Oral patologi och rättsodontologi + + + Oral protetik + + + Ortodonti + + + Parodontologi + + + Pedodonti + + + Radiologisk forskning + + + Övrig odontologi + + + ODONTOLOGI + + + + + Biofarmaci + + + Biologisk beroendeforskning + + + Farmaceutisk biokemi + + + Farmaceutisk farmakologi + + + Farmaceutisk kemi + + + Farmaceutisk mikrobiologi + + + Toxikologi + + + Farmakognosi + + + Galenisk farmaci + + + Samhällsfarmaci + + + Övrig farmaci + + + FARMACI + + + + + Biokemi och klinisk kemi + + + Cellbiologi och genomforskning + + + Husdjurens etologi + + + Farmakologisk forskning + + + Fysiologi och näringslära + + + Husdjurshygien + + + Kirurgi + + + Livsmedelshygien + + + Medicin + + + Mikrobiologi och immunologi + + + Morfologi + + + Obstetrik och gynekologi + + + Patologi + + + Radiologi + + + Veterinärmedicinsk epidemiologi + + + Övrig veterinärmedicin + + + VETERINÄRMEDICIN + + + + + Barn + + + Hushålls- och kostvetenskap + + + Hälso- och sjukvård i samhället + + + Kommunikation mellan människor + + + Teknik och social förändring + + + Vatten i natur och samhälle + + + + + Arbetsterapi + + + Medicinsk laboratorievetenskap + + + Omvårdnad + + + Sjukgymnastik/fysioterapi + + + Social omsorg/socialpedagogik + + + Vetenskapsteori med inriktning mot vård- och omsorgsområdet + + + Vårdvetenskap + + + Etnicitet + + + Genus + + + Kulturarv och kulturproduktion + + + Idrott + + + Äldre och åldrande + + + TVÄRVETENSKAPLIGA FORSKNINGSOMRÅDEN + + + Ämneskategorier för vetenskapliga publikationer + \ No newline at end of file diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyEntryDetailsIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyEntryDetailsIT.java index dad6cd8b460..340780dc583 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyEntryDetailsIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyEntryDetailsIT.java @@ -162,6 +162,74 @@ public void srscSearchTopTest() throws Exception { .andExpect(jsonPath("$.page.totalElements", Matchers.is(12))); } + public void srscSearchTopNoAuthorityTest() throws Exception { + String tokenAdmin = getAuthToken(admin.getEmail(), password); + String tokenEPerson = getAuthToken(eperson.getEmail(), password); + getClient(tokenAdmin).perform(get("/api/submission/vocabularyEntryDetails/search/top") + .param("vocabulary", "srsc-noauthority")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._embedded.vocabularyEntryDetails", Matchers.containsInAnyOrder( + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB11", "HUMANITIES and RELIGION", + "HUMANITIES and RELIGION"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB12", "LAW/JURISPRUDENCE", + "LAW/JURISPRUDENCE"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB13", "SOCIAL SCIENCES", + "SOCIAL SCIENCES"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB14", "MATHEMATICS", + "MATHEMATICS"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB15", "NATURAL SCIENCES", + "NATURAL SCIENCES"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB16", "TECHNOLOGY", + "TECHNOLOGY"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB17", + "FORESTRY, AGRICULTURAL SCIENCES and LANDSCAPE PLANNING", + "FORESTRY, AGRICULTURAL SCIENCES and LANDSCAPE PLANNING"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB18", "MEDICINE", + "MEDICINE"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB19", "ODONTOLOGY", + "ODONTOLOGY"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB21", "PHARMACY", + "PHARMACY"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB22", "VETERINARY MEDICINE", + "VETERINARY MEDICINE"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB23", + "INTERDISCIPLINARY RESEARCH AREAS", "INTERDISCIPLINARY RESEARCH AREAS") + ))) + .andExpect(jsonPath("$.page.totalElements", Matchers.is(12))); + + getClient(tokenEPerson).perform(get("/api/submission/vocabularyEntryDetails/search/top") + .param("vocabulary", "srsc-noauthority")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._embedded.vocabularyEntryDetails", Matchers.containsInAnyOrder( + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB11", + "HUMANITIES and RELIGION", "HUMANITIES and RELIGION"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB12", "LAW/JURISPRUDENCE", + "LAW/JURISPRUDENCE"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB13", "SOCIAL SCIENCES", + "SOCIAL SCIENCES"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB14", "MATHEMATICS", + "MATHEMATICS"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB15", "NATURAL SCIENCES", + "NATURAL SCIENCES"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB16", "TECHNOLOGY", + "TECHNOLOGY"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB17", + "FORESTRY, AGRICULTURAL SCIENCES and LANDSCAPE PLANNING", + "FORESTRY, AGRICULTURAL SCIENCES and LANDSCAPE PLANNING"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB18", "MEDICINE", + "MEDICINE"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB19", "ODONTOLOGY", + "ODONTOLOGY"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB21", "PHARMACY", + "PHARMACY"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB22", "VETERINARY MEDICINE", + "VETERINARY MEDICINE"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB23", + "INTERDISCIPLINARY RESEARCH AREAS", "INTERDISCIPLINARY RESEARCH AREAS") + ))) + .andExpect(jsonPath("$.page.totalElements", Matchers.is(12))); + } + @Test public void srscSearchFirstLevel_MATHEMATICS_Test() throws Exception { String tokenAdmin = getAuthToken(admin.getEmail(), password); @@ -181,6 +249,25 @@ public void srscSearchFirstLevel_MATHEMATICS_Test() throws Exception { .andExpect(jsonPath("$.page.totalElements", Matchers.is(3))); } + @Test + public void srscSearchFirstLevel_MATHEMATICS_NoAuthorityTest() throws Exception { + String tokenAdmin = getAuthToken(admin.getEmail(), password); + getClient(tokenAdmin).perform(get("/api/submission/vocabularyEntryDetails/srsc-noauthority:SCB14/children")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._embedded.children", Matchers.containsInAnyOrder( + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB1401", + "Algebra, geometry and mathematical analysis", + "MATHEMATICS::Algebra, geometry and mathematical analysis"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB1402", + "Applied mathematics", "MATHEMATICS::Applied mathematics"), + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry("srsc-noauthority:SCB1409", "Other mathematics", + "MATHEMATICS::Other mathematics") + ))) + .andExpect(jsonPath("$._embedded.children[*].otherInformation.parent", + Matchers.everyItem(is("MATHEMATICS")))) + .andExpect(jsonPath("$.page.totalElements", Matchers.is(3))); + } + @Test public void srscSearchTopPaginationTest() throws Exception { String tokenAdmin = getAuthToken(admin.getEmail(), password); @@ -345,6 +432,17 @@ public void findParentByChildTest() throws Exception { ))); } + @Test + public void findParentByChildNoAuthorityTest() throws Exception { + String tokenEperson = getAuthToken(eperson.getEmail(), password); + getClient(tokenEperson).perform(get("/api/submission/vocabularyEntryDetails/srsc-noauthority:SCB180/parent")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", is( + VocabularyEntryDetailsMatcher.matchNoAuthorityEntry( + "srsc-noauthority:SCB18", "MEDICINE","MEDICINE") + ))); + } + @Test public void findParentByChildBadRequestTest() throws Exception { String tokenEperson = getAuthToken(eperson.getEmail(), password); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/VocabularyEntryDetailsMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/VocabularyEntryDetailsMatcher.java index 8eb2cba3c4b..d645ef1e05c 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/VocabularyEntryDetailsMatcher.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/VocabularyEntryDetailsMatcher.java @@ -8,6 +8,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.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; @@ -25,6 +26,14 @@ private VocabularyEntryDetailsMatcher() { public static Matcher matchAuthorityEntry(String id, String display, String value) { return allOf( matchProperties(id, display, value), + hasJsonPath("$.authority", is(id)), + matchLinks(id)); + } + + public static Matcher matchNoAuthorityEntry(String id, String display, String value) { + return allOf( + matchProperties(id, display, value), + hasNoJsonPath("$.authority"), matchLinks(id)); } From 96a5cbb90b1750758b984c1cbc434bf66d04a5e4 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Fri, 1 Mar 2024 08:52:55 +0100 Subject: [PATCH 79/82] DSC-1561 avoid tracking of placeholder for virtual metadata enhancement --- .../impl/RelatedEntityItemEnhancer.java | 382 ++++++++---------- .../app/matcher/MetadataValueMatcher.java | 7 +- .../consumer/ItemEnhancerConsumerIT.java | 101 +++-- .../enhancer/script/ItemEnhancerScriptIT.java | 69 ++-- 4 files changed, 251 insertions(+), 308 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java index f17e36ee90a..284f9b8bff8 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java @@ -11,10 +11,14 @@ import java.sql.SQLException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; -import java.util.Objects; +import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.dspace.content.Item; @@ -45,10 +49,19 @@ public class RelatedEntityItemEnhancer extends AbstractItemEnhancer { @Autowired private ItemService itemService; + /** + * the entity that can be extended by this enhancer, i.e. Publication + */ private String sourceEntityType; + /** + * the metadata used to navigate the relation, i.e. dc.contributor.author + */ private String sourceItemMetadataField; + /** + * the metadata that is copied from the linked entity, i.e. person.identifier.orcid + */ private String relatedItemMetadataField; @Override @@ -62,18 +75,17 @@ public boolean enhance(Context context, Item item, boolean deepMode) { if (!deepMode) { try { result = cleanObsoleteVirtualFields(context, item); - result = updateVirtualFieldsPlaces(context, item) || result; result = performEnhancement(context, item) || result; } catch (SQLException e) { LOGGER.error("An error occurs enhancing item with id {}: {}", item.getID(), e.getMessage(), e); throw new SQLRuntimeException(e); } } else { - List currMetadataValues = getCurrentVirtualMetadata(context, item); - List toBeMetadataValues = getToBeVirtualMetadata(context, item); + Map> currMetadataValues = getCurrentVirtualsMap(item); + Map> toBeMetadataValues = getToBeVirtualMetadata(context, item); if (!equivalent(currMetadataValues, toBeMetadataValues)) { try { - itemService.removeMetadataValues(context, item, currMetadataValues); + clearAllVirtualMetadata(context, item); addMetadata(context, item, toBeMetadataValues); } catch (SQLException e) { throw new SQLRuntimeException(e); @@ -84,20 +96,44 @@ public boolean enhance(Context context, Item item, boolean deepMode) { return result; } - private void addMetadata(Context context, Item item, List toBeMetadataValues) + private void clearAllVirtualMetadata(Context context, Item item) throws SQLException { + itemService.clearMetadata(context, item, VIRTUAL_METADATA_SCHEMA, VIRTUAL_SOURCE_METADATA_ELEMENT, + getVirtualQualifier(), Item.ANY); + itemService.clearMetadata(context, item, VIRTUAL_METADATA_SCHEMA, VIRTUAL_METADATA_ELEMENT, + getVirtualQualifier(), Item.ANY); + } + + private void addMetadata(Context context, Item item, Map> toBeMetadataValues) throws SQLException { - for (MetadataValueDTO dto : toBeMetadataValues) { - itemService.addMetadata(context, item, dto.getSchema(), dto.getElement(), dto.getQualifier(), null, - dto.getValue(), dto.getAuthority(), dto.getConfidence()); + for (Entry> metadataValues : toBeMetadataValues.entrySet()) { + addVirtualSourceField(context, item, metadataValues.getKey()); + for (MetadataValueDTO dto : metadataValues.getValue()) { + addVirtualField(context, item, dto.getValue(), dto.getAuthority(), dto.getLanguage(), + dto.getConfidence()); + } } } - private boolean equivalent(List currMetadataValues, List toBeMetadataValues) { + private boolean equivalent(Map> currMetadataValues, + Map> toBeMetadataValues) { if (currMetadataValues.size() != toBeMetadataValues.size()) { return false; } else { - for (int idx = 0; idx < currMetadataValues.size(); idx++) { - if (!equivalent(currMetadataValues.get(idx), toBeMetadataValues.get(idx))) { + for (String key : currMetadataValues.keySet()) { + if (!equivalent(currMetadataValues.get(key), toBeMetadataValues.get(key))) { + return false; + } + } + } + return true; + } + + private boolean equivalent(List metadataValue, List metadataValueDTO) { + if (metadataValue.size() != metadataValueDTO.size()) { + return false; + } else { + for (int i = 0; i < metadataValue.size(); i++) { + if (!equivalent(metadataValue.get(i), metadataValueDTO.get(i))) { return false; } } @@ -114,23 +150,14 @@ private boolean equivalent(MetadataValue metadataValue, MetadataValueDTO metadat && StringUtils.equals(metadataValue.getAuthority(), metadataValueDTO.getAuthority()); } - private List getToBeVirtualMetadata(Context context, Item item) { - List tobeVirtualMetadata = new ArrayList<>(); - List virtualSourceFields = getEnhanceableMetadataValue(item); - for (MetadataValue virtualSourceField : virtualSourceFields) { - MetadataValueDTO mv = new MetadataValueDTO(); - mv.setSchema(VIRTUAL_METADATA_SCHEMA); - mv.setElement(VIRTUAL_SOURCE_METADATA_ELEMENT); - mv.setQualifier(getVirtualQualifier()); - String authority = virtualSourceField.getAuthority(); + private Map> getToBeVirtualMetadata(Context context, Item item) { + Map> tobeVirtualMetadataMap = new HashMap>(); + + Set virtualSources = getVirtualSources(item); + for (String authority : virtualSources) { + List tobeVirtualMetadata = new ArrayList<>(); Item relatedItem = null; - if (StringUtils.isNotBlank(authority)) { - mv.setValue(authority); - relatedItem = findRelatedEntityItem(context, virtualSourceField); - } else { - mv.setValue(PLACEHOLDER_PARENT_METADATA_VALUE); - } - tobeVirtualMetadata.add(mv); + relatedItem = findRelatedEntityItem(context, authority); if (relatedItem == null) { MetadataValueDTO mvRelated = new MetadataValueDTO(); mvRelated.setSchema(VIRTUAL_METADATA_SCHEMA); @@ -138,44 +165,35 @@ private List getToBeVirtualMetadata(Context context, Item item mvRelated.setQualifier(getVirtualQualifier()); mvRelated.setValue(PLACEHOLDER_PARENT_METADATA_VALUE); tobeVirtualMetadata.add(mvRelated); - continue; - } - - List relatedItemMetadataValues = getMetadataValues(relatedItem, relatedItemMetadataField); - if (relatedItemMetadataValues.isEmpty()) { - MetadataValueDTO mvRelated = new MetadataValueDTO(); - mvRelated.setSchema(VIRTUAL_METADATA_SCHEMA); - mvRelated.setElement(VIRTUAL_METADATA_ELEMENT); - mvRelated.setQualifier(getVirtualQualifier()); - mvRelated.setValue(PLACEHOLDER_PARENT_METADATA_VALUE); - tobeVirtualMetadata.add(mvRelated); - continue; - } - for (MetadataValue relatedItemMetadataValue : relatedItemMetadataValues) { - MetadataValueDTO mvRelated = new MetadataValueDTO(); - mvRelated.setSchema(VIRTUAL_METADATA_SCHEMA); - mvRelated.setElement(VIRTUAL_METADATA_ELEMENT); - mvRelated.setQualifier(getVirtualQualifier()); - mvRelated.setValue(relatedItemMetadataValue.getValue()); - String authorityRelated = relatedItemMetadataValue.getAuthority(); - if (StringUtils.isNotBlank(authorityRelated)) { - mvRelated.setAuthority(authorityRelated); - mvRelated.setConfidence(Choices.CF_ACCEPTED); + } else { + List relatedItemMetadataValues = getMetadataValues(relatedItem, + relatedItemMetadataField); + if (relatedItemMetadataValues.isEmpty()) { + MetadataValueDTO mvRelated = new MetadataValueDTO(); + mvRelated.setSchema(VIRTUAL_METADATA_SCHEMA); + mvRelated.setElement(VIRTUAL_METADATA_ELEMENT); + mvRelated.setQualifier(getVirtualQualifier()); + mvRelated.setValue(PLACEHOLDER_PARENT_METADATA_VALUE); + tobeVirtualMetadata.add(mvRelated); + } else { + for (MetadataValue relatedItemMetadataValue : relatedItemMetadataValues) { + MetadataValueDTO mvRelated = new MetadataValueDTO(); + mvRelated.setSchema(VIRTUAL_METADATA_SCHEMA); + mvRelated.setElement(VIRTUAL_METADATA_ELEMENT); + mvRelated.setQualifier(getVirtualQualifier()); + mvRelated.setValue(relatedItemMetadataValue.getValue()); + String authorityRelated = relatedItemMetadataValue.getAuthority(); + if (StringUtils.isNotBlank(authorityRelated)) { + mvRelated.setAuthority(authorityRelated); + mvRelated.setConfidence(Choices.CF_ACCEPTED); + } + tobeVirtualMetadata.add(mvRelated); + } } - tobeVirtualMetadata.add(mvRelated); } + tobeVirtualMetadataMap.put(authority, tobeVirtualMetadata); } - return tobeVirtualMetadata; - } - - private List getCurrentVirtualMetadata(Context context, Item item) { - List currentVirtualMetadata = new ArrayList<>(); - List virtualSourceFields = getVirtualSourceFields(item); - for (MetadataValue virtualSourceField : virtualSourceFields) { - currentVirtualMetadata.add(virtualSourceField); - getRelatedVirtualField(item, virtualSourceField).ifPresent(currentVirtualMetadata::add); - } - return currentVirtualMetadata; + return tobeVirtualMetadataMap; } private boolean cleanObsoleteVirtualFields(Context context, Item item) throws SQLException { @@ -188,66 +206,17 @@ private boolean cleanObsoleteVirtualFields(Context context, Item item) throws SQ return result; } - private boolean updateVirtualFieldsPlaces(Context context, Item item) { - boolean result = false; - List virtualSourceFields = getVirtualSourceFields(item); - List enhanceableMetadataValue = getEnhanceableMetadataValue(item); - for (MetadataValue virtualSourceField : virtualSourceFields) { - Optional metadataWithPlaceToUpdate = metadataWithPlaceToUpdate(item, - enhanceableMetadataValue, virtualSourceField); - if (metadataWithPlaceToUpdate.isPresent()) { - updatePlaces(item, metadataWithPlaceToUpdate.get(), virtualSourceField); - result = true; - } - } - return result; - } - - private Optional metadataWithPlaceToUpdate(Item item, List enhanceableMetadataValue, - MetadataValue virtualSourceField) { - return findMetadataValueToUpdatePlace(enhanceableMetadataValue, virtualSourceField, - item); - } - - private boolean hasToUpdatePlace(MetadataValue metadataValue, MetadataValue virtualSourceField) { - return metadataValue.getPlace() != virtualSourceField.getPlace(); - } - - private void updatePlaces(Item item, MetadataValue mv, MetadataValue virtualSourceField) { - virtualSourceField.setPlace(mv.getPlace()); - getRelatedVirtualField(item, mv) - .ifPresent(relatedMv -> relatedMv.setPlace(mv.getPlace())); - } - - private Optional findMetadataValueToUpdatePlace(List enhanceableMetadataValue, - MetadataValue virtualSourceField, Item item) { - Optional exactMatch = enhanceableMetadataValue.stream() - .filter(metadataValue -> hasAuthorityEqualsTo(metadataValue, - virtualSourceField.getValue()) && !hasToUpdatePlace(metadataValue, virtualSourceField)) - .findFirst(); - if (exactMatch.isPresent()) { - enhanceableMetadataValue.remove(exactMatch.get()); - return Optional.empty(); - } else { - Optional authorityOnlyMatch = enhanceableMetadataValue.stream() - .filter(metadataValue -> hasAuthorityEqualsTo(metadataValue, - virtualSourceField.getValue()) && hasToUpdatePlace(metadataValue, virtualSourceField)) - .findFirst(); - enhanceableMetadataValue.remove(authorityOnlyMatch.get()); - return authorityOnlyMatch; - } - } - private List getObsoleteVirtualFields(Item item) { List obsoleteVirtualFields = new ArrayList<>(); - - List virtualSourceFields = getVirtualSourceFields(item); - List enhanceableMetadata = getEnhanceableMetadataValue(item); - for (MetadataValue virtualSourceField : virtualSourceFields) { - if (isRelatedSourceNoMorePresent(item, enhanceableMetadata, virtualSourceField)) { - obsoleteVirtualFields.add(virtualSourceField); - getRelatedVirtualField(item, virtualSourceField).ifPresent(obsoleteVirtualFields::add); + Map> currentVirtualsMap = getCurrentVirtualsMap(item); + Set virtualSources = getVirtualSources(item); + for (String authority : currentVirtualsMap.keySet()) { + if (!virtualSources.contains(authority)) { + for (MetadataValue mv : getVirtualSourceFields(item, authority)) { + obsoleteVirtualFields.add(mv); + getRelatedVirtualField(item, mv.getPlace()).ifPresent(obsoleteVirtualFields::add); + } } } @@ -255,142 +224,111 @@ private List getObsoleteVirtualFields(Item item) { } - /** - * This method will look in the enhanceableMetadata if the source metadata is still present. If so, it will remove - * form the list as it would not be used to validate other potential duplicate source metadata - * - * @param item - * @param enhanceableMetadata - * @param virtualSourceField - * @return true if the metadata containing a source of enhancement is still present in the list of the metadata to - * use to enhance the item - */ - private boolean isRelatedSourceNoMorePresent(Item item, List enhanceableMetadata, - MetadataValue virtualSourceField) { - Optional mv = enhanceableMetadata.stream() - .filter(metadataValue -> hasAuthorityEqualsTo(metadataValue, virtualSourceField.getValue())) - .findFirst(); - if (mv.isPresent()) { - enhanceableMetadata.remove(mv.get()); - return false; - } - return true; + private Set getVirtualSources(Item item) { + return itemService.getMetadataByMetadataString(item, sourceItemMetadataField).stream() + .filter(mv -> UUIDUtils.fromString(mv.getAuthority()) != null).map(mv -> mv.getAuthority()) + .collect(Collectors.toSet()); } - private Optional getRelatedVirtualField(Item item, MetadataValue virtualSourceField) { - return getVirtualFields(item).stream() - .filter(metadataValue -> metadataValue.getPlace() == virtualSourceField.getPlace()) - .findFirst(); - } + private Map> getCurrentVirtualsMap(Item item) { + Map> currentVirtualsMap = new HashMap>(); + List sources = itemService.getMetadata(item, VIRTUAL_METADATA_SCHEMA, + VIRTUAL_SOURCE_METADATA_ELEMENT, getVirtualQualifier(), Item.ANY); + List generated = itemService.getMetadata(item, VIRTUAL_METADATA_SCHEMA, VIRTUAL_METADATA_ELEMENT, + getVirtualQualifier(), Item.ANY); - private boolean performEnhancement(Context context, Item item) throws SQLException { - boolean result = false; - if (noEnhanceableMetadata(context, item)) { - return false; + if (sources.size() != generated.size()) { + LOGGER.error( + "inconsistent virtual metadata for the item {} got {} sources and {} generated virtual metadata", + item.getID().toString(), sources.size(), generated.size()); } - for (MetadataValue metadataValue : getEnhanceableMetadataValue(item)) { - - if (wasValueAlreadyUsedForEnhancement(item, metadataValue)) { - continue; - } - - Item relatedItem = findRelatedEntityItem(context, metadataValue); - if (relatedItem == null) { - addVirtualField(context, item, PLACEHOLDER_PARENT_METADATA_VALUE); - addVirtualSourceField(context, item, metadataValue); - continue; + for (int i = 0; i < Integer.max(sources.size(), generated.size()); i++) { + String authority; + if (i < sources.size()) { + authority = sources.get(i).getValue(); + } else { + // we have less source than virtual metadata let's generate a random uuid to + // associate with these extra metadata so that they will be managed as obsolete + // value + authority = UUID.randomUUID().toString(); } - - List relatedItemMetadataValues = getMetadataValues(relatedItem, relatedItemMetadataField); - if (relatedItemMetadataValues.isEmpty()) { - addVirtualField(context, item, PLACEHOLDER_PARENT_METADATA_VALUE); - addVirtualSourceField(context, item, metadataValue); - continue; + List mvalues = currentVirtualsMap.get(authority); + if (mvalues == null) { + mvalues = new ArrayList(); } - for (MetadataValue relatedItemMetadataValue : relatedItemMetadataValues) { - addVirtualField(context, item, relatedItemMetadataValue.getValue()); - addVirtualSourceField(context, item, metadataValue); + if (i < generated.size()) { + mvalues.add(generated.get(i)); } - result = true; + currentVirtualsMap.put(authority, mvalues); } - return result; - } - - private boolean noEnhanceableMetadata(Context context, Item item) { - - return getEnhanceableMetadataValue(item) - .stream() - .noneMatch(metadataValue -> validAuthority(context, metadataValue)); - } - - private boolean validAuthority(Context context, MetadataValue metadataValue) { - Item relatedItem = findRelatedEntityItem(context, metadataValue); - return Objects.nonNull(relatedItem); + return currentVirtualsMap; } - private List getEnhanceableMetadataValue(Item item) { - return getMetadataValues(item, sourceItemMetadataField); + private Optional getRelatedVirtualField(Item item, int pos) { + return getVirtualFields(item).stream() + .skip(pos) + .findFirst(); } - private boolean wasValueAlreadyUsedForEnhancement(Item item, MetadataValue metadataValue) { + private boolean performEnhancement(Context context, Item item) throws SQLException { + boolean result = false; + Map> currentVirtualsMap = getCurrentVirtualsMap(item); + Set virtualSources = getVirtualSources(item); + for (String authority : virtualSources) { + if (!currentVirtualsMap.containsKey(authority)) { + Item relatedItem = findRelatedEntityItem(context, authority); + if (relatedItem == null) { + addVirtualField(context, item, PLACEHOLDER_PARENT_METADATA_VALUE, null, null, Choices.CF_UNSET); + addVirtualSourceField(context, item, authority); + continue; + } - if (isPlaceholderAtPlace(getVirtualFields(item), metadataValue.getPlace())) { - return true; + List relatedItemMetadataValues = getMetadataValues(relatedItem, + relatedItemMetadataField); + if (relatedItemMetadataValues.isEmpty()) { + addVirtualField(context, item, PLACEHOLDER_PARENT_METADATA_VALUE, null, null, Choices.CF_UNSET); + addVirtualSourceField(context, item, authority); + continue; + } + for (MetadataValue relatedItemMetadataValue : relatedItemMetadataValues) { + addVirtualField(context, item, relatedItemMetadataValue.getValue(), + relatedItemMetadataValue.getAuthority(), relatedItemMetadataValue.getLanguage(), + relatedItemMetadataValue.getConfidence()); + addVirtualSourceField(context, item, authority); + } + result = true; + } } - - return getVirtualSourceFields(item).stream() - .anyMatch(virtualSourceField -> virtualSourceField.getPlace() == metadataValue.getPlace() - && hasAuthorityEqualsTo(metadataValue, virtualSourceField.getValue())); - - } - - private boolean isPlaceholderAtPlace(List metadataValues, int place) { - return place < metadataValues.size() ? isPlaceholder(metadataValues.get(place)) : false; - } - - private boolean hasAuthorityEqualsTo(MetadataValue metadataValue, String authority) { - return Objects.equals(metadataValue.getAuthority(), authority) - || (StringUtils.isBlank(metadataValue.getAuthority()) - && Objects.equals(PLACEHOLDER_PARENT_METADATA_VALUE, authority)); + return result; } - private Item findRelatedEntityItem(Context context, MetadataValue metadataValue) { + private Item findRelatedEntityItem(Context context, String authority) { try { - UUID relatedItemUUID = UUIDUtils.fromString(metadataValue.getAuthority()); + UUID relatedItemUUID = UUIDUtils.fromString(authority); return relatedItemUUID != null ? itemService.find(context, relatedItemUUID) : null; } catch (SQLException e) { throw new SQLRuntimeException(e); } } - private boolean isPlaceholder(MetadataValue metadataValue) { - return PLACEHOLDER_PARENT_METADATA_VALUE.equals(metadataValue.getValue()); - } - private List getMetadataValues(Item item, String metadataField) { return itemService.getMetadataByMetadataString(item, metadataField); } - private List getVirtualSourceFields(Item item) { - return getMetadataValues(item, getVirtualSourceMetadataField()); + private List getVirtualSourceFields(Item item, String authority) { + return getMetadataValues(item, getVirtualSourceMetadataField()).stream() + .filter(mv -> StringUtils.equals(authority, mv.getValue())).collect(Collectors.toList()); } private List getVirtualFields(Item item) { return getMetadataValues(item, getVirtualMetadataField()); } - private void addVirtualField(Context context, Item item, String value) throws SQLException { - itemService.addMetadata(context, item, VIRTUAL_METADATA_SCHEMA, VIRTUAL_METADATA_ELEMENT, - getVirtualQualifier(), null, value); - } - - private void addVirtualSourceField(Context context, Item item, MetadataValue sourceValue) throws SQLException { - if (StringUtils.isNotBlank(sourceValue.getAuthority())) { - addVirtualSourceField(context, item, sourceValue.getAuthority()); - } else { - addVirtualSourceField(context, item, PLACEHOLDER_PARENT_METADATA_VALUE); - } + private void addVirtualField(Context context, Item item, String value, String authority, String lang, + int confidence) throws SQLException { + itemService.addMetadata(context, item, VIRTUAL_METADATA_SCHEMA, VIRTUAL_METADATA_ELEMENT, getVirtualQualifier(), + lang, value, authority, confidence); } private void addVirtualSourceField(Context context, Item item, String sourceValueAuthority) throws SQLException { diff --git a/dspace-api/src/test/java/org/dspace/app/matcher/MetadataValueMatcher.java b/dspace-api/src/test/java/org/dspace/app/matcher/MetadataValueMatcher.java index 1439c9c37fa..55ceb779ba0 100644 --- a/dspace-api/src/test/java/org/dspace/app/matcher/MetadataValueMatcher.java +++ b/dspace-api/src/test/java/org/dspace/app/matcher/MetadataValueMatcher.java @@ -72,7 +72,8 @@ protected boolean matchesSafely(MetadataValue metadataValue) { Objects.equals(metadataValue.getMetadataField().toString('.'), field) && Objects.equals(metadataValue.getLanguage(), language) && Objects.equals(metadataValue.getAuthority(), authority) && - Objects.equals(metadataValue.getPlace(), place) && + (Objects.isNull(place) + || Objects.equals(metadataValue.getPlace(), place)) && Objects.equals(metadataValue.getConfidence(), confidence) && Objects.equals(metadataValue.getSecurityLevel(), securityLevel); } @@ -91,6 +92,10 @@ public static MetadataValueMatcher with(String field, String value) { return with(field, value, null, null, 0, -1); } + public static MetadataValueMatcher withNoPlace(String field, String value) { + return with(field, value, null, null, null, -1); + } + public static MetadataValueMatcher withSecurity(String field, String value, Integer securityLevel) { return with(field, value, null, null, 0, -1, securityLevel); } diff --git a/dspace-api/src/test/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumerIT.java b/dspace-api/src/test/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumerIT.java index 176f055a446..aa440574c07 100644 --- a/dspace-api/src/test/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumerIT.java +++ b/dspace-api/src/test/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumerIT.java @@ -8,6 +8,7 @@ package org.dspace.content.enhancer.consumer; import static org.dspace.app.matcher.MetadataValueMatcher.with; +import static org.dspace.app.matcher.MetadataValueMatcher.withNoPlace; import static org.dspace.core.CrisConstants.PLACEHOLDER_PARENT_METADATA_VALUE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; @@ -141,31 +142,29 @@ public void testManyMetadataValuesEnhancement() throws Exception { publication = commitAndReload(publication); List values = publication.getMetadata(); - assertThat(values, hasSize(26)); + assertThat(values, hasSize(22)); assertThat(values, hasItem(with("dc.contributor.author", "Red Smith"))); assertThat(values, hasItem(with("dc.contributor.author", "Walter White", person1.getID().toString(), 1, 600))); assertThat(values, hasItem(with("dc.contributor.author", "John Smith", person2.getID().toString(), 2, 600))); assertThat(values, hasItem(with("dc.contributor.author", "Jesse Pinkman", person3.getID().toString(), 3, 600))); - assertThat(values, hasItem(with("cris.virtual.department", PLACEHOLDER_PARENT_METADATA_VALUE, 0))); - assertThat(values, hasItem(with("cris.virtualsource.department", PLACEHOLDER_PARENT_METADATA_VALUE, 0))); - assertThat(values, hasItem(with("cris.virtual.department", "4Science", 1))); - assertThat(values, hasItem(with("cris.virtualsource.department", person1.getID().toString(), 1))); - assertThat(values, hasItem(with("cris.virtual.department", PLACEHOLDER_PARENT_METADATA_VALUE, 2))); - assertThat(values, hasItem(with("cris.virtualsource.department", person2.getID().toString(), 2))); - assertThat(values, hasItem(with("cris.virtual.department", "University of Rome", 3))); - assertThat(values, hasItem(with("cris.virtualsource.department", person3.getID().toString(), 3))); + // virtual source and virtual metadata are not required to respect the order of the source metadata + assertThat(values, hasItem(withNoPlace("cris.virtualsource.department", person1.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.department", person2.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.department", person3.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtual.department", PLACEHOLDER_PARENT_METADATA_VALUE))); + assertThat(values, hasItem(withNoPlace("cris.virtual.department", "4Science"))); + assertThat(values, hasItem(withNoPlace("cris.virtual.department", "University of Rome"))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.author-orcid", person1.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.author-orcid", person2.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.author-orcid", person3.getID().toString()))); + // we can check with the position as all the values are expected to be placeholder assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 0))); - assertThat(values, hasItem(with("cris.virtualsource.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 0))); assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 1))); - assertThat(values, hasItem(with("cris.virtualsource.author-orcid", person1.getID().toString(), 1))); assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 2))); - assertThat(values, hasItem(with("cris.virtualsource.author-orcid", person2.getID().toString(), 2))); - assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 3))); - assertThat(values, hasItem(with("cris.virtualsource.author-orcid", person3.getID().toString(), 3))); - assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(4)); - assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(4)); - assertThat(getMetadataValues(publication, "cris.virtual.author-orcid"), hasSize(4)); - assertThat(getMetadataValues(publication, "cris.virtualsource.author-orcid"), hasSize(4)); + assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(3)); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(3)); + assertThat(getMetadataValues(publication, "cris.virtual.author-orcid"), hasSize(3)); + assertThat(getMetadataValues(publication, "cris.virtualsource.author-orcid"), hasSize(3)); } @@ -246,22 +245,28 @@ public void testEnhancementWithMetadataRemoval() throws Exception { assertThat(values, hasItem(with("dc.contributor.author", "Walter White", person1.getID().toString(), 0, 600))); assertThat(values, hasItem(with("dc.contributor.author", "John Smith", person2.getID().toString(), 1, 600))); assertThat(values, hasItem(with("dc.contributor.author", "Jesse Pinkman", person3.getID().toString(), 2, 600))); - assertThat(values, hasItem(with("cris.virtual.department", "4Science"))); - assertThat(values, hasItem(with("cris.virtualsource.department", person1.getID().toString()))); - assertThat(values, hasItem(with("cris.virtual.department", "Company", 1))); - assertThat(values, hasItem(with("cris.virtualsource.department", person2.getID().toString(), 1))); - assertThat(values, hasItem(with("cris.virtual.department", "University of Rome", 2))); - assertThat(values, hasItem(with("cris.virtualsource.department", person3.getID().toString(), 2))); - assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); - assertThat(values, hasItem(with("cris.virtualsource.author-orcid", person1.getID().toString()))); - assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); - assertThat(values, hasItem(with("cris.virtualsource.author-orcid", person2.getID().toString(), 1))); - assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); - assertThat(values, hasItem(with("cris.virtualsource.author-orcid", person3.getID().toString(), 2))); + // virtual source and virtual metadata are not required to respect the order of the source metadata + assertThat(values, hasItem(withNoPlace("cris.virtualsource.department", person1.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.department", person2.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.department", person3.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtual.department", "4Science"))); + assertThat(values, hasItem(withNoPlace("cris.virtual.department", "Company"))); + assertThat(values, hasItem(withNoPlace("cris.virtual.department", "University of Rome"))); assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(3)); assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(3)); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.author-orcid", person1.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.author-orcid", person2.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.author-orcid", person3.getID().toString()))); + // we can check with the position as all the values are expected to be placeholder + assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 0))); + assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 1))); + assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 2))); + assertThat(getMetadataValues(publication, "cris.virtual.author-orcid"), hasSize(3)); + assertThat(getMetadataValues(publication, "cris.virtualsource.author-orcid"), hasSize(3)); + + MetadataValue authorToRemove = getMetadataValues(publication, "dc.contributor.author").get(1); context.turnOffAuthorisationSystem(); @@ -274,14 +279,16 @@ public void testEnhancementWithMetadataRemoval() throws Exception { assertThat(values, hasSize(16)); assertThat(values, hasItem(with("dc.contributor.author", "Walter White", person1.getID().toString(), 0, 600))); assertThat(values, hasItem(with("dc.contributor.author", "Jesse Pinkman", person3.getID().toString(), 1, 600))); - assertThat(values, hasItem(with("cris.virtual.department", "4Science"))); - assertThat(values, hasItem(with("cris.virtualsource.department", person1.getID().toString()))); - assertThat(values, hasItem(with("cris.virtual.department", "University of Rome", 1))); - assertThat(values, hasItem(with("cris.virtualsource.department", person3.getID().toString(), 1))); + // virtual source and virtual metadata are not required to respect the order of the source metadata + assertThat(values, hasItem(withNoPlace("cris.virtualsource.department", person1.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.department", person3.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtual.department", "4Science"))); + assertThat(values, hasItem(withNoPlace("cris.virtual.department", "University of Rome"))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.author-orcid", person1.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.author-orcid", person3.getID().toString()))); + // we can check with the position as all the values are expected to be placeholder assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 0))); - assertThat(values, hasItem(with("cris.virtualsource.author-orcid", person1.getID().toString(), 0))); assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 1))); - assertThat(values, hasItem(with("cris.virtualsource.author-orcid", person3.getID().toString(), 1))); assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(2)); assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(2)); assertThat(getMetadataValues(publication, "cris.virtual.author-orcid"), hasSize(2)); @@ -348,16 +355,10 @@ public void testEnhancementAfterItemUpdate() throws Exception { with("dc.contributor.author", "Gus Fring", 3))); assertThat(getMetadataValues(publication, "cris.virtual.author-orcid"), contains( - with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE), - with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 1), - with("cris.virtual.author-orcid", "0000-0000-1111-2222", 2), - with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 3))); + with("cris.virtual.author-orcid", "0000-0000-1111-2222"))); assertThat(getMetadataValues(publication, "cris.virtualsource.author-orcid"), contains( - with("cris.virtualsource.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE), - with("cris.virtualsource.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 1), - with("cris.virtualsource.author-orcid", personId, 2), - with("cris.virtualsource.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 3))); + with("cris.virtualsource.author-orcid", personId))); context.turnOffAuthorisationSystem(); itemService.addMetadata(context, publication, "dc", "title", "alternative", null, "Other name"); @@ -372,16 +373,10 @@ public void testEnhancementAfterItemUpdate() throws Exception { with("dc.contributor.author", "Gus Fring", 3))); assertThat(getMetadataValues(publication, "cris.virtual.author-orcid"), contains( - with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE), - with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 1), - with("cris.virtual.author-orcid", "0000-0000-1111-2222", 2), - with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 3))); + with("cris.virtual.author-orcid", "0000-0000-1111-2222"))); - assertThat(getMetadataValues(publication, "cris.virtualsource.author-orcid"), contains( - with("cris.virtualsource.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE), - with("cris.virtualsource.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 1), - with("cris.virtualsource.author-orcid", personId, 2), - with("cris.virtualsource.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 3))); + assertThat(getMetadataValues(publication, "cris.virtualsource.author-orcid"), contains( + with("cris.virtualsource.author-orcid", personId))); } diff --git a/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerScriptIT.java b/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerScriptIT.java index 33913368b0a..f8b2b0d2d77 100644 --- a/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerScriptIT.java +++ b/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerScriptIT.java @@ -8,11 +8,13 @@ package org.dspace.content.enhancer.script; import static org.dspace.app.matcher.MetadataValueMatcher.with; +import static org.dspace.app.matcher.MetadataValueMatcher.withNoPlace; import static org.dspace.content.Item.ANY; import static org.dspace.content.enhancer.consumer.ItemEnhancerConsumer.ITEMENHANCER_ENABLED; import static org.dspace.core.CrisConstants.PLACEHOLDER_PARENT_METADATA_VALUE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; @@ -171,11 +173,14 @@ public void testItemsEnhancement() throws Exception { assertThat(firstPublication.getMetadata(), hasItem(with("cris.virtual.department", "4Science"))); assertThat(firstPublication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); - assertThat(secondPublication.getMetadata(), hasItem(with("cris.virtual.department", "4Science"))); - assertThat(secondPublication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); - assertThat(secondPublication.getMetadata(), hasItem(with("cris.virtual.department", "Company", 1))); - assertThat(secondPublication.getMetadata(), hasItem(with("cris.virtualsource.department", secondAuthorId, 1))); - + assertThat(getMetadataValues(secondPublication, "cris.virtual.department"), + containsInAnyOrder( + withNoPlace("cris.virtual.department", "4Science"), + withNoPlace("cris.virtual.department", "Company"))); + assertThat(getMetadataValues(secondPublication, "cris.virtualsource.department"), + containsInAnyOrder( + withNoPlace("cris.virtualsource.department", firstAuthorId), + withNoPlace("cris.virtualsource.department", secondAuthorId))); assertThat(getMetadataValues(thirdPublication, "cris.virtual.department"), empty()); assertThat(getMetadataValues(thirdPublication, "cris.virtualsource.department"), empty()); @@ -223,10 +228,13 @@ public void testItemEnhancementWithoutForce() throws Exception { assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(2)); assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(2)); - assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "4Science"))); - assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); - assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "Company", 1))); - assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", secondAuthorId, 1))); + assertThat(getMetadataValues(publication, "cris.virtual.department"), containsInAnyOrder( + withNoPlace("cris.virtual.department", "4Science"), + withNoPlace("cris.virtual.department", "Company"))); + + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), containsInAnyOrder( + withNoPlace("cris.virtualsource.department", firstAuthorId), + withNoPlace("cris.virtualsource.department", secondAuthorId))); context.turnOffAuthorisationSystem(); @@ -293,10 +301,12 @@ public void testItemEnhancementWithForce() throws Exception { assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(2)); assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(2)); - assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "4Science"))); - assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); - assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "Company", 1))); - assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", secondAuthorId, 1))); + assertThat(getMetadataValues(publication, "cris.virtual.department"), containsInAnyOrder( + withNoPlace("cris.virtual.department", "4Science"), + withNoPlace("cris.virtual.department", "Company"))); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), containsInAnyOrder( + withNoPlace("cris.virtualsource.department", firstAuthorId), + withNoPlace("cris.virtualsource.department", secondAuthorId))); context.turnOffAuthorisationSystem(); @@ -375,17 +385,16 @@ public void testItemEnhancementMetadataPositions() throws Exception { assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(4)); assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(4)); - assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", - PLACEHOLDER_PARENT_METADATA_VALUE, 0))); - assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId,0))); - assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "4Science", 1))); - assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", secondAuthorId,1))); - assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "Company", 2))); - assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", thirdAuthorId, 2))); - assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", - PLACEHOLDER_PARENT_METADATA_VALUE, 3))); - assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", fourthAuthorId,3))); - + assertThat(getMetadataValues(publication, "cris.virtual.department"), containsInAnyOrder( + withNoPlace("cris.virtual.department", PLACEHOLDER_PARENT_METADATA_VALUE), + withNoPlace("cris.virtual.department", "4Science"), + withNoPlace("cris.virtual.department", "Company"), + withNoPlace("cris.virtual.department", PLACEHOLDER_PARENT_METADATA_VALUE))); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), containsInAnyOrder( + withNoPlace("cris.virtualsource.department", firstAuthorId), + withNoPlace("cris.virtualsource.department", secondAuthorId), + withNoPlace("cris.virtualsource.department", thirdAuthorId), + withNoPlace("cris.virtualsource.department", fourthAuthorId))); } @Test @@ -420,15 +429,11 @@ public void testItemEnhancementSourceWithoutAuthority() throws Exception { publication = reload(publication); - assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(2)); - assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(2)); + assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(1)); + assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(1)); - assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", - PLACEHOLDER_PARENT_METADATA_VALUE, 0))); - assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", - PLACEHOLDER_PARENT_METADATA_VALUE,0))); - assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "4Science", 1))); - assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", secondAuthorId,1))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtual.department", "4Science", 0))); + assertThat(publication.getMetadata(), hasItem(with("cris.virtualsource.department", secondAuthorId,0))); } From f6b4127a6b759c05751bd3722fe180ebda828d09 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Fri, 1 Mar 2024 20:15:51 +0100 Subject: [PATCH 80/82] DSC-1561 extend RelatedEntityItemEnhancer to manage a list of metadata to use as source and a list of related item metadata from which extract the enhanced metadata --- .../impl/RelatedEntityItemEnhancer.java | 77 ++++++--- .../api/extra-metadata-enhancers-for-test.xml | 40 +++++ .../service/BulkImportWorkbookBuilderIT.java | 6 +- .../consumer/ItemEnhancerConsumerIT.java | 153 +++++++++++++----- .../enhancer/script/ItemEnhancerScriptIT.java | 59 +++++-- .../org/dspace/harvest/OAIHarvesterIT.java | 6 +- dspace/config/registries/cris-types.xml | 18 +-- .../config/spring/api/metadata-enhancers.xml | 46 ++++-- 8 files changed, 290 insertions(+), 115 deletions(-) create mode 100644 dspace-api/src/test/data/dspaceFolder/config/spring/api/extra-metadata-enhancers-for-test.xml diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java index 284f9b8bff8..7a6f2927092 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java @@ -57,12 +57,12 @@ public class RelatedEntityItemEnhancer extends AbstractItemEnhancer { /** * the metadata used to navigate the relation, i.e. dc.contributor.author */ - private String sourceItemMetadataField; + private List sourceItemMetadataFields; /** * the metadata that is copied from the linked entity, i.e. person.identifier.orcid */ - private String relatedItemMetadataField; + private List relatedItemMetadataFields; @Override public boolean canEnhance(Context context, Item item) { @@ -166,16 +166,10 @@ private Map> getToBeVirtualMetadata(Context conte mvRelated.setValue(PLACEHOLDER_PARENT_METADATA_VALUE); tobeVirtualMetadata.add(mvRelated); } else { - List relatedItemMetadataValues = getMetadataValues(relatedItem, - relatedItemMetadataField); - if (relatedItemMetadataValues.isEmpty()) { - MetadataValueDTO mvRelated = new MetadataValueDTO(); - mvRelated.setSchema(VIRTUAL_METADATA_SCHEMA); - mvRelated.setElement(VIRTUAL_METADATA_ELEMENT); - mvRelated.setQualifier(getVirtualQualifier()); - mvRelated.setValue(PLACEHOLDER_PARENT_METADATA_VALUE); - tobeVirtualMetadata.add(mvRelated); - } else { + boolean foundAtLeastOneValue = false; + for (String relatedItemMetadataField : relatedItemMetadataFields) { + List relatedItemMetadataValues = getMetadataValues(relatedItem, + relatedItemMetadataField); for (MetadataValue relatedItemMetadataValue : relatedItemMetadataValues) { MetadataValueDTO mvRelated = new MetadataValueDTO(); mvRelated.setSchema(VIRTUAL_METADATA_SCHEMA); @@ -188,8 +182,17 @@ private Map> getToBeVirtualMetadata(Context conte mvRelated.setConfidence(Choices.CF_ACCEPTED); } tobeVirtualMetadata.add(mvRelated); + foundAtLeastOneValue = true; } } + if (!foundAtLeastOneValue) { + MetadataValueDTO mvRelated = new MetadataValueDTO(); + mvRelated.setSchema(VIRTUAL_METADATA_SCHEMA); + mvRelated.setElement(VIRTUAL_METADATA_ELEMENT); + mvRelated.setQualifier(getVirtualQualifier()); + mvRelated.setValue(PLACEHOLDER_PARENT_METADATA_VALUE); + tobeVirtualMetadata.add(mvRelated); + } } tobeVirtualMetadataMap.put(authority, tobeVirtualMetadata); } @@ -225,8 +228,10 @@ private List getObsoleteVirtualFields(Item item) { } private Set getVirtualSources(Item item) { - return itemService.getMetadataByMetadataString(item, sourceItemMetadataField).stream() - .filter(mv -> UUIDUtils.fromString(mv.getAuthority()) != null).map(mv -> mv.getAuthority()) + return sourceItemMetadataFields.stream() + .flatMap(field -> itemService.getMetadataByMetadataString(item, field).stream()) + .filter(mv -> UUIDUtils.fromString(mv.getAuthority()) != null) + .map(mv -> mv.getAuthority()) .collect(Collectors.toSet()); } @@ -276,7 +281,9 @@ private boolean performEnhancement(Context context, Item item) throws SQLExcepti Map> currentVirtualsMap = getCurrentVirtualsMap(item); Set virtualSources = getVirtualSources(item); for (String authority : virtualSources) { + boolean foundAtLeastOne = false; if (!currentVirtualsMap.containsKey(authority)) { + result = true; Item relatedItem = findRelatedEntityItem(context, authority); if (relatedItem == null) { addVirtualField(context, item, PLACEHOLDER_PARENT_METADATA_VALUE, null, null, Choices.CF_UNSET); @@ -284,20 +291,22 @@ private boolean performEnhancement(Context context, Item item) throws SQLExcepti continue; } - List relatedItemMetadataValues = getMetadataValues(relatedItem, - relatedItemMetadataField); - if (relatedItemMetadataValues.isEmpty()) { + for (String relatedItemMetadataField : relatedItemMetadataFields) { + List relatedItemMetadataValues = getMetadataValues(relatedItem, + relatedItemMetadataField); + for (MetadataValue relatedItemMetadataValue : relatedItemMetadataValues) { + foundAtLeastOne = true; + addVirtualField(context, item, relatedItemMetadataValue.getValue(), + relatedItemMetadataValue.getAuthority(), relatedItemMetadataValue.getLanguage(), + relatedItemMetadataValue.getConfidence()); + addVirtualSourceField(context, item, authority); + } + } + if (!foundAtLeastOne) { addVirtualField(context, item, PLACEHOLDER_PARENT_METADATA_VALUE, null, null, Choices.CF_UNSET); addVirtualSourceField(context, item, authority); continue; } - for (MetadataValue relatedItemMetadataValue : relatedItemMetadataValues) { - addVirtualField(context, item, relatedItemMetadataValue.getValue(), - relatedItemMetadataValue.getAuthority(), relatedItemMetadataValue.getLanguage(), - relatedItemMetadataValue.getConfidence()); - addVirtualSourceField(context, item, authority); - } - result = true; } } return result; @@ -340,12 +349,28 @@ public void setSourceEntityType(String sourceEntityType) { this.sourceEntityType = sourceEntityType; } + @Deprecated public void setSourceItemMetadataField(String sourceItemMetadataField) { - this.sourceItemMetadataField = sourceItemMetadataField; + LOGGER.warn( + "RelatedEntityItemEnhancer configured using the old single source item metadata field, " + + "please update the configuration to use the list"); + this.sourceItemMetadataFields = List.of(sourceItemMetadataField); } + @Deprecated public void setRelatedItemMetadataField(String relatedItemMetadataField) { - this.relatedItemMetadataField = relatedItemMetadataField; + LOGGER.warn( + "RelatedEntityItemEnhancer configured using the old single related item metadata field, " + + "please update the configuration to use the list"); + this.relatedItemMetadataFields = List.of(relatedItemMetadataField); + } + + public void setRelatedItemMetadataFields(List relatedItemMetadataFields) { + this.relatedItemMetadataFields = relatedItemMetadataFields; + } + + public void setSourceItemMetadataFields(List sourceItemMetadataFields) { + this.sourceItemMetadataFields = sourceItemMetadataFields; } } diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/extra-metadata-enhancers-for-test.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/extra-metadata-enhancers-for-test.xml new file mode 100644 index 00000000000..0311d8a26aa --- /dev/null +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/extra-metadata-enhancers-for-test.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + dc.contributor.author + dc.contributor.editor + + + + + person.affiliation.name + person.identifier.orcid + + + + + + diff --git a/dspace-api/src/test/java/org/dspace/app/bulkimport/service/BulkImportWorkbookBuilderIT.java b/dspace-api/src/test/java/org/dspace/app/bulkimport/service/BulkImportWorkbookBuilderIT.java index bef8ca45c09..4d4af87ddaa 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkimport/service/BulkImportWorkbookBuilderIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkimport/service/BulkImportWorkbookBuilderIT.java @@ -221,7 +221,7 @@ public void testWorkbookBuildingFromItemDtos() throws Exception { with("dc.contributor.author", "White, Walter", authorId, 600), with("oairecerif.author.affiliation", PLACEHOLDER_PARENT_METADATA_VALUE), with("cris.virtual.department", PLACEHOLDER_PARENT_METADATA_VALUE), - with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); + with("cris.virtual.orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); assertThat(getItemBitstreamsByBundle(firstItem, "ORIGINAL"), contains( bitstreamWith("Bitstream 1", "First bitstream content"), @@ -242,8 +242,8 @@ public void testWorkbookBuildingFromItemDtos() throws Exception { with("oairecerif.author.affiliation", "Company", 1), with("cris.virtual.department", PLACEHOLDER_PARENT_METADATA_VALUE), with("cris.virtual.department", PLACEHOLDER_PARENT_METADATA_VALUE), - with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE), - with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE) + with("cris.virtual.orcid", PLACEHOLDER_PARENT_METADATA_VALUE), + with("cris.virtual.orcid", PLACEHOLDER_PARENT_METADATA_VALUE) )); assertThat(getItemBitstreamsByBundle(secondItem, "ORIGINAL"), contains( diff --git a/dspace-api/src/test/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumerIT.java b/dspace-api/src/test/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumerIT.java index aa440574c07..b2b34c1074f 100644 --- a/dspace-api/src/test/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumerIT.java +++ b/dspace-api/src/test/java/org/dspace/content/enhancer/consumer/ItemEnhancerConsumerIT.java @@ -19,15 +19,19 @@ import java.sql.SQLException; import java.util.List; +import java.util.stream.Collectors; +import org.apache.commons.codec.binary.StringUtils; import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.authorize.AuthorizeException; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.ItemBuilder; +import org.dspace.builder.MetadataFieldBuilder; import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.content.Collection; import org.dspace.content.Item; +import org.dspace.content.MetadataSchema; import org.dspace.content.MetadataValue; import org.dspace.content.WorkspaceItem; import org.dspace.content.factory.ContentServiceFactory; @@ -84,8 +88,8 @@ public void testSingleMetadataValueEnhancement() throws Exception { assertThat(metadataValues, hasSize(11)); assertThat(metadataValues, hasItem(with("cris.virtual.department", "4Science"))); assertThat(metadataValues, hasItem(with("cris.virtualsource.department", personId))); - assertThat(metadataValues, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); - assertThat(metadataValues, hasItem(with("cris.virtualsource.author-orcid", personId))); + assertThat(metadataValues, hasItem(with("cris.virtual.orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); + assertThat(metadataValues, hasItem(with("cris.virtualsource.orcid", personId))); MetadataValue virtualField = getFirstMetadataValue(publication, "cris.virtual.department"); @@ -102,8 +106,8 @@ public void testSingleMetadataValueEnhancement() throws Exception { assertThat(metadataValues, hasItem(with("dc.contributor.author", "Walter White", personId, 600))); assertThat(metadataValues, hasItem(with("cris.virtual.department", "4Science"))); assertThat(metadataValues, hasItem(with("cris.virtualsource.department", personId))); - assertThat(metadataValues, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); - assertThat(metadataValues, hasItem(with("cris.virtualsource.author-orcid", personId))); + assertThat(metadataValues, hasItem(with("cris.virtual.orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); + assertThat(metadataValues, hasItem(with("cris.virtualsource.orcid", personId))); assertThat(virtualField, equalTo(getFirstMetadataValue(publication, "cris.virtual.department"))); assertThat(virtualSourceField, equalTo(getFirstMetadataValue(publication, "cris.virtualsource.department"))); @@ -135,7 +139,7 @@ public void testManyMetadataValuesEnhancement() throws Exception { .withAuthor("Red Smith") .withAuthor("Walter White", person1.getID().toString()) .withAuthor("John Smith", person2.getID().toString()) - .withAuthor("Jesse Pinkman", person3.getID().toString()) + .withEditor("Jesse Pinkman", person3.getID().toString()) .build(); context.restoreAuthSystemState(); @@ -146,7 +150,7 @@ public void testManyMetadataValuesEnhancement() throws Exception { assertThat(values, hasItem(with("dc.contributor.author", "Red Smith"))); assertThat(values, hasItem(with("dc.contributor.author", "Walter White", person1.getID().toString(), 1, 600))); assertThat(values, hasItem(with("dc.contributor.author", "John Smith", person2.getID().toString(), 2, 600))); - assertThat(values, hasItem(with("dc.contributor.author", "Jesse Pinkman", person3.getID().toString(), 3, 600))); + assertThat(values, hasItem(with("dc.contributor.editor", "Jesse Pinkman", person3.getID().toString(), 0, 600))); // virtual source and virtual metadata are not required to respect the order of the source metadata assertThat(values, hasItem(withNoPlace("cris.virtualsource.department", person1.getID().toString()))); assertThat(values, hasItem(withNoPlace("cris.virtualsource.department", person2.getID().toString()))); @@ -154,17 +158,17 @@ public void testManyMetadataValuesEnhancement() throws Exception { assertThat(values, hasItem(withNoPlace("cris.virtual.department", PLACEHOLDER_PARENT_METADATA_VALUE))); assertThat(values, hasItem(withNoPlace("cris.virtual.department", "4Science"))); assertThat(values, hasItem(withNoPlace("cris.virtual.department", "University of Rome"))); - assertThat(values, hasItem(withNoPlace("cris.virtualsource.author-orcid", person1.getID().toString()))); - assertThat(values, hasItem(withNoPlace("cris.virtualsource.author-orcid", person2.getID().toString()))); - assertThat(values, hasItem(withNoPlace("cris.virtualsource.author-orcid", person3.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.orcid", person1.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.orcid", person2.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.orcid", person3.getID().toString()))); // we can check with the position as all the values are expected to be placeholder - assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 0))); - assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 1))); - assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 2))); + assertThat(values, hasItem(with("cris.virtual.orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 0))); + assertThat(values, hasItem(with("cris.virtual.orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 1))); + assertThat(values, hasItem(with("cris.virtual.orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 2))); assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(3)); assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(3)); - assertThat(getMetadataValues(publication, "cris.virtual.author-orcid"), hasSize(3)); - assertThat(getMetadataValues(publication, "cris.virtualsource.author-orcid"), hasSize(3)); + assertThat(getMetadataValues(publication, "cris.virtual.orcid"), hasSize(3)); + assertThat(getMetadataValues(publication, "cris.virtualsource.orcid"), hasSize(3)); } @@ -256,15 +260,15 @@ public void testEnhancementWithMetadataRemoval() throws Exception { assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(3)); assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(3)); - assertThat(values, hasItem(withNoPlace("cris.virtualsource.author-orcid", person1.getID().toString()))); - assertThat(values, hasItem(withNoPlace("cris.virtualsource.author-orcid", person2.getID().toString()))); - assertThat(values, hasItem(withNoPlace("cris.virtualsource.author-orcid", person3.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.orcid", person1.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.orcid", person2.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.orcid", person3.getID().toString()))); // we can check with the position as all the values are expected to be placeholder - assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 0))); - assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 1))); - assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 2))); - assertThat(getMetadataValues(publication, "cris.virtual.author-orcid"), hasSize(3)); - assertThat(getMetadataValues(publication, "cris.virtualsource.author-orcid"), hasSize(3)); + assertThat(values, hasItem(with("cris.virtual.orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 0))); + assertThat(values, hasItem(with("cris.virtual.orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 1))); + assertThat(values, hasItem(with("cris.virtual.orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 2))); + assertThat(getMetadataValues(publication, "cris.virtual.orcid"), hasSize(3)); + assertThat(getMetadataValues(publication, "cris.virtualsource.orcid"), hasSize(3)); MetadataValue authorToRemove = getMetadataValues(publication, "dc.contributor.author").get(1); @@ -284,15 +288,15 @@ public void testEnhancementWithMetadataRemoval() throws Exception { assertThat(values, hasItem(withNoPlace("cris.virtualsource.department", person3.getID().toString()))); assertThat(values, hasItem(withNoPlace("cris.virtual.department", "4Science"))); assertThat(values, hasItem(withNoPlace("cris.virtual.department", "University of Rome"))); - assertThat(values, hasItem(withNoPlace("cris.virtualsource.author-orcid", person1.getID().toString()))); - assertThat(values, hasItem(withNoPlace("cris.virtualsource.author-orcid", person3.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.orcid", person1.getID().toString()))); + assertThat(values, hasItem(withNoPlace("cris.virtualsource.orcid", person3.getID().toString()))); // we can check with the position as all the values are expected to be placeholder - assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 0))); - assertThat(values, hasItem(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 1))); + assertThat(values, hasItem(with("cris.virtual.orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 0))); + assertThat(values, hasItem(with("cris.virtual.orcid", PLACEHOLDER_PARENT_METADATA_VALUE, 1))); assertThat(getMetadataValues(publication, "cris.virtual.department"), hasSize(2)); assertThat(getMetadataValues(publication, "cris.virtualsource.department"), hasSize(2)); - assertThat(getMetadataValues(publication, "cris.virtual.author-orcid"), hasSize(2)); - assertThat(getMetadataValues(publication, "cris.virtualsource.author-orcid"), hasSize(2)); + assertThat(getMetadataValues(publication, "cris.virtual.orcid"), hasSize(2)); + assertThat(getMetadataValues(publication, "cris.virtualsource.orcid"), hasSize(2)); } @@ -354,11 +358,11 @@ public void testEnhancementAfterItemUpdate() throws Exception { with("dc.contributor.author", "Walter White", personId, 2, 600), with("dc.contributor.author", "Gus Fring", 3))); - assertThat(getMetadataValues(publication, "cris.virtual.author-orcid"), contains( - with("cris.virtual.author-orcid", "0000-0000-1111-2222"))); + assertThat(getMetadataValues(publication, "cris.virtual.orcid"), contains( + with("cris.virtual.orcid", "0000-0000-1111-2222"))); - assertThat(getMetadataValues(publication, "cris.virtualsource.author-orcid"), contains( - with("cris.virtualsource.author-orcid", personId))); + assertThat(getMetadataValues(publication, "cris.virtualsource.orcid"), contains( + with("cris.virtualsource.orcid", personId))); context.turnOffAuthorisationSystem(); itemService.addMetadata(context, publication, "dc", "title", "alternative", null, "Other name"); @@ -372,12 +376,89 @@ public void testEnhancementAfterItemUpdate() throws Exception { with("dc.contributor.author", "Walter White", personId, 2, 600), with("dc.contributor.author", "Gus Fring", 3))); - assertThat(getMetadataValues(publication, "cris.virtual.author-orcid"), contains( - with("cris.virtual.author-orcid", "0000-0000-1111-2222"))); + assertThat(getMetadataValues(publication, "cris.virtual.orcid"), contains( + with("cris.virtual.orcid", "0000-0000-1111-2222"))); + + assertThat(getMetadataValues(publication, "cris.virtualsource.orcid"), contains( + with("cris.virtualsource.orcid", personId))); + + } + + @Test + public void testMultipleRelatedItemValuesEnhancement() throws Exception { + + context.turnOffAuthorisationSystem(); + MetadataSchema schema = ContentServiceFactory.getInstance() + .getMetadataSchemaService().find(context, "cris"); + MetadataFieldBuilder.createMetadataField(context, schema, "virtual", "testmultival", null); + MetadataFieldBuilder.createMetadataField(context, schema, "virtualsource", "testmultival", null); + + Item person1 = ItemBuilder.createItem(context, collection) + .withTitle("Walter White") + .withPersonMainAffiliation("4Science") + .withPersonMainAffiliation("DSpace") + .withOrcidIdentifier("orcid1") + .build(); + + Item person2 = ItemBuilder.createItem(context, collection) + .withTitle("John Smith") + .build(); + + Item person3 = ItemBuilder.createItem(context, collection) + .withTitle("Jesse Pinkman") + .withPersonMainAffiliation("University of Rome") + .build(); + + Item testEntity = ItemBuilder.createItem(context, collection) + .withTitle("Test publication") + // let's use our custom entity for test purpose, see extra-metadata-enhancers-for-test.xml + .withEntityType("TestEntity") + .withAuthor("Red Smith") + .withAuthor("Walter White", person1.getID().toString()) + .withAuthor("John Smith", person2.getID().toString()) + .withEditor("Jesse Pinkman", person3.getID().toString()) + .build(); + + context.restoreAuthSystemState(); + testEntity = commitAndReload(testEntity); - assertThat(getMetadataValues(publication, "cris.virtualsource.author-orcid"), contains( - with("cris.virtualsource.author-orcid", personId))); + List values = testEntity.getMetadata(); + assertThat(values, hasItem(with("dc.contributor.author", "Red Smith"))); + assertThat(values, hasItem(with("dc.contributor.author", "Walter White", person1.getID().toString(), 1, 600))); + assertThat(values, hasItem(with("dc.contributor.author", "John Smith", person2.getID().toString(), 2, 600))); + assertThat(values, hasItem(with("dc.contributor.editor", "Jesse Pinkman", person3.getID().toString(), 0, 600))); + // virtual source and virtual metadata are not required to respect the order of the source metadata + List posPerson1 = getPlacesAsVirtualSource(person1, testEntity, "cris.virtualsource.testmultival"); + List posPerson2 = getPlacesAsVirtualSource(person2, testEntity, "cris.virtualsource.testmultival"); + List posPerson3 = getPlacesAsVirtualSource(person3, testEntity, "cris.virtualsource.testmultival"); + assertThat(values, + hasItem(with("cris.virtualsource.testmultival", person1.getID().toString(), posPerson1.get(0)))); + assertThat(values, hasItem(with("cris.virtual.testmultival", "4Science", posPerson1.get(0)))); + assertThat(values, + hasItem(with("cris.virtualsource.testmultival", person1.getID().toString(), posPerson1.get(1)))); + assertThat(values, hasItem(with("cris.virtual.testmultival", "DSpace", posPerson1.get(1)))); + assertThat(values, + hasItem(with("cris.virtualsource.testmultival", person1.getID().toString(), posPerson1.get(2)))); + assertThat(values, hasItem(with("cris.virtual.testmultival", "orcid1", posPerson1.get(2)))); + + assertThat(values, + hasItem(with("cris.virtualsource.testmultival", person2.getID().toString(), posPerson2.get(0)))); + assertThat(values, + hasItem(with("cris.virtual.testmultival", PLACEHOLDER_PARENT_METADATA_VALUE, posPerson2.get(0)))); + + assertThat(values, + hasItem(with("cris.virtualsource.testmultival", person3.getID().toString(), posPerson3.get(0)))); + assertThat(values, hasItem(with("cris.virtual.testmultival", "University of Rome", posPerson3.get(0)))); + + assertThat(getMetadataValues(testEntity, "cris.virtualsource.testmultival"), hasSize(5)); + assertThat(getMetadataValues(testEntity, "cris.virtual.testmultival"), hasSize(5)); + + } + private List getPlacesAsVirtualSource(Item person1, Item publication, String metadata) { + return getMetadataValues(publication, metadata).stream() + .filter(mv -> StringUtils.equals(mv.getValue(), person1.getID().toString())).map(mv -> mv.getPlace()) + .collect(Collectors.toList()); } private MetadataValue getFirstMetadataValue(Item item, String metadataField) { diff --git a/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerScriptIT.java b/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerScriptIT.java index f8b2b0d2d77..6d67c67a10b 100644 --- a/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerScriptIT.java +++ b/dspace-api/src/test/java/org/dspace/content/enhancer/script/ItemEnhancerScriptIT.java @@ -24,6 +24,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.UUID; import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.app.launcher.ScriptLauncher; @@ -39,6 +40,7 @@ import org.dspace.content.WorkspaceItem; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.ItemService; +import org.dspace.core.CrisConstants; import org.dspace.core.ReloadableEntity; import org.dspace.event.factory.EventServiceFactory; import org.dspace.event.service.EventService; @@ -138,24 +140,38 @@ public void testItemsEnhancement() throws Exception { .withAuthor("Jesse Pinkman", secondAuthorId) .build(); - WorkspaceItem thirdPublication = WorkspaceItemBuilder.createWorkspaceItem(context, collection) - .withTitle("Test publication 3") + final String randomUUID = UUID.randomUUID().toString(); + Item thirdPublication = ItemBuilder.createItem(context, collection) + .withTitle("Test publication 3") + .withEntityType("Publication") + .withAuthor("Walter White", firstAuthorId) + .withAuthor("Jesse Pinkman", secondAuthorId) + // same author multiple time + .withAuthor("Walter White", firstAuthorId) + // an author is also an editor + .withEditor("Jesse Pinkman", secondAuthorId) + .withEditor("Editor WithExternaAuthority", "external-authority") + .withEditor("Editor WithExternaUUIDAuthority", randomUUID) + .build(); + + WorkspaceItem wsPublication = WorkspaceItemBuilder.createWorkspaceItem(context, collection) + .withTitle("Test workspace publication") .withEntityType("Publication") - .withAuthor("Jesse Pinkman", secondAuthorId) + .withEditor("Jesse Pinkman", secondAuthorId) .build(); context.commit(); firstPublication = reload(firstPublication); secondPublication = reload(secondPublication); - thirdPublication = reload(thirdPublication); + wsPublication = reload(wsPublication); assertThat(getMetadataValues(firstPublication, "cris.virtual.department"), empty()); assertThat(getMetadataValues(firstPublication, "cris.virtualsource.department"), empty()); assertThat(getMetadataValues(secondPublication, "cris.virtual.department"), empty()); assertThat(getMetadataValues(secondPublication, "cris.virtualsource.department"), empty()); - assertThat(getMetadataValues(thirdPublication, "cris.virtual.department"), empty()); - assertThat(getMetadataValues(thirdPublication, "cris.virtualsource.department"), empty()); + assertThat(getMetadataValues(wsPublication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(wsPublication, "cris.virtualsource.department"), empty()); TestDSpaceRunnableHandler runnableHandler = runScript(false); @@ -164,15 +180,16 @@ public void testItemsEnhancement() throws Exception { firstPublication = reload(firstPublication); secondPublication = reload(secondPublication); + thirdPublication = reload(thirdPublication); + wsPublication = reload(wsPublication); assertThat(getMetadataValues(firstPublication, "cris.virtual.department"), hasSize(1)); assertThat(getMetadataValues(firstPublication, "cris.virtualsource.department"), hasSize(1)); + assertThat(firstPublication.getMetadata(), hasItem(with("cris.virtual.department", "4Science"))); + assertThat(firstPublication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); assertThat(getMetadataValues(secondPublication, "cris.virtual.department"), hasSize(2)); assertThat(getMetadataValues(secondPublication, "cris.virtualsource.department"), hasSize(2)); - - assertThat(firstPublication.getMetadata(), hasItem(with("cris.virtual.department", "4Science"))); - assertThat(firstPublication.getMetadata(), hasItem(with("cris.virtualsource.department", firstAuthorId))); assertThat(getMetadataValues(secondPublication, "cris.virtual.department"), containsInAnyOrder( withNoPlace("cris.virtual.department", "4Science"), @@ -181,8 +198,22 @@ public void testItemsEnhancement() throws Exception { containsInAnyOrder( withNoPlace("cris.virtualsource.department", firstAuthorId), withNoPlace("cris.virtualsource.department", secondAuthorId))); - assertThat(getMetadataValues(thirdPublication, "cris.virtual.department"), empty()); - assertThat(getMetadataValues(thirdPublication, "cris.virtualsource.department"), empty()); + + assertThat(getMetadataValues(thirdPublication, "cris.virtual.department"), hasSize(3)); + assertThat(getMetadataValues(thirdPublication, "cris.virtualsource.department"), hasSize(3)); + assertThat(getMetadataValues(thirdPublication, "cris.virtual.department"), + containsInAnyOrder( + withNoPlace("cris.virtual.department", "4Science"), + withNoPlace("cris.virtual.department", CrisConstants.PLACEHOLDER_PARENT_METADATA_VALUE), + withNoPlace("cris.virtual.department", "Company"))); + assertThat(getMetadataValues(thirdPublication, "cris.virtualsource.department"), + containsInAnyOrder( + withNoPlace("cris.virtualsource.department", randomUUID), + withNoPlace("cris.virtualsource.department", firstAuthorId), + withNoPlace("cris.virtualsource.department", secondAuthorId))); + + assertThat(getMetadataValues(wsPublication, "cris.virtual.department"), empty()); + assertThat(getMetadataValues(wsPublication, "cris.virtualsource.department"), empty()); } @@ -281,7 +312,7 @@ public void testItemEnhancementWithForce() throws Exception { Item publication = ItemBuilder.createItem(context, collection) .withTitle("Test publication 2 ") .withEntityType("Publication") - .withAuthor("Walter White", firstAuthorId) + .withEditor("Walter White", firstAuthorId) .withAuthor("Jesse Pinkman", secondAuthorId) .build(); @@ -310,7 +341,7 @@ public void testItemEnhancementWithForce() throws Exception { context.turnOffAuthorisationSystem(); - MetadataValue authorToRemove = getMetadataValues(publication, "dc.contributor.author").get(1); + MetadataValue authorToRemove = getMetadataValues(publication, "dc.contributor.author").get(0); itemService.removeMetadataValues(context, publication, List.of(authorToRemove)); replaceMetadata(firstAuthor, "person", "affiliation", "name", "University"); @@ -413,7 +444,7 @@ public void testItemEnhancementSourceWithoutAuthority() throws Exception { .withTitle("Test publication 2 ") .withEntityType("Publication") .withAuthor("Jesse Pinkman") - .withAuthor("Jesse Smith", secondAuthorId) + .withEditor("Jesse Smith", secondAuthorId) .build(); context.commit(); diff --git a/dspace-api/src/test/java/org/dspace/harvest/OAIHarvesterIT.java b/dspace-api/src/test/java/org/dspace/harvest/OAIHarvesterIT.java index b305ccc1806..9c402b4e3a0 100644 --- a/dspace-api/src/test/java/org/dspace/harvest/OAIHarvesterIT.java +++ b/dspace-api/src/test/java/org/dspace/harvest/OAIHarvesterIT.java @@ -781,7 +781,7 @@ public void testRunHarvestWithPublicationAndThenPerson() throws Exception { assertThat(values, hasItems(with("dc.identifier.doi", "10.1007/978-3-642-35233-1_18"))); assertThat(values, hasItems(with("oairecerif.author.affiliation", PLACEHOLDER_PARENT_METADATA_VALUE))); assertThat(values, hasItems(with("cris.virtual.department", PLACEHOLDER_PARENT_METADATA_VALUE))); - assertThat(values, hasItems(with("cris.virtual.author-orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); + assertThat(values, hasItems(with("cris.virtual.orcid", PLACEHOLDER_PARENT_METADATA_VALUE))); assertThat(values, hasItems(with("cris.sourceId", "test-harvest::3"))); assertThat(values, hasItems(with("dspace.entity.type", "Publication"))); @@ -889,8 +889,8 @@ public void testRunHarvestWithPersonAndThenPublication() throws Exception { assertThat(values, hasItems(with("dspace.entity.type", "Publication"))); assertThat(values, hasItems(with("cris.virtual.department", PLACEHOLDER_PARENT_METADATA_VALUE))); assertThat(values, hasItems(with("cris.virtualsource.department", UUIDUtils.toString(person.getID())))); - assertThat(values, hasItems(with("cris.virtual.author-orcid", "0000-0002-9079-5932"))); - assertThat(values, hasItems(with("cris.virtualsource.author-orcid", + assertThat(values, hasItems(with("cris.virtual.orcid", "0000-0002-9079-5932"))); + assertThat(values, hasItems(with("cris.virtualsource.orcid", UUIDUtils.toString(person.getID())))); MetadataValue author = itemService.getMetadata(publication, "dc", "contributor", "author", Item.ANY).get(0); diff --git a/dspace/config/registries/cris-types.xml b/dspace/config/registries/cris-types.xml index 7f0f8d7ec63..90597ddffbc 100644 --- a/dspace/config/registries/cris-types.xml +++ b/dspace/config/registries/cris-types.xml @@ -152,14 +152,7 @@ cris virtual - author-orcid - - - - - cris - virtual - editor-orcid + orcid @@ -173,14 +166,7 @@ cris virtualsource - author-orcid - - - - - cris - virtualsource - editor-orcid + orcid diff --git a/dspace/config/spring/api/metadata-enhancers.xml b/dspace/config/spring/api/metadata-enhancers.xml index e36727959c2..eb87c320269 100644 --- a/dspace/config/spring/api/metadata-enhancers.xml +++ b/dspace/config/spring/api/metadata-enhancers.xml @@ -25,38 +25,50 @@ - + + + dc.contributor.author + dc.contributor.editor + + - - - - + + + + dc.contributor.author + dc.contributor.editor + + + + - + + + dc.contributor.author + dc.contributor.editor + + - + - + + + dc.contributor.author + dc.contributor.editor + + - + - - - - - - - - From e0bc2eb589b869f4f6de0dc817698add4f457999 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 6 Mar 2024 08:44:06 +0100 Subject: [PATCH 81/82] [DSC-1550] Add missing SupervisionOrder discovery configuration --- dspace/config/spring/api/discovery.xml | 82 +++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/dspace/config/spring/api/discovery.xml b/dspace/config/spring/api/discovery.xml index de6145afd67..3c7a70cf9e0 100644 --- a/dspace/config/spring/api/discovery.xml +++ b/dspace/config/spring/api/discovery.xml @@ -61,9 +61,6 @@ dc.contributor.editor - - - @@ -72,6 +69,9 @@ + + + @@ -91,6 +91,7 @@ + @@ -785,6 +786,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + search.resourcetype:WorkspaceItem AND supervised:true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Date: Mon, 11 Mar 2024 15:00:41 +0100 Subject: [PATCH 82/82] [DSC-1587] Add check for item submitter --- .../org/dspace/app/rest/converter/EditItemConverter.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/EditItemConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/EditItemConverter.java index f19ce63d6b5..8a2135e032b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/EditItemConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/EditItemConverter.java @@ -7,6 +7,7 @@ */ package org.dspace.app.rest.converter; +import java.util.Objects; import javax.servlet.http.HttpServletRequest; import org.apache.logging.log4j.Logger; @@ -138,7 +139,9 @@ protected void fillFromModel(EditItem obj, EditItemRest rest, Projection project } rest.setCollection(collection != null ? converter.toRest(collection, projection) : null); rest.setItem(converter.toRest(item, projection)); - rest.setSubmitter(converter.toRest(submitter, projection)); + if (Objects.nonNull(submitter)) { + rest.setSubmitter(converter.toRest(submitter, projection)); + } } private void addValidationErrorsToItem(EditItem obj, EditItemRest rest) {