diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/Application.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/WebApplication.java
similarity index 84%
rename from dspace-server-webapp/src/main/java/org/dspace/app/rest/Application.java
rename to dspace-server-webapp/src/main/java/org/dspace/app/rest/WebApplication.java
index 07b802b684ee..401ad626e305 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/Application.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/WebApplication.java
@@ -17,8 +17,6 @@
import org.dspace.app.rest.parameter.resolver.SearchFilterResolver;
import org.dspace.app.rest.utils.ApplicationConfig;
import org.dspace.app.rest.utils.DSpaceAPIRequestLoggingFilter;
-import org.dspace.app.rest.utils.DSpaceConfigurationInitializer;
-import org.dspace.app.rest.utils.DSpaceKernelInitializer;
import org.dspace.app.sitemap.GenerateSitemaps;
import org.dspace.app.solrdatabaseresync.SolrDatabaseResyncCli;
import org.dspace.app.util.DSpaceContextListener;
@@ -27,11 +25,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.builder.SpringApplicationBuilder;
-import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.hateoas.server.LinkRelationProvider;
import org.springframework.lang.NonNull;
@@ -46,24 +42,18 @@
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
- * Define the Spring Boot Application settings itself. This class takes the place
- * of a web.xml file, and configures all Filters/Listeners as methods (see below).
- *
- * NOTE: Requires a Servlet 3.0 container, e.g. Tomcat 7.0 or above.
- *
- * NOTE: This extends SpringBootServletInitializer in order to allow us to build
- * a deployable WAR file with Spring Boot. See:
- * http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-deployable-war-file
+ * Main configuration for the dspace web module.
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
* @author Tim Donohue
+ * @author Luca Giamminonni (luca.giamminonni at 4science.it)
*/
-@SpringBootApplication
@EnableScheduling
@EnableCaching
-public class Application extends SpringBootServletInitializer {
+@Configuration
+public class WebApplication {
- private static final Logger log = LoggerFactory.getLogger(Application.class);
+ private static final Logger log = LoggerFactory.getLogger(WebApplication.class);
@Autowired
private ApplicationConfig configuration;
@@ -86,26 +76,6 @@ public void sendGoogleAnalyticsEvents() {
googleAsyncEventListener.sendCollectedEvents();
}
- /**
- * 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());
- }
-
/**
* Register the "DSpaceContextListener" so that it is loaded
* for this Application.
@@ -253,7 +223,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
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/EPersonForgotPasswordFeature.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/EPersonForgotPasswordFeature.java
new file mode 100644
index 000000000000..c6e6b55526d6
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/EPersonForgotPasswordFeature.java
@@ -0,0 +1,58 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.authorization.impl;
+
+import java.sql.SQLException;
+
+import org.dspace.app.rest.authorization.AuthorizationFeature;
+import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
+import org.dspace.app.rest.model.BaseObjectRest;
+import org.dspace.app.rest.model.EPersonRest;
+import org.dspace.app.rest.model.SiteRest;
+import org.dspace.app.util.AuthorizeUtil;
+import org.dspace.core.Context;
+import org.springframework.stereotype.Component;
+
+/**
+ * Checks if the user provided is allowed to request a password reset.
+ * If none user specified, checks if the current context is allowed to set the password.
+ *
+ * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com)
+ **/
+@Component
+@AuthorizationFeatureDocumentation(name = EPersonForgotPasswordFeature.NAME,
+ description = "It can be used to check password reset for an eperson")
+public class EPersonForgotPasswordFeature implements AuthorizationFeature {
+
+ public static final String NAME = "epersonForgotPassword";
+
+ @Override
+ public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
+ boolean isEperson = object instanceof EPersonRest;
+ boolean isSite = object instanceof SiteRest;
+ if (!isEperson && !isSite) {
+ return false;
+ }
+ if (!AuthorizeUtil.authorizeForgotPassword()) {
+ return false;
+ }
+ if (isEperson) {
+ return AuthorizeUtil.authorizeUpdatePassword(context, ((EPersonRest) object).getEmail());
+ }
+ return true;
+ }
+
+ @Override
+ public String[] getSupportedTypes() {
+ return new String[] {
+ SiteRest.CATEGORY + "." + SiteRest.NAME,
+ EPersonRest.CATEGORY + "." + EPersonRest.NAME
+ };
+ }
+
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AInprogressItemConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AInprogressItemConverter.java
index a5431d90004f..a80c8bd948b9 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AInprogressItemConverter.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AInprogressItemConverter.java
@@ -82,7 +82,7 @@ protected void fillFromModel(T obj, R witem, Projection projection) {
if (collection != null) {
SubmissionDefinitionRest def = converter.toRest(
- submissionConfigService.getSubmissionConfigByCollection(collection.getHandle()), projection);
+ submissionConfigService.getSubmissionConfigByCollection(collection), projection);
witem.setSubmissionDefinition(def);
for (SubmissionSectionRest sections : def.getPanels()) {
SubmissionStepConfig stepConfig = submissionSectionConverter.toModel(sections);
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ProcessConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ProcessConverter.java
index de03d6063019..3a243f3946b0 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ProcessConverter.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ProcessConverter.java
@@ -43,6 +43,7 @@ public ProcessRest convert(Process process, Projection projection) {
processRest.setProcessStatus(process.getProcessStatus());
processRest.setStartTime(process.getStartTime());
processRest.setEndTime(process.getFinishedTime());
+ processRest.setCreationTime(process.getCreationTime());
processRest.setParameterRestList(processService.getParameters(process).stream()
.map(x -> (ParameterValueRest) converter.toRest(x, projection)).collect(Collectors.toList()));
return processRest;
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SuggestionConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SuggestionConverter.java
new file mode 100644
index 000000000000..8eed5fb78ac8
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SuggestionConverter.java
@@ -0,0 +1,52 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.converter;
+
+import org.dspace.app.rest.model.SuggestionRest;
+import org.dspace.app.rest.projection.Projection;
+import org.dspace.app.suggestion.Suggestion;
+import org.dspace.app.suggestion.SuggestionEvidence;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * This class provides the method to convert a Suggestion to its REST representation, the
+ * SuggestionRest
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@Component
+public class SuggestionConverter
+ implements DSpaceConverter {
+
+ @Autowired
+ private MetadataValueDTOListConverter metadataConverter;
+
+ @Override
+ public SuggestionRest convert(Suggestion target, Projection projection) {
+ SuggestionRest targetRest = new SuggestionRest();
+ targetRest.setProjection(projection);
+ targetRest.setId(target.getID());
+ targetRest.setDisplay(target.getDisplay());
+ targetRest.setExternalSourceUri(target.getExternalSourceUri());
+ targetRest.setSource(target.getSource());
+ targetRest.setScore(String.format("%.2f", target.getScore()));
+ for (SuggestionEvidence se : target.getEvidences()) {
+ targetRest.getEvidences().put(se.getName(),
+ new SuggestionRest.EvidenceRest(String.format("%.2f", se.getScore()), se.getNotes()));
+ }
+ targetRest.setMetadata(metadataConverter.convert(target.getMetadata()));
+ return targetRest;
+ }
+
+ @Override
+ public Class getModelClass() {
+ return Suggestion.class;
+ }
+
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SuggestionSourceConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SuggestionSourceConverter.java
new file mode 100644
index 000000000000..3506133b6f57
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SuggestionSourceConverter.java
@@ -0,0 +1,39 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.converter;
+
+import org.dspace.app.rest.model.SuggestionSourceRest;
+import org.dspace.app.rest.projection.Projection;
+import org.dspace.app.suggestion.SuggestionSource;
+import org.springframework.stereotype.Component;
+
+/**
+ * This class provides the method to convert a SuggestionSource to its REST representation, the
+ * SuggestionSourceRest
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@Component
+public class SuggestionSourceConverter
+ implements DSpaceConverter {
+
+ @Override
+ public SuggestionSourceRest convert(SuggestionSource target, Projection projection) {
+ SuggestionSourceRest targetRest = new SuggestionSourceRest();
+ targetRest.setProjection(projection);
+ targetRest.setId(target.getID());
+ targetRest.setTotal(target.getTotal());
+ return targetRest;
+ }
+
+ @Override
+ public Class getModelClass() {
+ return SuggestionSource.class;
+ }
+
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SuggestionTargetConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SuggestionTargetConverter.java
new file mode 100644
index 000000000000..4bf4be72263a
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SuggestionTargetConverter.java
@@ -0,0 +1,41 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.converter;
+
+import org.dspace.app.rest.model.SuggestionTargetRest;
+import org.dspace.app.rest.projection.Projection;
+import org.dspace.app.suggestion.SuggestionTarget;
+import org.springframework.stereotype.Component;
+
+/**
+ * This class provides the method to convert a SuggestionTarget to its REST representation, the
+ * SuggestionTargetRest
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@Component
+public class SuggestionTargetConverter
+ implements DSpaceConverter {
+
+ @Override
+ public SuggestionTargetRest convert(SuggestionTarget target, Projection projection) {
+ SuggestionTargetRest targetRest = new SuggestionTargetRest();
+ targetRest.setProjection(projection);
+ targetRest.setId(target.getID());
+ targetRest.setDisplay(target.getTarget().getName());
+ targetRest.setTotal(target.getTotal());
+ targetRest.setSource(target.getSource());
+ return targetRest;
+ }
+
+ @Override
+ public Class getModelClass() {
+ return SuggestionTarget.class;
+ }
+
+}
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 c306691eb352..30404e030ab6 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
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ProcessRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ProcessRest.java
index 8216e1617195..0344d7db0422 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ProcessRest.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ProcessRest.java
@@ -60,6 +60,7 @@ public String getType() {
private Integer processId;
private Date startTime;
private Date endTime;
+ private Date creationTime;
private ProcessStatus processStatus;
@JsonProperty(value = "parameters")
private List parameterRestList;
@@ -104,6 +105,14 @@ public void setStartTime(Date startTime) {
this.startTime = startTime;
}
+ public Date getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Date creationTime) {
+ this.creationTime = creationTime;
+ }
+
public String getScriptName() {
return scriptName;
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionRest.java
new file mode 100644
index 000000000000..b3f67fc2bede
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionRest.java
@@ -0,0 +1,114 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonProperty.Access;
+import org.dspace.app.rest.RestResourceController;
+
+/**
+ * The Suggestion REST Resource. A suggestion is an object, usually a
+ * publication, proposed by a source related to a specific Person (target) to be
+ * imported in the system.
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@LinksRest(links = { @LinkRest(name = SuggestionRest.TARGET, method = "getTarget") })
+public class SuggestionRest extends BaseObjectRest {
+ private static final long serialVersionUID = 1L;
+ public static final String NAME = "suggestion";
+ public static final String TARGET = "target";
+ public static final String CATEGORY = RestAddressableModel.INTEGRATION;
+
+ private String display;
+ private String source;
+ private String externalSourceUri;
+ private String score;
+ private Map evidences = new HashMap();
+ private MetadataRest metadata = new MetadataRest();
+
+ @Override
+ @JsonProperty(access = Access.READ_ONLY)
+ public String getType() {
+ return NAME;
+ }
+
+ @Override
+ public String getCategory() {
+ return CATEGORY;
+ }
+
+ @Override
+ public Class getController() {
+ return RestResourceController.class;
+ }
+
+ public String getDisplay() {
+ return display;
+ }
+
+ public void setDisplay(String display) {
+ this.display = display;
+ }
+
+ public String getSource() {
+ return source;
+ }
+
+ public void setSource(String source) {
+ this.source = source;
+ }
+
+ public String getExternalSourceUri() {
+ return externalSourceUri;
+ }
+
+ public void setExternalSourceUri(String externalSourceUri) {
+ this.externalSourceUri = externalSourceUri;
+ }
+
+ public void setScore(String score) {
+ this.score = score;
+ }
+
+ public String getScore() {
+ return score;
+ }
+
+ public Map getEvidences() {
+ return evidences;
+ }
+
+ public void setEvidences(Map evidences) {
+ this.evidences = evidences;
+ }
+
+ public MetadataRest getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(MetadataRest metadata) {
+ this.metadata = metadata;
+ }
+
+ /**
+ * inner class to encapsulate score & notes
+ * and map {@link SuggestionEvidence}
+ * */
+ public static class EvidenceRest {
+ public String score;
+ public String notes;
+ public EvidenceRest(String score, String notes) {
+ this.score = score;
+ this.notes = notes;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionSourceRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionSourceRest.java
new file mode 100644
index 000000000000..9c2aa80e82e5
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionSourceRest.java
@@ -0,0 +1,51 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonProperty.Access;
+import org.dspace.app.rest.RestResourceController;
+
+/**
+ * The Suggestion Source REST Resource. A suggestion source is a connector to an
+ * external system that provides suggestion for a target object of related
+ * objects to be imported in the system.
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+public class SuggestionSourceRest extends BaseObjectRest {
+ private static final long serialVersionUID = 1L;
+ public static final String CATEGORY = RestAddressableModel.INTEGRATION;
+ public static final String NAME = "suggestionsource";
+
+ private int total;
+
+ @Override
+ @JsonProperty(access = Access.READ_ONLY)
+ public String getType() {
+ return NAME;
+ }
+
+ @Override
+ public String getCategory() {
+ return CATEGORY;
+ }
+
+ @Override
+ public Class getController() {
+ return RestResourceController.class;
+ }
+
+ public int getTotal() {
+ return total;
+ }
+
+ public void setTotal(int total) {
+ this.total = total;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionTargetRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionTargetRest.java
new file mode 100644
index 000000000000..ba93ab4e52b7
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionTargetRest.java
@@ -0,0 +1,73 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonProperty.Access;
+import org.dspace.app.rest.RestResourceController;
+
+/**
+ * The Suggestion Target REST Resource. A suggestion target is a Person to whom
+ * one or more suggester sources have found related objects to be importe in the
+ * system.
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@LinksRest(links = {
+ @LinkRest(name = SuggestionTargetRest.TARGET, method = "getTarget")
+})
+public class SuggestionTargetRest extends BaseObjectRest {
+ private static final long serialVersionUID = 1L;
+ public static final String NAME = "suggestiontarget";
+ public static final String TARGET = "target";
+ public static final String CATEGORY = RestAddressableModel.INTEGRATION;
+
+ private String display;
+ private String source;
+ private int total;
+
+ @Override
+ @JsonProperty(access = Access.READ_ONLY)
+ public String getType() {
+ return NAME;
+ }
+
+ @Override
+ public String getCategory() {
+ return CATEGORY;
+ }
+
+ @Override
+ public Class getController() {
+ return RestResourceController.class;
+ }
+
+ public String getDisplay() {
+ return display;
+ }
+
+ public void setDisplay(String display) {
+ this.display = display;
+ }
+
+ public String getSource() {
+ return source;
+ }
+
+ public void setSource(String source) {
+ this.source = source;
+ }
+
+ public int getTotal() {
+ return total;
+ }
+
+ public void setTotal(int total) {
+ this.total = total;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SuggestionResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SuggestionResource.java
new file mode 100644
index 000000000000..66165f86983b
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SuggestionResource.java
@@ -0,0 +1,25 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.model.hateoas;
+
+import org.dspace.app.rest.model.SuggestionRest;
+import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource;
+import org.dspace.app.rest.utils.Utils;
+
+/**
+ * Suggestion Rest HAL Resource. The HAL Resource wraps the REST Resource
+ * adding support for the links and embedded resources
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@RelNameDSpaceResource(SuggestionRest.NAME)
+public class SuggestionResource extends DSpaceResource {
+ public SuggestionResource(SuggestionRest target, Utils utils) {
+ super(target, utils);
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SuggestionSourceResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SuggestionSourceResource.java
new file mode 100644
index 000000000000..1f01f27d862e
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SuggestionSourceResource.java
@@ -0,0 +1,25 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.model.hateoas;
+
+import org.dspace.app.rest.model.SuggestionSourceRest;
+import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource;
+import org.dspace.app.rest.utils.Utils;
+
+/**
+ * Suggestion Source Rest HAL Resource. The HAL Resource wraps the REST Resource
+ * adding support for the links and embedded resources
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@RelNameDSpaceResource(SuggestionSourceRest.NAME)
+public class SuggestionSourceResource extends DSpaceResource {
+ public SuggestionSourceResource(SuggestionSourceRest target, Utils utils) {
+ super(target, utils);
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SuggestionTargetResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SuggestionTargetResource.java
new file mode 100644
index 000000000000..26cd7c3c34e2
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SuggestionTargetResource.java
@@ -0,0 +1,25 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.model.hateoas;
+
+import org.dspace.app.rest.model.SuggestionTargetRest;
+import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource;
+import org.dspace.app.rest.utils.Utils;
+
+/**
+ * Suggestion Target Rest HAL Resource. The HAL Resource wraps the REST Resource
+ * adding support for the links and embedded resources
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@RelNameDSpaceResource(SuggestionTargetRest.NAME)
+public class SuggestionTargetResource extends DSpaceResource {
+ public SuggestionTargetResource(SuggestionTargetRest target, Utils utils) {
+ super(target, utils);
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ProcessRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ProcessRestRepository.java
index 2479eeda97f5..5f3b093b2007 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ProcessRestRepository.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ProcessRestRepository.java
@@ -230,6 +230,9 @@ private void handleSearchSort(Pageable pageable, ProcessQueryParameterContainer
} else if (StringUtils.equalsIgnoreCase(order.getProperty(), "endTime")) {
processQueryParameterContainer.setSortProperty(Process_.FINISHED_TIME);
processQueryParameterContainer.setSortOrder(order.getDirection().name());
+ } else if (StringUtils.equalsIgnoreCase(order.getProperty(), "creationTime")) {
+ processQueryParameterContainer.setSortProperty(Process_.CREATION_TIME);
+ processQueryParameterContainer.setSortOrder(order.getDirection().name());
} else {
throw new DSpaceBadRequestException("The given sort option was invalid: " + order.getProperty());
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RegistrationRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RegistrationRestRepository.java
index be28170d8a07..3d183fd3418c 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RegistrationRestRepository.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RegistrationRestRepository.java
@@ -127,6 +127,9 @@ public RegistrationRest createAndReturn(Context context) {
}
if (eperson != null && accountType.equalsIgnoreCase(TYPE_FORGOT)) {
try {
+ if (!AuthorizeUtil.authorizeForgotPassword()) {
+ throw new AccessDeniedException("Password reset is not allowed!");
+ }
if (!AuthorizeUtil.authorizeUpdatePassword(context, eperson.getEmail())) {
throw new DSpaceBadRequestException("Password cannot be updated for the given EPerson with email: "
+ eperson.getEmail());
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionDefinitionRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionDefinitionRestRepository.java
index d964994928eb..17eb90b7901e 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionDefinitionRestRepository.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionDefinitionRestRepository.java
@@ -70,7 +70,7 @@ public SubmissionDefinitionRest findByCollection(@Parameter(value = "uuid", requ
return null;
}
SubmissionDefinitionRest def = converter
- .toRest(submissionConfigService.getSubmissionConfigByCollection(col.getHandle()),
+ .toRest(submissionConfigService.getSubmissionConfigByCollection(col),
utils.obtainProjection());
return def;
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SuggestionRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SuggestionRestRepository.java
new file mode 100644
index 000000000000..e2e1c3ce7ccb
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SuggestionRestRepository.java
@@ -0,0 +1,88 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.repository;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.logging.log4j.Logger;
+import org.dspace.app.rest.Parameter;
+import org.dspace.app.rest.SearchRestMethod;
+import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
+import org.dspace.app.rest.model.SuggestionRest;
+import org.dspace.app.rest.model.SuggestionTargetRest;
+import org.dspace.app.suggestion.Suggestion;
+import org.dspace.app.suggestion.SuggestionService;
+import org.dspace.authorize.AuthorizeException;
+import org.dspace.core.Context;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+/**
+ * This is the repository responsible to manage Suggestion Target Rest object
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+
+@Component(SuggestionRest.CATEGORY + "." + SuggestionRest.NAME)
+public class SuggestionRestRepository extends DSpaceRestRepository {
+ private final static String ORDER_FIELD = "trust";
+
+ private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(SuggestionRestRepository.class);
+
+ @Autowired
+ private SuggestionService suggestionService;
+
+ @Override
+ @PreAuthorize("hasPermission(#id, 'SUGGESTION', 'READ')")
+ public SuggestionRest findOne(Context context, String id) {
+ Suggestion suggestion = suggestionService.findUnprocessedSuggestion(context, id);
+ if (suggestion == null) {
+ return null;
+ }
+ return converter.toRest(suggestion, utils.obtainProjection());
+ }
+
+ @Override
+ @PreAuthorize("permitAll()")
+ public Page findAll(Context context, Pageable pageable) {
+ throw new RepositoryMethodNotImplementedException(SuggestionTargetRest.NAME, "findAll");
+ }
+
+ @PreAuthorize("hasPermission(#target, 'SUGGESTION.TARGET', 'READ')")
+ @SearchRestMethod(name = "findByTargetAndSource")
+ public Page findByTargetAndSource(
+ @Parameter(required = true, value = "source") String source,
+ @Parameter(required = true, value = "target") UUID target, Pageable pageable) {
+ Context context = obtainContext();
+ boolean ascending = false;
+ if (pageable.getSort() != null && pageable.getSort().getOrderFor(ORDER_FIELD) != null) {
+ ascending = pageable.getSort().getOrderFor(ORDER_FIELD).getDirection() == Direction.ASC;
+ }
+ List suggestions = suggestionService.findByTargetAndSource(context, target, source,
+ pageable.getPageSize(), pageable.getOffset(), ascending);
+ long tot = suggestionService.countAllByTargetAndSource(context, source, target);
+ return converter.toRestPage(suggestions, pageable, tot, utils.obtainProjection());
+ }
+
+ @Override
+ @PreAuthorize("hasPermission(#id, 'SUGGESTION', 'DELETE')")
+ protected void delete(Context context, String id)
+ throws AuthorizeException, RepositoryMethodNotImplementedException {
+ suggestionService.rejectSuggestion(context, id);
+ }
+
+ @Override
+ public Class getDomainClass() {
+ return SuggestionRest.class;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SuggestionSourceRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SuggestionSourceRestRepository.java
new file mode 100644
index 000000000000..6bc251749bce
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SuggestionSourceRestRepository.java
@@ -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/
+ */
+package org.dspace.app.rest.repository;
+
+import java.util.List;
+
+import org.apache.logging.log4j.Logger;
+import org.dspace.app.rest.model.SuggestionSourceRest;
+import org.dspace.app.suggestion.SuggestionService;
+import org.dspace.app.suggestion.SuggestionSource;
+import org.dspace.core.Context;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+/**
+ * This is the repository responsible to manage Suggestion Target Rest object
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+
+@Component(SuggestionSourceRest.CATEGORY + "." + SuggestionSourceRest.NAME)
+public class SuggestionSourceRestRepository extends DSpaceRestRepository {
+
+ private static final Logger log = org.apache.logging.log4j.LogManager
+ .getLogger(SuggestionSourceRestRepository.class);
+
+ @Autowired
+ private SuggestionService suggestionService;
+
+ @Override
+ @PreAuthorize("hasAuthority('ADMIN')")
+ public SuggestionSourceRest findOne(Context context, String source) {
+ SuggestionSource suggestionSource = suggestionService.findSource(context, source);
+ if (suggestionSource == null) {
+ return null;
+ }
+ return converter.toRest(suggestionSource, utils.obtainProjection());
+ }
+
+ @Override
+ @PreAuthorize("hasAuthority('ADMIN')")
+ public Page findAll(Context context, Pageable pageable) {
+ List suggestionSources = suggestionService.findAllSources(context, pageable.getPageSize(),
+ pageable.getOffset());
+ long count = suggestionService.countSources(context);
+ if (suggestionSources == null) {
+ return null;
+ }
+ return converter.toRestPage(suggestionSources, pageable, count, utils.obtainProjection());
+ }
+
+ @Override
+ public Class getDomainClass() {
+ return SuggestionSourceRest.class;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SuggestionTargetRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SuggestionTargetRestRepository.java
new file mode 100644
index 000000000000..e8498cb68c2b
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SuggestionTargetRestRepository.java
@@ -0,0 +1,98 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.repository;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.logging.log4j.Logger;
+import org.dspace.app.rest.Parameter;
+import org.dspace.app.rest.SearchRestMethod;
+import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
+import org.dspace.app.rest.model.SuggestionTargetRest;
+import org.dspace.app.suggestion.SuggestionService;
+import org.dspace.app.suggestion.SuggestionTarget;
+import org.dspace.core.Context;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+/**
+ * This is the repository responsible to manage Suggestion Target Rest object
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+
+@Component(SuggestionTargetRest.CATEGORY + "." + SuggestionTargetRest.NAME)
+public class SuggestionTargetRestRepository extends DSpaceRestRepository {
+
+ private static final Logger log = org.apache.logging.log4j.LogManager
+ .getLogger(SuggestionTargetRestRepository.class);
+
+ @Autowired
+ private SuggestionService suggestionService;
+
+ @Override
+ @PreAuthorize("hasPermission(#id, 'SUGGESTIONTARGET', 'READ')")
+ public SuggestionTargetRest findOne(Context context, String id) {
+ String source = null;
+ UUID uuid = null;
+ try {
+ source = id.split(":")[0];
+ uuid = UUID.fromString(id.split(":")[1]);
+ } catch (Exception e) {
+ return null;
+ }
+ SuggestionTarget suggestionTarget = suggestionService.find(context, source, uuid);
+ if (suggestionTarget == null) {
+ return null;
+ }
+ return converter.toRest(suggestionTarget, utils.obtainProjection());
+ }
+
+ @Override
+ @PreAuthorize("permitAll()")
+ public Page findAll(Context context, Pageable pageable) {
+ throw new RepositoryMethodNotImplementedException(SuggestionTargetRest.NAME, "findAll");
+ }
+
+ @PreAuthorize("hasAuthority('ADMIN')")
+ @SearchRestMethod(name = "findBySource")
+ public Page findBySource(@Parameter(required = true, value = "source") String source,
+ Pageable pageable) {
+ Context context = obtainContext();
+ List suggestionTargets = suggestionService.findAllTargets(context, source,
+ pageable.getPageSize(), pageable.getOffset());
+ long tot = suggestionService.countAll(context, source);
+ if (suggestionTargets == null) {
+ return null;
+ }
+ return converter.toRestPage(suggestionTargets, pageable, tot, utils.obtainProjection());
+ }
+
+ @PreAuthorize("hasPermission(#target, 'SUGGESTIONTARGET.TARGET', 'READ')")
+ @SearchRestMethod(name = "findByTarget")
+ public Page findByTarget(@Parameter(required = true, value = "target") UUID target,
+ Pageable pageable) {
+ Context context = obtainContext();
+ List suggestionTargets = suggestionService.findByTarget(context, target,
+ pageable.getPageSize(), pageable.getOffset());
+ long tot = suggestionService.countAllByTarget(context, target);
+ if (suggestionTargets == null) {
+ return null;
+ }
+ return converter.toRestPage(suggestionTargets, pageable, tot, utils.obtainProjection());
+ }
+
+ @Override
+ public Class getDomainClass() {
+ return SuggestionTargetRest.class;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SuggestionTargetTargetLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SuggestionTargetTargetLinkRepository.java
new file mode 100644
index 000000000000..50c6e4d48e27
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SuggestionTargetTargetLinkRepository.java
@@ -0,0 +1,70 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.repository;
+
+import java.sql.SQLException;
+import java.util.UUID;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang3.StringUtils;
+import org.dspace.app.rest.model.ItemRest;
+import org.dspace.app.rest.model.SuggestionTargetRest;
+import org.dspace.app.rest.projection.Projection;
+import org.dspace.content.Item;
+import org.dspace.content.service.ItemService;
+import org.dspace.core.Context;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.rest.webmvc.ResourceNotFoundException;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+/**
+ * Link repository for "target" subresource of an suggestion target.
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ *
+ */
+@Component(SuggestionTargetRest.CATEGORY + "." + SuggestionTargetRest.NAME + "." + SuggestionTargetRest.TARGET)
+public class SuggestionTargetTargetLinkRepository extends AbstractDSpaceRestRepository implements LinkRestRepository {
+
+ @Autowired
+ private ItemService itemService;
+
+ /**
+ * Returns the item related to the suggestion target with the given id.
+ *
+ * @param request the http servlet request
+ * @param id the suggestion target UUID
+ * @param pageable the optional pageable
+ * @param projection the projection object
+ * @return the target item rest representation
+ */
+ @PreAuthorize("hasPermission(#id, 'SUGGESTIONTARGET', 'READ')")
+ public ItemRest getTarget(@Nullable HttpServletRequest request, String id,
+ @Nullable Pageable pageable, Projection projection) {
+ String source = id.split(":")[0];
+ UUID uuid = UUID.fromString(id.split(":")[1]);
+ if (StringUtils.isBlank(source) || uuid == null) {
+ throw new ResourceNotFoundException("No such item related to a suggestion target with UUID: " + id);
+ }
+ try {
+ Context context = obtainContext();
+ Item profile = itemService.find(context, uuid);
+ if (profile == null) {
+ throw new ResourceNotFoundException("No such item related to a suggestion target with UUID: " + id);
+ }
+
+ return converter.toRest(profile, projection);
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java
index b4d04e59e32f..5f4bb0dfe927 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java
@@ -251,7 +251,7 @@ public Iterable upload(Context context, HttpServletRequest re
}
SubmissionConfig submissionConfig =
- submissionConfigService.getSubmissionConfigByCollection(collection.getHandle());
+ submissionConfigService.getSubmissionConfigByCollection(collection);
List result = null;
List records = new ArrayList<>();
try {
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 d619100bf67a..201a7ba1633d 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/main/java/org/dspace/app/rest/security/AdminRestPermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/AdminRestPermissionEvaluatorPlugin.java
index 0d251f6400f7..338eed4a7340 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);
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/MethodSecurityConfig.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/MethodSecurityConfig.java
index 29bfc13d83ce..5ee308c73ed8 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/MethodSecurityConfig.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/MethodSecurityConfig.java
@@ -8,6 +8,7 @@
package org.dspace.app.rest.security;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
@@ -22,10 +23,13 @@ public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Autowired
private PermissionEvaluator dSpacePermissionEvaluator;
+ @Autowired
+ private ApplicationContext applicationContext;
+
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
- DefaultMethodSecurityExpressionHandler expressionHandler =
- new DefaultMethodSecurityExpressionHandler();
+ DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
+ expressionHandler.setApplicationContext(applicationContext);
expressionHandler.setPermissionEvaluator(dSpacePermissionEvaluator);
return expressionHandler;
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SuggestionRestPermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SuggestionRestPermissionEvaluatorPlugin.java
new file mode 100644
index 000000000000..b56c0cf8965d
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SuggestionRestPermissionEvaluatorPlugin.java
@@ -0,0 +1,95 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.security;
+
+import static org.dspace.app.rest.security.DSpaceRestPermission.DELETE;
+import static org.dspace.app.rest.security.DSpaceRestPermission.READ;
+import static org.dspace.app.rest.security.DSpaceRestPermission.WRITE;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.commons.lang3.StringUtils;
+import org.dspace.app.rest.model.SuggestionRest;
+import org.dspace.app.rest.utils.ContextUtil;
+import org.dspace.content.Item;
+import org.dspace.content.MetadataValue;
+import org.dspace.content.service.ItemService;
+import org.dspace.core.Context;
+import org.dspace.eperson.EPerson;
+import org.dspace.util.UUIDUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * An authenticated user is allowed to view a suggestion
+ * related to a Target object that he owns (as defined by "dspace.object.owner" metadata field)
+ * See {@link RestPermissionEvaluatorPlugin} for the inherited contract.
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ *
+ */
+@Component
+public class SuggestionRestPermissionEvaluatorPlugin extends RestObjectPermissionEvaluatorPlugin {
+
+ @Autowired
+ private ItemService itemService;
+
+ @Override
+ public boolean hasDSpacePermission(Authentication authentication, Serializable targetId, String targetType,
+ DSpaceRestPermission restPermission) {
+
+ if (!READ.equals(restPermission) && !WRITE.equals(restPermission) && !DELETE.equals(restPermission)) {
+ return false;
+ }
+
+ if (!StringUtils.equalsIgnoreCase(targetType, SuggestionRest.NAME)
+ && !StringUtils.startsWithIgnoreCase(targetType, SuggestionRest.NAME)) {
+ return false;
+ }
+
+ Context context = ContextUtil.obtainCurrentRequestContext();
+
+ EPerson currentUser = context.getCurrentUser();
+ if (currentUser == null) {
+ return false;
+ }
+
+ try {
+ String id = targetId.toString();
+ UUID uuid = null;
+ if (id.contains(":")) {
+ uuid = UUIDUtils.fromString(id.split(":", 3)[1]);
+ } else {
+ uuid = UUIDUtils.fromString(id);
+ }
+ if (uuid == null) {
+ return false;
+ }
+ Item item = itemService.find(context, uuid);
+ if (item != null) {
+ List mvalues = itemService.getMetadataByMetadataString(item, "dspace.object.owner");
+ if (mvalues != null) {
+ for (MetadataValue mv : mvalues) {
+ if (StringUtils.equals(mv.getAuthority(), currentUser.getID().toString())) {
+ return true;
+ }
+ }
+ }
+ }
+ } catch (Exception ex) {
+ return false;
+ }
+
+ return false;
+ }
+
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SuggestionTargetRestPermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SuggestionTargetRestPermissionEvaluatorPlugin.java
new file mode 100644
index 000000000000..3aa4a9fcf46b
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SuggestionTargetRestPermissionEvaluatorPlugin.java
@@ -0,0 +1,96 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.security;
+
+import static org.dspace.app.rest.security.DSpaceRestPermission.DELETE;
+import static org.dspace.app.rest.security.DSpaceRestPermission.READ;
+import static org.dspace.app.rest.security.DSpaceRestPermission.WRITE;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.commons.lang3.StringUtils;
+import org.dspace.app.rest.model.SuggestionTargetRest;
+import org.dspace.app.rest.utils.ContextUtil;
+import org.dspace.content.Item;
+import org.dspace.content.MetadataValue;
+import org.dspace.content.service.ItemService;
+import org.dspace.core.Context;
+import org.dspace.eperson.EPerson;
+import org.dspace.util.UUIDUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * An authenticated user is allowed to view a suggestion summary
+ * (SuggestionTarget) related to a Target object that they own
+ * (as defined by "dspace.object.owner" metadata field)
+ * See {@link RestPermissionEvaluatorPlugin} for the inherited contract.
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ *
+ */
+@Component
+public class SuggestionTargetRestPermissionEvaluatorPlugin extends RestObjectPermissionEvaluatorPlugin {
+
+ @Autowired
+ private ItemService itemService;
+
+ @Override
+ public boolean hasDSpacePermission(Authentication authentication, Serializable targetId, String targetType,
+ DSpaceRestPermission restPermission) {
+
+ if (!READ.equals(restPermission) && !WRITE.equals(restPermission) && !DELETE.equals(restPermission)) {
+ return false;
+ }
+
+ if (!StringUtils.equalsIgnoreCase(targetType, SuggestionTargetRest.NAME)
+ && !StringUtils.startsWithIgnoreCase(targetType, SuggestionTargetRest.NAME)) {
+ return false;
+ }
+
+ Context context = ContextUtil.obtainCurrentRequestContext();
+
+ EPerson currentUser = context.getCurrentUser();
+ if (currentUser == null) {
+ return false;
+ }
+
+ try {
+ String id = targetId.toString();
+ UUID uuid = null;
+ if (id.contains(":")) {
+ uuid = UUIDUtils.fromString(id.split(":", 2)[1]);
+ } else {
+ uuid = UUIDUtils.fromString(id);
+ }
+ if (uuid == null) {
+ return false;
+ }
+ Item item = itemService.find(context, uuid);
+ if (item != null) {
+ List mvalues = itemService.getMetadataByMetadataString(item, "dspace.object.owner");
+ if (mvalues != null) {
+ for (MetadataValue mv : mvalues) {
+ if (StringUtils.equals(mv.getAuthority(), currentUser.getID().toString())) {
+ return true;
+ }
+ }
+ }
+ }
+ } catch (Exception ex) {
+ return false;
+ }
+
+ return false;
+ }
+
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WebSecurityConfiguration.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WebSecurityConfiguration.java
index 7bd698a63a60..b29f52939d20 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WebSecurityConfiguration.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WebSecurityConfiguration.java
@@ -20,7 +20,6 @@
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -41,7 +40,6 @@
@EnableWebSecurity
@Configuration
@EnableConfigurationProperties(SecurityProperties.class)
-@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
public static final String ADMIN_GRANT = "ADMIN";
diff --git a/dspace-server-webapp/src/main/resources/application.properties b/dspace-server-webapp/src/main/resources/application.properties
index f6fba076c0dd..8233298ef0b0 100644
--- a/dspace-server-webapp/src/main/resources/application.properties
+++ b/dspace-server-webapp/src/main/resources/application.properties
@@ -38,6 +38,11 @@
# 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
#
diff --git a/dspace-server-webapp/src/main/webapp/index.html b/dspace-server-webapp/src/main/resources/static/index.html
similarity index 100%
rename from dspace-server-webapp/src/main/webapp/index.html
rename to dspace-server-webapp/src/main/resources/static/index.html
diff --git a/dspace-server-webapp/src/main/webapp/js/hal/http/client.js b/dspace-server-webapp/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-server-webapp/src/main/resources/static/js/hal/http/client.js
diff --git a/dspace-server-webapp/src/main/webapp/js/vendor/CustomPostForm.js b/dspace-server-webapp/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-server-webapp/src/main/resources/static/js/vendor/CustomPostForm.js
diff --git a/dspace-server-webapp/src/main/webapp/login.html b/dspace-server-webapp/src/main/resources/static/login.html
similarity index 100%
rename from dspace-server-webapp/src/main/webapp/login.html
rename to dspace-server-webapp/src/main/resources/static/login.html
diff --git a/dspace-server-webapp/src/main/webapp/styles.css b/dspace-server-webapp/src/main/resources/static/styles.css
similarity index 100%
rename from dspace-server-webapp/src/main/webapp/styles.css
rename to dspace-server-webapp/src/main/resources/static/styles.css
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/TestApplication.java b/dspace-server-webapp/src/test/java/org/dspace/app/TestApplication.java
new file mode 100644
index 000000000000..8db55b6dedd1
--- /dev/null
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/TestApplication.java
@@ -0,0 +1,22 @@
+/**
+ * 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.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * Spring boot application for integration tests.
+ *
+ * @author Luca Giamminonni (luca.giamminonni at 4science.it)
+ *
+ */
+@SpringBootApplication(scanBasePackageClasses = WebApplication.class)
+public class TestApplication {
+
+}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EntityTypeRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EntityTypeRestRepositoryIT.java
index 740a2c0dc388..2de61bb43dfb 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EntityTypeRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EntityTypeRestRepositoryIT.java
@@ -416,6 +416,8 @@ public void findAllByAuthorizedExternalSource() throws Exception {
.setSupportedEntityTypes(Arrays.asList("Publication"));
((AbstractExternalDataProvider) externalDataService.getExternalDataProvider("pubmed"))
.setSupportedEntityTypes(Arrays.asList("Publication"));
+ ((AbstractExternalDataProvider) externalDataService.getExternalDataProvider("suggestion"))
+ .setSupportedEntityTypes(Arrays.asList("Publication"));
// these are similar to the previous checks but now we have restricted the mock and pubmed providers
// to support only publication, this mean that there are no providers suitable for funding
@@ -439,6 +441,8 @@ public void findAllByAuthorizedExternalSource() throws Exception {
.setSupportedEntityTypes(null);
((AbstractExternalDataProvider) externalDataService.getExternalDataProvider("pubmed"))
.setSupportedEntityTypes(null);
+ ((AbstractExternalDataProvider) externalDataService.getExternalDataProvider("suggestion"))
+ .setSupportedEntityTypes(null);
}
}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ExternalSourcesRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ExternalSourcesRestControllerIT.java
index 2b03b50c559c..55cc99567f59 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ExternalSourcesRestControllerIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ExternalSourcesRestControllerIT.java
@@ -41,6 +41,7 @@ public void findAllExternalSources() throws Exception {
.andExpect(jsonPath("$._embedded.externalsources", Matchers.hasItems(
ExternalSourceMatcher.matchExternalSource("mock", "mock", false),
ExternalSourceMatcher.matchExternalSource("orcid", "orcid", false),
+ ExternalSourceMatcher.matchExternalSource("suggestion", "suggestion", false),
ExternalSourceMatcher.matchExternalSource("scopus", "scopus", false),
ExternalSourceMatcher.matchExternalSource(
"sherpaJournalIssn", "sherpaJournalIssn", false),
@@ -53,7 +54,7 @@ public void findAllExternalSources() throws Exception {
ExternalSourceMatcher.matchExternalSource(
"openaireFunding", "openaireFunding", false)
)))
- .andExpect(jsonPath("$.page.totalElements", Matchers.is(10)));
+ .andExpect(jsonPath("$.page.totalElements", Matchers.is(11)));
}
@Test
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ProcessRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ProcessRestRepositoryIT.java
index 670d8e2f35b0..6f71011933c8 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ProcessRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ProcessRestRepositoryIT.java
@@ -18,6 +18,7 @@
import java.io.InputStream;
import java.sql.SQLException;
+import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
@@ -689,6 +690,78 @@ public void searchProcessTestByUserSortedOnStartTimeDesc() throws Exception {
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 3))));
}
+ @Test
+ public void searchProcessTestByUserSortedOnCreationTimeAsc() throws Exception {
+ SimpleDateFormat date = new SimpleDateFormat("dd/MM/yyyy");
+ Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
+ // not realistic to have creationTime after startTime,
+ // but proves startTime is ignored on sort
+ .withCreationTime(date.parse("01/01/2000"))
+ .withStartAndEndTime("01/01/1990", "01/01/1995").build();
+ Process newProcess2 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
+ .withCreationTime(date.parse("01/01/2005"))
+ .withStartAndEndTime(null, null).build();
+ Process newProcess3 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
+ .withCreationTime(date.parse("01/01/2010"))
+ .withStartAndEndTime("01/01/2015", "01/01/2020").build();
+
+ String token = getAuthToken(admin.getEmail(), password);
+
+ getClient(token).perform(get("/api/system/processes/search/byProperty")
+ .param("userId", eperson.getID().toString())
+ .param("sort", "creationTime,asc"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.processes", contains(
+ ProcessMatcher.matchProcess(newProcess1.getName(),
+ String.valueOf(eperson.getID().toString()),
+ newProcess1.getID(), parameters, ProcessStatus.SCHEDULED),
+ ProcessMatcher.matchProcess(newProcess2.getName(),
+ String.valueOf(eperson.getID().toString()),
+ newProcess2.getID(), parameters, ProcessStatus.SCHEDULED),
+ ProcessMatcher.matchProcess(newProcess3.getName(),
+ String.valueOf(eperson.getID().toString()),
+ newProcess3.getID(), parameters, ProcessStatus.SCHEDULED)
+ )))
+ .andExpect(jsonPath("$.page", is(
+ PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 3))));
+ }
+
+ @Test
+ public void searchProcessTestByUserSortedOnCreationTimeDesc() throws Exception {
+ SimpleDateFormat date = new SimpleDateFormat("dd/MM/yyyy");
+ Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
+ // not realistic to have creationTime after startTime,
+ // but proves startTime is ignored on sort
+ .withCreationTime(date.parse("01/01/2000"))
+ .withStartAndEndTime("01/01/1990", "01/01/1995").build();
+ Process newProcess2 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
+ .withCreationTime(date.parse("01/01/2005"))
+ .withStartAndEndTime(null, null).build();
+ Process newProcess3 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
+ .withCreationTime(date.parse("01/01/2010"))
+ .withStartAndEndTime("01/01/2015", "01/01/2020").build();
+
+ String token = getAuthToken(admin.getEmail(), password);
+
+ getClient(token).perform(get("/api/system/processes/search/byProperty")
+ .param("userId", eperson.getID().toString())
+ .param("sort", "creationTime,desc"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.processes", contains(
+ ProcessMatcher.matchProcess(newProcess3.getName(),
+ String.valueOf(eperson.getID().toString()),
+ newProcess3.getID(), parameters, ProcessStatus.SCHEDULED),
+ ProcessMatcher.matchProcess(newProcess2.getName(),
+ String.valueOf(eperson.getID().toString()),
+ newProcess2.getID(), parameters, ProcessStatus.SCHEDULED),
+ ProcessMatcher.matchProcess(newProcess1.getName(),
+ String.valueOf(eperson.getID().toString()),
+ newProcess1.getID(), parameters, ProcessStatus.SCHEDULED)
+ )))
+ .andExpect(jsonPath("$.page", is(
+ PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 3))));
+ }
+
@Test
public void searchProcessTestByUserSortedOnEndTimeAsc() throws Exception {
Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
@@ -797,7 +870,7 @@ public void searchProcessTestByUserSortedOnDefault() throws Exception {
}
@Test
- public void searchProcessTestByUserSortedOnNonExistingIsSortedAsDefault() throws Exception {
+ public void searchProcessTestByUserSortedOnNonExistingBadRequest() throws Exception {
Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("10/01/1990", "20/01/1990").build();
Process newProcess2 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestRepositoryIT.java
index 3ba84f344b4c..7bab342c18a6 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestRepositoryIT.java
@@ -286,6 +286,34 @@ public void forgotPasswordTest() throws Exception {
}
}
+ @Test
+ public void testUnauthorizedForgotPasswordTest() throws Exception {
+ configurationService.setProperty("user.registration", false);
+ configurationService.setProperty("user.forgot-password", false);
+
+ List registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class);
+ try {
+ assertEquals(0, registrationDataList.size());
+
+ ObjectMapper mapper = new ObjectMapper();
+ RegistrationRest registrationRest = new RegistrationRest();
+ registrationRest.setEmail(eperson.getEmail());
+ getClient().perform(post("/api/eperson/registrations")
+ .param(TYPE_QUERY_PARAM, TYPE_FORGOT)
+ .content(mapper.writeValueAsBytes(registrationRest))
+ .contentType(contentType))
+ .andExpect(status().isUnauthorized());
+ registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class);
+ assertEquals(0, registrationDataList.size());
+ } finally {
+ Iterator iterator = registrationDataList.iterator();
+ while (iterator.hasNext()) {
+ RegistrationData registrationData = iterator.next();
+ registrationDataDAO.delete(context, registrationData);
+ }
+ }
+ }
+
@Test
public void registrationFlowWithNoHeaderCaptchaTokenTest() throws Exception {
String originVerification = configurationService.getProperty("registration.verification.enabled");
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java
index d8e53c770c70..ea164af7318a 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java
@@ -1035,11 +1035,11 @@ public void addRelationshipsAndMetadataToValidatePlaceTest() throws Exception {
list = itemService.getMetadata(publication1, "dc", "contributor", Item.ANY, Item.ANY);
assertEquals(10, list.size()); //same size as authors
list = itemService.getMetadata(publication1, "dc", Item.ANY, Item.ANY, Item.ANY);
- assertEquals(16, list.size()); //also includes title, 4 date fields, uri
+ assertEquals(15, list.size()); //also includes title, 3 date fields, uri
list = itemService.getMetadata(publication1, Item.ANY, Item.ANY, Item.ANY, Item.ANY);
// also includes type, 3 relation.isAuthorOfPublication and 3 relation.isAuthorOfPublication.latestForDiscovery
// values
- assertEquals(23, list.size());
+ assertEquals(22, list.size());
} finally {
RelationshipBuilder.deleteRelationship(idRef1.get());
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RorImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RorImportMetadataSourceServiceIT.java
new file mode 100644
index 000000000000..9a8d14f3d658
--- /dev/null
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RorImportMetadataSourceServiceIT.java
@@ -0,0 +1,164 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest;
+
+import static org.dspace.app.matcher.LambdaMatcher.matches;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Optional;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.dspace.importer.external.datamodel.ImportRecord;
+import org.dspace.importer.external.liveimportclient.service.LiveImportClientImpl;
+import org.dspace.importer.external.ror.service.RorImportMetadataSourceServiceImpl;
+import org.hamcrest.Matcher;
+import org.junit.Test;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mockito;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class RorImportMetadataSourceServiceIT extends AbstractLiveImportIntegrationTest {
+
+ @Autowired
+ private LiveImportClientImpl liveImportClient;
+
+ @Autowired
+ private RorImportMetadataSourceServiceImpl rorServiceImpl;
+
+ @Test
+ public void tesGetRecords() throws Exception {
+ context.turnOffAuthorisationSystem();
+ CloseableHttpClient originalHttpClient = liveImportClient.getHttpClient();
+ CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
+
+ try (InputStream file = getClass().getResourceAsStream("ror-records.json")) {
+
+ String jsonResponse = IOUtils.toString(file, Charset.defaultCharset());
+
+ liveImportClient.setHttpClient(httpClient);
+ CloseableHttpResponse response = mockResponse(jsonResponse, 200, "OK");
+ when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
+
+ context.restoreAuthSystemState();
+ Collection recordsImported = rorServiceImpl.getRecords("test query", 0, 2);
+ assertThat(recordsImported, hasSize(20));
+
+ ImportRecord record = recordsImported.iterator().next();
+
+ assertThat(record.getValueList(), hasSize(11));
+
+ assertThat(
+ record.getSingleValue("organization.legalName"),
+ is("The University of Texas")
+ );
+ assertThat(record.getSingleValue("organization.identifier.ror"), is("https://ror.org/02f6dcw23"));
+ assertThat(record.getSingleValue("organization.alternateName"), is("UTHSCSA"));
+ assertThat(record.getSingleValue("organization.url"), is("http://www.uthscsa.edu/"));
+ assertThat(record.getSingleValue("dc.type"), is("Education"));
+ assertThat(record.getSingleValue("organization.address.addressCountry"), is("US"));
+ assertThat(record.getSingleValue("organization.foundingDate"), is("1959"));
+ assertThat(record.getValue("organization", "identifier", "crossrefid"), hasSize(2));
+ assertThat(record.getSingleValue("organization.identifier.isni"), is("0000 0001 0629 5880"));
+ assertThat(record.getSingleValue("organization.parentOrganization"), is("The University of Texas System"));
+
+ } finally {
+ liveImportClient.setHttpClient(originalHttpClient);
+ }
+ }
+
+ @Test
+ public void tesCount() throws Exception {
+ context.turnOffAuthorisationSystem();
+ CloseableHttpClient originalHttpClient = liveImportClient.getHttpClient();
+ CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
+
+ try (InputStream file = getClass().getResourceAsStream("ror-records.json")) {
+
+ String jsonResponse = IOUtils.toString(file, Charset.defaultCharset());
+
+ liveImportClient.setHttpClient(httpClient);
+ CloseableHttpResponse response = mockResponse(jsonResponse, 200, "OK");
+ when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
+
+ context.restoreAuthSystemState();
+ Integer count = rorServiceImpl.count("test");
+ assertThat(count, equalTo(200));
+ } finally {
+ liveImportClient.setHttpClient(originalHttpClient);
+ }
+ }
+
+ @Test
+ public void tesGetRecord() throws Exception {
+ context.turnOffAuthorisationSystem();
+ CloseableHttpClient originalHttpClient = liveImportClient.getHttpClient();
+ CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
+
+ try (InputStream file = getClass().getResourceAsStream("ror-record.json")) {
+
+ String jsonResponse = IOUtils.toString(file, Charset.defaultCharset());
+
+ liveImportClient.setHttpClient(httpClient);
+ CloseableHttpResponse response = mockResponse(jsonResponse, 200, "OK");
+ when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
+
+ context.restoreAuthSystemState();
+ ImportRecord record = rorServiceImpl.getRecord("https://ror.org/01sps7q28");
+ assertThat(record.getValueList(), hasSize(9));
+ assertThat(
+ record.getSingleValue("organization.legalName"),
+ is("The University of Texas Health Science Center at Tyler")
+ );
+ assertThat(record.getSingleValue("organization.identifier.ror"), is("https://ror.org/01sps7q28"));
+ assertThat(record.getSingleValue("organization.alternateName"), is("UTHSCT"));
+ assertThat(record.getSingleValue("organization.url"),
+ is("https://www.utsystem.edu/institutions/university-texas-health-science-center-tyler"));
+ assertThat(record.getSingleValue("dc.type"), is("Healthcare"));
+ assertThat(record.getSingleValue("organization.address.addressCountry"), is("US"));
+ assertThat(record.getSingleValue("organization.foundingDate"), is("1947"));
+ assertThat(record.getSingleValue("organization.identifier.isni"), is("0000 0000 9704 5790"));
+ assertThat(record.getSingleValue("organization.parentOrganization"), is("The University of Texas System"));
+
+ } finally {
+ liveImportClient.setHttpClient(originalHttpClient);
+ }
+ }
+
+ @Test
+ public void tesGetRecordsCount() throws Exception {
+ context.turnOffAuthorisationSystem();
+ CloseableHttpClient originalHttpClient = liveImportClient.getHttpClient();
+ CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
+ try (InputStream rorResponse = getClass().getResourceAsStream("ror-records.json")) {
+ String rorJsonResponse = IOUtils.toString(rorResponse, Charset.defaultCharset());
+
+ liveImportClient.setHttpClient(httpClient);
+ CloseableHttpResponse response = mockResponse(rorJsonResponse, 200, "OK");
+ when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
+
+ context.restoreAuthSystemState();
+ int tot = rorServiceImpl.getRecordsCount("test query");
+ assertEquals(200, tot);
+ } finally {
+ liveImportClient.setHttpClient(originalHttpClient);
+ }
+ }
+
+ private Matcher> is(String value) {
+ return matches(optionalValue -> optionalValue.isPresent() && optionalValue.get().equals(value));
+ }
+}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java
index babb1fac2326..1346be3fa902 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java
@@ -91,6 +91,7 @@ public void findDefault() throws Exception {
//The status has to be 200 OK
.andExpect(status().isOk())
.andExpect(jsonPath("$", SubmissionDefinitionsMatcher.matchFullEmbeds()))
+ .andExpect(jsonPath("$", SubmissionDefinitionsMatcher.matchLinks()))
//We expect the content type to be "application/hal+json;charset=UTF-8"
.andExpect(content().contentType(contentType))
@@ -257,10 +258,10 @@ public void findAllPaginationTest() throws Exception {
Matchers.containsString("page=1"), Matchers.containsString("size=1"))))
.andExpect(jsonPath("$._links.last.href", Matchers.allOf(
Matchers.containsString("/api/config/submissiondefinitions?"),
- Matchers.containsString("page=6"), Matchers.containsString("size=1"))))
+ Matchers.containsString("page=9"), Matchers.containsString("size=1"))))
.andExpect(jsonPath("$.page.size", is(1)))
- .andExpect(jsonPath("$.page.totalElements", is(7)))
- .andExpect(jsonPath("$.page.totalPages", is(7)))
+ .andExpect(jsonPath("$.page.totalElements", is(10)))
+ .andExpect(jsonPath("$.page.totalPages", is(10)))
.andExpect(jsonPath("$.page.number", is(0)));
getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions")
@@ -268,7 +269,7 @@ public void findAllPaginationTest() throws Exception {
.param("page", "1"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
- .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("test-hidden")))
+ .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("languagetestprocess")))
.andExpect(jsonPath("$._links.first.href", Matchers.allOf(
Matchers.containsString("/api/config/submissiondefinitions?"),
Matchers.containsString("page=0"), Matchers.containsString("size=1"))))
@@ -283,10 +284,10 @@ public void findAllPaginationTest() throws Exception {
Matchers.containsString("page=1"), Matchers.containsString("size=1"))))
.andExpect(jsonPath("$._links.last.href", Matchers.allOf(
Matchers.containsString("/api/config/submissiondefinitions?"),
- Matchers.containsString("page="), Matchers.containsString("size=1"))))
+ Matchers.containsString("page=9"), Matchers.containsString("size=1"))))
.andExpect(jsonPath("$.page.size", is(1)))
- .andExpect(jsonPath("$.page.totalElements", is(7)))
- .andExpect(jsonPath("$.page.totalPages", is(7)))
+ .andExpect(jsonPath("$.page.totalElements", is(10)))
+ .andExpect(jsonPath("$.page.totalPages", is(10)))
.andExpect(jsonPath("$.page.number", is(1)));
getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions")
@@ -294,7 +295,7 @@ public void findAllPaginationTest() throws Exception {
.param("page", "2"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
- .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("accessConditionNotDiscoverable")))
+ .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("extractiontestprocess")))
.andExpect(jsonPath("$._links.first.href", Matchers.allOf(
Matchers.containsString("/api/config/submissiondefinitions?"),
Matchers.containsString("page=0"), Matchers.containsString("size=1"))))
@@ -309,10 +310,10 @@ public void findAllPaginationTest() throws Exception {
Matchers.containsString("page=2"), Matchers.containsString("size=1"))))
.andExpect(jsonPath("$._links.last.href", Matchers.allOf(
Matchers.containsString("/api/config/submissiondefinitions?"),
- Matchers.containsString("page=6"), Matchers.containsString("size=1"))))
+ Matchers.containsString("page=9"), Matchers.containsString("size=1"))))
.andExpect(jsonPath("$.page.size", is(1)))
- .andExpect(jsonPath("$.page.totalElements", is(7)))
- .andExpect(jsonPath("$.page.totalPages", is(7)))
+ .andExpect(jsonPath("$.page.totalElements", is(10)))
+ .andExpect(jsonPath("$.page.totalPages", is(10)))
.andExpect(jsonPath("$.page.number", is(2)));
getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions")
@@ -320,7 +321,7 @@ public void findAllPaginationTest() throws Exception {
.param("page", "3"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
- .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("languagetestprocess")))
+ .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("qualdroptest")))
.andExpect(jsonPath("$._links.first.href", Matchers.allOf(
Matchers.containsString("/api/config/submissiondefinitions?"),
Matchers.containsString("page=0"), Matchers.containsString("size=1"))))
@@ -335,10 +336,10 @@ public void findAllPaginationTest() throws Exception {
Matchers.containsString("page=3"), Matchers.containsString("size=1"))))
.andExpect(jsonPath("$._links.last.href", Matchers.allOf(
Matchers.containsString("/api/config/submissiondefinitions?"),
- Matchers.containsString("page=6"), Matchers.containsString("size=1"))))
+ Matchers.containsString("page=9"), Matchers.containsString("size=1"))))
.andExpect(jsonPath("$.page.size", is(1)))
- .andExpect(jsonPath("$.page.totalElements", is(7)))
- .andExpect(jsonPath("$.page.totalPages", is(7)))
+ .andExpect(jsonPath("$.page.totalElements", is(10)))
+ .andExpect(jsonPath("$.page.totalPages", is(10)))
.andExpect(jsonPath("$.page.number", is(3)));
getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions")
@@ -346,7 +347,7 @@ public void findAllPaginationTest() throws Exception {
.param("page", "4"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
- .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("qualdroptest")))
+ .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("typebindtest")))
.andExpect(jsonPath("$._links.first.href", Matchers.allOf(
Matchers.containsString("/api/config/submissiondefinitions?"),
Matchers.containsString("page=0"), Matchers.containsString("size=1"))))
@@ -361,10 +362,10 @@ public void findAllPaginationTest() throws Exception {
Matchers.containsString("page=4"), Matchers.containsString("size=1"))))
.andExpect(jsonPath("$._links.last.href", Matchers.allOf(
Matchers.containsString("/api/config/submissiondefinitions?"),
- Matchers.containsString("page=6"), Matchers.containsString("size=1"))))
+ Matchers.containsString("page=9"), Matchers.containsString("size=1"))))
.andExpect(jsonPath("$.page.size", is(1)))
- .andExpect(jsonPath("$.page.totalElements", is(7)))
- .andExpect(jsonPath("$.page.totalPages", is(7)))
+ .andExpect(jsonPath("$.page.totalElements", is(10)))
+ .andExpect(jsonPath("$.page.totalPages", is(10)))
.andExpect(jsonPath("$.page.number", is(4)));
getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions")
@@ -372,7 +373,7 @@ public void findAllPaginationTest() throws Exception {
.param("page", "5"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
- .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("extractiontestprocess")))
+ .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("accessConditionNotDiscoverable")))
.andExpect(jsonPath("$._links.first.href", Matchers.allOf(
Matchers.containsString("/api/config/submissiondefinitions?"),
Matchers.containsString("page=0"), Matchers.containsString("size=1"))))
@@ -386,12 +387,139 @@ public void findAllPaginationTest() throws Exception {
Matchers.containsString("/api/config/submissiondefinitions?"),
Matchers.containsString("page=5"), Matchers.containsString("size=1"))))
.andExpect(jsonPath("$._links.last.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=9"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$.page.size", is(1)))
+ .andExpect(jsonPath("$.page.totalElements", is(10)))
+ .andExpect(jsonPath("$.page.totalPages", is(10)))
+ .andExpect(jsonPath("$.page.number", is(5)));
+
+ getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions")
+ .param("size", "1")
+ .param("page", "5"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("accessConditionNotDiscoverable")))
+ .andExpect(jsonPath("$._links.first.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=0"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.prev.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=4"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.next.href", Matchers.allOf(
Matchers.containsString("/api/config/submissiondefinitions?"),
Matchers.containsString("page=6"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.self.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=5"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.last.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=9"), Matchers.containsString("size=1"))))
.andExpect(jsonPath("$.page.size", is(1)))
- .andExpect(jsonPath("$.page.totalElements", is(7)))
- .andExpect(jsonPath("$.page.totalPages", is(7)))
+ .andExpect(jsonPath("$.page.totalElements", is(10)))
+ .andExpect(jsonPath("$.page.totalPages", is(10)))
.andExpect(jsonPath("$.page.number", is(5)));
+
+ getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions")
+ .param("size", "1")
+ .param("page", "6"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("test-hidden")))
+ .andExpect(jsonPath("$._links.first.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=0"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.prev.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=5"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.next.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=7"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.self.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=6"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.last.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=9"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$.page.size", is(1)))
+ .andExpect(jsonPath("$.page.totalElements", is(10)))
+ .andExpect(jsonPath("$.page.totalPages", is(10)))
+ .andExpect(jsonPath("$.page.number", is(6)));
+
+ getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions")
+ .param("size", "1")
+ .param("page", "7"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("topcommunitytest")))
+ .andExpect(jsonPath("$._links.first.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=0"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.prev.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=6"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.next.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=8"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.self.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=7"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.last.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=9"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$.page.size", is(1)))
+ .andExpect(jsonPath("$.page.totalElements", is(10)))
+ .andExpect(jsonPath("$.page.totalPages", is(10)))
+ .andExpect(jsonPath("$.page.number", is(7)));
+
+ getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions")
+ .param("size", "1")
+ .param("page", "8"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("subcommunitytest")))
+ .andExpect(jsonPath("$._links.first.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=0"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.prev.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=7"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.next.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=9"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.self.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=8"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.last.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=9"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$.page.size", is(1)))
+ .andExpect(jsonPath("$.page.totalElements", is(10)))
+ .andExpect(jsonPath("$.page.totalPages", is(10)))
+ .andExpect(jsonPath("$.page.number", is(8)));
+
+ getClient(tokenAdmin).perform(get("/api/config/submissiondefinitions")
+ .param("size", "1")
+ .param("page", "9"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.submissiondefinitions[0].id", is("collectiontest")))
+ .andExpect(jsonPath("$._links.first.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=0"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.prev.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=8"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.self.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=9"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.last.href", Matchers.allOf(
+ Matchers.containsString("/api/config/submissiondefinitions?"),
+ Matchers.containsString("page=9"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$.page.size", is(1)))
+ .andExpect(jsonPath("$.page.totalElements", is(10)))
+ .andExpect(jsonPath("$.page.totalPages", is(10)))
+ .andExpect(jsonPath("$.page.number", is(9)));
}
}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SuggestionRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SuggestionRestRepositoryIT.java
new file mode 100644
index 000000000000..aa8ad1de55b7
--- /dev/null
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SuggestionRestRepositoryIT.java
@@ -0,0 +1,475 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest;
+
+import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
+import static org.dspace.app.rest.matcher.SuggestionMatcher.matchSuggestion;
+import static org.dspace.builder.SuggestionTargetBuilder.EVIDENCE_MOCK_NAME;
+import static org.dspace.builder.SuggestionTargetBuilder.EVIDENCE_MOCK_NOTE;
+import static org.hamcrest.Matchers.is;
+import static org.springframework.data.rest.webmvc.RestMediaTypes.TEXT_URI_LIST_VALUE;
+import static org.springframework.http.MediaType.parseMediaType;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.util.Map;
+import java.util.UUID;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.dspace.app.rest.matcher.MetadataMatcher;
+import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
+import org.dspace.app.suggestion.MockSuggestionExternalDataSource;
+import org.dspace.app.suggestion.SuggestionTarget;
+import org.dspace.builder.CollectionBuilder;
+import org.dspace.builder.CommunityBuilder;
+import org.dspace.builder.ItemBuilder;
+import org.dspace.builder.SuggestionTargetBuilder;
+import org.dspace.builder.WorkspaceItemBuilder;
+import org.dspace.content.Collection;
+import org.dspace.content.Item;
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.test.web.servlet.MvcResult;
+
+/**
+ * Integration Tests against the /api/integration/suggestions endpoint
+ */
+public class SuggestionRestRepositoryIT extends AbstractControllerIntegrationTest {
+ private Collection colPeople;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ // We turn off the authorization system in order to create the structure as
+ // defined below
+ context.turnOffAuthorisationSystem();
+ parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
+ colPeople = CollectionBuilder.createCollection(context, parentCommunity).withName("People")
+ .withEntityType("Person").build();
+ context.restoreAuthSystemState();
+ }
+
+ @Test
+ public void findAllTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Item itemFirst = ItemBuilder.createItem(context, colPeople).withTitle("Bollini, Andrea").build();
+ SuggestionTarget targetFirstScopus = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("scopus", 3).build();
+ context.restoreAuthSystemState();
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken).perform(get("/api/integration/suggestions"))
+ .andExpect(status().isMethodNotAllowed());
+ String token = getAuthToken(eperson.getEmail(), password);
+ getClient(token).perform(get("/api/integration/suggestions")).andExpect(status().isMethodNotAllowed());
+ getClient().perform(get("/api/integration/suggestions")).andExpect(status().isMethodNotAllowed());
+ }
+
+ @Test
+ public void findByTargetAndSourceTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Item itemFirst = ItemBuilder.createItem(context, colPeople).withTitle("Bollini, Andrea").build();
+ SuggestionTarget targetFirstReciter = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("reciter", 31).build();
+ SuggestionTarget targetFirstScopus = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("scopus", 3).build();
+ SuggestionTarget targetSecond = SuggestionTargetBuilder
+ .createTarget(context, colPeople, "Digilio, Giuseppe", eperson).withSuggestionCount("reciter", 11)
+ .build();
+ context.restoreAuthSystemState();
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestions/search/findByTargetAndSource").param("source", "scopus")
+ .param("target", itemFirst.getID().toString()))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestions", Matchers.contains(
+ matchSuggestion("scopus", itemFirst, "Suggestion scopus 1", "1",
+ 100.0, EVIDENCE_MOCK_NAME, 100.0, EVIDENCE_MOCK_NOTE),
+ matchSuggestion("scopus", itemFirst, "Suggestion scopus 3", "3",
+ 98.0, EVIDENCE_MOCK_NAME, 98.0, EVIDENCE_MOCK_NOTE),
+ matchSuggestion("scopus", itemFirst, "Suggestion scopus 2", "2",
+ 0.5, EVIDENCE_MOCK_NAME, 0.5, EVIDENCE_MOCK_NOTE))))
+ .andExpect(jsonPath("$._links.self.href", Matchers.allOf(
+ Matchers.containsString(
+ "/api/integration/suggestions/search/findByTargetAndSource?"),
+ Matchers.containsString("source=scopus"),
+ Matchers.containsString("target=" + itemFirst.getID().toString()))))
+ .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(3)));
+ Item itemSecond = targetSecond.getTarget();
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestions/search/findByTargetAndSource").param("source", "reciter")
+ .param("target", itemSecond.getID().toString()))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestions", Matchers.containsInAnyOrder(
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 1", "1"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 2", "2"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 3", "3"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 4", "4"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 5", "5"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 6", "6"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 7", "7"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 8", "8"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 9", "9"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 10", "10"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 11", "11"))))
+ .andExpect(jsonPath("$._links.self.href", Matchers.allOf(
+ Matchers.containsString(
+ "/api/integration/suggestions/search/findByTargetAndSource?"),
+ Matchers.containsString("source=reciter"),
+ Matchers.containsString("target=" + itemSecond.getID().toString()))))
+ .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(11)));
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonToken)
+ .perform(get("/api/integration/suggestions/search/findByTargetAndSource").param("source", "reciter")
+ .param("target", itemSecond.getID().toString()))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestions", Matchers.containsInAnyOrder(
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 1", "1"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 2", "2"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 3", "3"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 4", "4"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 5", "5"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 6", "6"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 7", "7"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 8", "8"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 9", "9"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 10", "10"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 11", "11"))))
+ .andExpect(jsonPath("$._links.self.href", Matchers.allOf(
+ Matchers.containsString(
+ "/api/integration/suggestions/search/findByTargetAndSource?"),
+ Matchers.containsString("source=reciter"),
+ Matchers.containsString("target=" + itemSecond.getID().toString()))))
+ .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(11)));
+ }
+
+ @Test
+ public void findByTargetAndSourcePaginationTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ SuggestionTarget targetSecond = SuggestionTargetBuilder
+ .createTarget(context, colPeople, "Digilio, Giuseppe", eperson).withSuggestionCount("reciter", 11)
+ .build();
+ context.restoreAuthSystemState();
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ Item itemSecond = targetSecond.getTarget();
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestions/search/findByTargetAndSource").param("source", "reciter")
+ .param("size", "5")
+ .param("target", itemSecond.getID().toString()))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestions", Matchers.contains(
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 1", "1"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 3", "3"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 5", "5"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 7", "7"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 9", "9"))))
+ .andExpect(jsonPath("$._links.self.href", Matchers.allOf(
+ Matchers.containsString(
+ "/api/integration/suggestions/search/findByTargetAndSource?"),
+ Matchers.containsString("source=reciter"),
+ Matchers.containsString("size=5"),
+ Matchers.containsString("target=" + itemSecond.getID().toString()))))
+ .andExpect(jsonPath("$._links.next.href",
+ Matchers.allOf(
+ Matchers.containsString(
+ "/api/integration/suggestions/search/findByTargetAndSource?"),
+ Matchers.containsString("source=reciter"),
+ Matchers.containsString("page=1"),
+ Matchers.containsString("size=5"),
+ Matchers.containsString("target=" + itemSecond.getID().toString()))))
+ .andExpect(jsonPath("$._links.last.href",
+ Matchers.allOf(
+ Matchers.containsString(
+ "/api/integration/suggestions/search/findByTargetAndSource?"),
+ Matchers.containsString("source=reciter"),
+ Matchers.containsString("page=2"),
+ Matchers.containsString("size=5"),
+ Matchers.containsString("target=" + itemSecond.getID().toString()))))
+ .andExpect(jsonPath("$._links.first.href",
+ Matchers.allOf(
+ Matchers.containsString(
+ "/api/integration/suggestions/search/findByTargetAndSource?"),
+ Matchers.containsString("source=reciter"),
+ Matchers.containsString("page=0"),
+ Matchers.containsString("size=5"),
+ Matchers.containsString("target=" + itemSecond.getID().toString()))))
+ .andExpect(jsonPath("$._links.prev.href").doesNotExist())
+ .andExpect(jsonPath("$.page.size", is(5))).andExpect(jsonPath("$.page.totalElements", is(11)));
+
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestions/search/findByTargetAndSource").param("source", "reciter")
+ .param("size", "5")
+ .param("page", "1")
+ .param("target", itemSecond.getID().toString()))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestions", Matchers.contains(
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 11", "11"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 10", "10"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 8", "8"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 6", "6"),
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 4", "4"))))
+ .andExpect(jsonPath("$._links.self.href", Matchers.allOf(
+ Matchers.containsString(
+ "/api/integration/suggestions/search/findByTargetAndSource?"),
+ Matchers.containsString("source=reciter"),
+ Matchers.containsString("page=1"),
+ Matchers.containsString("size=5"),
+ Matchers.containsString("target=" + itemSecond.getID().toString()))))
+ .andExpect(jsonPath("$._links.next.href",
+ Matchers.allOf(
+ Matchers.containsString(
+ "/api/integration/suggestions/search/findByTargetAndSource?"),
+ Matchers.containsString("source=reciter"),
+ Matchers.containsString("page=2"),
+ Matchers.containsString("size=5"),
+ Matchers.containsString("target=" + itemSecond.getID().toString()))))
+ .andExpect(jsonPath("$._links.prev.href",
+ Matchers.allOf(
+ Matchers.containsString(
+ "/api/integration/suggestions/search/findByTargetAndSource?"),
+ Matchers.containsString("source=reciter"),
+ Matchers.containsString("page=0"),
+ Matchers.containsString("size=5"),
+ Matchers.containsString("target=" + itemSecond.getID().toString()))))
+ .andExpect(jsonPath("$._links.last.href",
+ Matchers.allOf(
+ Matchers.containsString(
+ "/api/integration/suggestions/search/findByTargetAndSource?"),
+ Matchers.containsString("source=reciter"),
+ Matchers.containsString("page=2"),
+ Matchers.containsString("size=5"),
+ Matchers.containsString("target=" + itemSecond.getID().toString()))))
+ .andExpect(jsonPath("$._links.first.href",
+ Matchers.allOf(
+ Matchers.containsString(
+ "/api/integration/suggestions/search/findByTargetAndSource?"),
+ Matchers.containsString("source=reciter"),
+ Matchers.containsString("page=0"),
+ Matchers.containsString("size=5"),
+ Matchers.containsString("target=" + itemSecond.getID().toString()))))
+ .andExpect(jsonPath("$.page.size", is(5))).andExpect(jsonPath("$.page.totalElements", is(11)));
+
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestions/search/findByTargetAndSource").param("source", "reciter")
+ .param("size", "5")
+ .param("page", "2")
+ .param("target", itemSecond.getID().toString()))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestions", Matchers.contains(
+ matchSuggestion("reciter", itemSecond, "Suggestion reciter 2", "2"))))
+ .andExpect(jsonPath("$._links.self.href", Matchers.allOf(
+ Matchers.containsString(
+ "/api/integration/suggestions/search/findByTargetAndSource?"),
+ Matchers.containsString("source=reciter"),
+ Matchers.containsString("page=2"),
+ Matchers.containsString("size=5"),
+ Matchers.containsString("target=" + itemSecond.getID().toString()))))
+ .andExpect(jsonPath("$._links.prev.href",
+ Matchers.allOf(
+ Matchers.containsString(
+ "/api/integration/suggestions/search/findByTargetAndSource?"),
+ Matchers.containsString("source=reciter"),
+ Matchers.containsString("page=1"),
+ Matchers.containsString("size=5"),
+ Matchers.containsString("target=" + itemSecond.getID().toString()))))
+ .andExpect(jsonPath("$._links.last.href",
+ Matchers.allOf(
+ Matchers.containsString(
+ "/api/integration/suggestions/search/findByTargetAndSource?"),
+ Matchers.containsString("source=reciter"),
+ Matchers.containsString("page=2"),
+ Matchers.containsString("size=5"),
+ Matchers.containsString("target=" + itemSecond.getID().toString()))))
+ .andExpect(jsonPath("$._links.first.href",
+ Matchers.allOf(
+ Matchers.containsString(
+ "/api/integration/suggestions/search/findByTargetAndSource?"),
+ Matchers.containsString("source=reciter"),
+ Matchers.containsString("page=0"),
+ Matchers.containsString("size=5"),
+ Matchers.containsString("target=" + itemSecond.getID().toString()))))
+ .andExpect(jsonPath("$._links.next.href").doesNotExist())
+ .andExpect(jsonPath("$.page.size", is(5))).andExpect(jsonPath("$.page.totalElements", is(11)));
+ }
+
+ @Test
+ public void findByTargetAndSourceNotAdminTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Item itemFirst = ItemBuilder.createItem(context, colPeople).withTitle("Bollini, Andrea").build();
+ SuggestionTarget targetFirstReciter = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("reciter", 31).build();
+ SuggestionTarget targetFirstScopus = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("scopus", 3).build();
+ SuggestionTarget targetSecond = SuggestionTargetBuilder
+ .createTarget(context, colPeople, "Digilio, Giuseppe", eperson).withSuggestionCount("reciter", 11)
+ .build();
+ context.restoreAuthSystemState();
+ // anonymous cannot access the suggestions source endpoint
+ getClient()
+ .perform(get("/api/integration/suggestions/search/findByTargetAndSource")
+ .param("target", UUID.randomUUID().toString()).param("source", "reciter"))
+ .andExpect(status().isUnauthorized());
+ // nor normal user
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson)
+ .perform(get("/api/integration/suggestions/search/findByTargetAndSource")
+ .param("target", UUID.randomUUID().toString()).param("source", "reciter"))
+ .andExpect(status().isForbidden());
+ }
+
+ @Test
+ public void findOneTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Item itemFirst = ItemBuilder.createItem(context, colPeople).withTitle("Bollini, Andrea").build();
+ SuggestionTarget targetFirstReciter = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("reciter", 31).build();
+ SuggestionTarget targetFirstScopus = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("scopus", 3).build();
+ //targetSecond refers to eperson
+ SuggestionTarget targetSecond = SuggestionTargetBuilder
+ .createTarget(context, colPeople, "Digilio, Giuseppe", eperson).withSuggestionCount("reciter", 11)
+ .build();
+ context.restoreAuthSystemState();
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ String suggestionId = "reciter:" + itemFirst.getID().toString() + ":6";
+ getClient(adminToken).perform(get("/api/integration/suggestions/" + suggestionId)).andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$", matchSuggestion("reciter", itemFirst, "Suggestion reciter 6", "6")))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.endsWith("/api/integration/suggestions/" + suggestionId)));
+ //test targetSecond refers to eperson
+ Item itemSecond = targetSecond.getTarget();
+ String epersonSuggestionId = "reciter:" + itemSecond.getID().toString() + ":2";
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonToken).perform(get("/api/integration/suggestions/" + epersonSuggestionId))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$", matchSuggestion("reciter", itemSecond, "Suggestion reciter 2", "2")))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.endsWith("/api/integration/suggestions/" + epersonSuggestionId)));
+ }
+
+ @Test
+ public void findOneNotAdminTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Item itemFirst = ItemBuilder.createItem(context, colPeople).withTitle("Bollini, Andrea").build();
+ SuggestionTarget targetFirstReciter = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("reciter", 31).build();
+ SuggestionTarget targetFirstScopus = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("scopus", 3).build();
+ SuggestionTarget targetSecond = SuggestionTargetBuilder
+ .createTarget(context, colPeople, "Digilio, Giuseppe", eperson).withSuggestionCount("reciter", 11)
+ .build();
+ context.restoreAuthSystemState();
+
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ String suggestionId = "reciter:" + itemFirst.getID().toString() + ":6";
+ getClient(epersonToken).perform(get("/api/integration/suggestions/" + suggestionId))
+ .andExpect(status().isForbidden());
+ getClient(epersonToken).perform(get("/api/integration/suggestions/not-exist"))
+ .andExpect(status().isForbidden());
+ getClient().perform(get("/api/integration/suggestions/" + suggestionId)).andExpect(status().isUnauthorized());
+ getClient().perform(get("/api/integration/suggestions/not-exist")).andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ public void acceptSuggestionTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Collection colPublications = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Publications").build();
+ Item itemFirst = ItemBuilder.createItem(context, colPeople).withTitle("Bollini, Andrea").build();
+ SuggestionTarget targetFirstReciter = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("reciter", 2).build();
+ context.restoreAuthSystemState();
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ String suggestionId = "reciter:" + itemFirst.getID().toString() + ":1";
+ // the suggestion is here
+ getClient(adminToken).perform(get("/api/integration/suggestions/" + suggestionId)).andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$", matchSuggestion("reciter", itemFirst, "Suggestion reciter 1", "1")))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.endsWith("/api/integration/suggestions/" + suggestionId)));
+ Integer workspaceItemId = null;
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ MvcResult mvcResult = getClient(adminToken).perform(
+ post("/api/submission/workspaceitems?owningCollection=" + colPublications.getID().toString())
+ .contentType(parseMediaType(TEXT_URI_LIST_VALUE))
+ .content("http://localhost/api/integration/externalsources/"
+ + MockSuggestionExternalDataSource.NAME + "/entryValues/" + suggestionId))
+ .andExpect(status().isCreated()).andReturn();
+ String content = mvcResult.getResponse().getContentAsString();
+ Map map = mapper.readValue(content, Map.class);
+ workspaceItemId = (Integer) map.get("id");
+ String itemUuidString = String.valueOf(((Map) ((Map) map.get("_embedded")).get("item")).get("uuid"));
+
+ getClient(adminToken).perform(get("/api/submission/workspaceitems/" + workspaceItemId))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", Matchers.allOf(
+ hasJsonPath("$.id", is(workspaceItemId)),
+ hasJsonPath("$.type", is("workspaceitem")),
+ hasJsonPath("$._embedded.item", Matchers.allOf(
+ hasJsonPath("$.id", is(itemUuidString)),
+ hasJsonPath("$.uuid", is(itemUuidString)),
+ hasJsonPath("$.type", is("item")),
+ hasJsonPath("$.metadata", Matchers.allOf(
+ MetadataMatcher.matchMetadata("dc.title", "Title Suggestion 1")
+ )))))
+ ));
+
+ getClient(adminToken).perform(get("/api/integration/suggestions/" + suggestionId))
+ .andExpect(status().isNotFound());
+ // 1 suggestion is still pending
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestions/search/findByTargetAndSource").param("source", "reciter")
+ .param("target", itemFirst.getID().toString()))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestions", Matchers.contains(
+ matchSuggestion("reciter", itemFirst, "Suggestion reciter 2", "2"))))
+ .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(1)));
+ } finally {
+ if (workspaceItemId != null) {
+ WorkspaceItemBuilder.deleteWorkspaceItem(workspaceItemId);
+ }
+ }
+ }
+
+ @Test
+ public void rejectSuggestionTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Collection colPublications = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Publications").build();
+ Item itemFirst = ItemBuilder.createItem(context, colPeople).withTitle("Bollini, Andrea").build();
+ SuggestionTarget targetFirstReciter = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("reciter", 2).build();
+ context.restoreAuthSystemState();
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ String suggestionId = "reciter:" + itemFirst.getID().toString() + ":1";
+ // reject the suggestion
+ getClient(adminToken).perform(delete("/api/integration/suggestions/" + suggestionId))
+ .andExpect(status().isNoContent());
+ getClient(adminToken).perform(get("/api/integration/suggestions/" + suggestionId))
+ .andExpect(status().isNotFound());
+ // 1 suggestion is still pending
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestions/search/findByTargetAndSource").param("source", "reciter")
+ .param("target", itemFirst.getID().toString()))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestions", Matchers.contains(
+ matchSuggestion("reciter", itemFirst, "Suggestion reciter 2", "2"))))
+ .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(1)));
+ }
+}
\ No newline at end of file
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SuggestionSourceRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SuggestionSourceRestRepositoryIT.java
new file mode 100644
index 000000000000..30a1779fbd80
--- /dev/null
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SuggestionSourceRestRepositoryIT.java
@@ -0,0 +1,168 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest;
+
+import static org.dspace.app.rest.matcher.SuggestionSourceMatcher.matchSuggestionSource;
+import static org.hamcrest.Matchers.is;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
+import org.dspace.app.suggestion.SuggestionTarget;
+import org.dspace.builder.CollectionBuilder;
+import org.dspace.builder.CommunityBuilder;
+import org.dspace.builder.ItemBuilder;
+import org.dspace.builder.SuggestionTargetBuilder;
+import org.dspace.content.Collection;
+import org.dspace.content.Item;
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Integration Tests against the /api/integration/suggestionsources endpoint
+ */
+public class SuggestionSourceRestRepositoryIT extends AbstractControllerIntegrationTest {
+ private Collection colPeople;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ // We turn off the authorization system in order to create the structure as
+ // defined below
+ context.turnOffAuthorisationSystem();
+ parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
+ colPeople = CollectionBuilder.createCollection(context, parentCommunity).withName("People")
+ .withEntityType("Person").build();
+ context.restoreAuthSystemState();
+ }
+
+ /**
+ * Build a list of suggestion target, Bollini, Andrea has suggestion from both
+ * sources, Digilio, Giuseppe only from reciter Test 0, 3, 6 from both sources,
+ * Test 1, 2, 4, 5 only from ReCiter and finally Lombardi, Corrado only from
+ * scopus
+ */
+ private void buildSuggestionTargetsList() {
+ // We turn off the authorization system in order to create the structure as
+ // defined below
+ context.turnOffAuthorisationSystem();
+ Item itemFirst = ItemBuilder.createItem(context, colPeople).withTitle("Bollini, Andrea").build();
+ SuggestionTarget targetFirstReciter = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("reciter", 31).build();
+ SuggestionTarget targetFirstScopus = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("scopus", 3).build();
+ SuggestionTarget targetSecond = SuggestionTargetBuilder
+ .createTarget(context, colPeople, "Digilio, Giuseppe", eperson).withSuggestionCount("reciter", 11)
+ .build();
+ for (int idx = 0; idx < 8; idx++) {
+ Item item = ItemBuilder.createItem(context, colPeople).withTitle("Test " + idx).build();
+ SuggestionTargetBuilder.createTarget(context, item).withSuggestionCount("reciter", idx + 3).build();
+ if (idx % 3 == 0) {
+ SuggestionTargetBuilder.createTarget(context, item).withSuggestionCount("scopus", idx + 7).build();
+ }
+ }
+ Item itemLast = ItemBuilder.createItem(context, colPeople).withTitle("Lombardi, Corrado").build();
+ SuggestionTarget targetLast = SuggestionTargetBuilder.createTarget(context, itemLast)
+ .withSuggestionCount("scopus", 3).build();
+ context.restoreAuthSystemState();
+ }
+
+ @Test
+ public void findAllTest() throws Exception {
+ buildSuggestionTargetsList();
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken).perform(get("/api/integration/suggestionsources")).andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestionsources",
+ Matchers.contains(matchSuggestionSource("reciter", 10), matchSuggestionSource("scopus", 5))))
+ .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(2)));
+ }
+
+ @Test
+ public void findAllPaginationTest() throws Exception {
+ buildSuggestionTargetsList();
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken).perform(get("/api/integration/suggestionsources").param("size", "1"))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestionsources",
+ Matchers.contains(matchSuggestionSource("reciter", 10))))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/integration/suggestionsources")))
+ .andExpect(jsonPath("$._links.next.href",
+ Matchers.allOf(Matchers.containsString("/api/integration/suggestionsources?"),
+ Matchers.containsString("page=1"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.last.href",
+ Matchers.allOf(Matchers.containsString("/api/integration/suggestionsources?"),
+ Matchers.containsString("page=1"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.first.href",
+ Matchers.allOf(Matchers.containsString("/api/integration/suggestionsources?"),
+ Matchers.containsString("page=0"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.prev.href").doesNotExist()).andExpect(jsonPath("$.page.size", is(1)))
+ .andExpect(jsonPath("$.page.totalElements", is(2)));
+ getClient(adminToken).perform(get("/api/integration/suggestionsources").param("size", "1").param("page", "1"))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestionsources",
+ Matchers.contains(matchSuggestionSource("scopus", 5))))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/integration/suggestionsources")))
+ .andExpect(jsonPath("$._links.next.href").doesNotExist())
+ .andExpect(jsonPath("$._links.last.href",
+ Matchers.allOf(Matchers.containsString("/api/integration/suggestionsources?"),
+ Matchers.containsString("page=1"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.first.href",
+ Matchers.allOf(Matchers.containsString("/api/integration/suggestionsources?"),
+ Matchers.containsString("page=0"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.prev.href",
+ Matchers.allOf(Matchers.containsString("/api/integration/suggestionsources?"),
+ Matchers.containsString("page=0"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$.page.size", is(1))).andExpect(jsonPath("$.page.totalElements", is(2)));
+ }
+
+ @Test
+ public void findAllNotAdminTest() throws Exception {
+ buildSuggestionTargetsList();
+ // anonymous cannot access the suggestions source endpoint
+ getClient().perform(get("/api/integration/suggestionsources")).andExpect(status().isUnauthorized());
+ // nor normal user
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson).perform(get("/api/integration/suggestionsources")).andExpect(status().isForbidden());
+
+ }
+
+ @Test
+ public void findOneTest() throws Exception {
+ buildSuggestionTargetsList();
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken).perform(get("/api/integration/suggestionsources/reciter")).andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$", matchSuggestionSource("reciter", 10))).andExpect(jsonPath("$._links.self.href",
+ Matchers.endsWith("/api/integration/suggestionsources/reciter")));
+ getClient(adminToken).perform(get("/api/integration/suggestionsources/scopus")).andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$", matchSuggestionSource("scopus", 5))).andExpect(
+ jsonPath("$._links.self.href", Matchers.endsWith("/api/integration/suggestionsources/scopus")));
+
+ }
+
+ @Test
+ public void findOneNotAdminTest() throws Exception {
+ buildSuggestionTargetsList();
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonToken).perform(get("/api/integration/suggestionsources/reciter"))
+ .andExpect(status().isForbidden());
+ getClient(epersonToken).perform(get("/api/integration/suggestionsources/not-exist"))
+ .andExpect(status().isForbidden());
+ getClient().perform(get("/api/integration/suggestionsources/reciter")).andExpect(status().isUnauthorized());
+ getClient().perform(get("/api/integration/suggestionsources/not-exist")).andExpect(status().isUnauthorized());
+ }
+
+}
\ No newline at end of file
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SuggestionTargetRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SuggestionTargetRestRepositoryIT.java
new file mode 100644
index 000000000000..21108010f56c
--- /dev/null
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SuggestionTargetRestRepositoryIT.java
@@ -0,0 +1,597 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest;
+
+import static org.dspace.app.rest.matcher.SuggestionTargetMatcher.matchSuggestionTarget;
+import static org.hamcrest.Matchers.is;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.util.UUID;
+
+import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
+import org.dspace.app.suggestion.SuggestionTarget;
+import org.dspace.builder.CollectionBuilder;
+import org.dspace.builder.CommunityBuilder;
+import org.dspace.builder.EPersonBuilder;
+import org.dspace.builder.ItemBuilder;
+import org.dspace.builder.SuggestionTargetBuilder;
+import org.dspace.content.Collection;
+import org.dspace.content.Item;
+import org.dspace.eperson.EPerson;
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Integration Tests against the /api/integration/suggestiontargets endpoint
+ */
+public class SuggestionTargetRestRepositoryIT extends AbstractControllerIntegrationTest {
+ private Collection colPeople;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ // We turn off the authorization system in order to create the structure as
+ // defined below
+ context.turnOffAuthorisationSystem();
+ parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
+ colPeople = CollectionBuilder.createCollection(context, parentCommunity).withName("People")
+ .withEntityType("Person").build();
+ context.restoreAuthSystemState();
+ }
+
+ /**
+ * Build a list of suggestion target, Bollini, Andrea has suggestion from both
+ * sources, Digilio, Giuseppe only from reciter Test 0, 3, 6 from both sources,
+ * Test 1, 2, 4, 5 only from ReCiter and finally Lombardi, Corrado only from
+ * scopus
+ */
+ private void buildSuggestionTargetsList() {
+ // We turn off the authorization system in order to create the structure as
+ // defined below
+ context.turnOffAuthorisationSystem();
+ Item itemFirst = ItemBuilder.createItem(context, colPeople).withTitle("Bollini, Andrea").build();
+ SuggestionTarget targetFirstReciter = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("reciter", 31).build();
+ SuggestionTarget targetFirstScopus = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("scopus", 3).build();
+ SuggestionTarget targetSecond = SuggestionTargetBuilder
+ .createTarget(context, colPeople, "Digilio, Giuseppe", eperson).withSuggestionCount("reciter", 11)
+ .build();
+ for (int idx = 0; idx < 8; idx++) {
+ Item item = ItemBuilder.createItem(context, colPeople).withTitle("Test " + idx).build();
+ SuggestionTargetBuilder.createTarget(context, item).withSuggestionCount("reciter", idx + 3).build();
+ if (idx % 3 == 0) {
+ SuggestionTargetBuilder.createTarget(context, item).withSuggestionCount("scopus", idx + 7).build();
+ }
+ }
+ Item itemLast = ItemBuilder.createItem(context, colPeople).withTitle("Lombardi, Corrado").build();
+ SuggestionTarget targetLast = SuggestionTargetBuilder.createTarget(context, itemLast)
+ .withSuggestionCount("scopus", 3).build();
+ context.restoreAuthSystemState();
+ }
+
+ @Test
+ public void findAllTest() throws Exception {
+ buildSuggestionTargetsList();
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken).perform(get("/api/integration/suggestiontargets"))
+ .andExpect(status().isMethodNotAllowed());
+ String token = getAuthToken(eperson.getEmail(), password);
+ getClient(token).perform(get("/api/integration/suggestiontargets")).andExpect(status().isMethodNotAllowed());
+ getClient().perform(get("/api/integration/suggestiontargets")).andExpect(status().isMethodNotAllowed());
+ }
+
+ @Test
+ public void findBySourceTest() throws Exception {
+ buildSuggestionTargetsList();
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestiontargets/search/findBySource").param("source", "reciter"))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestiontargets", Matchers.contains(
+ matchSuggestionTarget("Bollini, Andrea", "reciter", 31),
+ matchSuggestionTarget("Digilio, Giuseppe", "reciter", 11),
+ matchSuggestionTarget("Test 7", "reciter", 10), matchSuggestionTarget("Test 6", "reciter", 9),
+ matchSuggestionTarget("Test 5", "reciter", 8), matchSuggestionTarget("Test 4", "reciter", 7),
+ matchSuggestionTarget("Test 3", "reciter", 6), matchSuggestionTarget("Test 2", "reciter", 5),
+ matchSuggestionTarget("Test 1", "reciter", 4), matchSuggestionTarget("Test 0", "reciter", 3)
+ )))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString(
+ "/api/integration/suggestiontargets/search/findBySource?source=reciter")))
+ .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(10)));
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestiontargets/search/findBySource").param("source", "scopus"))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestiontargets",
+ Matchers.containsInAnyOrder(
+ matchSuggestionTarget("Test 6", "scopus", 13),
+ matchSuggestionTarget("Test 3", "scopus", 10),
+ matchSuggestionTarget("Test 0", "scopus", 7),
+ matchSuggestionTarget("Bollini, Andrea", "scopus", 3),
+ matchSuggestionTarget("Lombardi, Corrado", "scopus", 3))))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString(
+ "/api/integration/suggestiontargets/search/findBySource?source=scopus")))
+ .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(5)));
+ }
+
+ @Test
+ public void findBySourcePaginationTest() throws Exception {
+ buildSuggestionTargetsList();
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestiontargets/search/findBySource")
+ .param("source", "reciter").param("size", "1"))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestiontargets",
+ Matchers.contains(matchSuggestionTarget("Bollini, Andrea", "reciter", 31))))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=reciter"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.next.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=reciter"), Matchers.containsString("page=1"),
+ Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.last.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=reciter"), Matchers.containsString("page=9"),
+ Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.first.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=reciter"), Matchers.containsString("page=0"),
+ Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.prev.href").doesNotExist()).andExpect(jsonPath("$.page.size", is(1)))
+ .andExpect(jsonPath("$.page.totalElements", is(10)));
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestiontargets/search/findBySource").param("source", "reciter")
+ .param("size", "1").param("page", "1"))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestiontargets",
+ Matchers.contains(matchSuggestionTarget("Digilio, Giuseppe", "reciter", 11))))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=reciter"), Matchers.containsString("page=1"),
+ Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.next.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=reciter"), Matchers.containsString("page=2"),
+ Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.last.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=reciter"), Matchers.containsString("page=9"),
+ Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.first.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=reciter"), Matchers.containsString("page=0"),
+ Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.prev.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=reciter"), Matchers.containsString("page=0"),
+ Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$.page.size", is(1))).andExpect(jsonPath("$.page.totalElements", is(10)));
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestiontargets/search/findBySource").param("source", "reciter")
+ .param("size", "1").param("page", "9"))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestiontargets",
+ Matchers.contains(matchSuggestionTarget("Test 0", "reciter", 3))))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=reciter"), Matchers.containsString("page=9"),
+ Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.next.href").doesNotExist())
+ .andExpect(jsonPath("$._links.last.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=reciter"), Matchers.containsString("page=9"),
+ Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.first.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=reciter"), Matchers.containsString("page=0"),
+ Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.prev.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=reciter"), Matchers.containsString("page=8"),
+ Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$.page.size", is(1))).andExpect(jsonPath("$.page.totalElements", is(10)));
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestiontargets/search/findBySource").param("source", "scopus")
+ .param("size", "3").param("page", "0"))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestiontargets",
+ Matchers.contains(
+ matchSuggestionTarget("Test 6", "scopus", 13),
+ matchSuggestionTarget("Test 3", "scopus", 10),
+ matchSuggestionTarget("Test 0", "scopus", 7))))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=scopus"), Matchers.containsString("page=0"),
+ Matchers.containsString("size=3"))))
+ .andExpect(jsonPath("$._links.next.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=scopus"), Matchers.containsString("page=1"),
+ Matchers.containsString("size=3"))))
+ .andExpect(jsonPath("$._links.last.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=scopus"), Matchers.containsString("page=1"),
+ Matchers.containsString("size=3"))))
+ .andExpect(jsonPath("$._links.first.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=scopus"), Matchers.containsString("page=0"),
+ Matchers.containsString("size=3"))))
+ .andExpect(jsonPath("$._links.prev.href").doesNotExist()).andExpect(jsonPath("$.page.size", is(3)))
+ .andExpect(jsonPath("$.page.totalElements", is(5)));
+
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestiontargets/search/findBySource").param("source", "scopus")
+ .param("size", "3").param("page", "1"))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestiontargets", Matchers.iterableWithSize(2)))
+ .andExpect(jsonPath("$._embedded.suggestiontargets",
+ Matchers.containsInAnyOrder(matchSuggestionTarget("Bollini, Andrea", "scopus", 3),
+ matchSuggestionTarget("Lombardi, Corrado", "scopus", 3))))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=scopus"), Matchers.containsString("page=1"),
+ Matchers.containsString("size=3"))))
+ .andExpect(jsonPath("$._links.next.href").doesNotExist())
+ .andExpect(jsonPath("$._links.last.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=scopus"), Matchers.containsString("page=1"),
+ Matchers.containsString("size=3"))))
+ .andExpect(jsonPath("$._links.first.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=scopus"), Matchers.containsString("page=0"),
+ Matchers.containsString("size=3"))))
+ .andExpect(jsonPath("$._links.prev.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findBySource?"),
+ Matchers.containsString("source=scopus"), Matchers.containsString("page=0"),
+ Matchers.containsString("size=3"))))
+ .andExpect(jsonPath("$.page.size", is(3))).andExpect(jsonPath("$.page.totalElements", is(5)));
+ }
+
+ @Test
+ public void findBySourceUnAuthenticatedTest() throws Exception {
+ buildSuggestionTargetsList();
+ // anonymous cannot access the suggestions endpoint
+ getClient().perform(get("/api/integration/suggestiontargets/search/findBySource").param("source", "reciter"))
+ .andExpect(status().isUnauthorized());
+ getClient().perform(get("/api/integration/suggestiontargets/search/findBySource").param("source", "not-exist"))
+ .andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ public void findBySourceForbiddenTest() throws Exception {
+ buildSuggestionTargetsList();
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson)
+ .perform(get("/api/integration/suggestiontargets/search/findBySource").param("source", "reciter"))
+ .andExpect(status().isForbidden());
+ getClient(tokenEperson)
+ .perform(get("/api/integration/suggestiontargets/search/findBySource").param("source", "not-exist"))
+ .andExpect(status().isForbidden());
+ }
+
+ @Test
+ public void findBySourceBadRequestTest() throws Exception {
+ String tokenEperson = getAuthToken(admin.getEmail(), password);
+ getClient(tokenEperson).perform(get("/api/integration/suggestiontargets/search/findBySource"))
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ public void findOneTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ SuggestionTarget target = SuggestionTargetBuilder.createTarget(context, colPeople, "Bollini, Andrea")
+ .withSuggestionCount("scopus", 3).build();
+ SuggestionTarget targetEPerson = SuggestionTargetBuilder
+ .createTarget(context, colPeople, "Digilio, Giuseppe", eperson).withSuggestionCount("reciter", 11)
+ .build();
+ context.restoreAuthSystemState();
+ String uuidStr = target.getID().toString();
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken).perform(get("/api/integration/suggestiontargets/" + uuidStr)).andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$", matchSuggestionTarget("Bollini, Andrea", "scopus", 3))).andExpect(jsonPath(
+ "$._links.self.href", Matchers.endsWith("/api/integration/suggestiontargets/" + uuidStr)));
+ // build a person profile linked to our eperson
+ String uuidStrEpersonProfile = targetEPerson.getID().toString();
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson).perform(get("/api/integration/suggestiontargets/" + uuidStrEpersonProfile))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$", matchSuggestionTarget("Digilio, Giuseppe", "reciter", 11)))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.endsWith("/api/integration/suggestiontargets/" + uuidStrEpersonProfile)));
+ }
+
+ @Test
+ public void findOneFullProjectionTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ SuggestionTarget target = SuggestionTargetBuilder.createTarget(context, colPeople, "Bollini, Andrea")
+ .withSuggestionCount("scopus", 3).build();
+ SuggestionTarget targetEPerson = SuggestionTargetBuilder
+ .createTarget(context, colPeople, "Digilio, Giuseppe", eperson).withSuggestionCount("reciter", 11)
+ .build();
+ context.restoreAuthSystemState();
+ String uuidStrTarget = target.getID().toString();
+ String uuidStrProfile = target.getTarget().getID().toString();
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestiontargets/" + uuidStrTarget).param("projection", "full"))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$", matchSuggestionTarget("Bollini, Andrea", "scopus", 3)))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.endsWith("/api/integration/suggestiontargets/" + uuidStrTarget)))
+ .andExpect(jsonPath("$._embedded.target.id", Matchers.is(uuidStrProfile)));
+ String uuidStrEpersonTarget = targetEPerson.getID().toString();
+ String uuidStrEpersonProfile = targetEPerson.getTarget().getID().toString();
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonToken)
+ .perform(get("/api/integration/suggestiontargets/" + uuidStrEpersonTarget).param("projection", "full"))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$", matchSuggestionTarget("Digilio, Giuseppe", "reciter", 11)))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.endsWith("/api/integration/suggestiontargets/" + uuidStrEpersonTarget)))
+ .andExpect(jsonPath("$._embedded.target.id", Matchers.is(uuidStrEpersonProfile)));
+ }
+
+ @Test
+ public void findOneUnAuthenticatedTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ SuggestionTarget target = SuggestionTargetBuilder.createTarget(context, colPeople, "Bollini, Andrea")
+ .withSuggestionCount("reciter", 31).build();
+ context.restoreAuthSystemState();
+ String uuidStr = target.getID().toString();
+ getClient().perform(get("/api/integration/suggestiontargets/" + uuidStr)).andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ public void findOneForbiddenTest() throws Exception {
+ // build a generic person profile
+ context.turnOffAuthorisationSystem();
+ SuggestionTarget target = SuggestionTargetBuilder.createTarget(context, colPeople, "Bollini, Andrea")
+ .withSuggestionCount("reciter", 31).build();
+ context.restoreAuthSystemState();
+ String uuidStr = target.getID().toString();
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson).perform(get("/api/integration/suggestiontargets/" + uuidStr))
+ .andExpect(status().isForbidden());
+ }
+
+ @Test
+ public void findOneTestWrongID() throws Exception {
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken).perform(get("/api/integration/suggestiontargets/not-an-uuid"))
+ .andExpect(status().isNotFound());
+ getClient(adminToken).perform(get("/api/integration/suggestiontargets/" + UUID.randomUUID()))
+ .andExpect(status().isNotFound());
+ getClient(adminToken).perform(get("/api/integration/suggestiontargets/scopus:" + UUID.randomUUID()))
+ .andExpect(status().isNotFound());
+ getClient(adminToken).perform(get("/api/integration/suggestiontargets/invalid:" + UUID.randomUUID()))
+ .andExpect(status().isNotFound());
+
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonToken).perform(get("/api/integration/suggestiontargets/not-an-uuid"))
+ .andExpect(status().isForbidden());
+ getClient(epersonToken).perform(get("/api/integration/suggestiontargets/" + UUID.randomUUID()))
+ .andExpect(status().isForbidden());
+ getClient(epersonToken).perform(get("/api/integration/suggestiontargets/scopus:" + UUID.randomUUID()))
+ .andExpect(status().isForbidden());
+ getClient(epersonToken).perform(get("/api/integration/suggestiontargets/invalid:" + UUID.randomUUID()))
+ .andExpect(status().isForbidden());
+
+ getClient().perform(get("/api/integration/suggestiontargets/not-an-uuid")).andExpect(status().isUnauthorized());
+ getClient().perform(get("/api/integration/suggestiontargets/" + UUID.randomUUID()))
+ .andExpect(status().isUnauthorized());
+ getClient().perform(get("/api/integration/suggestiontargets/scopus:" + UUID.randomUUID()))
+ .andExpect(status().isUnauthorized());
+ getClient().perform(get("/api/integration/suggestiontargets/invalid:" + UUID.randomUUID()))
+ .andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ public void findByTargetTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Item itemFirst = ItemBuilder.createItem(context, colPeople).withTitle("Bollini, Andrea").build();
+ SuggestionTarget targetFirstReciter = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("reciter", 31).build();
+ SuggestionTarget targetFirstScopus = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("scopus", 3).build();
+ Item itemLast = ItemBuilder.createItem(context, colPeople).withTitle("Lombardi, Corrado")
+ .withDSpaceObjectOwner(eperson.getFullName(), eperson.getID().toString()).build();
+ SuggestionTarget targetLast = SuggestionTargetBuilder.createTarget(context, itemLast)
+ .withSuggestionCount("scopus", 2).build();
+ context.restoreAuthSystemState();
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestiontargets/search/findByTarget").param("target",
+ itemFirst.getID().toString()))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestiontargets",
+ Matchers.contains(matchSuggestionTarget("Bollini, Andrea", "reciter", 31),
+ matchSuggestionTarget("Bollini, Andrea", "scopus", 3))))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/integration/suggestiontargets/search/findByTarget?target="
+ + itemFirst.getID().toString())))
+ .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(2)));
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestiontargets/search/findByTarget").param("target",
+ itemLast.getID().toString()))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestiontargets",
+ Matchers.contains(matchSuggestionTarget("Lombardi, Corrado", "scopus", 2))))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/integration/suggestiontargets/search/findByTarget?target="
+ + itemLast.getID().toString())))
+ .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(1)));
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonToken)
+ .perform(get("/api/integration/suggestiontargets/search/findByTarget").param("target",
+ itemLast.getID().toString()))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestiontargets",
+ Matchers.contains(matchSuggestionTarget("Lombardi, Corrado", "scopus", 2))))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/integration/suggestiontargets/search/findByTarget?target="
+ + itemLast.getID().toString())))
+ .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(1)));
+ }
+
+ @Test
+ public void findByTargetPaginationTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Item itemFirst = ItemBuilder.createItem(context, colPeople).withTitle("Bollini, Andrea").build();
+ SuggestionTarget targetFirstReciter = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("reciter", 31).build();
+ SuggestionTarget targetFirstScopus = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("scopus", 3).build();
+ context.restoreAuthSystemState();
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestiontargets/search/findByTarget").param("size", "1")
+ .param("target", itemFirst.getID().toString()))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestiontargets",
+ Matchers.contains(matchSuggestionTarget("Bollini, Andrea", "reciter", 31))))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findByTarget?"),
+ Matchers.containsString("target=" + itemFirst.getID().toString()),
+ Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.next.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findByTarget?"),
+ Matchers.containsString("target=" + itemFirst.getID().toString()),
+ Matchers.containsString("page=1"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.last.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findByTarget?"),
+ Matchers.containsString("target=" + itemFirst.getID().toString()),
+ Matchers.containsString("page=1"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.first.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findByTarget?"),
+ Matchers.containsString("target=" + itemFirst.getID().toString()),
+ Matchers.containsString("page=0"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.prev.href").doesNotExist()).andExpect(jsonPath("$.page.size", is(1)))
+ .andExpect(jsonPath("$.page.size", is(1))).andExpect(jsonPath("$.page.totalElements", is(2)));
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestiontargets/search/findByTarget").param("size", "1")
+ .param("page", "1").param("target", itemFirst.getID().toString()))
+ .andExpect(status().isOk()).andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.suggestiontargets",
+ Matchers.contains(matchSuggestionTarget("Bollini, Andrea", "scopus", 3))))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findByTarget?"),
+ Matchers.containsString("target=" + itemFirst.getID().toString()),
+ Matchers.containsString("size=1"), Matchers.containsString("page=1"))))
+ .andExpect(jsonPath("$._links.prev.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findByTarget?"),
+ Matchers.containsString("target=" + itemFirst.getID().toString()),
+ Matchers.containsString("page=0"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.last.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findByTarget?"),
+ Matchers.containsString("target=" + itemFirst.getID().toString()),
+ Matchers.containsString("page=1"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.first.href",
+ Matchers.allOf(
+ Matchers.containsString("/api/integration/suggestiontargets/search/findByTarget?"),
+ Matchers.containsString("target=" + itemFirst.getID().toString()),
+ Matchers.containsString("page=0"), Matchers.containsString("size=1"))))
+ .andExpect(jsonPath("$._links.next.href").doesNotExist()).andExpect(jsonPath("$.page.size", is(1)))
+ .andExpect(jsonPath("$.page.size", is(1))).andExpect(jsonPath("$.page.totalElements", is(2)));
+ }
+
+ @Test
+ public void findByTargetUnAuthenticatedTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Item itemFirst = ItemBuilder.createItem(context, colPeople).withTitle("Bollini, Andrea").build();
+ SuggestionTarget targetFirstReciter = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("reciter", 31).build();
+ SuggestionTarget targetFirstScopus = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("scopus", 3).build();
+ Item itemLast = ItemBuilder.createItem(context, colPeople).withTitle("Lombardi, Corrado")
+ .withDSpaceObjectOwner(eperson.getFullName(), eperson.getID().toString()).build();
+ SuggestionTarget targetLast = SuggestionTargetBuilder.createTarget(context, itemLast)
+ .withSuggestionCount("scopus", 2).build();
+ context.restoreAuthSystemState();
+
+ // anonymous cannot access the suggestions endpoint
+ getClient().perform(get("/api/integration/suggestiontargets/search/findByTarget").param("target",
+ itemFirst.getID().toString())).andExpect(status().isUnauthorized());
+ getClient().perform(get("/api/integration/suggestiontargets/search/findByTarget").param("target",
+ itemLast.getID().toString())).andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ public void findByTargetForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Item itemFirst = ItemBuilder.createItem(context, colPeople).withTitle("Bollini, Andrea").build();
+ SuggestionTarget targetFirstReciter = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("reciter", 31).build();
+ SuggestionTarget targetFirstScopus = SuggestionTargetBuilder.createTarget(context, itemFirst)
+ .withSuggestionCount("scopus", 3).build();
+ Item itemLast = ItemBuilder.createItem(context, colPeople).withTitle("Lombardi, Corrado")
+ .withDSpaceObjectOwner(eperson.getFullName(), eperson.getID().toString()).build();
+ SuggestionTarget targetLast = SuggestionTargetBuilder.createTarget(context, itemLast)
+ .withSuggestionCount("scopus", 2).build();
+ EPerson anotherEPerson = EPersonBuilder.createEPerson(context).withEmail("another@example.com")
+ .withPassword(password).withNameInMetadata("Test", "Test").build();
+ context.restoreAuthSystemState();
+
+ String tokenAnother = getAuthToken(anotherEPerson.getEmail(), password);
+ getClient(tokenAnother).perform(get("/api/integration/suggestiontargets/search/findByTarget").param("target",
+ itemFirst.getID().toString())).andExpect(status().isForbidden());
+ getClient(tokenAnother).perform(get("/api/integration/suggestiontargets/search/findByTarget").param("target",
+ itemLast.getID().toString())).andExpect(status().isForbidden());
+ }
+
+ @Test
+ public void findByTargetBadRequestTest() throws Exception {
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken)
+ .perform(get("/api/integration/suggestiontargets/search/findByTarget").param("target", "not-exist"))
+ .andExpect(status().isBadRequest());
+ getClient(adminToken).perform(get("/api/integration/suggestiontargets/search/findByTarget"))
+ .andExpect(status().isBadRequest());
+ getClient().perform(get("/api/integration/suggestiontargets/search/findByTarget").param("target", "not-exist"))
+ .andExpect(status().isBadRequest());
+ }
+
+}
\ No newline at end of file
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java
index 81d2dba67911..30890d7ef838 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java
@@ -92,6 +92,11 @@ public void setup() throws Exception {
// the properties that we're altering above and this is only used within the tests
DCInputAuthority.reset();
pluginService.clearNamedPluginClasses();
+
+ // The following line is needed to call init() method in the ChoiceAuthorityServiceImpl class, without it
+ // the `submissionConfigService` will be null what will cause a NPE in the clearCache() method
+ // https://github.com/DSpace/DSpace/issues/9292
+ cas.getChoiceAuthoritiesNames();
cas.clearCache();
context.turnOffAuthorisationSystem();
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EPersonForgotPasswordFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EPersonForgotPasswordFeatureIT.java
new file mode 100644
index 000000000000..23f5aaabb655
--- /dev/null
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EPersonForgotPasswordFeatureIT.java
@@ -0,0 +1,143 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.authorization;
+
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.is;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.dspace.app.rest.authorization.impl.EPersonForgotPasswordFeature;
+import org.dspace.app.rest.converter.EPersonConverter;
+import org.dspace.app.rest.converter.SiteConverter;
+import org.dspace.app.rest.model.EPersonRest;
+import org.dspace.app.rest.model.SiteRest;
+import org.dspace.app.rest.projection.Projection;
+import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
+import org.dspace.app.rest.utils.Utils;
+import org.dspace.builder.EPersonBuilder;
+import org.dspace.content.Site;
+import org.dspace.content.service.SiteService;
+import org.dspace.eperson.EPerson;
+import org.dspace.services.ConfigurationService;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com)
+ **/
+public class EPersonForgotPasswordFeatureIT extends AbstractControllerIntegrationTest {
+ @Autowired
+ private AuthorizationFeatureService authorizationFeatureService;
+
+ @Autowired
+ private ConfigurationService configurationService;
+
+ @Autowired
+ private SiteService siteService;
+
+ @Autowired
+ private SiteConverter siteConverter;
+
+
+ @Autowired
+ private EPersonConverter personConverter;
+
+ @Autowired
+ private Utils utils;
+
+ private AuthorizationFeature epersonForgotPasswordFeature;
+
+ public static final String[] SHIB_ONLY = {"org.dspace.authenticate.ShibAuthentication"};
+
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ epersonForgotPasswordFeature = authorizationFeatureService.find(EPersonForgotPasswordFeature.NAME);
+ }
+
+ @Test
+ public void userForgotPasswordSuccessTest() throws Exception {
+
+ context.turnOffAuthorisationSystem();
+ EPerson epersonPassLogin = EPersonBuilder.createEPerson(context)
+ .withNameInMetadata("Vincenzo", "Mecca")
+ .withCanLogin(true)
+ .withPassword("Strong-Password")
+ .withEmail("vincenzo.mecca@4science.it")
+ .build();
+ context.restoreAuthSystemState();
+
+ configurationService.setProperty("user.forgot-password", true);
+ EPersonRest personRest = personConverter.convert(epersonPassLogin, Projection.DEFAULT);
+ String personUri = utils.linkToSingleResource(personRest, "self").getHref();
+
+ getClient().perform(get("/api/authz/authorizations/search/object")
+ .param("uri", personUri)
+ .param("feature", epersonForgotPasswordFeature.getName()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.page.totalElements", greaterThan(0)));
+ }
+
+ @Test
+ public void userForgotPasswordFeatureUnauthorizedTest() throws Exception {
+
+ Site site = siteService.findSite(context);
+ SiteRest SiteRest = siteConverter.convert(site, Projection.DEFAULT);
+ String siteUri = utils.linkToSingleResource(SiteRest, "self").getHref();
+
+ configurationService.setProperty("user.forgot-password", false);
+
+ getClient().perform(get("/api/authz/authorizations/search/object")
+ .param("uri", siteUri)
+ .param("feature", epersonForgotPasswordFeature.getName()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.page.totalElements", is(0)));
+ }
+
+
+ @Test
+ public void userForgotPasswordNoLoginTest() throws Exception {
+
+ context.turnOffAuthorisationSystem();
+ EPerson noLoginPerson = EPersonBuilder.createEPerson(context)
+ .withNameInMetadata("User", "NoLogin")
+ .withCanLogin(false)
+ .withPassword("Strong-Password")
+ .build();
+ context.restoreAuthSystemState();
+
+ EPersonRest personRest = personConverter.convert(noLoginPerson, Projection.DEFAULT);
+ String personUri = utils.linkToSingleResource(personRest, "self").getHref();
+ configurationService.setProperty("user.forgot-password", true);
+ getClient().perform(get("/api/authz/authorizations/search/object")
+ .param("uri", personUri)
+ .param("feature", epersonForgotPasswordFeature.getName()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.page.totalElements", is(0)));
+ }
+
+ @Test
+ public void userForgotPasswordUnauthorizedNoPasswordAuthMethodTest() throws Exception {
+ //Enable Shibboleth and password login
+ configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
+
+ EPersonRest personRest = personConverter.convert(eperson, Projection.DEFAULT);
+ String personUri = utils.linkToSingleResource(personRest, "self").getHref();
+
+ getClient().perform(get("/api/authz/authorizations/search/object")
+ .param("uri", personUri)
+ .param("feature", epersonForgotPasswordFeature.getName()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.page.totalElements", is(0)));
+ }
+}
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 1d3b5b051605..3e990ff81097 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
@@ -38,6 +38,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:
@@ -53,6 +54,8 @@
*/
public class GenericAuthorizationFeatureIT extends AbstractControllerIntegrationTest {
+ private static final int SIZE = 100;
+
@Autowired
ConfigurationService configurationService;
@@ -209,215 +212,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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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());
@@ -430,41 +381,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?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?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?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?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?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());
@@ -479,73 +420,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?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?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?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?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?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?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?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?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?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());
@@ -553,65 +476,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?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?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?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?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?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?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?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?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());
@@ -619,57 +526,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?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?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?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?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?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?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?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());
@@ -681,17 +574,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?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?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());
@@ -699,25 +588,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?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?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?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());
@@ -755,41 +638,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?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?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?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?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?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());
@@ -806,9 +679,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?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());
@@ -829,9 +700,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="
- + "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());
@@ -867,29 +736,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?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?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?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?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());
@@ -908,162 +773,139 @@ 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="
- + "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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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?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());
@@ -1090,8 +932,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?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());
@@ -1105,8 +946,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?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());
@@ -1120,8 +960,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="
- + "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());
@@ -1151,14 +990,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?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?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());
@@ -1179,20 +1016,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?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?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?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());
@@ -1212,20 +1046,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?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?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?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());
@@ -1245,8 +1076,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?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());
@@ -1266,8 +1096,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="
- + "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());
@@ -1292,23 +1121,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?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?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?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());
@@ -1328,20 +1151,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?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?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?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());
@@ -1361,8 +1181,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?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());
@@ -1388,8 +1207,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?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());
@@ -1404,36 +1222,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?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?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?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?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?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());
@@ -1447,28 +1260,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?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?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?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?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());
@@ -1483,36 +1292,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?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?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?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?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?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());
@@ -1526,22 +1330,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?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?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?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());
@@ -1561,8 +1362,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="
- + "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());
@@ -1582,8 +1382,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="
- + "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());
@@ -1619,8 +1418,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?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());
@@ -1639,22 +1437,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?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?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?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());
@@ -1679,10 +1474,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?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/java/org/dspace/app/rest/matcher/SuggestionMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/SuggestionMatcher.java
new file mode 100644
index 000000000000..38be403cb222
--- /dev/null
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/SuggestionMatcher.java
@@ -0,0 +1,57 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.matcher;
+
+import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
+import static org.hamcrest.Matchers.is;
+
+import org.dspace.content.Item;
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+
+public class SuggestionMatcher {
+
+ private SuggestionMatcher() { }
+
+ // Matcher for a suggestion
+ public static Matcher super Object> matchSuggestion(String source, Item target, String display,
+ String suggestionId) {
+ return Matchers.allOf(
+ hasJsonPath("$.display", is(display)),
+ hasJsonPath("$.source", is(source)),
+ hasJsonPath("$.id", is(source + ":" + target.getID().toString() + ":" + suggestionId)),
+ hasJsonPath("$.metadata['dc.title'][0].value", is("Title Suggestion " + suggestionId )),
+ hasJsonPath("$.metadata['dc.source'][0].value", is("Source 1")),
+ hasJsonPath("$.metadata['dc.source'][1].value", is("Source 2")),
+ hasJsonPath("$.score"),
+ hasJsonPath("$.evidences"),
+ hasJsonPath("$.type", is("suggestion"))
+ );
+ }
+
+ public static Matcher super Object> matchSuggestion(String source, Item target, String display,
+ String suggestionId, double score, String evidenceName, double evidenceScore, String evidenceNote) {
+ return Matchers.allOf(
+ hasJsonPath("$.display", is(display)),
+ hasJsonPath("$.source", is(source)),
+ hasJsonPath("$.id", is(source + ":" + target.getID().toString() + ":" + suggestionId)),
+ hasJsonPath("$.metadata['dc.title'][0].value", is("Title Suggestion " + suggestionId )),
+ hasJsonPath("$.metadata['dc.source'][0].value", is("Source 1")),
+ hasJsonPath("$.metadata['dc.source'][1].value", is("Source 2")),
+ hasJsonPath("$.score", is(String.format("%.2f", score))),
+ hasJsonPath("$.evidences." + evidenceName, Matchers.is(
+ hasJsonPath("$",
+ Matchers.allOf(
+ hasJsonPath("$.score", is(String.format("%.2f", evidenceScore))),
+ hasJsonPath("$.notes", is(evidenceNote))))
+ )),
+ hasJsonPath("$.type", is("suggestion"))
+ );
+ }
+
+}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/SuggestionSourceMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/SuggestionSourceMatcher.java
new file mode 100644
index 000000000000..f9d70cef8681
--- /dev/null
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/SuggestionSourceMatcher.java
@@ -0,0 +1,28 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.matcher;
+
+import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
+import static org.hamcrest.Matchers.is;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+
+public class SuggestionSourceMatcher {
+
+ private SuggestionSourceMatcher() { }
+
+ // Matcher for a suggestion target
+ public static Matcher super Object> matchSuggestionSource(String name, int total) {
+ return Matchers.allOf(
+ hasJsonPath("$.id", is(name)),
+ hasJsonPath("$.total", is(total)),
+ hasJsonPath("$.type", is("suggestionsource"))
+ );
+ }
+}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/SuggestionTargetMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/SuggestionTargetMatcher.java
new file mode 100644
index 000000000000..b88b51020e76
--- /dev/null
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/SuggestionTargetMatcher.java
@@ -0,0 +1,29 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.matcher;
+
+import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
+import static org.hamcrest.Matchers.is;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+
+public class SuggestionTargetMatcher {
+
+ private SuggestionTargetMatcher() { }
+
+ // Matcher for a suggestion target
+ public static Matcher super Object> matchSuggestionTarget(String name, String source, int total) {
+ return Matchers.allOf(
+ hasJsonPath("$.display", is(name)),
+ hasJsonPath("$.source", is(source)),
+ hasJsonPath("$.total", is(total)),
+ hasJsonPath("$.type", is("suggestiontarget"))
+ );
+ }
+}
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 00339ba2e482..4ec66fb00081 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.rest.Application;
+import org.dspace.app.TestApplication;
import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.utils.DSpaceConfigurationInitializer;
import org.dspace.app.rest.utils.DSpaceKernelInitializer;
@@ -68,7 +68,7 @@
// Specify main class to use to load Spring ApplicationContext
// NOTE: By default, Spring caches and reuses ApplicationContext for each integration test (to speed up tests)
// See: https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#integration-testing
-@SpringBootTest(classes = Application.class)
+@SpringBootTest(classes = TestApplication.class)
// Load DSpace initializers in Spring ApplicationContext (to initialize DSpace Kernel & Configuration)
@ContextConfiguration(initializers = { DSpaceKernelInitializer.class, DSpaceConfigurationInitializer.class })
// Tell Spring to make ApplicationContext an instance of WebApplicationContext (for web-based tests)
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 6556624c6b11..be0a27b4ebd1 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.rest.Application;
+import org.dspace.app.TestApplication;
import org.dspace.app.rest.utils.DSpaceConfigurationInitializer;
import org.dspace.app.rest.utils.DSpaceKernelInitializer;
import org.junit.runner.RunWith;
@@ -46,7 +46,7 @@
// ALSO tell Spring to start a web server on a random port
// NOTE: By default, Spring caches and reuses ApplicationContext for each integration test (to speed up tests)
// See: https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#integration-testing
-@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@SpringBootTest(classes = TestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
// Load DSpace initializers in Spring ApplicationContext (to initialize DSpace Kernel & Configuration)
@ContextConfiguration(initializers = { DSpaceKernelInitializer.class, DSpaceConfigurationInitializer.class })
// Load our src/test/resources/application-test.properties to override some settings in default application.properties
diff --git a/dspace-server-webapp/src/test/resources/application-test.properties b/dspace-server-webapp/src/test/resources/application-test.properties
index 9a396cf8e5b1..bd9e2ea4a17b 100644
--- a/dspace-server-webapp/src/test/resources/application-test.properties
+++ b/dspace-server-webapp/src/test/resources/application-test.properties
@@ -14,4 +14,7 @@
## 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
+
+# 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-server-webapp/src/test/resources/org/dspace/app/rest/ror-record.json b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ror-record.json
new file mode 100644
index 000000000000..51924485b347
--- /dev/null
+++ b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ror-record.json
@@ -0,0 +1,107 @@
+{
+ "id": "https://ror.org/01sps7q28",
+ "name": "The University of Texas Health Science Center at Tyler",
+ "email_address": null,
+ "ip_addresses": [
+
+ ],
+ "established": 1947,
+ "types": [
+ "Healthcare"
+ ],
+ "relationships": [
+ {
+ "label": "The University of Texas System",
+ "type": "Parent",
+ "id": "https://ror.org/01gek1696"
+ }
+ ],
+ "addresses": [
+ {
+ "lat": 32.426014,
+ "lng": -95.212728,
+ "state": "Texas",
+ "state_code": "US-TX",
+ "city": "Tyler",
+ "geonames_city": {
+ "id": 4738214,
+ "city": "Tyler",
+ "geonames_admin1": {
+ "name": "Texas",
+ "id": 4736286,
+ "ascii_name": "Texas",
+ "code": "US.TX"
+ },
+ "geonames_admin2": {
+ "name": "Smith County",
+ "id": 4729130,
+ "ascii_name": "Smith County",
+ "code": "US.TX.423"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 6252001
+ }
+ ],
+ "links": [
+ "https://www.utsystem.edu/institutions/university-texas-health-science-center-tyler"
+ ],
+ "aliases": [
+ "East Texas Tuberculosis Sanitarium",
+ "UT Health Northeast"
+ ],
+ "acronyms": [
+ "UTHSCT"
+ ],
+ "status": "active",
+ "wikipedia_url": "https://en.wikipedia.org/wiki/University_of_Texas_Health_Science_Center_at_Tyler",
+ "labels": [
+
+ ],
+ "country": {
+ "country_name": "United States",
+ "country_code": "US"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0000 9704 5790"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "3446655"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q7896437"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267310.1",
+ "all": "grid.267310.1"
+ }
+ }
+}
\ No newline at end of file
diff --git a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ror-records.json b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ror-records.json
new file mode 100644
index 000000000000..91ce8d33e084
--- /dev/null
+++ b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ror-records.json
@@ -0,0 +1,2383 @@
+{
+ "number_of_results": 200,
+ "time_taken": 12,
+ "items": [
+ {
+ "id": "https://ror.org/02f6dcw23",
+ "name": "The University of Texas",
+ "email_address": null,
+ "ip_addresses": [
+
+ ],
+ "established": 1959,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+ {
+ "label": "Audie L. Murphy Memorial VA Hospital",
+ "type": "Related",
+ "id": "https://ror.org/035xhk118"
+ },
+ {
+ "label": "San Antonio Military Medical Center",
+ "type": "Related",
+ "id": "https://ror.org/00m1mwc36"
+ },
+ {
+ "label": "The University of Texas System",
+ "type": "Parent",
+ "id": "https://ror.org/01gek1696"
+ }
+ ],
+ "addresses": [
+ {
+ "lat": 29.508129,
+ "lng": -98.574025,
+ "state": "Texas",
+ "state_code": "US-TX",
+ "city": "San Antonio",
+ "geonames_city": {
+ "id": 4726206,
+ "city": "San Antonio",
+ "geonames_admin1": {
+ "name": "Texas",
+ "id": 4736286,
+ "ascii_name": "Texas",
+ "code": "US.TX"
+ },
+ "geonames_admin2": {
+ "name": "Bexar County",
+ "id": 4674023,
+ "ascii_name": "Bexar County",
+ "code": "US.TX.029"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 6252001
+ }
+ ],
+ "links": [
+ "http://www.uthscsa.edu/"
+ ],
+ "aliases": [
+
+ ],
+ "acronyms": [
+ "UTHSCSA"
+ ],
+ "status": "active",
+ "wikipedia_url": "https://en.wikipedia.org/wiki/University_of_Texas_Health_Science_Center_at_San_Antonio",
+ "labels": [
+
+ ],
+ "country": {
+ "country_name": "United States",
+ "country_code": "US"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0001 0629 5880"
+ ]
+ },
+ "FundRef": {
+ "preferred": "100008635",
+ "all": [
+ "100008635",
+ "100008636"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "1593427"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q4005868"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267309.9",
+ "all": "grid.267309.9"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/01sps7q28",
+ "name": "The University of Texas Health Science Center at Tyler",
+ "email_address": null,
+ "ip_addresses": [
+
+ ],
+ "established": 1947,
+ "types": [
+ "Healthcare"
+ ],
+ "relationships": [
+ {
+ "label": "The University of Texas System",
+ "type": "Parent",
+ "id": "https://ror.org/01gek1696"
+ }
+ ],
+ "addresses": [
+ {
+ "lat": 32.426014,
+ "lng": -95.212728,
+ "state": "Texas",
+ "state_code": "US-TX",
+ "city": "Tyler",
+ "geonames_city": {
+ "id": 4738214,
+ "city": "Tyler",
+ "geonames_admin1": {
+ "name": "Texas",
+ "id": 4736286,
+ "ascii_name": "Texas",
+ "code": "US.TX"
+ },
+ "geonames_admin2": {
+ "name": "Smith County",
+ "id": 4729130,
+ "ascii_name": "Smith County",
+ "code": "US.TX.423"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 6252001
+ }
+ ],
+ "links": [
+ "https://www.utsystem.edu/institutions/university-texas-health-science-center-tyler"
+ ],
+ "aliases": [
+ "East Texas Tuberculosis Sanitarium",
+ "UT Health Northeast"
+ ],
+ "acronyms": [
+ "UTHSCT"
+ ],
+ "status": "active",
+ "wikipedia_url": "https://en.wikipedia.org/wiki/University_of_Texas_Health_Science_Center_at_Tyler",
+ "labels": [
+
+ ],
+ "country": {
+ "country_name": "United States",
+ "country_code": "US"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0000 9704 5790"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "3446655"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q7896437"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267310.1",
+ "all": "grid.267310.1"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/05byvp690",
+ "name": "The University of Texas Southwestern Medical Center",
+ "email_address": null,
+ "ip_addresses": [
+
+ ],
+ "established": 1943,
+ "types": [
+ "Healthcare"
+ ],
+ "relationships": [
+ {
+ "label": "Children's Medical Center",
+ "type": "Related",
+ "id": "https://ror.org/02ndk3y82"
+ },
+ {
+ "label": "Parkland Memorial Hospital",
+ "type": "Related",
+ "id": "https://ror.org/0208r0146"
+ },
+ {
+ "label": "VA North Texas Health Care System",
+ "type": "Related",
+ "id": "https://ror.org/01nzxq896"
+ },
+ {
+ "label": "The University of Texas System",
+ "type": "Parent",
+ "id": "https://ror.org/01gek1696"
+ },
+ {
+ "label": "Institute for Exercise and Environmental Medicine",
+ "type": "Child",
+ "id": "https://ror.org/03gqc7y13"
+ },
+ {
+ "label": "Texas Health Dallas",
+ "type": "Child",
+ "id": "https://ror.org/05k07p323"
+ }
+ ],
+ "addresses": [
+ {
+ "lat": 32.812185,
+ "lng": -96.840174,
+ "state": "Texas",
+ "state_code": "US-TX",
+ "city": "Dallas",
+ "geonames_city": {
+ "id": 4684888,
+ "city": "Dallas",
+ "geonames_admin1": {
+ "name": "Texas",
+ "id": 4736286,
+ "ascii_name": "Texas",
+ "code": "US.TX"
+ },
+ "geonames_admin2": {
+ "name": "Dallas County",
+ "id": 4684904,
+ "ascii_name": "Dallas County",
+ "code": "US.TX.113"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 6252001
+ }
+ ],
+ "links": [
+ "http://www.utsouthwestern.edu/"
+ ],
+ "aliases": [
+ "UT Southwestern"
+ ],
+ "acronyms": [
+
+ ],
+ "status": "active",
+ "wikipedia_url": "https://en.wikipedia.org/wiki/University_of_Texas_Southwestern_Medical_Center",
+ "labels": [
+
+ ],
+ "country": {
+ "country_name": "United States",
+ "country_code": "US"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0000 9482 7121"
+ ]
+ },
+ "FundRef": {
+ "preferred": "100007914",
+ "all": [
+ "100007914",
+ "100010487",
+ "100008260"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "617906"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q2725999"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267313.2",
+ "all": "grid.267313.2"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/019kgqr73",
+ "name": "The University of Texas at Arlington",
+ "email_address": "",
+ "ip_addresses": [
+
+ ],
+ "established": 1895,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+ {
+ "label": "VA North Texas Health Care System",
+ "type": "Related",
+ "id": "https://ror.org/01nzxq896"
+ },
+ {
+ "label": "The University of Texas System",
+ "type": "Parent",
+ "id": "https://ror.org/01gek1696"
+ }
+ ],
+ "addresses": [
+ {
+ "lat": 32.731,
+ "lng": -97.115,
+ "state": "Texas",
+ "state_code": "US-TX",
+ "city": "Arlington",
+ "geonames_city": {
+ "id": 4671240,
+ "city": "Arlington",
+ "geonames_admin1": {
+ "name": "Texas",
+ "id": 4736286,
+ "ascii_name": "Texas",
+ "code": "US.TX"
+ },
+ "geonames_admin2": {
+ "name": "Tarrant County",
+ "id": 4735638,
+ "ascii_name": "Tarrant County",
+ "code": "US.TX.439"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 6252001
+ }
+ ],
+ "links": [
+ "http://www.uta.edu/uta/"
+ ],
+ "aliases": [
+ "UT Arlington"
+ ],
+ "acronyms": [
+ "UTA"
+ ],
+ "status": "active",
+ "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Texas_at_Arlington",
+ "labels": [
+ {
+ "label": "Université du Texas à Arlington",
+ "iso639": "fr"
+ }
+ ],
+ "country": {
+ "country_name": "United States",
+ "country_code": "US"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0001 2181 9515"
+ ]
+ },
+ "FundRef": {
+ "preferred": null,
+ "all": [
+ "100009497"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "906409"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q1230739"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267315.4",
+ "all": "grid.267315.4"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/051smbs96",
+ "name": "The University of Texas of the Permian Basin",
+ "email_address": null,
+ "ip_addresses": [
+
+ ],
+ "established": 1973,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+ {
+ "label": "The University of Texas System",
+ "type": "Parent",
+ "id": "https://ror.org/01gek1696"
+ }
+ ],
+ "addresses": [
+ {
+ "lat": 31.889444,
+ "lng": -102.329531,
+ "state": "Texas",
+ "state_code": "US-TX",
+ "city": "Odessa",
+ "geonames_city": {
+ "id": 5527554,
+ "city": "Odessa",
+ "geonames_admin1": {
+ "name": "Texas",
+ "id": 4736286,
+ "ascii_name": "Texas",
+ "code": "US.TX"
+ },
+ "geonames_admin2": {
+ "name": "Ector County",
+ "id": 5520910,
+ "ascii_name": "Ector County",
+ "code": "US.TX.135"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 6252001
+ }
+ ],
+ "links": [
+ "http://www.utpb.edu/"
+ ],
+ "aliases": [
+ "UT Permian Basin"
+ ],
+ "acronyms": [
+ "UTPB"
+ ],
+ "status": "active",
+ "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Texas_of_the_Permian_Basin",
+ "labels": [
+
+ ],
+ "country": {
+ "country_name": "United States",
+ "country_code": "US"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0000 9140 1491"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "1419441"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q2495935"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267328.a",
+ "all": "grid.267328.a"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/044vy1d05",
+ "name": "Tokushima University",
+ "email_address": "",
+ "ip_addresses": [
+
+ ],
+ "established": 1949,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+ {
+ "label": "Tokushima University Hospital",
+ "type": "Related",
+ "id": "https://ror.org/021ph5e41"
+ }
+ ],
+ "addresses": [
+ {
+ "lat": 34.07,
+ "lng": 134.56,
+ "state": null,
+ "state_code": null,
+ "city": "Tokushima",
+ "geonames_city": {
+ "id": 1850158,
+ "city": "Tokushima",
+ "geonames_admin1": {
+ "name": "Tokushima",
+ "id": 1850157,
+ "ascii_name": "Tokushima",
+ "code": "JP.39"
+ },
+ "geonames_admin2": {
+ "name": "Tokushima Shi",
+ "id": 1850156,
+ "ascii_name": "Tokushima Shi",
+ "code": "JP.39.1850156"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 1861060
+ }
+ ],
+ "links": [
+ "https://www.tokushima-u.ac.jp/"
+ ],
+ "aliases": [
+ "Tokushima Daigaku",
+ "University of Tokushima"
+ ],
+ "acronyms": [
+
+ ],
+ "status": "active",
+ "wikipedia_url": "https://en.wikipedia.org/wiki/University_of_Tokushima",
+ "labels": [
+ {
+ "label": "徳島大学",
+ "iso639": "ja"
+ }
+ ],
+ "country": {
+ "country_name": "Japan",
+ "country_code": "JP"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0001 1092 3579"
+ ]
+ },
+ "FundRef": {
+ "preferred": null,
+ "all": [
+ "501100005623"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "15696836"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q1150231"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267335.6",
+ "all": "grid.267335.6"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/03np13864",
+ "name": "University of Trinidad and Tobago",
+ "email_address": null,
+ "ip_addresses": [
+
+ ],
+ "established": 2004,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+
+ ],
+ "addresses": [
+ {
+ "lat": 10.616667,
+ "lng": -61.216667,
+ "state": null,
+ "state_code": null,
+ "city": "Arima",
+ "geonames_city": {
+ "id": 3575051,
+ "city": "Arima",
+ "geonames_admin1": {
+ "name": "Borough of Arima",
+ "id": 3575052,
+ "ascii_name": "Borough of Arima",
+ "code": "TT.01"
+ },
+ "geonames_admin2": {
+ "name": null,
+ "id": null,
+ "ascii_name": null,
+ "code": null
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 3573591
+ }
+ ],
+ "links": [
+ "https://utt.edu.tt/"
+ ],
+ "aliases": [
+
+ ],
+ "acronyms": [
+ "UTT"
+ ],
+ "status": "active",
+ "wikipedia_url": "https://en.wikipedia.org/wiki/University_of_Trinidad_and_Tobago",
+ "labels": [
+ {
+ "label": "Universidad de Trinidad y Tobago",
+ "iso639": "es"
+ }
+ ],
+ "country": {
+ "country_name": "Trinidad and Tobago",
+ "country_code": "TT"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0000 9490 0886"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "8706288"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q648244"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267355.0",
+ "all": "grid.267355.0"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/04wn28048",
+ "name": "University of Tulsa",
+ "email_address": "",
+ "ip_addresses": [
+
+ ],
+ "established": 1894,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+
+ ],
+ "addresses": [
+ {
+ "lat": 36.152222,
+ "lng": -95.946389,
+ "state": "Oklahoma",
+ "state_code": "US-OK",
+ "city": "Tulsa",
+ "geonames_city": {
+ "id": 4553433,
+ "city": "Tulsa",
+ "geonames_admin1": {
+ "name": "Oklahoma",
+ "id": 4544379,
+ "ascii_name": "Oklahoma",
+ "code": "US.OK"
+ },
+ "geonames_admin2": {
+ "name": "Tulsa County",
+ "id": 4553440,
+ "ascii_name": "Tulsa County",
+ "code": "US.OK.143"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 6252001
+ }
+ ],
+ "links": [
+ "http://utulsa.edu/"
+ ],
+ "aliases": [
+
+ ],
+ "acronyms": [
+ "TU"
+ ],
+ "status": "active",
+ "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Tulsa",
+ "labels": [
+ {
+ "label": "Université de tulsa",
+ "iso639": "fr"
+ }
+ ],
+ "country": {
+ "country_name": "United States",
+ "country_code": "US"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0001 2160 264X"
+ ]
+ },
+ "FundRef": {
+ "preferred": "100007147",
+ "all": [
+ "100007147",
+ "100006455"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "32043"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q1848657"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267360.6",
+ "all": "grid.267360.6"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/04scfb908",
+ "name": "Alfred Health",
+ "email_address": null,
+ "ip_addresses": [
+
+ ],
+ "established": 1871,
+ "types": [
+ "Healthcare"
+ ],
+ "relationships": [
+ {
+ "label": "Caulfield Hospital",
+ "type": "Child",
+ "id": "https://ror.org/01fcxf261"
+ },
+ {
+ "label": "Melbourne Sexual Health Centre",
+ "type": "Child",
+ "id": "https://ror.org/013fdz725"
+ },
+ {
+ "label": "National Trauma Research Institute",
+ "type": "Child",
+ "id": "https://ror.org/048t93218"
+ },
+ {
+ "label": "The Alfred Hospital",
+ "type": "Child",
+ "id": "https://ror.org/01wddqe20"
+ }
+ ],
+ "addresses": [
+ {
+ "lat": -37.845542,
+ "lng": 144.981632,
+ "state": "Victoria",
+ "state_code": "AU-VIC",
+ "city": "Melbourne",
+ "geonames_city": {
+ "id": 2158177,
+ "city": "Melbourne",
+ "geonames_admin1": {
+ "name": "Victoria",
+ "id": 2145234,
+ "ascii_name": "Victoria",
+ "code": "AU.07"
+ },
+ "geonames_admin2": {
+ "name": "Melbourne",
+ "id": 7839805,
+ "ascii_name": "Melbourne",
+ "code": "AU.07.24600"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 2077456
+ }
+ ],
+ "links": [
+ "http://www.alfred.org.au/"
+ ],
+ "aliases": [
+
+ ],
+ "acronyms": [
+
+ ],
+ "status": "active",
+ "wikipedia_url": "",
+ "labels": [
+
+ ],
+ "country": {
+ "country_name": "Australia",
+ "country_code": "AU"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0004 0432 5259"
+ ]
+ },
+ "FundRef": {
+ "preferred": null,
+ "all": [
+ "501100002716"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267362.4",
+ "all": "grid.267362.4"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/02c2f8975",
+ "name": "University of Ulsan",
+ "email_address": null,
+ "ip_addresses": [
+
+ ],
+ "established": 1970,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+ {
+ "label": "Ulsan University Hospital",
+ "type": "Related",
+ "id": "https://ror.org/03sab2a45"
+ }
+ ],
+ "addresses": [
+ {
+ "lat": 35.542772,
+ "lng": 129.256725,
+ "state": null,
+ "state_code": null,
+ "city": "Ulsan",
+ "geonames_city": {
+ "id": 1833747,
+ "city": "Ulsan",
+ "geonames_admin1": {
+ "name": "Ulsan",
+ "id": 1833742,
+ "ascii_name": "Ulsan",
+ "code": "KR.21"
+ },
+ "geonames_admin2": {
+ "name": null,
+ "id": null,
+ "ascii_name": null,
+ "code": null
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 1835841
+ }
+ ],
+ "links": [
+ "http://en.ulsan.ac.kr/contents/main/"
+ ],
+ "aliases": [
+
+ ],
+ "acronyms": [
+ "UOU"
+ ],
+ "status": "active",
+ "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Ulsan",
+ "labels": [
+ {
+ "label": "울산대학교",
+ "iso639": "ko"
+ }
+ ],
+ "country": {
+ "country_name": "South Korea",
+ "country_code": "KR"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0004 0533 4667"
+ ]
+ },
+ "FundRef": {
+ "preferred": null,
+ "all": [
+ "501100002568"
+ ]
+ },
+ "OrgRef": {
+ "preferred": "10458246",
+ "all": [
+ "10458246",
+ "15162872"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q491717"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267370.7",
+ "all": "grid.267370.7"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/010acrp16",
+ "name": "University of West Alabama",
+ "email_address": null,
+ "ip_addresses": [
+
+ ],
+ "established": 1835,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+
+ ],
+ "addresses": [
+ {
+ "lat": 32.59,
+ "lng": -88.186,
+ "state": "Alabama",
+ "state_code": "US-AL",
+ "city": "Livingston",
+ "geonames_city": {
+ "id": 4073383,
+ "city": "Livingston",
+ "geonames_admin1": {
+ "name": "Alabama",
+ "id": 4829764,
+ "ascii_name": "Alabama",
+ "code": "US.AL"
+ },
+ "geonames_admin2": {
+ "name": "Sumter County",
+ "id": 4092386,
+ "ascii_name": "Sumter County",
+ "code": "US.AL.119"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 6252001
+ }
+ ],
+ "links": [
+ "http://www.uwa.edu/"
+ ],
+ "aliases": [
+ "Livingston Female Academy"
+ ],
+ "acronyms": [
+ "UWA"
+ ],
+ "status": "active",
+ "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_West_Alabama",
+ "labels": [
+
+ ],
+ "country": {
+ "country_name": "United States",
+ "country_code": "US"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0000 9963 9197"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "2425212"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q637346"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267434.0",
+ "all": "grid.267434.0"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/002w4zy91",
+ "name": "University of West Florida",
+ "email_address": null,
+ "ip_addresses": [
+
+ ],
+ "established": 1963,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+ {
+ "label": "State University System of Florida",
+ "type": "Parent",
+ "id": "https://ror.org/05sqd3t97"
+ }
+ ],
+ "addresses": [
+ {
+ "lat": 30.549493,
+ "lng": -87.21812,
+ "state": "Florida",
+ "state_code": "US-FL",
+ "city": "Pensacola",
+ "geonames_city": {
+ "id": 4168228,
+ "city": "Pensacola",
+ "geonames_admin1": {
+ "name": "Florida",
+ "id": 4155751,
+ "ascii_name": "Florida",
+ "code": "US.FL"
+ },
+ "geonames_admin2": {
+ "name": "Escambia County",
+ "id": 4154550,
+ "ascii_name": "Escambia County",
+ "code": "US.FL.033"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 6252001
+ }
+ ],
+ "links": [
+ "http://uwf.edu/"
+ ],
+ "aliases": [
+
+ ],
+ "acronyms": [
+ "UWF"
+ ],
+ "status": "active",
+ "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_West_Florida",
+ "labels": [
+
+ ],
+ "country": {
+ "country_name": "United States",
+ "country_code": "US"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0001 2112 2427"
+ ]
+ },
+ "FundRef": {
+ "preferred": null,
+ "all": [
+ "100009842"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "750756"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q659255"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267436.2",
+ "all": "grid.267436.2"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/01cqxk816",
+ "name": "University of West Georgia",
+ "email_address": null,
+ "ip_addresses": [
+
+ ],
+ "established": 1906,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+ {
+ "label": "University System of Georgia",
+ "type": "Parent",
+ "id": "https://ror.org/017wcm924"
+ }
+ ],
+ "addresses": [
+ {
+ "lat": 33.573357,
+ "lng": -85.099593,
+ "state": "Georgia",
+ "state_code": "US-GA",
+ "city": "Carrollton",
+ "geonames_city": {
+ "id": 4186416,
+ "city": "Carrollton",
+ "geonames_admin1": {
+ "name": "Georgia",
+ "id": 4197000,
+ "ascii_name": "Georgia",
+ "code": "US.GA"
+ },
+ "geonames_admin2": {
+ "name": "Carroll County",
+ "id": 4186396,
+ "ascii_name": "Carroll County",
+ "code": "US.GA.045"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 6252001
+ }
+ ],
+ "links": [
+ "http://www.westga.edu/"
+ ],
+ "aliases": [
+
+ ],
+ "acronyms": [
+ "UWG"
+ ],
+ "status": "active",
+ "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_West_Georgia",
+ "labels": [
+
+ ],
+ "country": {
+ "country_name": "United States",
+ "country_code": "US"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0001 2223 6696"
+ ]
+ },
+ "FundRef": {
+ "preferred": null,
+ "all": [
+ "100007922"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "595315"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q2495945"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267437.3",
+ "all": "grid.267437.3"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/03c8vvr84",
+ "name": "University of Western States",
+ "email_address": null,
+ "ip_addresses": [
+
+ ],
+ "established": 1904,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+
+ ],
+ "addresses": [
+ {
+ "lat": 45.543351,
+ "lng": -122.523973,
+ "state": "Oregon",
+ "state_code": "US-OR",
+ "city": "Portland",
+ "geonames_city": {
+ "id": 5746545,
+ "city": "Portland",
+ "geonames_admin1": {
+ "name": "Oregon",
+ "id": 5744337,
+ "ascii_name": "Oregon",
+ "code": "US.OR"
+ },
+ "geonames_admin2": {
+ "name": "Multnomah County",
+ "id": 5742126,
+ "ascii_name": "Multnomah County",
+ "code": "US.OR.051"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 6252001
+ }
+ ],
+ "links": [
+ "http://www.uws.edu/"
+ ],
+ "aliases": [
+ "Western States Chiropractic College"
+ ],
+ "acronyms": [
+ "UWS"
+ ],
+ "status": "active",
+ "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Western_States",
+ "labels": [
+
+ ],
+ "country": {
+ "country_name": "United States",
+ "country_code": "US"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0004 0455 9493"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "1655050"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q7896612"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267451.3",
+ "all": "grid.267451.3"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/03fmjzx88",
+ "name": "University of Winchester",
+ "email_address": null,
+ "ip_addresses": [
+
+ ],
+ "established": 1840,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+
+ ],
+ "addresses": [
+ {
+ "lat": 51.060338,
+ "lng": -1.325418,
+ "state": null,
+ "state_code": null,
+ "city": "Winchester",
+ "geonames_city": {
+ "id": 2633858,
+ "city": "Winchester",
+ "geonames_admin1": {
+ "name": "England",
+ "id": 6269131,
+ "ascii_name": "England",
+ "code": "GB.ENG"
+ },
+ "geonames_admin2": {
+ "name": "Hampshire",
+ "id": 2647554,
+ "ascii_name": "Hampshire",
+ "code": "GB.ENG.F2"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": "SOUTH EAST (ENGLAND)",
+ "code": "UKJ"
+ },
+ "nuts_level2": {
+ "name": "Hampshire and Isle of Wight",
+ "code": "UKJ3"
+ },
+ "nuts_level3": {
+ "name": "Central Hampshire",
+ "code": "UKJ36"
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 2635167
+ }
+ ],
+ "links": [
+ "http://www.winchester.ac.uk/pages/home.aspx"
+ ],
+ "aliases": [
+
+ ],
+ "acronyms": [
+
+ ],
+ "status": "active",
+ "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Winchester",
+ "labels": [
+
+ ],
+ "country": {
+ "country_name": "United Kingdom",
+ "country_code": "GB"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0000 9422 2878"
+ ]
+ },
+ "FundRef": {
+ "preferred": null,
+ "all": [
+ "100010057"
+ ]
+ },
+ "HESA": {
+ "preferred": null,
+ "all": [
+ "0021"
+ ]
+ },
+ "UCAS": {
+ "preferred": null,
+ "all": [
+ "W76"
+ ]
+ },
+ "UKPRN": {
+ "preferred": null,
+ "all": [
+ "10003614"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "3140939"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q3551690"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267454.6",
+ "all": "grid.267454.6"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/01gw3d370",
+ "name": "University of Windsor",
+ "email_address": "",
+ "ip_addresses": [
+
+ ],
+ "established": 1857,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+
+ ],
+ "addresses": [
+ {
+ "lat": 42.305196,
+ "lng": -83.067483,
+ "state": "Ontario",
+ "state_code": "CA-ON",
+ "city": "Windsor",
+ "geonames_city": {
+ "id": 6182962,
+ "city": "Windsor",
+ "geonames_admin1": {
+ "name": "Ontario",
+ "id": 6093943,
+ "ascii_name": "Ontario",
+ "code": "CA.08"
+ },
+ "geonames_admin2": {
+ "name": null,
+ "id": null,
+ "ascii_name": null,
+ "code": null
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 6251999
+ }
+ ],
+ "links": [
+ "http://www.uwindsor.ca/"
+ ],
+ "aliases": [
+ "UWindsor",
+ "Assumption University of Windsor"
+ ],
+ "acronyms": [
+
+ ],
+ "status": "active",
+ "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Windsor",
+ "labels": [
+ {
+ "label": "Université de windsor",
+ "iso639": "fr"
+ }
+ ],
+ "country": {
+ "country_name": "Canada",
+ "country_code": "CA"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0004 1936 9596"
+ ]
+ },
+ "FundRef": {
+ "preferred": "100009154",
+ "all": [
+ "100009154",
+ "501100000083"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "342733"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q2065769"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267455.7",
+ "all": "grid.267455.7"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/02gdzyx04",
+ "name": "University of Winnipeg",
+ "email_address": null,
+ "ip_addresses": [
+
+ ],
+ "established": 1871,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+ {
+ "label": "Winnipeg Institute for Theoretical Physics",
+ "type": "Child",
+ "id": "https://ror.org/010tw2j24"
+ }
+ ],
+ "addresses": [
+ {
+ "lat": 49.890122,
+ "lng": -97.153367,
+ "state": "Manitoba",
+ "state_code": "CA-MB",
+ "city": "Winnipeg",
+ "geonames_city": {
+ "id": 6183235,
+ "city": "Winnipeg",
+ "geonames_admin1": {
+ "name": "Manitoba",
+ "id": 6065171,
+ "ascii_name": "Manitoba",
+ "code": "CA.03"
+ },
+ "geonames_admin2": {
+ "name": null,
+ "id": null,
+ "ascii_name": null,
+ "code": null
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 6251999
+ }
+ ],
+ "links": [
+ "http://www.uwinnipeg.ca/"
+ ],
+ "aliases": [
+
+ ],
+ "acronyms": [
+
+ ],
+ "status": "active",
+ "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Winnipeg",
+ "labels": [
+ {
+ "label": "Université de winnipeg",
+ "iso639": "fr"
+ }
+ ],
+ "country": {
+ "country_name": "Canada",
+ "country_code": "CA"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0001 1703 4731"
+ ]
+ },
+ "FundRef": {
+ "preferred": null,
+ "all": [
+ "100009367"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "587404"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q472167"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267457.5",
+ "all": "grid.267457.5"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/03mnm0t94",
+ "name": "University of Wisconsin–Eau Claire",
+ "email_address": "",
+ "ip_addresses": [
+
+ ],
+ "established": 1916,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+ {
+ "label": "University of Wisconsin System",
+ "type": "Parent",
+ "id": "https://ror.org/03ydkyb10"
+ }
+ ],
+ "addresses": [
+ {
+ "lat": 44.79895,
+ "lng": -91.499346,
+ "state": "Wisconsin",
+ "state_code": "US-WI",
+ "city": "Eau Claire",
+ "geonames_city": {
+ "id": 5251436,
+ "city": "Eau Claire",
+ "geonames_admin1": {
+ "name": "Wisconsin",
+ "id": 5279468,
+ "ascii_name": "Wisconsin",
+ "code": "US.WI"
+ },
+ "geonames_admin2": {
+ "name": "Eau Claire County",
+ "id": 5251439,
+ "ascii_name": "Eau Claire County",
+ "code": "US.WI.035"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 6252001
+ }
+ ],
+ "links": [
+ "http://www.uwec.edu/"
+ ],
+ "aliases": [
+
+ ],
+ "acronyms": [
+ "UWEC"
+ ],
+ "status": "active",
+ "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Wisconsin%E2%80%93Eau_Claire",
+ "labels": [
+ {
+ "label": "Université du Wisconsin à Eau Claire",
+ "iso639": "fr"
+ }
+ ],
+ "country": {
+ "country_name": "United States",
+ "country_code": "US"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0001 2227 2494"
+ ]
+ },
+ "FundRef": {
+ "preferred": null,
+ "all": [
+ "100010315"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "496729"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q3551771"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267460.1",
+ "all": "grid.267460.1"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/05hbexn54",
+ "name": "University of Wisconsin–Green Bay",
+ "email_address": null,
+ "ip_addresses": [
+
+ ],
+ "established": 1965,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+ {
+ "label": "University of Wisconsin System",
+ "type": "Parent",
+ "id": "https://ror.org/03ydkyb10"
+ }
+ ],
+ "addresses": [
+ {
+ "lat": 44.533203,
+ "lng": -87.921521,
+ "state": "Wisconsin",
+ "state_code": "US-WI",
+ "city": "Green Bay",
+ "geonames_city": {
+ "id": 5254962,
+ "city": "Green Bay",
+ "geonames_admin1": {
+ "name": "Wisconsin",
+ "id": 5279468,
+ "ascii_name": "Wisconsin",
+ "code": "US.WI"
+ },
+ "geonames_admin2": {
+ "name": "Brown County",
+ "id": 5246898,
+ "ascii_name": "Brown County",
+ "code": "US.WI.009"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 6252001
+ }
+ ],
+ "links": [
+ "http://www.uwgb.edu/"
+ ],
+ "aliases": [
+
+ ],
+ "acronyms": [
+ "UWGB"
+ ],
+ "status": "active",
+ "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Wisconsin%E2%80%93Green_Bay",
+ "labels": [
+ {
+ "label": "Université du Wisconsin–Green Bay",
+ "iso639": "fr"
+ }
+ ],
+ "country": {
+ "country_name": "United States",
+ "country_code": "US"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0001 0559 7692"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "1513886"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q2378091"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267461.0",
+ "all": "grid.267461.0"
+ }
+ }
+ },
+ {
+ "id": "https://ror.org/00x8ccz20",
+ "name": "University of Wisconsin–La Crosse",
+ "email_address": "",
+ "ip_addresses": [
+
+ ],
+ "established": 1909,
+ "types": [
+ "Education"
+ ],
+ "relationships": [
+ {
+ "label": "University of Wisconsin System",
+ "type": "Parent",
+ "id": "https://ror.org/03ydkyb10"
+ }
+ ],
+ "addresses": [
+ {
+ "lat": 43.815576,
+ "lng": -91.233517,
+ "state": "Wisconsin",
+ "state_code": "US-WI",
+ "city": "La Crosse",
+ "geonames_city": {
+ "id": 5258957,
+ "city": "La Crosse",
+ "geonames_admin1": {
+ "name": "Wisconsin",
+ "id": 5279468,
+ "ascii_name": "Wisconsin",
+ "code": "US.WI"
+ },
+ "geonames_admin2": {
+ "name": "La Crosse County",
+ "id": 5258961,
+ "ascii_name": "La Crosse County",
+ "code": "US.WI.063"
+ },
+ "license": {
+ "attribution": "Data from geonames.org under a CC-BY 3.0 license",
+ "license": "http://creativecommons.org/licenses/by/3.0/"
+ },
+ "nuts_level1": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level2": {
+ "name": null,
+ "code": null
+ },
+ "nuts_level3": {
+ "name": null,
+ "code": null
+ }
+ },
+ "postcode": null,
+ "primary": false,
+ "line": null,
+ "country_geonames_id": 6252001
+ }
+ ],
+ "links": [
+ "http://www.uwlax.edu/Home/Future-Students/"
+ ],
+ "aliases": [
+
+ ],
+ "acronyms": [
+ "UW–L"
+ ],
+ "status": "active",
+ "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Wisconsin%E2%80%93La_Crosse",
+ "labels": [
+ {
+ "label": "Université du Wisconsin–La Crosse",
+ "iso639": "fr"
+ }
+ ],
+ "country": {
+ "country_name": "United States",
+ "country_code": "US"
+ },
+ "external_ids": {
+ "ISNI": {
+ "preferred": null,
+ "all": [
+ "0000 0001 2169 5137"
+ ]
+ },
+ "OrgRef": {
+ "preferred": null,
+ "all": [
+ "2422287"
+ ]
+ },
+ "Wikidata": {
+ "preferred": null,
+ "all": [
+ "Q2688358"
+ ]
+ },
+ "GRID": {
+ "preferred": "grid.267462.3",
+ "all": "grid.267462.3"
+ }
+ }
+ }
+ ],
+ "meta": {
+ "types": [
+ {
+ "id": "company",
+ "title": "Company",
+ "count": 29790
+ },
+ {
+ "id": "education",
+ "title": "Education",
+ "count": 20325
+ },
+ {
+ "id": "nonprofit",
+ "title": "Nonprofit",
+ "count": 14187
+ },
+ {
+ "id": "healthcare",
+ "title": "Healthcare",
+ "count": 13107
+ },
+ {
+ "id": "facility",
+ "title": "Facility",
+ "count": 10080
+ },
+ {
+ "id": "other",
+ "title": "Other",
+ "count": 8369
+ },
+ {
+ "id": "government",
+ "title": "Government",
+ "count": 6511
+ },
+ {
+ "id": "archive",
+ "title": "Archive",
+ "count": 2967
+ }
+ ],
+ "countries": [
+ {
+ "id": "us",
+ "title": "United States",
+ "count": 31196
+ },
+ {
+ "id": "gb",
+ "title": "United Kingdom",
+ "count": 7410
+ },
+ {
+ "id": "de",
+ "title": "Germany",
+ "count": 5189
+ },
+ {
+ "id": "cn",
+ "title": "China",
+ "count": 4846
+ },
+ {
+ "id": "fr",
+ "title": "France",
+ "count": 4344
+ },
+ {
+ "id": "jp",
+ "title": "Japan",
+ "count": 3940
+ },
+ {
+ "id": "ca",
+ "title": "Canada",
+ "count": 3392
+ },
+ {
+ "id": "in",
+ "title": "India",
+ "count": 3075
+ },
+ {
+ "id": "cz",
+ "title": "Czech Republic",
+ "count": 2780
+ },
+ {
+ "id": "ru",
+ "title": "Russia",
+ "count": 2109
+ }
+ ],
+ "statuses": [
+ {
+ "id": "active",
+ "title": "active",
+ "count": 105336
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl
index 16c63c9c1a13..7acc3378d62d 100644
--- a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl
+++ b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl
@@ -457,6 +457,13 @@
+
+
+
+
+
+
+
@@ -883,7 +890,32 @@