Merge branch 'main' into CST-5340-OpenAIREFundingDataProvider

This commit is contained in:
Mykhaylo
2022-04-11 14:58:28 +02:00
71 changed files with 871 additions and 494 deletions

View File

@@ -63,56 +63,11 @@
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.version}</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-base</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-base</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
<version>${jackson.version}</version>
<exclusions>
<!-- Use versions provided by Solr / Tika -->
<exclusion>
<groupId>jakarta.activation</groupId>
<artifactId>jakarta.activation-api</artifactId>
</exclusion>
<exclusion>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</exclusion>
</exclusions>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-jaxb</artifactId>
<version>${jersey.version}</version>
</dependency>
<!-- Spring dependencies -->
@@ -167,11 +122,6 @@
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
</exclusion>
<!-- Newer version provided by solr-cell -->
<exclusion>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@@ -204,30 +154,9 @@
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
<!-- Use DSpace, for now, an older version to minimize spring generated dependency on Discovery -->
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-api</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Connecting to DSpace datasource sets a dependency on Postgres DB-->

View File

@@ -22,7 +22,10 @@ The only tested way right now is to run this webapp inside your IDE (Eclipse). J
> dspace.dir = d:/install/dspace7
## HAL Browser
The modified version of the HAL Browser from the Spring Data REST project is included, the index.html file is overriden locally to support the /api baseURL (see [DATAREST-971](https://jira.spring.io/browse/DATAREST-971))
The modified version of the HAL Browser from https://github.com/mikekelly/hal-browser
We've updated/customized the HAL Browser to integrate better with our authentication system, provide CSRF support, and use a more recent version of its dependencies.
## Packages and main classes
*[org.dspace.app.rest.Application](src/main/java/org/dspace/app/rest/Application.java)* is the spring boot main class it initializes

View File

@@ -68,7 +68,9 @@
<!--Skip license check of third party files included/customized from HAL Browser -->
<exclude>src/main/webapp/index.html</exclude>
<exclude>src/main/webapp/login.html</exclude>
<exclude>src/main/webapp/styles.css</exclude>
<exclude>src/main/webapp/js/hal/**</exclude>
<exclude>src/main/webapp/js/vendor/**</exclude>
</excludes>
</configuration>
</plugin>
@@ -238,24 +240,7 @@
<dependencies>
<!-- These next two dependencies build a WAR that is BOTH executable
AND deployable into an external container (Tomcat).
See: http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#build-tool-plugins-maven-packaging -->
<!-- NOTE: For rapid development (if you don't need Solr or other webapps),
you can temporarily comment these out, and switch <packaging> to "jar".
This lets you develop in a standalone, runnable JAR application. -->
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>-->
<!-- Ensure embedded servlet container doesn't interfere when this
WAR is deployed to an external Tomcat (i.e. provided). -->
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>-->
<!-- Spring Boot dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
@@ -293,20 +278,24 @@
<version>0.4.6</version>
</dependency>
<!-- The HAL Browser -->
<!-- HAL Browser (via WebJars) : https://github.com/mikekelly/hal-browser -->
<!-- This is primarily used to pull in the HAL Browser core Javascript code ('js' folder), as we've overridden
many dependencies below and the HTML pages in src/main/webapp/ -->
<!-- NOTE: Eventually this should be replaced by the HAL Explorer included in Spring Data REST,
see https://github.com/DSpace/DSpace/issues/3017 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-hal-browser</artifactId>
<version>${spring-hal-browser.version}</version>
<groupId>org.webjars</groupId>
<artifactId>hal-browser</artifactId>
<version>ad9b865</version>
</dependency>
<!-- WebJars dependencies used to update/enhance the default HAL Browser -->
<!-- Pull in several WebJars dependencies used to update/enhance the default HAL Browser -->
<!-- Pull in current version of JQuery via WebJars
Made available at: webjars/jquery/dist/jquery.min.js -->
<dependency>
<groupId>org.webjars.bowergithub.jquery</groupId>
<artifactId>jquery-dist</artifactId>
<version>3.5.1</version>
<version>3.6.0</version>
</dependency>
<!-- Pull in current version of Toastr (toastrjs.com) via WebJars
Made available at: webjars/toastr/build/toastr.min.js -->
@@ -315,17 +304,46 @@
<artifactId>toastr</artifactId>
<version>2.1.4</version>
</dependency>
<!-- Also pull in current version of Bootstrap via WebJars. This is currently ONLY used by our OAI-PMH
interface. But, it is include here so that it's accessible to all interfaces enabled in server webapp.
<!-- Pull in current version of URI.js (https://medialize.github.io/URI.js/) via WebJars
Made available at: webjars/urijs/src/URI.min.js -->
<dependency>
<groupId>org.webjars.bowergithub.medialize</groupId>
<artifactId>uri.js</artifactId>
<version>1.19.10</version>
</dependency>
<!-- Pull in current version of Underscore.js (https://underscorejs.org/) via WebJars
Made available at: webjars/underscore/underscore-min.js -->
<dependency>
<groupId>org.webjars.bowergithub.jashkenas</groupId>
<artifactId>underscore</artifactId>
<version>1.13.2</version>
</dependency>
<!-- Pull in current version of Backbone.js (http://backbonejs.org/) via WebJars
Made available at: webjars/backbone/backbone-min.js -->
<dependency>
<groupId>org.webjars.bowergithub.jashkenas</groupId>
<artifactId>backbone</artifactId>
<version>1.4.1</version>
</dependency>
<!-- Pull in current version of json-editor.js (https://github.com/json-editor/json-editor) via WebJars
Made available at: webjars/json-editor__json-editor/2.6.1/dist/jsoneditor.js
(Required by js/vendor/CustomPostForm.js)
NOTE: Because the path contains the version, you MUST update index.html when updating this dependency -->
<dependency>
<groupId>org.webjars.npm</groupId>
<artifactId>json-editor__json-editor</artifactId>
<version>2.6.1</version>
</dependency>
<!-- Also pull in current version of Bootstrap via WebJars.
This is used by BOTH our HAL Browser and our OAI-PMH interface.
Made available at: webjars/bootstrap/dist/js/bootstrap.min.js and
webjars/bootstrap/dist/css/bootstrap.min.css -->
<dependency>
<groupId>org.webjars.bowergithub.twbs</groupId>
<artifactId>bootstrap</artifactId>
<version>4.5.2</version>
<version>4.6.1</version>
</dependency>
<!-- Add in Spring Security for AuthN and AuthZ -->
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -463,13 +481,11 @@
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>${json-path.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<version>${json-path.version}</version>
<scope>test</scope>
</dependency>
<dependency>

View File

@@ -40,6 +40,7 @@ import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
@@ -192,13 +193,31 @@ public class Application extends SpringBootServletInitializer {
}
}
/**
* Add a ViewController for the root path, to load HAL Browser
* @param registry ViewControllerRegistry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// Ensure accessing the root path will load the index.html of the HAL Browser
registry.addViewController("/").setViewName("forward:/index.html");
}
/**
* Add a new ResourceHandler to allow us to use WebJars.org to pull in web dependencies
* dynamically for HAL Browser, and access them off the /webjars path.
* dynamically for HAL Browser, etc.
* @param registry ResourceHandlerRegistry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// First, "mount" the Hal Browser resources at the /browser path
// NOTE: the hal-browser directory uses the version of the Hal browser, so this needs to be synced
// with the org.webjars.hal-browser version in the POM
registry
.addResourceHandler("/browser/**")
.addResourceLocations("/webjars/hal-browser/ad9b865/");
// Make all other Webjars available off the /webjars path
registry
.addResourceHandler("/webjars/**")
.addResourceLocations("/webjars/");

View File

@@ -80,7 +80,7 @@ public class AuthenticationRestController implements InitializingBean {
@Override
public void afterPropertiesSet() {
discoverableEndpointsService
.register(this, Arrays.asList(new Link("/api/" + AuthnRest.CATEGORY, AuthnRest.NAME)));
.register(this, Arrays.asList(Link.of("/api/" + AuthnRest.CATEGORY, AuthnRest.NAME)));
}
@RequestMapping(method = RequestMethod.GET)

View File

@@ -59,7 +59,7 @@ import org.springframework.web.multipart.MultipartFile;
* </pre>
*/
@RestController
@RequestMapping("/api/" + BundleRest.CATEGORY + "/" + BundleRest.PLURAL_NAME + "/"
@RequestMapping("/api/" + BundleRest.CATEGORY + "/" + BundleRest.PLURAL_NAME
+ REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID + "/" + BitstreamRest.PLURAL_NAME)
public class BundleUploadBitstreamController {

View File

@@ -73,7 +73,7 @@ public class DiscoveryRestController implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
discoverableEndpointsService
.register(this, Arrays.asList(new Link("/api/" + SearchResultsRest.CATEGORY, SearchResultsRest.CATEGORY)));
.register(this, Arrays.asList(Link.of("/api/" + SearchResultsRest.CATEGORY, SearchResultsRest.CATEGORY)));
}
@RequestMapping(method = RequestMethod.GET)

View File

@@ -66,8 +66,8 @@ public class IdentifierRestController implements InitializingBean {
discoverableEndpointsService
.register(this,
Arrays.asList(
new Link(
new UriTemplate("/api/" + CATEGORY + "/" + ACTION,
Link.of(
UriTemplate.of("/api/" + CATEGORY + "/" + ACTION,
new TemplateVariables(
new TemplateVariable(PARAM, VariableType.REQUEST_PARAM))),
CATEGORY)));

View File

@@ -45,7 +45,7 @@ public class OidcRestController {
@PostConstruct
public void afterPropertiesSet() {
discoverableEndpointsService.register(this, List.of(new Link("/api/" + AuthnRest.CATEGORY, "oidc")));
discoverableEndpointsService.register(this, List.of(Link.of("/api/" + AuthnRest.CATEGORY, "oidc")));
}
@RequestMapping(method = RequestMethod.GET)

View File

@@ -127,7 +127,7 @@ public class RestResourceController implements InitializingBean {
// Link l = linkTo(this.getClass(), r).withRel(r);
String[] split = r.split("\\.", 2);
String plural = English.plural(split[1]);
Link l = new Link("/api/" + split[0] + "/" + plural, plural);
Link l = Link.of("/api/" + split[0] + "/" + plural, plural);
links.add(l);
log.debug(l.getRel().value() + " " + l.getHref());
}
@@ -821,7 +821,7 @@ public class RestResourceController implements InitializingBean {
link = linkTo(this.getClass(), apiCategory, model).slash(uuid).slash(subpath).withSelfRel();
}
return new EntityModel(new EmbeddedPage(link.getHref(),
return EntityModel.of(new EmbeddedPage(link.getHref(),
pageResult.map(converter::toResource), null, subpath));
} else {
RestModel object = (RestModel) linkMethod.invoke(linkRepository, request,

View File

@@ -55,13 +55,15 @@ public class ScriptProcessesController {
* This method can be called by sending a POST request to the system/scripts/{name}/processes endpoint
* This will start a process for the script that matches the given name
* @param scriptName The name of the script that we want to start a process for
* @param files (Optional) any files that need to be passed to the script for it to run
* @return The ProcessResource object for the created process
* @throws Exception If something goes wrong
*/
@RequestMapping(method = RequestMethod.POST)
@PreAuthorize("hasAuthority('ADMIN')")
public ResponseEntity<RepresentationModel<?>> startProcess(@PathVariable(name = "name") String scriptName,
@RequestParam(name = "file") List<MultipartFile> files)
public ResponseEntity<RepresentationModel<?>> startProcess(
@PathVariable(name = "name") String scriptName,
@RequestParam(name = "file", required = false) List<MultipartFile> files)
throws Exception {
if (log.isTraceEnabled()) {
log.trace("Starting Process for Script with name: " + scriptName);

View File

@@ -57,7 +57,7 @@ public class StatisticsRestController implements InitializingBean {
public void afterPropertiesSet() throws Exception {
discoverableEndpointsService
.register(this, Arrays
.asList(new Link("/api/" + RestAddressableModel.STATISTICS, RestAddressableModel.STATISTICS)));
.asList(Link.of("/api/" + RestAddressableModel.STATISTICS, RestAddressableModel.STATISTICS)));
}
@RequestMapping(method = RequestMethod.GET)

View File

@@ -132,7 +132,7 @@ public class SubmissionCCLicenseUrlRepository extends DSpaceRestRepository<Submi
@Override
public void afterPropertiesSet() {
discoverableEndpointsService.register(this, Arrays.asList(
new Link("/api/" + SubmissionCCLicenseUrlRest.CATEGORY + "/" +
Link.of("/api/" + SubmissionCCLicenseUrlRest.CATEGORY + "/" +
SubmissionCCLicenseUrlRest.PLURAL + "/search",
SubmissionCCLicenseUrlRest.PLURAL + "-search")));
}

View File

@@ -73,8 +73,8 @@ public class UUIDLookupRestController implements InitializingBean {
discoverableEndpointsService
.register(this,
Arrays.asList(
new Link(
new UriTemplate("/api/" + CATEGORY + "/" + ACTION,
Link.of(
UriTemplate.of("/api/" + CATEGORY + "/" + ACTION,
new TemplateVariables(
new TemplateVariable(PARAM, VariableType.REQUEST_PARAM))),
CATEGORY)));

View File

@@ -27,6 +27,7 @@ import org.dspace.content.InProgressSubmission;
import org.dspace.content.Item;
import org.dspace.eperson.EPerson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
/**
* Abstract implementation providing the common functionalities for all the inprogressSubmission Converter
@@ -44,6 +45,8 @@ public abstract class AInprogressItemConverter<T extends InProgressSubmission,
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(AInprogressItemConverter.class);
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
private ConverterService converter;

View File

@@ -15,7 +15,6 @@ import org.dspace.app.rest.model.CheckSumRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -27,9 +26,6 @@ import org.springframework.stereotype.Component;
@Component
public class BitstreamConverter extends DSpaceObjectConverter<Bitstream, BitstreamRest> {
@Autowired
ConverterService converter;
@Override
public BitstreamRest convert(org.dspace.content.Bitstream obj, Projection projection) {
BitstreamRest b = super.convert(obj, projection);

View File

@@ -14,6 +14,7 @@ import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
@@ -26,6 +27,8 @@ import org.springframework.stereotype.Component;
public class ClaimedTaskConverter
implements IndexableObjectConverter<ClaimedTask, ClaimedTaskRest> {
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
private ConverterService converter;

View File

@@ -38,6 +38,7 @@ import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.data.domain.Page;
@@ -52,9 +53,12 @@ import org.springframework.stereotype.Service;
/**
* Converts domain objects from the DSpace service layer to rest objects, and from rest objects to resource
* objects, applying {@link Projection}s where applicable.
*
* <P>
* MUST be loaded @Lazy, as this service requires other services to be preloaded (especially DSpaceConverter components)
* and that can result in circular references if those services need this ConverterService (and many do).
* @author Luca Giamminonni (luca.giamminonni at 4science dot it)
*/
@Lazy
@Service
public class ConverterService {

View File

@@ -24,6 +24,7 @@ import org.dspace.content.MetadataValue;
import org.dspace.core.Context;
import org.dspace.services.RequestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
/**
* This is the base converter from/to objects in the DSpace API data model and
@@ -38,6 +39,8 @@ public abstract class DSpaceObjectConverter<M extends DSpaceObject, R extends or
private static final Logger log = LogManager.getLogger(DSpaceObjectConverter.class);
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
ConverterService converter;

View File

@@ -18,6 +18,7 @@ import org.dspace.app.rest.projection.Projection;
import org.dspace.content.Collection;
import org.dspace.harvest.HarvestedCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
@@ -28,6 +29,8 @@ import org.springframework.stereotype.Component;
@Component
public class HarvestedCollectionConverter implements DSpaceConverter<HarvestedCollection, HarvestedCollectionRest> {
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
private ConverterService converter;

View File

@@ -28,6 +28,7 @@ import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.DSpaceObjectService;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
@@ -39,6 +40,8 @@ public class MetadataConverter implements DSpaceConverter<MetadataValueList, Met
@Autowired
private ContentServiceFactory contentServiceFactory;
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
private ConverterService converter;

View File

@@ -11,6 +11,7 @@ import org.dspace.app.rest.model.MetadataFieldRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.content.MetadataField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
@@ -21,6 +22,8 @@ import org.springframework.stereotype.Component;
*/
@Component
public class MetadataFieldConverter implements DSpaceConverter<MetadataField, MetadataFieldRest> {
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
private ConverterService converter;

View File

@@ -13,6 +13,7 @@ import org.dspace.discovery.IndexableObject;
import org.dspace.xmlworkflow.storedcomponents.PoolTask;
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
@@ -25,6 +26,8 @@ import org.springframework.stereotype.Component;
public class PoolTaskConverter
implements IndexableObjectConverter<PoolTask, org.dspace.app.rest.model.PoolTaskRest> {
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
private ConverterService converter;

View File

@@ -15,6 +15,7 @@ import org.dspace.app.rest.projection.Projection;
import org.dspace.scripts.Process;
import org.dspace.scripts.service.ProcessService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
@@ -23,6 +24,8 @@ import org.springframework.stereotype.Component;
@Component
public class ProcessConverter implements DSpaceConverter<Process, ProcessRest> {
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
private ConverterService converter;

View File

@@ -11,6 +11,7 @@ import org.dspace.app.rest.model.RelationshipRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.content.Relationship;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
@@ -20,6 +21,8 @@ import org.springframework.stereotype.Component;
@Component
public class RelationshipConverter implements DSpaceConverter<Relationship, RelationshipRest> {
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
private ConverterService converter;

View File

@@ -11,6 +11,7 @@ import org.dspace.app.rest.model.RelationshipTypeRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.content.RelationshipType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
@@ -20,6 +21,8 @@ import org.springframework.stereotype.Component;
@Component
public class RelationshipTypeConverter implements DSpaceConverter<RelationshipType, RelationshipTypeRest> {
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
private ConverterService converter;

View File

@@ -12,6 +12,7 @@ import org.dspace.app.rest.projection.Projection;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.ResourcePolicyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
@@ -26,6 +27,8 @@ public class ResourcePolicyConverter implements DSpaceConverter<ResourcePolicy,
@Autowired
ResourcePolicyService resourcePolicyService;
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
ConverterService converterService;

View File

@@ -16,6 +16,7 @@ import org.dspace.app.rest.projection.Projection;
import org.dspace.license.CCLicense;
import org.dspace.license.CCLicenseField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
@@ -25,6 +26,8 @@ import org.springframework.stereotype.Component;
@Component
public class SubmissionCCLicenseConverter implements DSpaceConverter<CCLicense, SubmissionCCLicenseRest> {
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
private ConverterService converter;

View File

@@ -16,6 +16,7 @@ import org.dspace.app.rest.projection.Projection;
import org.dspace.license.CCLicenseField;
import org.dspace.license.CCLicenseFieldEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
@@ -27,6 +28,8 @@ import org.springframework.stereotype.Component;
public class SubmissionCCLicenseFieldConverter
implements DSpaceConverter<CCLicenseField, SubmissionCCLicenseFieldRest> {
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
private ConverterService converter;

View File

@@ -27,6 +27,7 @@ import org.dspace.content.Collection;
import org.dspace.core.Context;
import org.dspace.services.RequestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
@@ -47,6 +48,8 @@ public class SubmissionDefinitionConverter implements DSpaceConverter<Submission
@Autowired
private RequestService requestService;
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
private ConverterService converter;

View File

@@ -18,6 +18,7 @@ import org.dspace.content.Item;
import org.dspace.content.MetadataValue;
import org.dspace.content.service.ItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
@@ -28,6 +29,8 @@ import org.springframework.stereotype.Component;
public class TemplateItemConverter
implements DSpaceConverter<TemplateItem, TemplateItemRest> {
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
ConverterService converter;

View File

@@ -15,6 +15,7 @@ import org.dspace.app.rest.projection.Projection;
import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
import org.dspace.xmlworkflow.state.Workflow;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
@@ -28,6 +29,8 @@ public class WorkflowDefinitionConverter implements DSpaceConverter<Workflow, Wo
@Autowired
protected XmlWorkflowFactory xmlWorkflowFactory;
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
ConverterService converter;

View File

@@ -14,6 +14,7 @@ import org.dspace.app.rest.model.WorkflowStepRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.xmlworkflow.state.Step;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
@@ -24,6 +25,8 @@ import org.springframework.stereotype.Component;
@Component
public class WorkflowStepConverter implements DSpaceConverter<Step, WorkflowStepRest> {
// Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components
@Lazy
@Autowired
ConverterService converter;

View File

@@ -12,9 +12,9 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.dspace.app.rest.security.WebSecurityConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.csrf.CsrfToken;
@@ -40,8 +40,9 @@ import org.springframework.web.servlet.HandlerExceptionResolver;
@Component
public class DSpaceAccessDeniedHandler implements AccessDeniedHandler {
@Lazy
@Autowired
private WebSecurityConfiguration webSecurityConfiguration;
private CsrfTokenRepository csrfTokenRepository;
@Autowired
@Qualifier("handlerExceptionResolver")
@@ -69,9 +70,6 @@ public class DSpaceAccessDeniedHandler implements AccessDeniedHandler {
// switched clients (from HAL Browser to UI or visa versa) and has an out-of-sync token.
// NOTE: this logic is tested in AuthenticationRestControllerIT.testRefreshTokenWithInvalidCSRF()
if (ex instanceof InvalidCsrfTokenException) {
// Get access to our enabled CSRF token repository
CsrfTokenRepository csrfTokenRepository = webSecurityConfiguration.getCsrfTokenRepository();
// Remove current token & generate a new one
csrfTokenRepository.saveToken(null, request, response);
CsrfToken newToken = csrfTokenRepository.generateToken(request);

View File

@@ -56,9 +56,7 @@ public abstract class HalLinkFactory<RESOURCE, CONTROLLER> {
}
protected Link buildLink(String rel, String href) {
Link link = new Link(href, rel);
return link;
return Link.of(href, rel);
}
protected CONTROLLER getMethodOn(Object... parameters) {

View File

@@ -14,6 +14,7 @@ import org.dspace.core.Context;
import org.dspace.services.RequestService;
import org.dspace.utils.DSpace;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
/**
* This is the base class for any Rest Repository. It provides utility method to
@@ -26,6 +27,7 @@ public abstract class AbstractDSpaceRestRepository {
@Autowired
protected Utils utils;
@Lazy
@Autowired
protected ConverterService converter;

View File

@@ -287,7 +287,7 @@ public class ClaimedTaskRestRepository extends DSpaceRestRepository<ClaimedTaskR
@Override
public void afterPropertiesSet() throws Exception {
discoverableEndpointsService.register(this, Arrays.asList(
new Link("/api/" + ClaimedTaskRest.CATEGORY + "/" + ClaimedTaskRest.PLURAL_NAME + "/search",
Link.of("/api/" + ClaimedTaskRest.CATEGORY + "/" + ClaimedTaskRest.PLURAL_NAME + "/search",
ClaimedTaskRest.PLURAL_NAME + "-search")));
}
}

View File

@@ -22,12 +22,15 @@ import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.exception.RESTAuthorizationException;
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.RestAddressableModel;
import org.dspace.app.rest.model.patch.Patch;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.service.MetadataFieldService;
import org.dspace.core.Context;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@@ -45,20 +48,44 @@ import org.springframework.web.multipart.MultipartFile;
*/
public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID extends Serializable>
extends AbstractDSpaceRestRepository
implements PagingAndSortingRepository<T, ID> {
implements PagingAndSortingRepository<T, ID>, BeanNameAware {
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(DSpaceRestRepository.class);
//Trick to make inner-calls to ourselves that are checked by Spring security
//See:
// https://stackoverflow.com/questions/13564627/spring-aop-not-working-for-method-call-inside-another-method
// https://docs.spring.io/spring/docs/4.3.18.RELEASE/spring-framework-reference/htmlsingle/#aop-understanding-aop-proxies
@Autowired
private String thisRepositoryBeanName;
private DSpaceRestRepository<T, ID> thisRepository;
@Autowired
private ApplicationContext applicationContext;
@Autowired
private MetadataFieldService metadataFieldService;
/**
* From BeanNameAware. Allows us to capture the name of the bean, so we can load it into thisRepository.
* See getThisRepository() method.
* @param beanName name of ourselves
*/
@Override
public void setBeanName(String beanName) {
this.thisRepositoryBeanName = beanName;
}
/**
* Get access to our current DSpaceRestRepository bean. This is a trick to make inner-calls to ourselves that are
* checked by Spring Security
* See:
* https://stackoverflow.com/questions/13564627/spring-aop-not-working-for-method-call-inside-another-method
* https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-understanding-aop-proxies
* @return current DSpaceRestRepository
*/
private DSpaceRestRepository<T, ID> getThisRepository() {
if (thisRepository == null) {
thisRepository = (DSpaceRestRepository<T, ID>) applicationContext.getBean(thisRepositoryBeanName);
}
return thisRepository;
}
@Override
public <S extends T> S save(S entity) {
Context context = null;
@@ -108,7 +135,7 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
*/
public Optional<T> findById(ID id) {
Context context = obtainContext();
final T object = thisRepository.findOne(context, id);
final T object = getThisRepository().findOne(context, id);
if (object == null) {
return Optional.empty();
} else {
@@ -171,7 +198,7 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
public void deleteById(ID id) {
Context context = obtainContext();
try {
thisRepository.delete(context, id);
getThisRepository().delete(context, id);
context.commit();
} catch (AuthorizeException e) {
throw new RESTAuthorizationException(e);
@@ -212,6 +239,14 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
}
@Override
/**
* Deletes all instances of the type T with the given IDs.
*/
public void deleteAllById(Iterable<? extends ID> ids) {
// TODO Auto-generated method stub
}
@Override
/**
* Method to implement to support bulk delete of ALL entity instances
@@ -235,7 +270,7 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
*/
public Page<T> findAll(Pageable pageable) {
Context context = obtainContext();
return thisRepository.findAll(context, pageable);
return getThisRepository().findAll(context, pageable);
}
/**
@@ -263,7 +298,7 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
Context context = null;
try {
context = obtainContext();
T entity = thisRepository.createAndReturn(context);
T entity = getThisRepository().createAndReturn(context);
context.commit();
return entity;
} catch (AuthorizeException e) {
@@ -284,7 +319,7 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
Context context = null;
try {
context = obtainContext();
T entity = thisRepository.createAndReturn(context, uuid);
T entity = getThisRepository().createAndReturn(context, uuid);
context.commit();
return entity;
} catch (AuthorizeException e) {
@@ -305,7 +340,7 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
Context context = null;
try {
context = obtainContext();
T entity = thisRepository.createAndReturn(context, list);
T entity = getThisRepository().createAndReturn(context, list);
context.commit();
return entity;
} catch (AuthorizeException e) {
@@ -406,7 +441,7 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
throws UnprocessableEntityException, DSpaceBadRequestException {
Context context = obtainContext();
try {
thisRepository.patch(context, request, apiCategory, model, id, patch);
getThisRepository().patch(context, request, apiCategory, model, id, patch);
context.commit();
} catch (AuthorizeException ae) {
throw new RESTAuthorizationException(ae);
@@ -507,7 +542,7 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
public T put(HttpServletRequest request, String apiCategory, String model, ID uuid, JsonNode jsonNode) {
Context context = obtainContext();
try {
thisRepository.put(context, request, apiCategory, model, uuid, jsonNode);
getThisRepository().put(context, request, apiCategory, model, uuid, jsonNode);
context.commit();
} catch (SQLException e) {
throw new RuntimeException("Unable to update DSpace object " + model + " with id=" + uuid, e);
@@ -532,7 +567,7 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
List<String> stringList) {
Context context = obtainContext();
try {
thisRepository.put(context, request, apiCategory, model, id, stringList);
getThisRepository().put(context, request, apiCategory, model, id, stringList);
context.commit();
} catch (SQLException | AuthorizeException e) {
throw new RuntimeException(e.getMessage(), e);

View File

@@ -336,6 +336,6 @@ public class EPersonRestRepository extends DSpaceObjectRestRepository<EPerson, E
@Override
public void afterPropertiesSet() throws Exception {
discoverableEndpointsService.register(this, Arrays.asList(
new Link("/api/" + EPersonRest.CATEGORY + "/registrations", EPersonRest.NAME + "-registration")));
Link.of("/api/" + EPersonRest.CATEGORY + "/registrations", EPersonRest.NAME + "-registration")));
}
}

View File

@@ -131,7 +131,7 @@ public class PoolTaskRestRepository extends DSpaceRestRepository<PoolTaskRest, I
@Override
public void afterPropertiesSet() throws Exception {
discoverableEndpointsService.register(this, Arrays.asList(
new Link("/api/" + PoolTaskRest.CATEGORY + "/" + PoolTaskRest.PLURAL_NAME + "/search",
Link.of("/api/" + PoolTaskRest.CATEGORY + "/" + PoolTaskRest.PLURAL_NAME + "/search",
PoolTaskRest.PLURAL_NAME + "-search")));
}

View File

@@ -324,7 +324,7 @@ public class ResourcePolicyRestRepository extends DSpaceRestRepository<ResourceP
@Override
public void afterPropertiesSet() throws Exception {
discoverableEndpointsService.register(this, Arrays.asList(
new Link("/api/" + ResourcePolicyRest.CATEGORY + "/" + ResourcePolicyRest.PLURAL_NAME + "/search",
Link.of("/api/" + ResourcePolicyRest.CATEGORY + "/" + ResourcePolicyRest.PLURAL_NAME + "/search",
ResourcePolicyRest.PLURAL_NAME + "-search")));
}
}

View File

@@ -147,8 +147,10 @@ public class ScriptRestRepository extends DSpaceRestRepository<ScriptRest, Strin
DSpaceRunnable dSpaceRunnable = scriptService.createDSpaceRunnableForScriptConfiguration(scriptToExecute);
try {
dSpaceRunnable.initialize(args.toArray(new String[0]), restDSpaceRunnableHandler, context.getCurrentUser());
checkFileNames(dSpaceRunnable, files);
processFiles(context, restDSpaceRunnableHandler, files);
if (files != null && !files.isEmpty()) {
checkFileNames(dSpaceRunnable, files);
processFiles(context, restDSpaceRunnableHandler, files);
}
restDSpaceRunnableHandler.schedule(dSpaceRunnable);
} catch (ParseException e) {
dSpaceRunnable.printHelp();

View File

@@ -56,7 +56,7 @@ public class VocabularyEntryDetailsRestRepository extends DSpaceRestRepository<V
@Override
public void afterPropertiesSet() throws Exception {
discoverableEndpointsService.register(this, Arrays.asList(
new Link("/api/" + VocabularyRest.CATEGORY + "/" + VocabularyEntryDetailsRest.PLURAL_NAME + "/search",
Link.of("/api/" + VocabularyRest.CATEGORY + "/" + VocabularyEntryDetailsRest.PLURAL_NAME + "/search",
VocabularyEntryDetailsRest.PLURAL_NAME + "-search")));
}

View File

@@ -13,7 +13,9 @@ import org.dspace.services.RequestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@@ -102,7 +104,7 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
// (both are defined below as methods).
// While we primarily use JWT in headers, CSRF protection is needed because we also support JWT via Cookies
.csrf()
.csrfTokenRepository(this.getCsrfTokenRepository())
.csrfTokenRepository(this.csrfTokenRepository())
.sessionAuthenticationStrategy(this.sessionAuthenticationStrategy())
.and()
.exceptionHandling()
@@ -168,7 +170,9 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
*
* @return CsrfTokenRepository as described above
*/
public CsrfTokenRepository getCsrfTokenRepository() {
@Lazy
@Bean
public CsrfTokenRepository csrfTokenRepository() {
return new DSpaceCsrfTokenRepository();
}
@@ -177,7 +181,7 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
* is only refreshed when it is used (or attempted to be used) by the client.
*/
private SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new DSpaceCsrfAuthenticationStrategy(getCsrfTokenRepository());
return new DSpaceCsrfAuthenticationStrategy(csrfTokenRepository());
}
}

View File

@@ -21,7 +21,6 @@ import org.apache.commons.lang3.StringUtils;
import org.dspace.app.rest.model.wrapper.AuthenticationToken;
import org.dspace.app.rest.security.DSpaceAuthentication;
import org.dspace.app.rest.security.RestAuthenticationService;
import org.dspace.app.rest.security.WebSecurityConfiguration;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.authenticate.AuthenticationMethod;
import org.dspace.authenticate.service.AuthenticationService;
@@ -32,6 +31,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.ResponseCookie;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.CsrfTokenRepository;
@@ -65,8 +65,9 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
@Autowired
private AuthenticationService authenticationService;
@Lazy
@Autowired
private WebSecurityConfiguration webSecurityConfiguration;
private CsrfTokenRepository csrfTokenRepository;
@Override
public void afterPropertiesSet() throws Exception {
@@ -331,9 +332,6 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
* @param response current response
*/
private void resetCSRFToken(HttpServletRequest request, HttpServletResponse response) {
// Get access to our enabled CSRF token repository
CsrfTokenRepository csrfTokenRepository = webSecurityConfiguration.getCsrfTokenRepository();
// Remove current CSRF token & generate a new one
// We do this as we want the token to change anytime you login or logout
csrfTokenRepository.saveToken(null, request, response);

View File

@@ -63,6 +63,7 @@ import org.dspace.workflow.WorkflowItemService;
import org.dspace.workflow.WorkflowService;
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.rest.webmvc.json.patch.PatchException;
import org.springframework.jdbc.datasource.init.UncategorizedScriptException;
import org.springframework.stereotype.Component;
@@ -94,6 +95,7 @@ public class SubmissionService {
protected CreativeCommonsService creativeCommonsService;
@Autowired
private RequestService requestService;
@Lazy
@Autowired
private ConverterService converter;
@Autowired

View File

@@ -11,7 +11,6 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
/**
* This class provides extra configuration for our Spring Boot Application
@@ -23,7 +22,6 @@ import org.springframework.data.web.config.EnableSpringDataWebSupport;
* @author Tim Donohue
*/
@Configuration
@EnableSpringDataWebSupport
@ComponentScan( {"org.dspace.app.rest.converter", "org.dspace.app.rest.repository", "org.dspace.app.rest.utils",
"org.dspace.app.configuration", "org.dspace.iiif", "org.dspace.app.iiif"})
public class ApplicationConfig {

View File

@@ -17,6 +17,7 @@ import org.dspace.content.authority.Choice;
import org.dspace.content.authority.ChoiceAuthority;
import org.dspace.content.authority.service.ChoiceAuthorityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
@@ -38,6 +39,9 @@ public class AuthorityUtils {
@Autowired
private ChoiceAuthorityService cas;
// Lazy load required so that AuthorityUtils can be used from DSpaceConverter components
// (because ConverterService autowires all DSpaceConverter components)
@Lazy
@Autowired
private ConverterService converter;

View File

@@ -84,6 +84,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
@@ -131,6 +132,8 @@ public class Utils {
@Autowired
private BitstreamFormatService bitstreamFormatService;
// Must be loaded @Lazy, as ConverterService also autowires Utils
@Lazy
@Autowired
private ConverterService converter;

View File

@@ -38,11 +38,6 @@
# interact with or read its configuration from dspace.cfg.
dspace.dir=${dspace.dir}
########################
# Spring DATA Rest settings
#
spring.data.rest.basePath=
########################
# Jackson serialization settings
#
@@ -60,11 +55,9 @@ spring.messages.encoding=UTF-8
#
#
# Charset of HTTP requests and responses. Added to the "Content-Type" header if not set explicitly.
spring.http.encoding.charset=UTF-8
# Enable http encoding support.
spring.http.encoding.enabled=true
server.servlet.encoding.charset=UTF-8
# Force the encoding to the configured charset on HTTP requests and responses.
spring.http.encoding.force=true
server.servlet.encoding.force=true
###########################
# Server Properties

View File

@@ -6,51 +6,54 @@
* This DSpace version has be customized to include:
* * Download file functionality (see new downloadFile() method)
* * Improved AuthorizationHeader parsing (see new getAuthorizationHeader() method)
* * Upgraded third party dependencies (JQuery)
* * Upgraded third party dependencies via WebJars
* * Updated to use Bootstrap 4, based loosely on this PR to HAL Browser:
https://github.com/mikekelly/hal-browser/pull/102
-->
<!doctype html>
<head>
<meta charset="utf-8">
<title>The HAL Browser (customized for DSpace Server Webapp)</title>
<link rel="stylesheet" media="screen" href="browser/vendor/css/bootstrap.css" />
<title>The HAL Browser (customized for DSpace)</title>
<link rel="stylesheet" media="screen" href="webjars/bootstrap/dist/css/bootstrap.min.css" />
<style type="text/css">
body {
padding-top: 60px;
padding-bottom: 40px;
height: 100vh;
}
.sidebar-nav {
padding: 9px 0;
}
</style>
<link rel="stylesheet" media="screen" href="browser/vendor/css/bootstrap-responsive.css" />
<link rel="stylesheet" media="screen" href="browser/styles.css" />
<link rel="stylesheet" media="screen" href="styles.css" />
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="brand">The HAL Browser</a>
<div class="nav-collapse">
<ul class="nav">
<li><a href="#/" id="entryPointLink">Go To Entry Point</a></li>
<li><a href="https://github.com/mikekelly/hal-browser">About The HAL Browser</a></li>
<li><a href="login.html">Login</a></li>
</ul>
</div>
</div>
<nav class="navbar sticky-top navbar-expand-md navbar-light bg-light">
<a class="navbar-brand" href="#">The HAL Browser</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="#/" id="entryPointLink">Go To Entry Point</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://github.com/mikekelly/hal-browser">About The HAL Browser</a>
</li>
<li class="nav-item">
<a class="nav-link" href="login.html">Login</a>
</li>
</ul>
</div>
</div>
</nav>
<div id="browser" class="container-fluid"></div>
<script id="location-bar-template" type="text/template">
<form>
<div class="input-append span12 location-bar-container">
<input class="span11" id="appendedInputButton" type="text" value="<%= _.escape(url) %>">
<button class="btn" type="submit">Go!</button>
<span class="ajax-loader"></span>
<div class="input-group mb-3">
<input type="text" class="form-control" id="appendedInputButton" type="text" value="<%= _.escape(url) %>">
<div class="input-group-append">
<button class="btn btn-outline-secondary input-group-text" type="submit">Go!</button>
</div>
</div>
</form>
</script>
@@ -60,12 +63,12 @@
<table class="table">
<thead>
<tr>
<th>rel</th>
<th>title</th>
<th>name / index</th>
<th>docs</th>
<th>GET</th>
<th>NON-GET</th>
<th scope="col">rel</th>
<th scope="col">title</th>
<th scope="col">name / index</th>
<th scope="col">docs</th>
<th scope="col">GET</th>
<th scope="col">NON-GET</th>
</tr>
</thead>
<tbody>
@@ -78,14 +81,14 @@
<td><%= link.name ? 'name: ' + link.name : 'index: ' + i %></td>
<td>
<% if (HAL.isUrl(rel)) { %>
<a class="dox" href="<%= HAL.normalizeUrl(HAL.buildUrl(rel)) %>"><i class="icon-book"></i></a>
<a class="dox" href="<%= HAL.normalizeUrl(HAL.buildUrl(rel)) %>">&#128366;</a>
<% } %>
</td>
<td>
<% if (link.templated === true) { %>
<a class="query btn btn-success" href="<%= HAL.normalizeUrl(link.href) %>" title="Query URI template"><i class="icon-question-sign"></i></a>
<a class="query btn btn-success" href="<%= HAL.normalizeUrl(link.href) %>" title="Query URI template">?</a>
<% } else { %>
<a class="follow btn btn-success" href="<%= HAL.normalizeUrl(link.href) %>" title="Follow link"><i class="icon-arrow-right"></i></a>
<a class="follow btn btn-success" href="<%= HAL.normalizeUrl(link.href) %>" title="Follow link">&#10140;</a>
<% } %>
</td>
<td>
@@ -100,14 +103,14 @@
<td><%= obj.name || '' %></td>
<td>
<% if (HAL.isUrl(rel)) { %>
<a class="dox" href="<%= HAL.normalizeUrl(HAL.buildUrl(rel)) %>"><i class="icon-book"></i></a>
<a class="dox btn btn-success" href="<%= HAL.normalizeUrl(HAL.buildUrl(rel)) %>">&#128366;</a>
<% } %>
</td>
<td>
<% if (obj.templated === true) { %>
<a class="query btn btn-success" href="<%= HAL.normalizeUrl(obj.href) %>" title="Query URI template"><i class="icon-question-sign"></i></a>
<a class="query btn btn-success" href="<%= HAL.normalizeUrl(obj.href) %>" title="Query URI template">?</a>
<% } else { %>
<a class="follow btn btn-success" href="<%= HAL.normalizeUrl(obj.href) %>" title="Follow link"><i class="icon-arrow-right"></i></a>
<a class="follow btn btn-success" href="<%= HAL.normalizeUrl(obj.href) %>" title="Follow link">&#10140;</a>
<% } %>
</td>
<td>
@@ -122,17 +125,25 @@
<script id="properties-template" type="text/template">
<h2>Properties</h2>
<pre><%= properties %></pre>
<div class="card read-only">
<div class="card-body">
<pre><%= properties %></pre>
</div>
</div>
</script>
<script id="request-headers-template" type="text/template">
<h2>Custom Request Headers</h2>
<textarea class="span12"></textarea>
<div class="input-group">
<textarea class="form-control"></textarea>
</div>
</script>
<script id="response-headers-template" type="text/template">
<h2>Response Headers</h2>
<pre><%= status.code %> <%= status.text %>
<div class="card read-only">
<div class="card-body">
<pre><%= status.code %> <%= status.text %>
<% _.each(headers, function(value, name) {
%><%= _.escape(name) %>: <%
@@ -143,97 +154,119 @@
%><% if(HAL.isFollowableHeader(name)) {
%></a><%
} %>
<% }) %></pre>
<% }) %>
</pre>
</div>
</div>
</script>
<script id="response-body-template" type="text/template">
<h2>Response Body</h2>
<pre><%= _.escape(body) %></pre>
<div class="card read-only">
<div class="card-body">
<pre><%= _.escape(body) %></pre>
</div>
</div>
</script>
<script id="query-uri-template" type="text/template">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Expand URI Template</h3>
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5>Expand URI Template</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<form id="query" action="<%= href %>">
<div class="modal-body">
<label for="uri">URI Template:</label>
<div class="card read-only" id="uri">
<div class="card-body">
<pre><%- href %></pre>
</div>
</div>
<label for="input">Input (JSON):</label>
<textarea class="form-control" id="input"><%= input %></textarea>
<label for="preview">Expanded URI:</label>
<div class="card read-only" id="preview">
<div class="card-body">
<pre class="preview">&nbsp;</pre>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Follow URI</button>
</div>
</form>
</div>
</div>
<form id="query" action="<%= href %>">
<div class="modal-body">
<p>URI Template:</p>
<pre><%- href %></pre>
<p>Input (JSON):</p>
<textarea><%= input %></textarea>
<p>Expanded URI:</p>
<pre class="preview">&nbsp;</pre>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Follow URI</button>
</div>
</form>
</script>
<script id="non-safe-request-template" type="text/template">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Make a non-GET request</h3>
</div>
<form class="non-safe" action="<%= _.escape(href) %>">
<div class="modal-body">
<p>Target URI</p>
<input name="url" type="text" class="url" value="<%= _.escape(href) %>" />
<p>Method:</p>
<input name="method" type="text" class="method" value="POST" />
<p>Headers:</p>
<textarea name="headers" class="headers" style="height: 100px">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Make a NON-GET request</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<form class="non-safe" action="<%= href %>">
<div class="modal-body">
<label for="url">Target URI</label>
<input name="url" type="text" class="url form-control" id="url" value="<%= href %>" />
<label for="method">Method:</label>
<input name="method" type="text" class="method form-control" id="method" value="POST" />
<label for="headers">Headers:</label>
<textarea name="headers" class="headers form-control" style="height: 100px" id="headers">
Content-Type: application/json
<%= user_defined_headers %>
</textarea>
<p>Body:</p>
<textarea name="body" class="body" style="height: 200px">
</textarea>
<label for="body">Body:</label>
<textarea name="body" class="body form-control" style="height: 200px" id="body">
{
}
</textarea>
</textarea>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Make Request</button>
</div>
</form>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Make Request</button>
</div>
</form>
</div>
</script>
<script id="dynamic-request-template" type="text/template">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Create/Update</h3>
</div>
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Create/Update</h4>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<form class="non-safe" action="<%= href %>">
<div class="modal-body" style="padding-top: 0px">
<div id="jsoneditor"></div>
<form class="non-safe" action="<%= href %>">
<div class="modal-body">
<div id="jsoneditor"></div>
<div class="well well-small" style="padding-bottom: 0px;">
<div class="container-fluid">
<div class="row-fluid">
<div class="control-group">
<label class="control-label" style="display: inline-block; font-weight: bold;">Action:</label>
<input name="method" type="text" class="method controls" style="width: 98%" value="POST" />
<input name="url" type="text" class="url controls" style="width: 98%" value="<%= href %>" />
<div class="card card-body bg-light">
<div class="container-fluid">
<div class="row-fluid">
<div class="form-group">
<label class="col-form-label" style="display: inline-block; font-weight: bold;">Action:</label>
<input name="method" type="text" class="method form-control" style="width: 98%" value="POST" />
<input name="url" type="text" class="url form-control" style="width: 98%" value="<%= href %>" />
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Make Request</button>
</div>
</form>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Make Request</button>
</div>
</form>
</div>
</script>
<script id="embedded-resources-template" type="text/template">
<h2>Embedded Resources</h2>
</script>
@@ -250,13 +283,13 @@ Content-Type: application/json
</div>
</script>
<!-- Customized (to use WebJars) for DSpace -->
<!-- Pull in updated versions of "vendor" libraries using WebJars -->
<script src="webjars/jquery/dist/jquery.min.js"></script>
<script src="browser/vendor/js/underscore.js"></script>
<script src="browser/vendor/js/backbone.js"></script>
<script src="webjars/underscore/underscore-min.js"></script>
<script src="webjars/backbone/backbone-min.js"></script>
<script src="browser/vendor/js/uritemplates.js"></script>
<script src="browser/vendor/js/URI.min.js"></script>
<script src="browser/vendor/js/bootstrap.js"></script>
<script src="webjars/urijs/src/URI.min.js"></script>
<script src="webjars/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="browser/js/hal.js"></script>
<script src="browser/js/hal/browser.js"></script>
@@ -288,8 +321,8 @@ Content-Type: application/json
<script src="browser/js/hal/views/documentation.js"></script>
<script src="browser/vendor/js/jsoneditor.js"></script>
<script src="browser/js/CustomPostForm.js"></script>
<script src="webjars/json-editor__json-editor/2.6.1/dist/jsoneditor.js"></script>
<script src="js/vendor/CustomPostForm.js"></script>
<script>
var browser = new HAL.Browser({

View File

@@ -0,0 +1,208 @@
/**
* Custom Backbone view that uses JSON Schema metadata to create pop-up dialog with actual field names instead of
* asking user to input raw JSON.
*
* NOTE: Because JSON Schema lists all properties, including those that are links, they have to be filtered out.
* Links have to be set via a PUT operation with the proper media type.
*
* @author Greg Turnquist
* @author Gregory Frank
* @since 2.4
* @see DATAREST-627, DATAREST-1077
*
* This code was copied/borrowed from Spring Data REST version 3.3.x:
* https://github.com/spring-projects/spring-data-rest/blob/3.3.x/spring-data-rest-hal-browser/src/main/resources/META-INF/spring-data-rest/hal-browser/js/CustomPostForm.js
* NOTE: For DSpace, we made minor style/theme updates to align with Bootstrap 4.
* This script requires json-editor (https://github.com/json-editor/json-editor) which we pull in via WebJars
*/
/* jshint strict: true */
/* globals HAL, Backbone, _, $, window, jqxhr */
'use strict';
var CustomPostForm = Backbone.View.extend({
initialize: function (opts) {
this.href = opts.href.split('{')[0];
this.vent = opts.vent;
_.bindAll(this, 'createNewResource');
},
events: {
'submit form': 'createNewResource'
},
className: 'modal fade',
/**
* Perform a POST/PUT operation on the resource.
*
* @param e
*/
createNewResource: function (e) {
e.preventDefault();
var self = this;
var opts = {
url: this.$('.url').val(),
headers: _.defaults({'Content-Type': 'application/json'}, HAL.client.getHeaders()),
method: this.$('.method').val(),
data: this.getNewResourceData()
};
HAL.client.request(opts).done(function (response) {
self.vent.trigger('response', {resource: response, jqxhr: jqxhr});
}).fail(function (e) {
self.vent.trigger('fail-response', {jqxhr: jqxhr});
}).always(function (e) {
self.vent.trigger('response-headers', {jqxhr: jqxhr});
window.location.hash = 'NON-GET:' + opts.url;
});
this.$el.modal('hide');
},
/**
* Draw the dialog after fetching the resource's JSON Schema metadata. If no metadata is available, use the
* fallback editor.
*
* @param opts
* @returns {CustomPostForm}
*/
render: function (opts) {
var self = this;
try {
HAL.client.request({
method: 'HEAD',
headers: HAL.client.getHeaders(),
url: this.href
}).done(function (message, text, jqXHR) {
self.$el.html(self.template({href: self.href}));
try {
var hal = self.w3cLinksToHalLinks(jqXHR.getResponseHeader('Link'));
HAL.client.request({
method: 'GET',
url: hal._links.profile.href,
headers: _.defaults({'Accept': 'application/schema+json'}, HAL.client.getHeaders())
}).done(function (schema) {
self.loadJsonEditor(schema);
});
} catch (e) {
self.loadFallbackEditor();
}
self.$el.modal();
});
} catch (e) {
self.loadFallbackEditor();
self.$el.modal();
}
return this;
},
/**
* Load the JSON Schema-driven editor.
*
* @see https://github.com/jdorn/json-editor
*/
loadJsonEditor: function (schema) {
var self = this;
/**
* Remove URI-based fields since this dialog doesn't handle relationships.
*/
Object.keys(schema.properties).forEach(function (property) {
if (schema.properties[property].hasOwnProperty('format') &&
schema.properties[property].format === 'uri') {
delete schema.properties[property];
}
});
/**
* See https://github.com/jdorn/json-editor#options for more customizing options.
*/
this.editor = new window.JSONEditor(this.$('#jsoneditor')[0], {
theme: 'bootstrap4',
schema: schema,
disable_collapse: true,
disable_edit_json: true,
disable_properties: true
});
this.getNewResourceData = function() {
return JSON.stringify(self.editor.getValue());
}
},
/**
* Load fallback editor that doesn't depend on any form of metadata.
*/
loadFallbackEditor: function () {
var editor = this.$('#jsoneditor');
editor.append($('<h5>' + this.href + '</h5>'));
var inputBox = $('<textarea name="body" class="body" style="height: 200px">{\n}</textarea>');
editor.append(inputBox);
this.getNewResourceData = function() {
return inputBox.val();
}
},
/**
* Convert a W3C link header into a HAL-based set of _links.
*
* e.g.
* <http://localhost:8080/persons>; rel="persons",<http://localhost:8080/profile/persons>; rel="profile"
* to
* {
* _links: {
* persons: {
* href: http://localhost:8080/persons
* },
* profile: {
* href: http://localhost:8080/profile/persons
* }
* }
* }
*
* @param linkHeader - HTTP Response header containing a list of W3C compliant links with rels.
* @see https://www.w3.org/wiki/LinkHeader
*/
w3cLinksToHalLinks: function (linkHeader) {
var w3cLinks = linkHeader.split(',');
var halLinks = {_links: {}};
w3cLinks.forEach(function (w3cLink) {
var parts = w3cLink.split(';');
var hrefWrappedWithBrackets = parts[0];
var href = hrefWrappedWithBrackets.slice(1, parts[0].length - 1);
var w3cRel = parts[1];
var relWrappedWithQuotes = w3cRel.split('=')[1];
var rel = relWrappedWithQuotes.slice(1, relWrappedWithQuotes.length - 1);
halLinks._links[rel] = { "href": href };
});
return halLinks;
},
/**
* Look up the HTML template.
*/
template: _.template($('#dynamic-request-template').html())
});
/**
* Inject the form into the HAL Browser.
*/
HAL.customPostForm = CustomPostForm;

View File

@@ -6,7 +6,7 @@
* This DSpace version has be customized to include:
* * Shibboleth login functionality
* * Other customization to use DSpace's login endpoint
* * Upgraded third party dependencies (JQuery)
* * Upgraded third party dependencies (Bootstrap, JQuery, Toastr) using webjars
-->
<!DOCTYPE html>
<html lang="en">
@@ -14,9 +14,9 @@
<meta charset="utf-8">
<title>Sign in - HAL Browser</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="webjars/toastr/build/toastr.min.css" rel="stylesheet"/>
<link href="browser/vendor/css/bootstrap.css" rel="stylesheet">
<link href="browser/vendor/css/bootstrap-responsive.css" rel="stylesheet">
<link rel="stylesheet" media="screen" href="webjars/bootstrap/dist/css/bootstrap.min.css" />
<!-- Toastr CSS must be loaded after Bootstrap -->
<link rel="stylesheet" href="webjars/toastr/build/toastr.min.css"/>
<style type="text/css">
body {
padding-top: 40px;
@@ -24,16 +24,12 @@
background-color: #f5f5f5;
}
.form-signin {
max-width: 300px;
max-width: 350px;
padding: 19px 29px 29px;
margin: 0 auto 20px;
background-color: #fff;
border: 1px solid #e5e5e5;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
}
.form-signin .form-signin-heading, .form-signin .checkbox {
@@ -51,17 +47,17 @@
}
</style>
<script src="webjars/jquery/dist/jquery.min.js"></script>
<script src="browser/vendor/js/bootstrap.js"></script>
<script src="webjars/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="webjars/toastr/build/toastr.min.js"></script>
</head>
<body>
<div class="container">
<form id="login-form" class="form-signin">
<h2 class="form-signin-heading">HAL Browser</h2>
<input type="text" class="input-block-level" placeholder="Username" id="username">
<input type="password" class="input-block-level" placeholder="Password" id="password">
<button type="submit" class="btn btn-large btn-primary" id="login">Sign in</button>
<div class="other-login-methods hidden">
<input type="text" class="form-control" placeholder="Username" id="username">
<input type="password" class="form-control" placeholder="Password" id="password">
<button type="submit" class="btn btn-large btn-primary form-signin-btn" id="login">Sign in</button>
<div class="other-login-methods d-none">
<h3>Other login methods:</h3>
</div>
@@ -158,7 +154,7 @@
});
function addLocationButton(realm, element){
element.removeClass("hidden");
element.removeClass("d-none");
var loc = /location="([^,]*)"/.exec(realm);
var name = /(\w+) (\w+=((".*?")|[^,]*)(, )?)*/.exec(realm);
if (loc !== null && loc.length === 2) {

View File

@@ -0,0 +1,104 @@
/**
* Customized version of the HAL Browser styles.css provided from https://github.com/mikekelly/hal-browser
* Copyright (c) 2012 Mike Kelly, http://stateless.co/
* MIT LICENSE: https://github.com/mikekelly/hal-browser/blob/master/MIT-LICENSE.txt
*
* This DSpace version has be customized to include:
* * Updates to align with Bootstrap 4.
**/
html, body, #browser, .hal-browser { height: 100%; }
#browser #location-bar { margin: 10px 0; }
#browser #location-bar .address {
border: 1px solid #999;
padding: 4px; margin: 0;
}
#browser #headers-bar {
border: 1px solid #888;
padding: 5px
}
#request-headers {
border: 0;
outline: none;
padding: 0;
resize: vertical;
width: 100%;
height: 40px;
margin-bottom: 0px;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
/* Updated to roughly match Bootstrap 4 'row' class */
.hal-browser {
display: flex;
flex-wrap: wrap;
}
/* Updated to roughly match Bootstrap 4 'col-6' class */
.inspector { height: 100%;
flex: 0 0 49%;
max-width: 49%;
margin-left: 2%}
/* Updated to roughly match Bootstrap 4 'col-6' class */
.explorer { flex: 0 0 49%;
max-width: 49%;}
.documentation { height: 100%; }
.documentation iframe { width: 100%; height: 100%; border-color: transparent;}
.modal input, .modal textarea {
width: 90%;
}
.modal textarea {
height: 100px;
}
.read-only {
background-color: #f5f5f5 !important;
}
.links .btn {
padding: 2px 5px 2px;
font-size: 12px;
line-height: 14px;
}
body table.table {
font-size: 14px;
}
.location-bar-container {
line-height: 30px;
}
.ajax-loader {
vertical-align: middle;
background-image: url("./vendor/img/ajax-loader.gif");
background-repeat: no-repeat;
width: 16px;
height: 16px;
margin-left: 6px;
opacity: 0;
display: inline-block;
transition: opacity 1s;
}
.ajax-loader.visible {
opacity: 1;
}
.embedded-resource-title {
font-style: italic;
}
.embedded-resource-title:before {
content: "\"";
}
.embedded-resource-title:after {
content: "\"";
}

View File

@@ -153,8 +153,8 @@ public class OAIpmhIT extends AbstractControllerIntegrationTest {
getClient().perform(get(DEFAULT_CONTEXT).param("verb", "Identify"))
// Expect a 200 response code
.andExpect(status().isOk())
// Expect the content type to be "text/xml"
.andExpect(content().contentType("text/xml"))
// Expect the content type to be "text/xml;charset=UTF-8"
.andExpect(content().contentType("text/xml;charset=UTF-8"))
// Expect <scheme>oai</scheme>
.andExpect(xpath("OAI-PMH/Identify/description/oai-identifier/scheme").string("oai"))
// Expect protocol version 2.0

View File

@@ -40,8 +40,8 @@ public class OpenSearchControllerDisabledIT extends AbstractControllerIntegratio
.param("query", "dog"))
//The status has to be 404 Not Found
.andExpect(status().isNotFound())
//We expect the content type to be "application/html"
.andExpect(content().contentType("text/html"))
//We expect the content type to be "text/html"
.andExpect(content().contentType("text/html;charset=UTF-8"))
.andExpect(content().string("OpenSearch Service is disabled"))
;
}
@@ -52,7 +52,7 @@ public class OpenSearchControllerDisabledIT extends AbstractControllerIntegratio
getClient().perform(get("/opensearch/service"))
//The status has to be 404 Not Found
.andExpect(status().isNotFound())
.andExpect(content().contentType("text/html"))
.andExpect(content().contentType("text/html;charset=UTF-8"))
.andExpect(content().string("OpenSearch Service is disabled"))
;
}

View File

@@ -203,7 +203,7 @@ public class BitstreamRestControllerIT extends AbstractControllerIntegrationTest
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
.andExpect(header().string("ETag", "\"" + bitstream.getChecksum() + "\""))
//We expect the content type to match the bitstream mime type
.andExpect(content().contentType("text/plain"))
.andExpect(content().contentType("text/plain;charset=UTF-8"))
//THe bytes of the content must match the original content
.andExpect(content().bytes(bitstreamContent.getBytes()));
@@ -265,7 +265,7 @@ public class BitstreamRestControllerIT extends AbstractControllerIntegrationTest
//The response should give us details about the range
.andExpect(header().string("Content-Range", "bytes 1-3/10"))
//We expect the content type to match the bitstream mime type
.andExpect(content().contentType("text/plain"))
.andExpect(content().contentType("text/plain;charset=UTF-8"))
//We only expect the bytes 1, 2 and 3
.andExpect(content().bytes("123".getBytes()));
@@ -286,7 +286,7 @@ public class BitstreamRestControllerIT extends AbstractControllerIntegrationTest
//The response should give us details about the range
.andExpect(header().string("Content-Range", "bytes 4-9/10"))
//We expect the content type to match the bitstream mime type
.andExpect(content().contentType("text/plain"))
.andExpect(content().contentType("text/plain;charset=UTF-8"))
//We all remaining bytes, starting at byte 4
.andExpect(content().bytes("456789".getBytes()));
@@ -827,7 +827,7 @@ public class BitstreamRestControllerIT extends AbstractControllerIntegrationTest
//The ETag has to be based on the checksum
.andExpect(header().string("ETag", "\"" + bitstream.getChecksum() + "\""))
//We expect the content type to match the bitstream mime type
.andExpect(content().contentType("application/pdf"))
.andExpect(content().contentType("application/pdf;charset=UTF-8"))
//THe bytes of the content must match the original content
.andReturn().getResponse().getContentAsByteArray();

View File

@@ -103,7 +103,7 @@ public class BundleUploadBitstreamControllerIT extends AbstractEntityIntegration
context.restoreAuthSystemState();
MvcResult mvcResult = getClient(token).perform(
MockMvcRequestBuilders.fileUpload("/api/core/bundles/" + bundle.getID() + "/bitstreams")
MockMvcRequestBuilders.multipart("/api/core/bundles/" + bundle.getID() + "/bitstreams")
.file(file)
.param("properties", mapper
.writeValueAsString(bitstreamRest)))
@@ -167,7 +167,7 @@ public class BundleUploadBitstreamControllerIT extends AbstractEntityIntegration
input.getBytes());
context.restoreAuthSystemState();
MvcResult mvcResult = getClient(token)
.perform(MockMvcRequestBuilders.fileUpload("/api/core/bundles/" + bundle.getID() + "/bitstreams")
.perform(MockMvcRequestBuilders.multipart("/api/core/bundles/" + bundle.getID() + "/bitstreams")
.file(file))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.bundleName", is("TESTINGBUNDLE")))
@@ -223,7 +223,7 @@ public class BundleUploadBitstreamControllerIT extends AbstractEntityIntegration
context.restoreAuthSystemState();
MvcResult mvcResult = getClient(token)
.perform(MockMvcRequestBuilders.fileUpload("/api/core/bundles/" + bundle.getID() + "/bitstreams")
.perform(MockMvcRequestBuilders.multipart("/api/core/bundles/" + bundle.getID() + "/bitstreams")
.file(file))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.uuid", notNullValue())).andReturn();
@@ -273,7 +273,7 @@ public class BundleUploadBitstreamControllerIT extends AbstractEntityIntegration
context.restoreAuthSystemState();
getClient(token).perform(MockMvcRequestBuilders
.fileUpload("/api/core/bundles/" + bundle.getID() + "/bitstreams")
.multipart("/api/core/bundles/" + bundle.getID() + "/bitstreams")
.file(file))
.andExpect(status().isForbidden());
@@ -315,7 +315,7 @@ public class BundleUploadBitstreamControllerIT extends AbstractEntityIntegration
input.getBytes());
context.restoreAuthSystemState();
getClient().perform(MockMvcRequestBuilders.fileUpload("/api/core/bundles/" + bundle.getID() + "/bitstreams")
getClient().perform(MockMvcRequestBuilders.multipart("/api/core/bundles/" + bundle.getID() + "/bitstreams")
.file(file))
.andExpect(status().isUnauthorized());
@@ -367,7 +367,7 @@ public class BundleUploadBitstreamControllerIT extends AbstractEntityIntegration
context.restoreAuthSystemState();
MvcResult mvcResult = getClient(token)
.perform(MockMvcRequestBuilders.fileUpload("/api/core/bundles/" + bundle.getID() + "/bitstreams")
.perform(MockMvcRequestBuilders.multipart("/api/core/bundles/" + bundle.getID() + "/bitstreams")
.file(file)
.param("properties", mapper
.writeValueAsString(bitstreamRest)))
@@ -422,7 +422,7 @@ public class BundleUploadBitstreamControllerIT extends AbstractEntityIntegration
input.getBytes());
context.restoreAuthSystemState();
getClient(token)
.perform(MockMvcRequestBuilders.fileUpload("/api/core/bundles/" + bundle.getID() + "/bitstreams")
.perform(MockMvcRequestBuilders.multipart("/api/core/bundles/" + bundle.getID() + "/bitstreams")
.file(file).file(file2))
.andExpect(status().isUnprocessableEntity());
}

View File

@@ -51,7 +51,7 @@ public class CollectionLogoControllerIT extends AbstractControllerIntegrationTes
private String createLogoInternal() throws Exception {
MvcResult mvcPostResult = getClient(adminAuthToken).perform(
MockMvcRequestBuilders.fileUpload(getLogoUrlTemplate(childCollection.getID().toString()))
MockMvcRequestBuilders.multipart(getLogoUrlTemplate(childCollection.getID().toString()))
.file(bitstreamFile))
.andExpect(status().isCreated())
.andReturn();
@@ -64,7 +64,7 @@ public class CollectionLogoControllerIT extends AbstractControllerIntegrationTes
@Test
public void createLogoNotLoggedIn() throws Exception {
getClient().perform(
MockMvcRequestBuilders.fileUpload(getLogoUrlTemplate(childCollection.getID().toString()))
MockMvcRequestBuilders.multipart(getLogoUrlTemplate(childCollection.getID().toString()))
.file(bitstreamFile))
.andExpect(status().isUnauthorized());
}
@@ -88,7 +88,7 @@ public class CollectionLogoControllerIT extends AbstractControllerIntegrationTes
public void createLogoNoRights() throws Exception {
String userToken = getAuthToken(eperson.getEmail(), password);
getClient(userToken).perform(
MockMvcRequestBuilders.fileUpload(getLogoUrlTemplate(childCollection.getID().toString()))
MockMvcRequestBuilders.multipart(getLogoUrlTemplate(childCollection.getID().toString()))
.file(bitstreamFile))
.andExpect(status().isForbidden());
}
@@ -96,12 +96,12 @@ public class CollectionLogoControllerIT extends AbstractControllerIntegrationTes
@Test
public void createDuplicateLogo() throws Exception {
getClient(adminAuthToken).perform(
MockMvcRequestBuilders.fileUpload(getLogoUrlTemplate(childCollection.getID().toString()))
MockMvcRequestBuilders.multipart(getLogoUrlTemplate(childCollection.getID().toString()))
.file(bitstreamFile))
.andExpect(status().isCreated());
getClient(adminAuthToken).perform(
MockMvcRequestBuilders.fileUpload(getLogoUrlTemplate(childCollection.getID().toString()))
MockMvcRequestBuilders.multipart(getLogoUrlTemplate(childCollection.getID().toString()))
.file(bitstreamFile))
.andExpect(status().isUnprocessableEntity());
}
@@ -109,7 +109,7 @@ public class CollectionLogoControllerIT extends AbstractControllerIntegrationTes
@Test
public void createLogoForNonexisting() throws Exception {
getClient(adminAuthToken).perform(
MockMvcRequestBuilders.fileUpload(getLogoUrlTemplate("16a4b65b-3b3f-4ef5-8058-ef6f5a653ef9"))
MockMvcRequestBuilders.multipart(getLogoUrlTemplate("16a4b65b-3b3f-4ef5-8058-ef6f5a653ef9"))
.file(bitstreamFile))
.andExpect(status().isNotFound());
}

View File

@@ -46,7 +46,7 @@ public class CommunityLogoControllerIT extends AbstractControllerIntegrationTest
private String createLogoInternal() throws Exception {
MvcResult mvcPostResult = getClient(adminAuthToken).perform(
MockMvcRequestBuilders.fileUpload(getLogoUrlTemplate(parentCommunity.getID().toString()))
MockMvcRequestBuilders.multipart(getLogoUrlTemplate(parentCommunity.getID().toString()))
.file(bitstreamFile))
.andExpect(status().isCreated())
.andReturn();
@@ -59,7 +59,7 @@ public class CommunityLogoControllerIT extends AbstractControllerIntegrationTest
@Test
public void createLogoNotLoggedIn() throws Exception {
getClient().perform(
MockMvcRequestBuilders.fileUpload(getLogoUrlTemplate(parentCommunity.getID().toString()))
MockMvcRequestBuilders.multipart(getLogoUrlTemplate(parentCommunity.getID().toString()))
.file(bitstreamFile))
.andExpect(status().isUnauthorized());
}
@@ -83,7 +83,7 @@ public class CommunityLogoControllerIT extends AbstractControllerIntegrationTest
public void createLogoNoRights() throws Exception {
String userToken = getAuthToken(eperson.getEmail(), password);
getClient(userToken).perform(
MockMvcRequestBuilders.fileUpload(getLogoUrlTemplate(parentCommunity.getID().toString()))
MockMvcRequestBuilders.multipart(getLogoUrlTemplate(parentCommunity.getID().toString()))
.file(bitstreamFile))
.andExpect(status().isForbidden());
}
@@ -91,12 +91,12 @@ public class CommunityLogoControllerIT extends AbstractControllerIntegrationTest
@Test
public void createDuplicateLogo() throws Exception {
getClient(adminAuthToken).perform(
MockMvcRequestBuilders.fileUpload(getLogoUrlTemplate(parentCommunity.getID().toString()))
MockMvcRequestBuilders.multipart(getLogoUrlTemplate(parentCommunity.getID().toString()))
.file(bitstreamFile))
.andExpect(status().isCreated());
getClient(adminAuthToken).perform(
MockMvcRequestBuilders.fileUpload(getLogoUrlTemplate(parentCommunity.getID().toString()))
MockMvcRequestBuilders.multipart(getLogoUrlTemplate(parentCommunity.getID().toString()))
.file(bitstreamFile))
.andExpect(status().isUnprocessableEntity());
}
@@ -104,7 +104,7 @@ public class CommunityLogoControllerIT extends AbstractControllerIntegrationTest
@Test
public void createLogoForNonexisting() throws Exception {
getClient(adminAuthToken).perform(
MockMvcRequestBuilders.fileUpload(getLogoUrlTemplate("16a4b65b-3b3f-4ef5-8058-ef6f5a653ef9"))
MockMvcRequestBuilders.multipart(getLogoUrlTemplate("16a4b65b-3b3f-4ef5-8058-ef6f5a653ef9"))
.file(bitstreamFile))
.andExpect(status().isNotFound());
}

View File

@@ -12,8 +12,8 @@ import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
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;
@@ -211,29 +211,26 @@ public class ScriptRestRepositoryIT extends AbstractControllerIntegrationTest {
@Test
public void postProcessNonAdminAuthorizeException() throws Exception {
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(post("/api/system/scripts/mock-script/processes").contentType("multipart/form-data"))
getClient(token).perform(multipart("/api/system/scripts/mock-script/processes"))
.andExpect(status().isForbidden());
}
@Test
public void postProcessAnonymousAuthorizeException() throws Exception {
getClient().perform(post("/api/system/scripts/mock-script/processes").contentType("multipart/form-data"))
getClient().perform(multipart("/api/system/scripts/mock-script/processes"))
.andExpect(status().isUnauthorized());
}
@Test
public void postProcessAdminWrongOptionsException() throws Exception {
String token = getAuthToken(admin.getEmail(), password);
AtomicReference<Integer> idRef = new AtomicReference<>();
try {
getClient(token)
.perform(post("/api/system/scripts/mock-script/processes").contentType("multipart/form-data"))
.perform(multipart("/api/system/scripts/mock-script/processes"))
.andExpect(status().isAccepted())
.andExpect(jsonPath("$", is(
ProcessMatcher.matchProcess("mock-script",
@@ -277,9 +274,8 @@ public class ScriptRestRepositoryIT extends AbstractControllerIntegrationTest {
try {
getClient(token)
.perform(post("/api/system/scripts/mock-script/processes").contentType("multipart/form-data")
.param("properties",
new Gson().toJson(list)))
.perform(multipart("/api/system/scripts/mock-script/processes")
.param("properties", new Gson().toJson(list)))
.andExpect(status().isAccepted())
.andExpect(jsonPath("$", is(
ProcessMatcher.matchProcess("mock-script",
@@ -296,8 +292,7 @@ public class ScriptRestRepositoryIT extends AbstractControllerIntegrationTest {
public void postProcessNonExistingScriptNameException() throws Exception {
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(post("/api/system/scripts/mock-script-invalid/processes")
.contentType("multipart/form-data"))
getClient(token).perform(multipart("/api/system/scripts/mock-script-invalid/processes"))
.andExpect(status().isBadRequest());
}
@@ -323,9 +318,8 @@ public class ScriptRestRepositoryIT extends AbstractControllerIntegrationTest {
try {
getClient(token)
.perform(post("/api/system/scripts/mock-script/processes").contentType("multipart/form-data")
.param("properties",
new Gson().toJson(list)))
.perform(multipart("/api/system/scripts/mock-script/processes")
.param("properties", new Gson().toJson(list)))
.andExpect(status().isAccepted())
.andExpect(jsonPath("$", is(
ProcessMatcher.matchProcess("mock-script",
@@ -361,9 +355,8 @@ public class ScriptRestRepositoryIT extends AbstractControllerIntegrationTest {
try {
getClient(token)
.perform(post("/api/system/scripts/mock-script/processes").contentType("multipart/form-data")
.param("properties",
new Gson().toJson(list)))
.perform(multipart("/api/system/scripts/mock-script/processes")
.param("properties", new Gson().toJson(list)))
.andExpect(status().isAccepted())
.andExpect(jsonPath("$", is(
ProcessMatcher.matchProcess("mock-script",
@@ -468,9 +461,10 @@ public class ScriptRestRepositoryIT extends AbstractControllerIntegrationTest {
try {
getClient(token)
.perform(fileUpload("/api/system/scripts/mock-script/processes").file(bitstreamFile)
.param("properties",
new Gson().toJson(list)))
.perform(multipart("/api/system/scripts/mock-script/processes")
.file(bitstreamFile)
.characterEncoding("UTF-8")
.param("properties", new Gson().toJson(list)))
.andExpect(status().isAccepted())
.andExpect(jsonPath("$", is(
ProcessMatcher.matchProcess("mock-script",

View File

@@ -107,7 +107,7 @@ public class SitemapRestControllerIT extends AbstractControllerIntegrationTest {
//** THEN **
.andExpect(status().isOk())
//We expect the content type to match
.andExpect(content().contentType("text/html"))
.andExpect(content().contentType("text/html;charset=UTF-8"))
.andReturn();
String response = result.getResponse().getContentAsString();
@@ -123,7 +123,7 @@ public class SitemapRestControllerIT extends AbstractControllerIntegrationTest {
//** THEN **
.andExpect(status().isOk())
//We expect the content type to match
.andExpect(content().contentType("text/html"))
.andExpect(content().contentType("text/html;charset=UTF-8"))
.andReturn();
String response = result.getResponse().getContentAsString();
@@ -140,7 +140,7 @@ public class SitemapRestControllerIT extends AbstractControllerIntegrationTest {
//** THEN **
.andExpect(status().isOk())
//We expect the content type to match
.andExpect(content().contentType("application/xml"))
.andExpect(content().contentType("application/xml;charset=UTF-8"))
.andReturn();
String response = result.getResponse().getContentAsString();
@@ -156,7 +156,7 @@ public class SitemapRestControllerIT extends AbstractControllerIntegrationTest {
//** THEN **
.andExpect(status().isOk())
//We expect the content type to match
.andExpect(content().contentType("application/xml"))
.andExpect(content().contentType("application/xml;charset=UTF-8"))
.andReturn();
String response = result.getResponse().getContentAsString();

View File

@@ -15,8 +15,8 @@ import static org.junit.Assert.assertTrue;
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.fileUpload;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@@ -2650,7 +2650,7 @@ public class TaskRestRepositoriesIT extends AbstractControllerIntegrationTest {
InputStream bibtex = getClass().getResourceAsStream("bibtex-test.bib");
final MockMultipartFile bibtexFile = new MockMultipartFile("file", "bibtex-test.bib", "application/x-bibtex",
bibtex);
getClient(reviewer1Token).perform(fileUpload("/api/workflow/workflowitems/" + witem.getID())
getClient(reviewer1Token).perform(multipart("/api/workflow/workflowitems/" + witem.getID())
.file(bibtexFile))
.andExpect(status().isUnprocessableEntity());
@@ -2791,7 +2791,7 @@ public class TaskRestRepositoriesIT extends AbstractControllerIntegrationTest {
InputStream bibtex = getClass().getResourceAsStream("bibtex-test.bib");
final MockMultipartFile bibtexFile = new MockMultipartFile("file", "bibtex-test.bib", "application/x-bibtex",
bibtex);
getClient(authToken).perform(fileUpload("/api/workflow/workflowitems/" + witem.getID())
getClient(authToken).perform(multipart("/api/workflow/workflowitems/" + witem.getID())
.file(bibtexFile))
.andExpect(status().isUnprocessableEntity());
@@ -2944,7 +2944,7 @@ public class TaskRestRepositoriesIT extends AbstractControllerIntegrationTest {
InputStream bibtex = getClass().getResourceAsStream("bibtex-test.bib");
final MockMultipartFile bibtexFile = new MockMultipartFile("file", "bibtex-test.bib", "application/x-bibtex",
bibtex);
getClient(reviewer1Token).perform(fileUpload("/api/workflow/workflowitems/" + witem.getID())
getClient(reviewer1Token).perform(multipart("/api/workflow/workflowitems/" + witem.getID())
.file(bibtexFile))
.andExpect(status().isCreated());

View File

@@ -1862,7 +1862,7 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
}
@Test
public void whenWorkspaceitemBecomeWorkflowitemWithAccessConditionsTheBitstremMustBeDownloableTest()
public void whenWorkspaceitemBecomeWorkflowitemWithAccessConditionsTheBitstreamMustBeDownloableTest()
throws Exception {
context.turnOffAuthorisationSystem();
@@ -1890,8 +1890,6 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
String bitstreamContent = "0123456789";
AtomicReference<Integer> idRef = new AtomicReference<Integer>();
try (InputStream is = IOUtils.toInputStream(bitstreamContent, Charset.defaultCharset())) {
context.setCurrentUser(submitter);
@@ -1905,56 +1903,60 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
.withDescription("This is a bitstream to test range requests")
.withMimeType("text/plain")
.build();
}
context.restoreAuthSystemState();
context.restoreAuthSystemState();
String tokenEPerson = getAuthToken(eperson.getEmail(), password);
String tokenSubmitter = getAuthToken(submitter.getEmail(), password);
String tokenReviewer1 = getAuthToken(reviewer1.getEmail(), password);
String tokenEPerson = getAuthToken(eperson.getEmail(), password);
String tokenSubmitter = getAuthToken(submitter.getEmail(), password);
String tokenReviewer1 = getAuthToken(reviewer1.getEmail(), password);
// submitter can download the bitstream
getClient(tokenSubmitter).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content"))
.andExpect(status().isOk())
.andExpect(header().string("Accept-Ranges", "bytes"))
.andExpect(header().string("ETag", "\"" + bitstream.getChecksum() + "\""))
.andExpect(content().contentType("text/plain"))
.andExpect(content().bytes(bitstreamContent.getBytes()));
// submitter can download the bitstream
getClient(tokenSubmitter).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content"))
.andExpect(status().isOk())
.andExpect(header().string("Accept-Ranges", "bytes"))
.andExpect(header().string("ETag", "\"" + bitstream.getChecksum() + "\""))
.andExpect(content().contentType("text/plain;charset=UTF-8"))
.andExpect(content().bytes(bitstreamContent.getBytes()));
// reviewer can't still download the bitstream
getClient(tokenReviewer1).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content"))
.andExpect(status().isForbidden());
// reviewer can't still download the bitstream
getClient(tokenReviewer1).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content"))
.andExpect(status().isForbidden());
// others can't download the bitstream
getClient(tokenEPerson).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content"))
.andExpect(status().isForbidden());
// others can't download the bitstream
getClient(tokenEPerson).perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content"))
.andExpect(status().isForbidden());
// create a list of values to use in add operation
List<Operation> addAccessCondition = new ArrayList<>();
List<Map<String, String>> accessConditions = new ArrayList<Map<String,String>>();
// create a list of values to use in add operation
List<Operation> addAccessCondition = new ArrayList<>();
List<Map<String, String>> accessConditions = new ArrayList<Map<String,String>>();
Map<String, String> value = new HashMap<>();
value.put("name", "administrator");
Map<String, String> value = new HashMap<>();
value.put("name", "administrator");
accessConditions.add(value);
accessConditions.add(value);
addAccessCondition.add(new AddOperation("/sections/upload/files/0/accessConditions", accessConditions));
addAccessCondition.add(new AddOperation("/sections/upload/files/0/accessConditions", accessConditions));
String patchBody = getPatchContent(addAccessCondition);
getClient(tokenSubmitter).perform(patch("/api/submission/workspaceitems/" + witem.getID())
.content(patchBody)
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.sections.upload.files[0].accessConditions[0].name",is("administrator")))
.andExpect(jsonPath("$.sections.upload.files[0].accessConditions[0].startDate",nullValue()))
.andExpect(jsonPath("$.sections.upload.files[0].accessConditions[0].endDate", nullValue()));
String patchBody = getPatchContent(addAccessCondition);
getClient(tokenSubmitter).perform(patch("/api/submission/workspaceitems/" + witem.getID())
.content(patchBody)
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.sections.upload.files[0].accessConditions[0].name",is("administrator")))
.andExpect(jsonPath("$.sections.upload.files[0].accessConditions[0].startDate",nullValue()))
.andExpect(jsonPath("$.sections.upload.files[0].accessConditions[0].endDate", nullValue()));
// verify that the patch changes have been persisted
getClient(tokenSubmitter).perform(get("/api/submission/workspaceitems/" + witem.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.sections.upload.files[0].accessConditions[0].name",is("administrator")))
.andExpect(jsonPath("$.sections.upload.files[0].accessConditions[0].startDate",nullValue()))
.andExpect(jsonPath("$.sections.upload.files[0].accessConditions[0].endDate", nullValue()));
// verify that the patch changes have been persisted
getClient(tokenSubmitter).perform(get("/api/submission/workspaceitems/" + witem.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.sections.upload.files[0].accessConditions[0].name",is("administrator")))
.andExpect(jsonPath("$.sections.upload.files[0].accessConditions[0].startDate",nullValue()))
.andExpect(jsonPath("$.sections.upload.files[0].accessConditions[0].endDate", nullValue()));
AtomicReference<Integer> idRef = new AtomicReference<Integer>();
try {
// submit the workspaceitem to start the workflow
getClient(tokenSubmitter).perform(post(BASE_REST_SERVER_URL + "/api/workflow/workflowitems")
.content("/api/submission/workspaceitems/" + witem.getID())
@@ -1976,7 +1978,7 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
.andExpect(status().isOk())
.andExpect(header().string("Accept-Ranges", "bytes"))
.andExpect(header().string("ETag", "\"" + bitstream.getChecksum() + "\""))
.andExpect(content().contentType("text/plain"))
.andExpect(content().contentType("text/plain;charset=UTF-8"))
.andExpect(content().bytes(bitstreamContent.getBytes()));
// submitter can download the bitstream
@@ -1984,7 +1986,7 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
.andExpect(status().isOk())
.andExpect(header().string("Accept-Ranges", "bytes"))
.andExpect(header().string("ETag", "\"" + bitstream.getChecksum() + "\""))
.andExpect(content().contentType("text/plain"))
.andExpect(content().contentType("text/plain;charset=UTF-8"))
.andExpect(content().bytes(bitstreamContent.getBytes()));
// others can't download the bitstream

View File

@@ -19,8 +19,8 @@ import static org.hamcrest.Matchers.nullValue;
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.fileUpload;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@@ -934,7 +934,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
String authToken = getAuthToken(eperson.getEmail(), password);
try {
// create a workspaceitem from a single bibliographic entry file explicitly in the default collection (col1)
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(bibtexFile))
// create should return 200, 201 (created) is better for single resource
.andExpect(status().isOk())
@@ -962,7 +962,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// create a workspaceitem from a single bibliographic entry file explicitly in the col2
try {
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(bibtexFile)
.param("owningCollection", col2.getID().toString()))
.andExpect(status().isOk())
@@ -1026,7 +1026,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// create workspaceitems in the default collection (col1)
AtomicReference<List<Integer>> idRef = new AtomicReference<>();
try {
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(csvFile))
// create should return 200, 201 (created) is better for single resource
.andExpect(status().isOk())
@@ -1065,7 +1065,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// create workspaceitems explicitly in the col2
try {
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(csvFile)
.param("owningCollection", col2.getID().toString()))
.andExpect(status().isOk())
@@ -1143,7 +1143,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// create workspaceitems in the default collection (col1)
try {
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(csvFile))
// create should return 200, 201 (created) is better for single resource
.andExpect(status().isOk())
@@ -1221,7 +1221,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// create workspaceitems in the default collection (col1)
try {
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(tsvFile))
// create should return 200, 201 (created) is better for single resource
.andExpect(status().isOk())
@@ -1297,7 +1297,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// create workspaceitems in the default collection (col1)
try {
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(tsvFile))
// create should return 200, 201 (created) is better for single resource
.andExpect(status().isOk())
@@ -1374,7 +1374,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
AtomicReference<List<Integer>> idRef = new AtomicReference<>();
// create workspaceitems in the default collection (col1)
try {
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(endnoteFile))
// create should return 200, 201 (created) is better for single resource
.andExpect(status().isOk())
@@ -1453,7 +1453,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// create workspaceitems in the default collection (col1)
try {
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(csvFile))
// create should return 200, 201 (created) is better for single resource
.andExpect(status().isOk())
@@ -1534,7 +1534,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// create a workspaceitem from a single bibliographic entry file explicitly in the default collection (col1)
try {
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(bibtexFile).file(pubmedFile))
// create should return 200, 201 (created) is better for single resource
.andExpect(status().isOk())
@@ -1568,7 +1568,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// create a workspaceitem from a single bibliographic entry file explicitly in the col2
try {
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(bibtexFile).file(pubmedFile)
.param("owningCollection", col2.getID().toString()))
.andExpect(status().isOk())
@@ -1640,7 +1640,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
String authToken = getAuthToken(eperson.getEmail(), password);
// create a workspaceitem from a single bibliographic entry file explicitly in the default collection (col1)
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(bibtexFile))
// create should return return a 422 because we don't allow/support bibliographic files
// that have multiple metadata records
@@ -1685,7 +1685,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// create a workspaceitem from a single bibliographic entry file explicitly in the default collection (col1)
try {
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(pubmedFile))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.workspaceitems[0].sections.traditionalpageone['dc.title'][0].value",
@@ -1716,7 +1716,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// create a workspaceitem from a single bibliographic entry file explicitly in the col2
try {
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(pubmedFile)
.param("owningCollection", col2.getID().toString()))
.andExpect(status().isOk())
@@ -1776,7 +1776,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
context.restoreAuthSystemState();
// create a workspaceitem
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(pdfFile))
// create should return 200, 201 (created) is better for single resource
.andExpect(status().isOk())
@@ -3491,7 +3491,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
context.restoreAuthSystemState();
// upload the file in our workspaceitem
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems/" + witem.getID())
getClient(authToken).perform(multipart("/api/submission/workspaceitems/" + witem.getID())
.file(pdfFile))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value",
@@ -3534,7 +3534,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
context.restoreAuthSystemState();
// upload the file in our workspaceitem
getClient().perform(fileUpload("/api/submission/workspaceitems/" + witem.getID())
getClient().perform(multipart("/api/submission/workspaceitems/" + witem.getID())
.file(pdfFile))
.andExpect(status().isUnauthorized());
@@ -3580,7 +3580,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// upload the file in our workspaceitem
String authToken = getAuthToken(eperson2.getEmail(), "qwerty02");
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems/" + witem.getID())
getClient(authToken).perform(multipart("/api/submission/workspaceitems/" + witem.getID())
.file(pdfFile))
.andExpect(status().isForbidden());
@@ -3617,7 +3617,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
context.restoreAuthSystemState();
// upload the file in our workspaceitem
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems/" + witem.getID())
getClient(authToken).perform(multipart("/api/submission/workspaceitems/" + witem.getID())
.file(pdfFile))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value",
@@ -4386,7 +4386,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
try {
// adding a bibtex file with a single entry should automatically put the metadata in the bibtex file into
// the item
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems/" + witem.getID())
getClient(authToken).perform(multipart("/api/submission/workspaceitems/" + witem.getID())
.file(bibtexFile))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.sections.traditionalpageone['dc.title'][0].value",
@@ -4403,7 +4403,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
is("bibtex-test.bib")));
// do again over a submission that already has a title, the manual input should be preserved
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems/" + witem2.getID())
getClient(authToken).perform(multipart("/api/submission/workspaceitems/" + witem2.getID())
.file(bibtexFile))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.sections.traditionalpageone['dc.title'][0].value",
@@ -4944,7 +4944,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// upload file and verify response
getClient(authToken)
.perform(fileUpload("/api/submission/workspaceitems/" + wItem.getID()).file(pdfFile))
.perform(multipart("/api/submission/workspaceitems/" + wItem.getID()).file(pdfFile))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.sections.upload.files[0].accessConditions", empty()));
@@ -5415,7 +5415,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
context.restoreAuthSystemState();
try {
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
getClient(authToken).perform(multipart("/api/submission/workspaceitems")
.file(pdfFile))
.andExpect(status().is(500));
} finally {
@@ -6976,7 +6976,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.andExpect(status().isOk())
.andExpect(header().string("Accept-Ranges", "bytes"))
.andExpect(header().string("ETag", "\"" + bitstream.getChecksum() + "\""))
.andExpect(content().contentType("text/plain"))
.andExpect(content().contentType("text/plain;charset=UTF-8"))
.andExpect(content().bytes(bitstreamContent.getBytes()));
// others can't download the bitstream
@@ -7014,7 +7014,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.andExpect(status().isOk())
.andExpect(header().string("Accept-Ranges", "bytes"))
.andExpect(header().string("ETag", "\"" + bitstream.getChecksum() + "\""))
.andExpect(content().contentType("text/plain"))
.andExpect(content().contentType("text/plain;charset=UTF-8"))
.andExpect(content().bytes(bitstreamContent.getBytes()));
// others can't download the bitstream
@@ -7171,7 +7171,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.andExpect(jsonPath("$.sections.upload.files[1]").doesNotExist());
// upload second file
getClient(tokenEPerson).perform(fileUpload("/api/submission/workspaceitems/" + wItem.getID())
getClient(tokenEPerson).perform(multipart("/api/submission/workspaceitems/" + wItem.getID())
.file(xmlFile))
.andExpect(status().isCreated());

View File

@@ -9,7 +9,7 @@ package org.dspace.app.rest.csv;
import static com.jayway.jsonpath.JsonPath.read;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -76,9 +76,8 @@ public class CsvExportIT extends AbstractControllerIntegrationTest {
String token = getAuthToken(admin.getEmail(), password);
getClient(token)
.perform(fileUpload("/api/system/scripts/metadata-export/processes")
.param("properties",
new Gson().toJson(list)))
.perform(multipart("/api/system/scripts/metadata-export/processes")
.param("properties", new Gson().toJson(list)))
.andExpect(status().isAccepted())
.andExpect(jsonPath("$", is(
ProcessMatcher.matchProcess("metadata-export",
@@ -128,9 +127,8 @@ public class CsvExportIT extends AbstractControllerIntegrationTest {
String token = getAuthToken(admin.getEmail(), password);
getClient(token)
.perform(fileUpload("/api/system/scripts/metadata-export/processes")
.param("properties",
new Gson().toJson(list)))
.perform(multipart("/api/system/scripts/metadata-export/processes")
.param("properties", new Gson().toJson(list)))
.andExpect(status().isAccepted())
.andExpect(jsonPath("$", is(
ProcessMatcher.matchProcess("metadata-export",

View File

@@ -13,8 +13,8 @@ import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -284,8 +284,8 @@ public class CsvImportIT extends AbstractEntityIntegrationTest {
String token = getAuthToken(admin.getEmail(), password);
getClient(token)
.perform(fileUpload("/api/system/scripts/metadata-import/processes").file(bitstreamFile)
.param("properties",
.perform(multipart("/api/system/scripts/metadata-import/processes").file(bitstreamFile)
.param("properties",
new Gson().toJson(list)))
.andExpect(status().isAccepted())
.andDo(result -> idRef
@@ -344,7 +344,7 @@ public class CsvImportIT extends AbstractEntityIntegrationTest {
String token = getAuthToken(admin.getEmail(), password);
getClient(token)
.perform(fileUpload("/api/system/scripts/metadata-import/processes").file(bitstreamFile)
.perform(multipart("/api/system/scripts/metadata-import/processes").file(bitstreamFile)
.param("properties",
new Gson().toJson(list)))
.andExpect(status().isAccepted())

View File

@@ -13,6 +13,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
@@ -89,8 +90,9 @@ public class AbstractControllerIntegrationTest extends AbstractIntegrationTestWi
public static final String REST_SERVER_URL = "http://localhost/api/";
public static final String BASE_REST_SERVER_URL = "http://localhost";
// Our standard/expected content type
protected MediaType contentType = new MediaType(MediaTypes.HAL_JSON.getType(),
MediaTypes.HAL_JSON.getSubtype());
MediaTypes.HAL_JSON.getSubtype(), StandardCharsets.UTF_8);
protected MediaType textUriContentType = RestMediaTypes.TEXT_URI_LIST;

View File

@@ -9,7 +9,7 @@ package org.dspace.curate;
import static com.jayway.jsonpath.JsonPath.read;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -87,9 +87,8 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest {
// Request with -t <invalidTaskOption>
getClient(token)
.perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data")
.param("properties",
new Gson().toJson(list)))
.perform(multipart(CURATE_SCRIPT_ENDPOINT)
.param("properties", new Gson().toJson(list)))
// Illegal Argument Exception
.andExpect(status().isBadRequest());
}
@@ -109,9 +108,8 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest {
// Request with missing required -i <handle>
getClient(token)
.perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data")
.param("properties",
new Gson().toJson(list)))
.perform(multipart(CURATE_SCRIPT_ENDPOINT)
.param("properties", new Gson().toJson(list)))
// Illegal Argument Exception
.andExpect(status().isBadRequest());
}
@@ -132,9 +130,8 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest {
// Request with missing required -i <handle>
getClient(token)
.perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data")
.param("properties",
new Gson().toJson(list)))
.perform(multipart(CURATE_SCRIPT_ENDPOINT)
.param("properties", new Gson().toJson(list)))
// Illegal Argument Exception
.andExpect(status().isBadRequest());
}
@@ -173,9 +170,8 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest {
// Request without -t <task> or -T <taskFile> (and no -q <queue>)
getClient(token)
.perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data")
.param("properties",
new Gson().toJson(list)))
.perform(multipart(CURATE_SCRIPT_ENDPOINT)
.param("properties", new Gson().toJson(list)))
// Illegal Argument Exception
.andExpect(status().isBadRequest());
}
@@ -196,9 +192,8 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest {
// Request with invalid -s <scope>; must be object, curation or open
getClient(token)
.perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data")
.param("properties",
new Gson().toJson(list)))
.perform(multipart(CURATE_SCRIPT_ENDPOINT)
.param("properties", new Gson().toJson(list)))
// Illegal Argument Exception
.andExpect(status().isBadRequest());
}
@@ -219,9 +214,8 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest {
// Request with invalid -s <scope>; must be object, curation or open
getClient(token)
.perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data")
.param("properties",
new Gson().toJson(list)))
.perform(multipart(CURATE_SCRIPT_ENDPOINT)
.param("properties", new Gson().toJson(list)))
// Illegal Argument Exception
.andExpect(status().isBadRequest());
}
@@ -262,9 +256,8 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest {
try {
getClient(token)
.perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data")
.param("properties",
new Gson().toJson(list)))
.perform(multipart(CURATE_SCRIPT_ENDPOINT)
.param("properties", new Gson().toJson(list)))
.andExpect(status().isAccepted())
.andExpect(jsonPath("$", is(
ProcessMatcher.matchProcess("curate",
@@ -314,9 +307,8 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest {
try {
getClient(token)
.perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data")
.param("properties",
new Gson().toJson(list)))
.perform(multipart(CURATE_SCRIPT_ENDPOINT)
.param("properties", new Gson().toJson(list)))
.andExpect(status().isAccepted())
.andExpect(jsonPath("$", is(
ProcessMatcher.matchProcess("curate",
@@ -366,9 +358,8 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest {
try {
getClient(token)
.perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data")
.param("properties",
new Gson().toJson(list)))
.perform(multipart(CURATE_SCRIPT_ENDPOINT)
.param("properties", new Gson().toJson(list)))
.andExpect(jsonPath("$", is(
ProcessMatcher.matchProcess("curate",
String.valueOf(admin.getID()), parameters,

View File

@@ -113,20 +113,6 @@
<dependency>
<groupId>org.dspace.modules</groupId>
<artifactId>additions</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- DSpace REST Webapp -->

38
pom.xml
View File

@@ -13,15 +13,15 @@
<organization>
<name>LYRASIS</name>
<url>http://www.dspace.org</url>
<url>https://dspace.org</url>
</organization>
<properties>
<!--=== GENERAL / DSPACE-API DEPENDENCIES ===-->
<java.version>11</java.version>
<spring.version>5.2.5.RELEASE</spring.version>
<spring-boot.version>2.2.6.RELEASE</spring-boot.version>
<spring-security.version>5.2.2.RELEASE</spring-security.version> <!-- sync with version used by spring-boot-->
<spring.version>5.3.18</spring.version>
<spring-boot.version>2.6.6</spring-boot.version>
<spring-security.version>5.6.2</spring-security.version> <!-- sync with version used by spring-boot-->
<hibernate.version>5.6.5.Final</hibernate.version>
<hibernate-validator.version>6.0.23.Final</hibernate-validator.version>
<postgresql.driver.version>42.3.3</postgresql.driver.version>
@@ -46,10 +46,8 @@
<bouncycastle.version>1.70</bouncycastle.version>
<!--=== SERVER WEBAPP DEPENDENCIES ===-->
<!-- Spring Data REST HAL Browser (used by Server webapp) -->
<spring-hal-browser.version>3.2.6.RELEASE</spring-hal-browser.version>
<!-- Library for reading JSON documents: https://github.com/json-path/JsonPath (used by Server webapp) -->
<json-path.version>2.4.0</json-path.version>
<json-path.version>2.6.0</json-path.version>
<!-- Library for managing JSON Web Tokens (JWT): https://bitbucket.org/connect2id/nimbus-jose-jwt/wiki/Home
(used by Server webapp) -->
<nimbus-jose-jwt.version>7.9</nimbus-jose-jwt.version>
@@ -1108,13 +1106,6 @@
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.driver.version}</version>
<exclusions>
<!-- Use version provided by Solr -->
<exclusion>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@@ -1738,9 +1729,9 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
<version>31.0.1-jre</version>
<exclusions>
<!-- Use version provided by Solr -->
<!-- Use version provided by Solr / Postgres -->
<exclusion>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
@@ -1752,6 +1743,21 @@
<artifactId>xom</artifactId>
<version>1.2.5</version>
</dependency>
<!-- json-path is needed by Spring HATEOAS -->
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>${json-path.version}</version>
</dependency>
<!-- json-path-assert is just needed by tests -->
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<version>${json-path.version}</version>
<scope>test</scope>
</dependency>
<!-- JAXB API and implementation (no longer bundled as of Java 11) -->
<dependency>
<groupId>javax.xml.bind</groupId>