mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 18:14:26 +00:00
Merge pull request #13 from atmire/w2p-58898_place-column-calculation-error-prepare-merge
W2p 58898 place column calculation error prepare merge
This commit is contained in:
@@ -3,3 +3,4 @@
|
|||||||
.settings/
|
.settings/
|
||||||
*/target/
|
*/target/
|
||||||
dspace/modules/*/target/
|
dspace/modules/*/target/
|
||||||
|
Dockerfile.*
|
24
Dockerfile.dependencies
Normal file
24
Dockerfile.dependencies
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# This image will be published as dspace/dspace-dependencies
|
||||||
|
# The purpose of this image is to make the build for dspace/dspace run faster
|
||||||
|
|
||||||
|
# Step 1 - Run Maven Build
|
||||||
|
FROM maven:3-jdk-8 as build
|
||||||
|
ARG TARGET_DIR=dspace-installer
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN useradd dspace \
|
||||||
|
&& mkdir /home/dspace \
|
||||||
|
&& chown -Rv dspace: /home/dspace
|
||||||
|
USER dspace
|
||||||
|
|
||||||
|
# Copy the DSpace source code into the workdir (excluding .dockerignore contents)
|
||||||
|
ADD --chown=dspace . /app/
|
||||||
|
COPY dspace/src/main/docker/local.cfg /app/local.cfg
|
||||||
|
|
||||||
|
# Trigger the installation of all maven dependencies
|
||||||
|
# Clean up the built artifacts in the same step to keep the docker image small
|
||||||
|
RUN mvn package && mvn clean
|
||||||
|
|
||||||
|
# Clear the contents of the /app directory so no artifacts are left when dspace:dspace is built
|
||||||
|
USER root
|
||||||
|
RUN rm -rf /app/*
|
@@ -9,20 +9,31 @@
|
|||||||
# - default tag for branch: dspace/dspace: dspace/dspace:dspace-7_x-jdk8
|
# - default tag for branch: dspace/dspace: dspace/dspace:dspace-7_x-jdk8
|
||||||
|
|
||||||
# Step 1 - Run Maven Build
|
# Step 1 - Run Maven Build
|
||||||
FROM maven:3-jdk-8 as build
|
FROM dspace/dspace-dependencies:dspace-7_x as build
|
||||||
|
ARG TARGET_DIR=dspace-installer
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# The dspace-install directory will be written to /install
|
||||||
|
RUN mkdir /install \
|
||||||
|
&& chown -Rv dspace: /install \
|
||||||
|
&& chown -Rv dspace: /app
|
||||||
|
|
||||||
|
USER dspace
|
||||||
|
|
||||||
# Copy the DSpace source code into the workdir (excluding .dockerignore contents)
|
# Copy the DSpace source code into the workdir (excluding .dockerignore contents)
|
||||||
ADD . /app/
|
ADD --chown=dspace . /app/
|
||||||
COPY dspace/src/main/docker/local.cfg /app/local.cfg
|
COPY dspace/src/main/docker/local.cfg /app/local.cfg
|
||||||
|
|
||||||
RUN mvn package
|
# Build DSpace. Copy the dspace-install directory to /install. Clean up the build to keep the docker image small
|
||||||
|
RUN mvn package && \
|
||||||
|
mv /app/dspace/target/${TARGET_DIR}/* /install && \
|
||||||
|
mvn clean
|
||||||
|
|
||||||
# Step 2 - Run Ant Deploy
|
# Step 2 - Run Ant Deploy
|
||||||
FROM tomcat:8-jre8 as ant_build
|
FROM tomcat:8-jre8 as ant_build
|
||||||
ARG TARGET_DIR=dspace-installer
|
ARG TARGET_DIR=dspace-installer
|
||||||
COPY --from=build /app /dspace-src
|
COPY --from=build /install /dspace-src
|
||||||
WORKDIR /dspace-src/dspace/target/${TARGET_DIR}
|
WORKDIR /dspace-src
|
||||||
|
|
||||||
# Create the initial install deployment using ANT
|
# Create the initial install deployment using ANT
|
||||||
ENV ANT_VERSION 1.10.5
|
ENV ANT_VERSION 1.10.5
|
||||||
@@ -32,23 +43,15 @@ ENV PATH $ANT_HOME/bin:$PATH
|
|||||||
RUN mkdir $ANT_HOME && \
|
RUN mkdir $ANT_HOME && \
|
||||||
wget -qO- "https://www.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $ANT_HOME
|
wget -qO- "https://www.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $ANT_HOME
|
||||||
|
|
||||||
RUN ant update_configs update_code update_webapps update_solr_indexes
|
RUN ant init_installation update_configs update_code update_webapps update_solr_indexes
|
||||||
|
|
||||||
# Step 3 - Run tomcat
|
# Step 3 - Run tomcat
|
||||||
# Create a new tomcat image that does not retain the the build directory contents
|
# Create a new tomcat image that does not retain the the build directory contents
|
||||||
FROM tomcat:8-jre8
|
FROM tomcat:8-jre8
|
||||||
COPY --from=ant_build /dspace /dspace
|
ENV DSPACE_INSTALL=/dspace
|
||||||
|
COPY --from=ant_build /dspace $DSPACE_INSTALL
|
||||||
EXPOSE 8080 8009
|
EXPOSE 8080 8009
|
||||||
|
|
||||||
# Ant will be embedded in the final container to allow additional deployments
|
|
||||||
ENV ANT_VERSION 1.10.5
|
|
||||||
ENV ANT_HOME /tmp/ant-$ANT_VERSION
|
|
||||||
ENV PATH $ANT_HOME/bin:$PATH
|
|
||||||
|
|
||||||
RUN mkdir $ANT_HOME && \
|
|
||||||
wget -qO- "https://www.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $ANT_HOME
|
|
||||||
|
|
||||||
ENV DSPACE_INSTALL=/dspace
|
|
||||||
ENV JAVA_OPTS=-Xmx2000m
|
ENV JAVA_OPTS=-Xmx2000m
|
||||||
|
|
||||||
RUN ln -s $DSPACE_INSTALL/webapps/solr /usr/local/tomcat/webapps/solr && \
|
RUN ln -s $DSPACE_INSTALL/webapps/solr /usr/local/tomcat/webapps/solr && \
|
||||||
|
@@ -5,28 +5,35 @@
|
|||||||
# - tomcat:8-jre8
|
# - tomcat:8-jre8
|
||||||
# - ANT 1.10.5
|
# - ANT 1.10.5
|
||||||
# - maven:3-jdk-8
|
# - maven:3-jdk-8
|
||||||
# - note: expose /solr to any host; provide /rest over http
|
# - note:
|
||||||
# - default tag for branch: dspace/dspace: dspace/dspace:dspace-7_x-jdk8-test
|
# - default tag for branch: dspace/dspace: dspace/dspace:dspace-7_x-jdk8-test
|
||||||
|
|
||||||
# Step 1 - Run Maven Build
|
# Step 1 - Run Maven Build
|
||||||
FROM maven:3-jdk-8 as build
|
FROM dspace/dspace-dependencies:dspace-7_x as build
|
||||||
|
ARG TARGET_DIR=dspace-installer
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# The dspace-install directory will be written to /install
|
||||||
|
RUN mkdir /install \
|
||||||
|
&& chown -Rv dspace: /install \
|
||||||
|
&& chown -Rv dspace: /app
|
||||||
|
|
||||||
|
USER dspace
|
||||||
|
|
||||||
# Copy the DSpace source code into the workdir (excluding .dockerignore contents)
|
# Copy the DSpace source code into the workdir (excluding .dockerignore contents)
|
||||||
ADD . /app/
|
ADD --chown=dspace . /app/
|
||||||
COPY dspace/src/main/docker/local.cfg /app/local.cfg
|
COPY dspace/src/main/docker/local.cfg /app/local.cfg
|
||||||
|
|
||||||
# Provide web.xml overrides to make webapps easier to test
|
# Build DSpace. Copy the dspace-install directory to /install. Clean up the build to keep the docker image small
|
||||||
COPY dspace/src/main/docker/test/solr_web.xml /app/dspace-solr/src/main/webapp/WEB-INF/web.xml
|
RUN mvn package && \
|
||||||
COPY dspace/src/main/docker/test/rest_web.xml /app/dspace-rest/src/main/webapp/WEB-INF/web.xml
|
mv /app/dspace/target/${TARGET_DIR}/* /install && \
|
||||||
|
mvn clean
|
||||||
RUN mvn package
|
|
||||||
|
|
||||||
# Step 2 - Run Ant Deploy
|
# Step 2 - Run Ant Deploy
|
||||||
FROM tomcat:8-jre8 as ant_build
|
FROM tomcat:8-jre8 as ant_build
|
||||||
ARG TARGET_DIR=dspace-installer
|
ARG TARGET_DIR=dspace-installer
|
||||||
COPY --from=build /app /dspace-src
|
COPY --from=build /install /dspace-src
|
||||||
WORKDIR /dspace-src/dspace/target/${TARGET_DIR}
|
WORKDIR /dspace-src
|
||||||
|
|
||||||
# Create the initial install deployment using ANT
|
# Create the initial install deployment using ANT
|
||||||
ENV ANT_VERSION 1.10.5
|
ENV ANT_VERSION 1.10.5
|
||||||
@@ -36,23 +43,15 @@ ENV PATH $ANT_HOME/bin:$PATH
|
|||||||
RUN mkdir $ANT_HOME && \
|
RUN mkdir $ANT_HOME && \
|
||||||
wget -qO- "https://www.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $ANT_HOME
|
wget -qO- "https://www.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $ANT_HOME
|
||||||
|
|
||||||
RUN ant update_configs update_code update_webapps update_solr_indexes
|
RUN ant init_installation update_configs update_code update_webapps update_solr_indexes
|
||||||
|
|
||||||
# Step 3 - Run tomcat
|
# Step 3 - Run tomcat
|
||||||
# Create a new tomcat image that does not retain the the build directory contents
|
# Create a new tomcat image that does not retain the the build directory contents
|
||||||
FROM tomcat:8-jre8
|
FROM tomcat:8-jre8
|
||||||
COPY --from=ant_build /dspace /dspace
|
ENV DSPACE_INSTALL=/dspace
|
||||||
|
COPY --from=ant_build /dspace $DSPACE_INSTALL
|
||||||
EXPOSE 8080 8009
|
EXPOSE 8080 8009
|
||||||
|
|
||||||
# Ant will be embedded in the final container to allow additional deployments
|
|
||||||
ENV ANT_VERSION 1.10.5
|
|
||||||
ENV ANT_HOME /tmp/ant-$ANT_VERSION
|
|
||||||
ENV PATH $ANT_HOME/bin:$PATH
|
|
||||||
|
|
||||||
RUN mkdir $ANT_HOME && \
|
|
||||||
wget -qO- "https://www.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $ANT_HOME
|
|
||||||
|
|
||||||
ENV DSPACE_INSTALL=/dspace
|
|
||||||
ENV JAVA_OPTS=-Xmx2000m
|
ENV JAVA_OPTS=-Xmx2000m
|
||||||
|
|
||||||
RUN ln -s $DSPACE_INSTALL/webapps/solr /usr/local/tomcat/webapps/solr && \
|
RUN ln -s $DSPACE_INSTALL/webapps/solr /usr/local/tomcat/webapps/solr && \
|
||||||
@@ -62,3 +61,9 @@ RUN ln -s $DSPACE_INSTALL/webapps/solr /usr/local/tomcat/webapps/solr
|
|||||||
ln -s $DSPACE_INSTALL/webapps/rdf /usr/local/tomcat/webapps/rdf && \
|
ln -s $DSPACE_INSTALL/webapps/rdf /usr/local/tomcat/webapps/rdf && \
|
||||||
ln -s $DSPACE_INSTALL/webapps/sword /usr/local/tomcat/webapps/sword && \
|
ln -s $DSPACE_INSTALL/webapps/sword /usr/local/tomcat/webapps/sword && \
|
||||||
ln -s $DSPACE_INSTALL/webapps/swordv2 /usr/local/tomcat/webapps/swordv2
|
ln -s $DSPACE_INSTALL/webapps/swordv2 /usr/local/tomcat/webapps/swordv2
|
||||||
|
|
||||||
|
COPY dspace/src/main/docker/test/solr_web.xml $DSPACE_INSTALL/webapps/solr/WEB-INF/web.xml
|
||||||
|
COPY dspace/src/main/docker/test/rest_web.xml $DSPACE_INSTALL/webapps/rest/WEB-INF/web.xml
|
||||||
|
|
||||||
|
RUN sed -i -e "s|\${dspace.dir}|$DSPACE_INSTALL|" $DSPACE_INSTALL/webapps/solr/WEB-INF/web.xml && \
|
||||||
|
sed -i -e "s|\${dspace.dir}|$DSPACE_INSTALL|" $DSPACE_INSTALL/webapps/rest/WEB-INF/web.xml
|
||||||
|
51
README.md
51
README.md
@@ -79,6 +79,57 @@ install, upgrade, customize or host DSpace, then we recommend getting in touch w
|
|||||||
|
|
||||||
The DSpace Issue Tracker can be found at: https://jira.duraspace.org/projects/DS/summary
|
The DSpace Issue Tracker can be found at: https://jira.duraspace.org/projects/DS/summary
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
|
||||||
|
By default, in DSpace, Unit Tests and Integration Tests are disabled. However, they are
|
||||||
|
run automatically by [Travis CI](https://travis-ci.org/DSpace/DSpace/) for all Pull Requests and code commits.
|
||||||
|
|
||||||
|
* How to run both Unit Tests (via `maven-surefire-plugin`) and Integration Tests (via `maven-failsafe-plugin`):
|
||||||
|
```
|
||||||
|
# NOTE: while "mvn test" runs Unit Tests,
|
||||||
|
# Integration Tests only run for "verify" or "install" phases
|
||||||
|
mvn clean install -Dmaven.test.skip=false -DskipITs=false
|
||||||
|
```
|
||||||
|
* How to run just Unit Tests:
|
||||||
|
```
|
||||||
|
mvn clean test -Dmaven.test.skip=false
|
||||||
|
```
|
||||||
|
* How to run a *single* Unit Test
|
||||||
|
```
|
||||||
|
# Run all tests in a specific test class
|
||||||
|
# NOTE: testClassName is just the class name, do not include package
|
||||||
|
mvn clean test -Dmaven.test.skip=false -Dtest=[testClassName]
|
||||||
|
|
||||||
|
# Run one test method in a specific test class
|
||||||
|
mvn clean test -Dmaven.test.skip=false -Dtest=[testClassName]#[testMethodName]
|
||||||
|
```
|
||||||
|
* How to run Integration Tests (requires running Unit tests too)
|
||||||
|
```
|
||||||
|
mvn clean verify -Dmaven.test.skip=false -DskipITs=false
|
||||||
|
```
|
||||||
|
* How to run a *single* Integration Test (requires running Unit tests too)
|
||||||
|
```
|
||||||
|
# Run all integration tests in a specific test class
|
||||||
|
# NOTE: Integration Tests only run for "verify" or "install" phases
|
||||||
|
# NOTE: testClassName is just the class name, do not include package
|
||||||
|
mvn clean verify -Dmaven.test.skip=false -DskipITs=false -Dit.test=[testClassName]
|
||||||
|
|
||||||
|
# Run one test method in a specific test class
|
||||||
|
mvn clean verify -Dmaven.test.skip=false -DskipITs=false -Dit.test=[testClassName]#[testMethodName]
|
||||||
|
```
|
||||||
|
* How to run only tests of a specific DSpace module
|
||||||
|
```
|
||||||
|
# Before you can run only one module's tests, other modules may need installing into your ~/.m2
|
||||||
|
cd [dspace-src]
|
||||||
|
mvn clean install
|
||||||
|
|
||||||
|
# Then, move into a module subdirectory, and run the test command
|
||||||
|
cd [dspace-src]/dspace-spring-rest
|
||||||
|
# Choose your test command from the lists above
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
DSpace source code is freely available under a standard [BSD 3-Clause license](https://opensource.org/licenses/BSD-3-Clause).
|
DSpace source code is freely available under a standard [BSD 3-Clause license](https://opensource.org/licenses/BSD-3-Clause).
|
||||||
|
@@ -14,6 +14,7 @@ import org.apache.logging.log4j.Logger;
|
|||||||
import org.dspace.authorize.AuthorizeException;
|
import org.dspace.authorize.AuthorizeException;
|
||||||
import org.dspace.authorize.service.AuthorizeService;
|
import org.dspace.authorize.service.AuthorizeService;
|
||||||
import org.dspace.content.dao.MetadataSchemaDAO;
|
import org.dspace.content.dao.MetadataSchemaDAO;
|
||||||
|
import org.dspace.content.service.MetadataFieldService;
|
||||||
import org.dspace.content.service.MetadataSchemaService;
|
import org.dspace.content.service.MetadataSchemaService;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
import org.dspace.core.LogManager;
|
import org.dspace.core.LogManager;
|
||||||
@@ -33,6 +34,9 @@ public class MetadataSchemaServiceImpl implements MetadataSchemaService {
|
|||||||
*/
|
*/
|
||||||
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(MetadataSchemaServiceImpl.class);
|
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(MetadataSchemaServiceImpl.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected MetadataFieldService metadataFieldService;
|
||||||
|
|
||||||
@Autowired(required = true)
|
@Autowired(required = true)
|
||||||
protected AuthorizeService authorizeService;
|
protected AuthorizeService authorizeService;
|
||||||
|
|
||||||
@@ -115,10 +119,14 @@ public class MetadataSchemaServiceImpl implements MetadataSchemaService {
|
|||||||
"Only administrators may modify the metadata registry");
|
"Only administrators may modify the metadata registry");
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(LogManager.getHeader(context, "delete_metadata_schema",
|
for (MetadataField metadataField : metadataFieldService.findAllInSchema(context, metadataSchema)) {
|
||||||
"metadata_schema_id=" + metadataSchema.getID()));
|
metadataFieldService.delete(context, metadataField);
|
||||||
|
}
|
||||||
|
|
||||||
metadataSchemaDAO.delete(context, metadataSchema);
|
metadataSchemaDAO.delete(context, metadataSchema);
|
||||||
|
|
||||||
|
log.info(LogManager.getHeader(context, "delete_metadata_schema",
|
||||||
|
"metadata_schema_id=" + metadataSchema.getID()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dspace.ctask.test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.dspace.content.DSpaceObject;
|
||||||
|
import org.dspace.curate.AbstractCurationTask;
|
||||||
|
import org.dspace.curate.Curator;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Curation task which simply reports its invocation without changing anything.
|
||||||
|
* Meant for testing.
|
||||||
|
*
|
||||||
|
* @author mhwood
|
||||||
|
*/
|
||||||
|
public class WorkflowReportTest
|
||||||
|
extends AbstractCurationTask {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(WorkflowReportTest.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int perform(DSpaceObject dso)
|
||||||
|
throws IOException {
|
||||||
|
LOG.info("Class {} as task {} received 'perform' for object {}",
|
||||||
|
WorkflowReportTest.class.getSimpleName(), taskId, dso);
|
||||||
|
curator.report(String.format(
|
||||||
|
"Class %s as task %s received 'perform' for object %s%n",
|
||||||
|
WorkflowReportTest.class.getSimpleName(), taskId, dso));
|
||||||
|
return Curator.CURATE_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
@@ -9,6 +9,10 @@ package org.dspace.curate;
|
|||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.io.Writer;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -18,6 +22,7 @@ import org.apache.commons.cli.CommandLineParser;
|
|||||||
import org.apache.commons.cli.HelpFormatter;
|
import org.apache.commons.cli.HelpFormatter;
|
||||||
import org.apache.commons.cli.Options;
|
import org.apache.commons.cli.Options;
|
||||||
import org.apache.commons.cli.PosixParser;
|
import org.apache.commons.cli.PosixParser;
|
||||||
|
import org.apache.commons.io.output.NullOutputStream;
|
||||||
import org.dspace.content.factory.ContentServiceFactory;
|
import org.dspace.content.factory.ContentServiceFactory;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
import org.dspace.core.factory.CoreServiceFactory;
|
import org.dspace.core.factory.CoreServiceFactory;
|
||||||
@@ -57,7 +62,9 @@ public class CurationCli {
|
|||||||
options.addOption("e", "eperson", true,
|
options.addOption("e", "eperson", true,
|
||||||
"email address of curating eperson");
|
"email address of curating eperson");
|
||||||
options.addOption("r", "reporter", true,
|
options.addOption("r", "reporter", true,
|
||||||
"reporter to manage results - use '-' to report to console. If absent, no reporting");
|
"relative or absolute path to the desired report file. "
|
||||||
|
+ "Use '-' to report to console. "
|
||||||
|
+ "If absent, no reporting");
|
||||||
options.addOption("s", "scope", true,
|
options.addOption("s", "scope", true,
|
||||||
"transaction scope to impose: use 'object', 'curation', or 'open'. If absent, 'open' " +
|
"transaction scope to impose: use 'object', 'curation', or 'open'. If absent, 'open' " +
|
||||||
"applies");
|
"applies");
|
||||||
@@ -165,9 +172,17 @@ public class CurationCli {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Curator curator = new Curator();
|
Curator curator = new Curator();
|
||||||
if (reporterName != null) {
|
OutputStream reporter;
|
||||||
curator.setReporter(reporterName);
|
if (null == reporterName) {
|
||||||
|
reporter = new NullOutputStream();
|
||||||
|
} else if ("-".equals(reporterName)) {
|
||||||
|
reporter = System.out;
|
||||||
|
} else {
|
||||||
|
reporter = new PrintStream(reporterName);
|
||||||
}
|
}
|
||||||
|
Writer reportWriter = new OutputStreamWriter(reporter);
|
||||||
|
curator.setReporter(reportWriter);
|
||||||
|
|
||||||
if (scope != null) {
|
if (scope != null) {
|
||||||
Curator.TxScope txScope = Curator.TxScope.valueOf(scope.toUpperCase());
|
Curator.TxScope txScope = Curator.TxScope.valueOf(scope.toUpperCase());
|
||||||
curator.setTransactionScope(txScope);
|
curator.setTransactionScope(txScope);
|
||||||
|
@@ -15,6 +15,7 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.dspace.content.Collection;
|
import org.dspace.content.Collection;
|
||||||
import org.dspace.content.Community;
|
import org.dspace.content.Community;
|
||||||
@@ -69,16 +70,12 @@ public class Curator {
|
|||||||
INTERACTIVE, BATCH, ANY
|
INTERACTIVE, BATCH, ANY
|
||||||
}
|
}
|
||||||
|
|
||||||
;
|
|
||||||
|
|
||||||
// transaction scopes
|
// transaction scopes
|
||||||
public static enum TxScope {
|
public static enum TxScope {
|
||||||
OBJECT, CURATION, OPEN
|
OBJECT, CURATION, OPEN
|
||||||
}
|
}
|
||||||
|
|
||||||
;
|
private static final Logger log = LogManager.getLogger();
|
||||||
|
|
||||||
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(Curator.class);
|
|
||||||
|
|
||||||
protected static final ThreadLocal<Context> curationCtx = new ThreadLocal<>();
|
protected static final ThreadLocal<Context> curationCtx = new ThreadLocal<>();
|
||||||
|
|
||||||
@@ -86,7 +83,7 @@ public class Curator {
|
|||||||
protected Map<String, TaskRunner> trMap = new HashMap<>();
|
protected Map<String, TaskRunner> trMap = new HashMap<>();
|
||||||
protected List<String> perfList = new ArrayList<>();
|
protected List<String> perfList = new ArrayList<>();
|
||||||
protected TaskQueue taskQ = null;
|
protected TaskQueue taskQ = null;
|
||||||
protected String reporter = null;
|
protected Appendable reporter = null;
|
||||||
protected Invoked iMode = null;
|
protected Invoked iMode = null;
|
||||||
protected TaskResolver resolver = new TaskResolver();
|
protected TaskResolver resolver = new TaskResolver();
|
||||||
protected TxScope txScope = TxScope.OPEN;
|
protected TxScope txScope = TxScope.OPEN;
|
||||||
@@ -193,7 +190,7 @@ public class Curator {
|
|||||||
* causes reporting to standard out.
|
* causes reporting to standard out.
|
||||||
* @return return self (Curator instance) with reporter set
|
* @return return self (Curator instance) with reporter set
|
||||||
*/
|
*/
|
||||||
public Curator setReporter(String reporter) {
|
public Curator setReporter(Appendable reporter) {
|
||||||
this.reporter = reporter;
|
this.reporter = reporter;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -346,9 +343,10 @@ public class Curator {
|
|||||||
* @param message the message to output to the reporting stream.
|
* @param message the message to output to the reporting stream.
|
||||||
*/
|
*/
|
||||||
public void report(String message) {
|
public void report(String message) {
|
||||||
// Stub for now
|
try {
|
||||||
if ("-".equals(reporter)) {
|
reporter.append(message);
|
||||||
System.out.println(message);
|
} catch (IOException ex) {
|
||||||
|
log.error("Task reporting failure", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
88
dspace-api/src/main/java/org/dspace/curate/FileReporter.java
Normal file
88
dspace-api/src/main/java/org/dspace/curate/FileReporter.java
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dspace.curate;
|
||||||
|
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.dspace.utils.DSpace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save a curation report to a unique file in the reports directory.
|
||||||
|
* Reports are named by the date and time of day, for example:
|
||||||
|
* "curation-20180916T113903045.report".
|
||||||
|
*
|
||||||
|
* @author mhwood
|
||||||
|
*/
|
||||||
|
public class FileReporter
|
||||||
|
implements Reporter {
|
||||||
|
private final Writer writer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a writer to a file in a directory named by the configuration
|
||||||
|
* property {@code report.dir}, or in {@code [DSpace]/reports} if not
|
||||||
|
* configured.
|
||||||
|
*
|
||||||
|
* @throws IOException if there is a problem with the file path.
|
||||||
|
*/
|
||||||
|
public FileReporter()
|
||||||
|
throws IOException {
|
||||||
|
// Calculate a unique(?) file name.
|
||||||
|
Date now = GregorianCalendar.getInstance().getTime();
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'hhmmssSSS");
|
||||||
|
String filename = String.format("curation-%s.report", sdf.format(now));
|
||||||
|
|
||||||
|
// Build a path to the directory which is to receive the file.
|
||||||
|
ConfigurationService cfg = new DSpace().getConfigurationService();
|
||||||
|
String reportDir = cfg.getProperty("report.dir");
|
||||||
|
Path reportPath;
|
||||||
|
if (null == reportDir) {
|
||||||
|
reportPath = Paths.get(cfg.getProperty("dspace.dir"),
|
||||||
|
"reports",
|
||||||
|
filename);
|
||||||
|
} else {
|
||||||
|
reportPath = Paths.get(reportDir, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the file.
|
||||||
|
writer = new FileWriter(reportPath.toFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Appendable append(CharSequence cs)
|
||||||
|
throws IOException {
|
||||||
|
writer.append(cs);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Appendable append(CharSequence cs, int i, int i1)
|
||||||
|
throws IOException {
|
||||||
|
writer.append(cs, i, i1);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Appendable append(char c) throws IOException {
|
||||||
|
writer.append(c);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception {
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
|
}
|
62
dspace-api/src/main/java/org/dspace/curate/LogReporter.java
Normal file
62
dspace-api/src/main/java/org/dspace/curate/LogReporter.java
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dspace.curate;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write curation report records through the logging framework.
|
||||||
|
* Whole lines (strings ending in '\n') are written to the log category "curation".
|
||||||
|
* Any partial line is flushed when the reporter is {@code close()}d.
|
||||||
|
*
|
||||||
|
* @author mhwood
|
||||||
|
*/
|
||||||
|
public class LogReporter
|
||||||
|
implements Reporter {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger("curation");
|
||||||
|
private final StringBuilder buffer = new StringBuilder();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Appendable append(CharSequence cs)
|
||||||
|
throws IOException {
|
||||||
|
for (int pos = 0; pos < cs.length(); pos++) {
|
||||||
|
char c = cs.charAt(pos);
|
||||||
|
if (c == '\n') {
|
||||||
|
LOG.info(buffer.toString());
|
||||||
|
buffer.delete(0, buffer.length()); // Clear the buffer
|
||||||
|
} else {
|
||||||
|
buffer.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Appendable append(CharSequence cs, int i, int i1)
|
||||||
|
throws IOException {
|
||||||
|
return append(cs.subSequence(i, i1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Appendable append(char c)
|
||||||
|
throws IOException {
|
||||||
|
return append(String.valueOf(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close()
|
||||||
|
throws Exception {
|
||||||
|
if (buffer.length() > 0) {
|
||||||
|
LOG.info(buffer.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
dspace-api/src/main/java/org/dspace/curate/Reporter.java
Normal file
18
dspace-api/src/main/java/org/dspace/curate/Reporter.java
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dspace.curate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A marker interface needed to make curation reporter classes into plugins.
|
||||||
|
*
|
||||||
|
* @author mhwood
|
||||||
|
*/
|
||||||
|
public interface Reporter
|
||||||
|
extends Appendable, AutoCloseable {
|
||||||
|
}
|
@@ -30,6 +30,8 @@ import org.dspace.content.Item;
|
|||||||
import org.dspace.content.service.CollectionService;
|
import org.dspace.content.service.CollectionService;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
import org.dspace.core.LogManager;
|
import org.dspace.core.LogManager;
|
||||||
|
import org.dspace.core.factory.CoreServiceFactory;
|
||||||
|
import org.dspace.core.service.PluginService;
|
||||||
import org.dspace.curate.service.WorkflowCuratorService;
|
import org.dspace.curate.service.WorkflowCuratorService;
|
||||||
import org.dspace.eperson.EPerson;
|
import org.dspace.eperson.EPerson;
|
||||||
import org.dspace.eperson.Group;
|
import org.dspace.eperson.Group;
|
||||||
@@ -56,9 +58,10 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
public class WorkflowCuratorServiceImpl implements WorkflowCuratorService {
|
public class WorkflowCuratorServiceImpl implements WorkflowCuratorService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* log4j logger
|
* Logging category
|
||||||
*/
|
*/
|
||||||
private Logger log = org.apache.logging.log4j.LogManager.getLogger(WorkflowCuratorServiceImpl.class);
|
private static final Logger log
|
||||||
|
= org.apache.logging.log4j.LogManager.getLogger();
|
||||||
|
|
||||||
protected Map<String, TaskSet> tsMap = new HashMap<String, TaskSet>();
|
protected Map<String, TaskSet> tsMap = new HashMap<String, TaskSet>();
|
||||||
|
|
||||||
@@ -118,6 +121,7 @@ public class WorkflowCuratorServiceImpl implements WorkflowCuratorService {
|
|||||||
Curator curator = new Curator();
|
Curator curator = new Curator();
|
||||||
// are we going to perform, or just put on queue?
|
// are we going to perform, or just put on queue?
|
||||||
if (step.queue != null) {
|
if (step.queue != null) {
|
||||||
|
// The queue runner will call setReporter
|
||||||
for (Task task : step.tasks) {
|
for (Task task : step.tasks) {
|
||||||
curator.addTask(task.name);
|
curator.addTask(task.name);
|
||||||
}
|
}
|
||||||
@@ -125,7 +129,18 @@ public class WorkflowCuratorServiceImpl implements WorkflowCuratorService {
|
|||||||
basicWorkflowItemService.update(c, wfi);
|
basicWorkflowItemService.update(c, wfi);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return curate(curator, c, wfi);
|
PluginService plugins = CoreServiceFactory.getInstance()
|
||||||
|
.getPluginService();
|
||||||
|
try (Reporter reporter
|
||||||
|
= (Reporter) plugins
|
||||||
|
.getSinglePlugin(Reporter.class);) {
|
||||||
|
curator.setReporter(reporter);
|
||||||
|
boolean status = curate(curator, c, wfi);
|
||||||
|
reporter.close();
|
||||||
|
return status;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to close report", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@@ -10,6 +10,7 @@ package org.dspace.storage.rdbms;
|
|||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DatabaseMetaData;
|
import java.sql.DatabaseMetaData;
|
||||||
@@ -23,6 +24,7 @@ import java.util.regex.Matcher;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
@@ -88,7 +90,8 @@ public class DatabaseUtils {
|
|||||||
// Usage checks
|
// Usage checks
|
||||||
if (argv.length < 1) {
|
if (argv.length < 1) {
|
||||||
System.out.println("\nDatabase action argument is missing.");
|
System.out.println("\nDatabase action argument is missing.");
|
||||||
System.out.println("Valid actions: 'test', 'info', 'migrate', 'repair', 'validate' or 'clean'");
|
System.out.println("Valid actions: 'test', 'info', 'migrate', 'repair', 'validate', " +
|
||||||
|
"'update-sequences' or 'clean'");
|
||||||
System.out.println("\nOr, type 'database help' for more information.\n");
|
System.out.println("\nOr, type 'database help' for more information.\n");
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
@@ -328,23 +331,48 @@ public class DatabaseUtils {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
} else if (argv[0].equalsIgnoreCase("update-sequences")) {
|
||||||
|
try (Connection connection = dataSource.getConnection()) {
|
||||||
|
String dbType = getDbType(connection);
|
||||||
|
String sqlfile = "org/dspace/storage/rdbms/sqlmigration/" + dbType +
|
||||||
|
"/update-sequences.sql";
|
||||||
|
InputStream sqlstream = DatabaseUtils.class.getClassLoader().getResourceAsStream(sqlfile);
|
||||||
|
if (sqlstream != null) {
|
||||||
|
String s = IOUtils.toString(sqlstream, "UTF-8");
|
||||||
|
if (!s.isEmpty()) {
|
||||||
|
System.out.println("Running " + sqlfile);
|
||||||
|
connection.createStatement().execute(s);
|
||||||
|
System.out.println("update-sequences complete");
|
||||||
|
} else {
|
||||||
|
System.err.println(sqlfile + " contains no SQL to execute");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.err.println(sqlfile + " not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
System.out.println("\nUsage: database [action]");
|
System.out.println("\nUsage: database [action]");
|
||||||
System.out.println("Valid actions: 'test', 'info', 'migrate', 'repair' or 'clean'");
|
System.out.println("Valid actions: 'test', 'info', 'migrate', 'repair', " +
|
||||||
|
"'update-sequences' or 'clean'");
|
||||||
System.out.println(
|
System.out.println(
|
||||||
" - test = Performs a test connection to database to validate connection settings");
|
" - test = Performs a test connection to database to " +
|
||||||
|
"validate connection settings");
|
||||||
System.out.println(
|
System.out.println(
|
||||||
" - info / status = Describe basic info/status about database, including validating the " +
|
" - info / status = Describe basic info/status about database, including validating the " +
|
||||||
"compatibility of this database");
|
"compatibility of this database");
|
||||||
System.out.println(" - migrate = Migrate the database to the latest version");
|
|
||||||
System.out.println(
|
System.out.println(
|
||||||
" - repair = Attempt to repair any previously failed database migrations or checksum " +
|
" - migrate = Migrate the database to the latest version");
|
||||||
"mismatches (via Flyway repair)");
|
System.out.println(
|
||||||
|
" - repair = Attempt to repair any previously failed database " +
|
||||||
|
"migrations or checksum mismatches (via Flyway repair)");
|
||||||
System.out.println(
|
System.out.println(
|
||||||
" - validate = Validate current database's migration status (via Flyway validate), " +
|
" - validate = Validate current database's migration status (via Flyway validate), " +
|
||||||
"validating all migration checksums.");
|
"validating all migration checksums.");
|
||||||
System.out.println(
|
System.out.println(
|
||||||
" - clean = DESTROY all data and tables in database (WARNING there is no going back!). " +
|
" - update-sequences = Update database sequences after running AIP ingest.");
|
||||||
|
System.out.println(
|
||||||
|
" - clean = DESTROY all data and tables in database " +
|
||||||
|
"(WARNING there is no going back!). " +
|
||||||
"Requires 'db.cleanDisabled=false' setting in config.");
|
"Requires 'db.cleanDisabled=false' setting in config.");
|
||||||
System.out.println("");
|
System.out.println("");
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
|
@@ -0,0 +1,79 @@
|
|||||||
|
--
|
||||||
|
-- The contents of this file are subject to the license and copyright
|
||||||
|
-- detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
-- tree and available online at
|
||||||
|
--
|
||||||
|
-- http://www.dspace.org/license/
|
||||||
|
--
|
||||||
|
|
||||||
|
-- SQL code to update the ID (primary key) generating sequences, if some
|
||||||
|
-- import operation has set explicit IDs.
|
||||||
|
--
|
||||||
|
-- Sequences are used to generate IDs for new rows in the database. If a
|
||||||
|
-- bulk import operation, such as an SQL dump, specifies primary keys for
|
||||||
|
-- imported data explicitly, the sequences are out of sync and need updating.
|
||||||
|
-- This SQL code does just that.
|
||||||
|
--
|
||||||
|
-- This should rarely be needed; any bulk import should be performed using the
|
||||||
|
-- org.dspace.content API which is safe to use concurrently and in multiple
|
||||||
|
-- JVMs. The SQL code below will typically only be required after a direct
|
||||||
|
-- SQL data dump from a backup or somesuch.
|
||||||
|
|
||||||
|
-- The 'updateseq' procedure was derived from incseq.sql found at:
|
||||||
|
-- http://www.akadia.com/services/scripts/incseq.sql
|
||||||
|
|
||||||
|
DECLARE
|
||||||
|
PROCEDURE updateseq ( seq IN VARCHAR,
|
||||||
|
tbl IN VARCHAR,
|
||||||
|
attr IN VARCHAR,
|
||||||
|
cond IN VARCHAR DEFAULT '' ) IS
|
||||||
|
curr NUMBER := 0;
|
||||||
|
BEGIN
|
||||||
|
EXECUTE IMMEDIATE 'SELECT max(' || attr
|
||||||
|
|| ') FROM ' || tbl
|
||||||
|
|| ' ' || cond
|
||||||
|
INTO curr;
|
||||||
|
curr := curr + 1;
|
||||||
|
EXECUTE IMMEDIATE 'DROP SEQUENCE ' || seq;
|
||||||
|
EXECUTE IMMEDIATE 'CREATE SEQUENCE '
|
||||||
|
|| seq
|
||||||
|
|| ' START WITH '
|
||||||
|
|| NVL(curr, 1);
|
||||||
|
END updateseq;
|
||||||
|
|
||||||
|
BEGIN
|
||||||
|
updateseq('bitstreamformatregistry_seq', 'bitstreamformatregistry',
|
||||||
|
'bitstream_format_id');
|
||||||
|
updateseq('fileextension_seq', 'fileextension', 'file_extension_id');
|
||||||
|
updateseq('resourcepolicy_seq', 'resourcepolicy', 'policy_id');
|
||||||
|
updateseq('workspaceitem_seq', 'workspaceitem', 'workspace_item_id');
|
||||||
|
updateseq('workflowitem_seq', 'workflowitem', 'workflow_id');
|
||||||
|
updateseq('tasklistitem_seq', 'tasklistitem', 'tasklist_id');
|
||||||
|
updateseq('registrationdata_seq', 'registrationdata',
|
||||||
|
'registrationdata_id');
|
||||||
|
updateseq('subscription_seq', 'subscription', 'subscription_id');
|
||||||
|
updateseq('metadatafieldregistry_seq', 'metadatafieldregistry',
|
||||||
|
'metadata_field_id');
|
||||||
|
updateseq('metadatavalue_seq', 'metadatavalue', 'metadata_value_id');
|
||||||
|
updateseq('metadataschemaregistry_seq', 'metadataschemaregistry',
|
||||||
|
'metadata_schema_id');
|
||||||
|
updateseq('harvested_collection_seq', 'harvested_collection', 'id');
|
||||||
|
updateseq('harvested_item_seq', 'harvested_item', 'id');
|
||||||
|
updateseq('webapp_seq', 'webapp', 'webapp_id');
|
||||||
|
updateseq('requestitem_seq', 'requestitem', 'requestitem_id');
|
||||||
|
updateseq('handle_id_seq', 'handle', 'handle_id');
|
||||||
|
|
||||||
|
-- Handle Sequence is a special case. Since Handles minted by DSpace
|
||||||
|
-- use the 'handle_seq', we need to ensure the next assigned handle
|
||||||
|
-- will *always* be unique. So, 'handle_seq' always needs to be set
|
||||||
|
-- to the value of the *largest* handle suffix. That way when the
|
||||||
|
-- next handle is assigned, it will use the next largest number. This
|
||||||
|
-- query does the following:
|
||||||
|
-- For all 'handle' values which have a number in their suffix
|
||||||
|
-- (after '/'), find the maximum suffix value, convert it to a
|
||||||
|
-- number, and set the 'handle_seq' to start at the next value (see
|
||||||
|
-- updateseq above for more).
|
||||||
|
updateseq('handle_seq', 'handle',
|
||||||
|
q'{to_number(regexp_replace(handle, '.*/', ''), '999999999999')}',
|
||||||
|
q'{WHERE REGEXP_LIKE(handle, '^.*/[0123456789]*$')}');
|
||||||
|
END;
|
@@ -17,4 +17,16 @@ not realize you manually ran one or more scripts.
|
|||||||
|
|
||||||
Please see the Flyway Documentation for more information: http://flywaydb.org/
|
Please see the Flyway Documentation for more information: http://flywaydb.org/
|
||||||
|
|
||||||
|
## Using the update-sequences.sql script
|
||||||
|
|
||||||
|
The `update-sequences.sql` script in this directory may still be used to update
|
||||||
|
your internal database counts if you feel they have gotten out of "sync". This
|
||||||
|
may sometimes occur after large restores of content (e.g. when using the DSpace
|
||||||
|
[AIP Backup and Restore](https://wiki.duraspace.org/display/DSDOC5x/AIP+Backup+and+Restore)
|
||||||
|
feature).
|
||||||
|
|
||||||
|
This `update-sequences.sql` script can be executed by running
|
||||||
|
"dspace database update-sequences". It will not harm your
|
||||||
|
database (or its contents) in any way. It just ensures all database counts (i.e.
|
||||||
|
sequences) are properly set to the next available value.
|
||||||
|
|
||||||
|
@@ -1,35 +1,10 @@
|
|||||||
--
|
--
|
||||||
-- update-sequences.sql
|
-- The contents of this file are subject to the license and copyright
|
||||||
|
-- detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
-- tree and available online at
|
||||||
--
|
--
|
||||||
-- Copyright (c) 2002-2016, The DSpace Foundation. All rights reserved.
|
-- http://www.dspace.org/license/
|
||||||
--
|
--
|
||||||
-- Redistribution and use in source and binary forms, with or without
|
|
||||||
-- modification, are permitted provided that the following conditions are
|
|
||||||
-- met:
|
|
||||||
--
|
|
||||||
-- - Redistributions of source code must retain the above copyright
|
|
||||||
-- notice, this list of conditions and the following disclaimer.
|
|
||||||
--
|
|
||||||
-- - Redistributions in binary form must reproduce the above copyright
|
|
||||||
-- notice, this list of conditions and the following disclaimer in the
|
|
||||||
-- documentation and/or other materials provided with the distribution.
|
|
||||||
--
|
|
||||||
-- Neither the name of the DSpace Foundation nor the names of its
|
|
||||||
-- contributors may be used to endorse or promote products derived from
|
|
||||||
-- this software without specific prior written permission.
|
|
||||||
--
|
|
||||||
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
-- ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
-- HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
-- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
||||||
-- BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
||||||
-- OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
|
||||||
-- TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
||||||
-- USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
|
||||||
-- DAMAGE.
|
|
||||||
|
|
||||||
-- SQL code to update the ID (primary key) generating sequences, if some
|
-- SQL code to update the ID (primary key) generating sequences, if some
|
||||||
-- import operation has set explicit IDs.
|
-- import operation has set explicit IDs.
|
@@ -58,7 +58,7 @@ public class CuratorTest
|
|||||||
|
|
||||||
// Get and configure a Curator.
|
// Get and configure a Curator.
|
||||||
Curator instance = new Curator();
|
Curator instance = new Curator();
|
||||||
instance.setReporter("-"); // Send any report to standard out. FIXME when DS-3989 is merged
|
instance.setReporter(System.out); // Send any report to standard out.
|
||||||
instance.addTask(TASK_NAME);
|
instance.addTask(TASK_NAME);
|
||||||
|
|
||||||
// Configure the run.
|
// Configure the run.
|
||||||
|
202
dspace-api/src/test/java/org/dspace/curate/ITCurator.java
Normal file
202
dspace-api/src/test/java/org/dspace/curate/ITCurator.java
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dspace.curate;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.dspace.AbstractUnitTest;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.content.Community;
|
||||||
|
import org.dspace.content.DSpaceObject;
|
||||||
|
import org.dspace.content.Site;
|
||||||
|
import org.dspace.content.factory.ContentServiceFactory;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drive the Curator and check results.
|
||||||
|
*
|
||||||
|
* @author mhwood
|
||||||
|
*/
|
||||||
|
public class ITCurator
|
||||||
|
extends AbstractUnitTest {
|
||||||
|
Logger LOG = LoggerFactory.getLogger(ITCurator.class);
|
||||||
|
|
||||||
|
public ITCurator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setUpClass() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void tearDownClass() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The report should contain contributions from all tasks and all curated objects.
|
||||||
|
*
|
||||||
|
* @throws SQLException passed through.
|
||||||
|
* @throws IOException passed through.
|
||||||
|
* @throws AuthorizeException passed through.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCurate_Reporting()
|
||||||
|
throws SQLException, IOException, AuthorizeException {
|
||||||
|
// Configure for testing.
|
||||||
|
ConfigurationService cfg = kernelImpl.getConfigurationService();
|
||||||
|
cfg.setProperty("plugin.named.org.dspace.curate.CurationTask",
|
||||||
|
Task1.class.getName() + " = task1");
|
||||||
|
cfg.addPropertyValue("plugin.named.org.dspace.curate.CurationTask",
|
||||||
|
Task2.class.getName() + " = task2");
|
||||||
|
|
||||||
|
// Create some structure.
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
Site site = ContentServiceFactory.getInstance()
|
||||||
|
.getSiteService()
|
||||||
|
.findSite(context);
|
||||||
|
Community community = ContentServiceFactory.getInstance()
|
||||||
|
.getCommunityService()
|
||||||
|
.create(null, context);
|
||||||
|
|
||||||
|
// Run some tasks.
|
||||||
|
ListReporter reporter = new ListReporter();
|
||||||
|
Curator curator = new Curator();
|
||||||
|
curator.setReporter(reporter);
|
||||||
|
curator.addTask("task1");
|
||||||
|
curator.addTask("task2");
|
||||||
|
curator.curate(context, site);
|
||||||
|
|
||||||
|
// Validate the results.
|
||||||
|
List<String> report = reporter.getReport();
|
||||||
|
for (String aReport : report) {
|
||||||
|
LOG.info("Report: {}", aReport);
|
||||||
|
}
|
||||||
|
Pattern pattern;
|
||||||
|
pattern = Pattern.compile(String.format("task1.*%s", site.getHandle()));
|
||||||
|
Assert.assertTrue("A report should mention 'task1' and site's handle",
|
||||||
|
reportMatcher(report, pattern));
|
||||||
|
pattern = Pattern.compile(String.format("task1.*%s", community.getHandle()));
|
||||||
|
Assert.assertTrue("A report should mention 'task1' and the community's handle",
|
||||||
|
reportMatcher(report, pattern));
|
||||||
|
pattern = Pattern.compile(String.format("task2.*%s", site.getHandle()));
|
||||||
|
Assert.assertTrue("A report should mention 'task2' and the Site's handle",
|
||||||
|
reportMatcher(report, pattern));
|
||||||
|
pattern = Pattern.compile(String.format("task2.*%s", community.getHandle()));
|
||||||
|
Assert.assertTrue("A report should mention 'task2' and the community's handle",
|
||||||
|
reportMatcher(report, pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match a collection of strings against a regular expression.\
|
||||||
|
*
|
||||||
|
* @param reports strings to be searched.
|
||||||
|
* @param pattern expression to be matched.
|
||||||
|
* @return true if at least one string matches the expression.
|
||||||
|
*/
|
||||||
|
private boolean reportMatcher(List<String> reports, Pattern pattern) {
|
||||||
|
for (String aReport : reports) {
|
||||||
|
if (pattern.matcher(aReport).find()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy curation task for testing. Reports how it was invoked.
|
||||||
|
*
|
||||||
|
* @author mhwood
|
||||||
|
*/
|
||||||
|
public static class Task1 extends AbstractCurationTask {
|
||||||
|
public Task1() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int perform(DSpaceObject dso)
|
||||||
|
throws IOException {
|
||||||
|
curator.report(String.format(
|
||||||
|
"Task1 received 'perform' on taskId '%s' for object '%s'%n",
|
||||||
|
taskId, dso.getHandle()));
|
||||||
|
return Curator.CURATE_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy curation task for testing. Reports how it was invoked.
|
||||||
|
*
|
||||||
|
* @author mhwood
|
||||||
|
*/
|
||||||
|
public static class Task2 extends AbstractCurationTask {
|
||||||
|
public Task2() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int perform(DSpaceObject dso) throws IOException {
|
||||||
|
curator.report(String.format(
|
||||||
|
"Task2 received 'perform' on taskId '%s' for object '%s'%n",
|
||||||
|
taskId, dso.getHandle()));
|
||||||
|
return Curator.CURATE_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Absorb report strings into a sequential collection.
|
||||||
|
*/
|
||||||
|
class ListReporter
|
||||||
|
implements Appendable {
|
||||||
|
private final List<String> report = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the content of the report accumulator.
|
||||||
|
* @return accumulated reports.
|
||||||
|
*/
|
||||||
|
List<String> getReport() {
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Appendable append(CharSequence cs)
|
||||||
|
throws IOException {
|
||||||
|
report.add(cs.toString());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Appendable append(CharSequence cs, int i, int i1)
|
||||||
|
throws IOException {
|
||||||
|
throw new UnsupportedOperationException("Not supported yet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Appendable append(char c)
|
||||||
|
throws IOException {
|
||||||
|
throw new UnsupportedOperationException("Not supported yet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -54,15 +54,15 @@ import org.dspace.content.MetadataField;
|
|||||||
import org.dspace.content.MetadataValue;
|
import org.dspace.content.MetadataValue;
|
||||||
import org.dspace.content.factory.ContentServiceFactory;
|
import org.dspace.content.factory.ContentServiceFactory;
|
||||||
import org.dspace.content.service.ItemService;
|
import org.dspace.content.service.ItemService;
|
||||||
import org.dspace.core.ConfigurationManager;
|
|
||||||
import org.dspace.core.Constants;
|
import org.dspace.core.Constants;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||||
import org.dspace.xoai.exceptions.CompilingException;
|
import org.dspace.xoai.exceptions.CompilingException;
|
||||||
import org.dspace.xoai.services.api.CollectionsService;
|
import org.dspace.xoai.services.api.CollectionsService;
|
||||||
import org.dspace.xoai.services.api.cache.XOAICacheService;
|
import org.dspace.xoai.services.api.cache.XOAICacheService;
|
||||||
import org.dspace.xoai.services.api.cache.XOAIItemCacheService;
|
import org.dspace.xoai.services.api.cache.XOAIItemCacheService;
|
||||||
import org.dspace.xoai.services.api.cache.XOAILastCompilationCacheService;
|
import org.dspace.xoai.services.api.cache.XOAILastCompilationCacheService;
|
||||||
import org.dspace.xoai.services.api.config.ConfigurationService;
|
|
||||||
import org.dspace.xoai.services.api.solr.SolrServerResolver;
|
import org.dspace.xoai.services.api.solr.SolrServerResolver;
|
||||||
import org.dspace.xoai.solr.DSpaceSolrSearch;
|
import org.dspace.xoai.solr.DSpaceSolrSearch;
|
||||||
import org.dspace.xoai.solr.exceptions.DSpaceSolrException;
|
import org.dspace.xoai.solr.exceptions.DSpaceSolrException;
|
||||||
@@ -94,6 +94,8 @@ public class XOAI {
|
|||||||
private final AuthorizeService authorizeService;
|
private final AuthorizeService authorizeService;
|
||||||
private final ItemService itemService;
|
private final ItemService itemService;
|
||||||
|
|
||||||
|
private final static ConfigurationService configurationService = DSpaceServicesFactory
|
||||||
|
.getInstance().getConfigurationService();
|
||||||
|
|
||||||
private List<String> getFileFormats(Item item) {
|
private List<String> getFileFormats(Item item) {
|
||||||
List<String> formats = new ArrayList<>();
|
List<String> formats = new ArrayList<>();
|
||||||
@@ -283,14 +285,16 @@ public class XOAI {
|
|||||||
throws DSpaceSolrIndexerException {
|
throws DSpaceSolrIndexerException {
|
||||||
try {
|
try {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
int batchSize = configurationService.getIntProperty("oai.import.batch.size", 1000);
|
||||||
SolrServer server = solrServerResolver.getServer();
|
SolrServer server = solrServerResolver.getServer();
|
||||||
|
ArrayList<SolrInputDocument> list = new ArrayList<>();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
try {
|
try {
|
||||||
Item item = iterator.next();
|
Item item = iterator.next();
|
||||||
if (item.getHandle() == null) {
|
if (item.getHandle() == null) {
|
||||||
log.warn("Skipped item without handle: " + item.getID());
|
log.warn("Skipped item without handle: " + item.getID());
|
||||||
} else {
|
} else {
|
||||||
server.add(this.index(item));
|
list.add(this.index(item));
|
||||||
}
|
}
|
||||||
//Uncache the item to keep memory consumption low
|
//Uncache the item to keep memory consumption low
|
||||||
context.uncacheEntity(item);
|
context.uncacheEntity(item);
|
||||||
@@ -300,12 +304,22 @@ public class XOAI {
|
|||||||
log.error(ex.getMessage(), ex);
|
log.error(ex.getMessage(), ex);
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
if (i % 100 == 0) {
|
if (i % 1000 == 0 && batchSize != 1000) {
|
||||||
System.out.println(i + " items imported so far...");
|
System.out.println(i + " items imported so far...");
|
||||||
}
|
}
|
||||||
|
if (i % batchSize == 0) {
|
||||||
|
System.out.println(i + " items imported so far...");
|
||||||
|
server.add(list);
|
||||||
|
server.commit();
|
||||||
|
list.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
System.out.println("Total: " + i + " items");
|
System.out.println("Total: " + i + " items");
|
||||||
server.commit();
|
if (i > 0) {
|
||||||
|
server.add(list);
|
||||||
|
server.commit(true, true);
|
||||||
|
list.clear();
|
||||||
|
}
|
||||||
return i;
|
return i;
|
||||||
} catch (SolrServerException | IOException ex) {
|
} catch (SolrServerException | IOException ex) {
|
||||||
throw new DSpaceSolrIndexerException(ex.getMessage(), ex);
|
throw new DSpaceSolrIndexerException(ex.getMessage(), ex);
|
||||||
@@ -334,6 +348,7 @@ public class XOAI {
|
|||||||
dates.add(policy.getEndDate());
|
dates.add(policy.getEndDate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
context.uncacheEntity(policy);
|
||||||
}
|
}
|
||||||
dates.add(item.getLastModified());
|
dates.add(item.getLastModified());
|
||||||
Collections.sort(dates);
|
Collections.sort(dates);
|
||||||
@@ -458,6 +473,7 @@ public class XOAI {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
context.uncacheEntity(policy);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -477,7 +493,7 @@ public class XOAI {
|
|||||||
private static boolean getKnownExplanation(Throwable t) {
|
private static boolean getKnownExplanation(Throwable t) {
|
||||||
if (t instanceof ConnectException) {
|
if (t instanceof ConnectException) {
|
||||||
System.err.println("Solr server ("
|
System.err.println("Solr server ("
|
||||||
+ ConfigurationManager.getProperty("oai", "solr.url")
|
+ configurationService.getProperty("oai.solr.url", "")
|
||||||
+ ") is down, turn it on.");
|
+ ") is down, turn it on.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -525,7 +541,6 @@ public class XOAI {
|
|||||||
BasicConfiguration.class
|
BasicConfiguration.class
|
||||||
});
|
});
|
||||||
|
|
||||||
ConfigurationService configurationService = applicationContext.getBean(ConfigurationService.class);
|
|
||||||
XOAICacheService cacheService = applicationContext.getBean(XOAICacheService.class);
|
XOAICacheService cacheService = applicationContext.getBean(XOAICacheService.class);
|
||||||
XOAIItemCacheService itemCacheService = applicationContext.getBean(XOAIItemCacheService.class);
|
XOAIItemCacheService itemCacheService = applicationContext.getBean(XOAIItemCacheService.class);
|
||||||
|
|
||||||
@@ -547,7 +562,7 @@ public class XOAI {
|
|||||||
|
|
||||||
|
|
||||||
boolean solr = true; // Assuming solr by default
|
boolean solr = true; // Assuming solr by default
|
||||||
solr = !("database").equals(configurationService.getProperty("oai", "storage"));
|
solr = !("database").equals(configurationService.getProperty("oai.storage", "solr"));
|
||||||
|
|
||||||
|
|
||||||
boolean run = false;
|
boolean run = false;
|
||||||
@@ -652,7 +667,7 @@ public class XOAI {
|
|||||||
|
|
||||||
private static void usage() {
|
private static void usage() {
|
||||||
boolean solr = true; // Assuming solr by default
|
boolean solr = true; // Assuming solr by default
|
||||||
solr = !("database").equals(ConfigurationManager.getProperty("oai", "storage"));
|
solr = !("database").equals(configurationService.getProperty("oai.storage","solr"));
|
||||||
|
|
||||||
if (solr) {
|
if (solr) {
|
||||||
System.out.println("OAI Manager Script");
|
System.out.println("OAI Manager Script");
|
||||||
|
@@ -346,6 +346,26 @@ public final class DSpaceConfigurationService implements ConfigurationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean addPropertyValue(String name, Object value) {
|
||||||
|
if (name == null) {
|
||||||
|
throw new IllegalArgumentException("name cannot be null for setting configuration");
|
||||||
|
}
|
||||||
|
if (value == null) {
|
||||||
|
throw new IllegalArgumentException("configuration value may not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the value is a type of String, trim any leading/trailing spaces before saving it.
|
||||||
|
if (String.class.isInstance(value)) {
|
||||||
|
value = ((String) value).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
Configuration configuration = getConfiguration();
|
||||||
|
boolean isNew = !configuration.containsKey(name);
|
||||||
|
configuration.addProperty(name, value);
|
||||||
|
return isNew;
|
||||||
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see org.dspace.services.ConfigurationService#setProperty(java.lang.String, java.lang.Object)
|
* @see org.dspace.services.ConfigurationService#setProperty(java.lang.String, java.lang.Object)
|
||||||
*/
|
*/
|
||||||
|
@@ -237,6 +237,16 @@ public interface ConfigurationService {
|
|||||||
*/
|
*/
|
||||||
public boolean hasProperty(String name);
|
public boolean hasProperty(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a value to a configuration property.
|
||||||
|
*
|
||||||
|
* @param name the property name. May not be null.
|
||||||
|
* @param value the property value. May not be null.
|
||||||
|
* @return true if a new property was created.
|
||||||
|
* @throws IllegalArgumentException if the name or value is null.
|
||||||
|
*/
|
||||||
|
public boolean addPropertyValue(String name, Object value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a configuration property (setting) in the system.
|
* Set a configuration property (setting) in the system.
|
||||||
* Type is not important here since conversion happens automatically
|
* Type is not important here since conversion happens automatically
|
||||||
|
@@ -226,6 +226,12 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.flipkart.zjsonpatch</groupId>
|
||||||
|
<artifactId>zjsonpatch</artifactId>
|
||||||
|
<version>0.4.6</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- The HAL Browser -->
|
<!-- The HAL Browser -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.data</groupId>
|
<groupId>org.springframework.data</groupId>
|
||||||
|
@@ -43,7 +43,6 @@ import org.dspace.discovery.SearchUtils;
|
|||||||
import org.dspace.discovery.configuration.DiscoveryConfiguration;
|
import org.dspace.discovery.configuration.DiscoveryConfiguration;
|
||||||
import org.dspace.discovery.configuration.DiscoverySearchFilter;
|
import org.dspace.discovery.configuration.DiscoverySearchFilter;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.web.ErrorController;
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@@ -60,7 +59,7 @@ import org.w3c.dom.Document;
|
|||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping("/opensearch")
|
@RequestMapping("/opensearch")
|
||||||
public class OpenSearchController implements ErrorController {
|
public class OpenSearchController {
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger(ScopeResolver.class);
|
private static final Logger log = Logger.getLogger(ScopeResolver.class);
|
||||||
private static final String errorpath = "/error";
|
private static final String errorpath = "/error";
|
||||||
@@ -192,16 +191,6 @@ public class OpenSearchController implements ErrorController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = errorpath)
|
|
||||||
public String error() {
|
|
||||||
return "Error handling";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getErrorPath() {
|
|
||||||
return errorpath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal method for controller initialization
|
* Internal method for controller initialization
|
||||||
*/
|
*/
|
||||||
|
@@ -1047,6 +1047,33 @@ public class RestResourceController implements InitializingBean {
|
|||||||
return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT);
|
return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a PUT request for an entity with id of type UUID;
|
||||||
|
*
|
||||||
|
* curl -X PUT http://<dspace.restUrl>/api/{apiCategory}/{model}/{uuid}
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* curl -X PUT http://<dspace.restUrl>/api/core/collection/8b632938-77c2-487c-81f0-e804f63e68e6
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param request the http request
|
||||||
|
* @param apiCategory the API category e.g. "core"
|
||||||
|
* @param model the DSpace model e.g. "collection"
|
||||||
|
* @param uuid the ID of the target REST object
|
||||||
|
* @param jsonNode the part of the request body representing the updated rest object
|
||||||
|
* @return the relevant REST resource
|
||||||
|
*/
|
||||||
|
@RequestMapping(method = RequestMethod.PUT, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID)
|
||||||
|
public DSpaceResource<RestAddressableModel> put(HttpServletRequest request,
|
||||||
|
@PathVariable String apiCategory, @PathVariable String model,
|
||||||
|
@PathVariable UUID uuid,
|
||||||
|
@RequestBody JsonNode jsonNode) {
|
||||||
|
return putOneJsonInternal(request, apiCategory, model, uuid, jsonNode);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a PUT request for an entity with id of type Integer;
|
* Execute a PUT request for an entity with id of type Integer;
|
||||||
*
|
*
|
||||||
|
@@ -25,6 +25,7 @@ import org.dspace.content.service.CollectionService;
|
|||||||
import org.dspace.core.Constants;
|
import org.dspace.core.Constants;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
import org.dspace.services.RequestService;
|
import org.dspace.services.RequestService;
|
||||||
|
import org.dspace.services.model.Request;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@@ -71,12 +72,17 @@ public class CollectionConverter
|
|||||||
|
|
||||||
private List<ResourcePolicyRest> getDefaultBitstreamPoliciesForCollection(UUID uuid) {
|
private List<ResourcePolicyRest> getDefaultBitstreamPoliciesForCollection(UUID uuid) {
|
||||||
|
|
||||||
HttpServletRequest request = requestService.getCurrentRequest().getHttpServletRequest();
|
|
||||||
Context context = null;
|
Context context = null;
|
||||||
|
Request currentRequest = requestService.getCurrentRequest();
|
||||||
|
if (currentRequest != null) {
|
||||||
|
HttpServletRequest request = currentRequest.getHttpServletRequest();
|
||||||
|
context = ContextUtil.obtainContext(request);
|
||||||
|
} else {
|
||||||
|
context = new Context();
|
||||||
|
}
|
||||||
Collection collection = null;
|
Collection collection = null;
|
||||||
List<ResourcePolicy> defaultCollectionPolicies = null;
|
List<ResourcePolicy> defaultCollectionPolicies = null;
|
||||||
try {
|
try {
|
||||||
context = ContextUtil.obtainContext(request);
|
|
||||||
collection = collectionService.find(context, uuid);
|
collection = collectionService.find(context, uuid);
|
||||||
defaultCollectionPolicies = authorizeService.getPoliciesActionFilter(context, collection,
|
defaultCollectionPolicies = authorizeService.getPoliciesActionFilter(context, collection,
|
||||||
Constants.DEFAULT_BITSTREAM_READ);
|
Constants.DEFAULT_BITSTREAM_READ);
|
||||||
|
@@ -7,12 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest.converter;
|
package org.dspace.app.rest.converter;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.dspace.app.rest.model.MetadataEntryRest;
|
|
||||||
import org.dspace.content.DSpaceObject;
|
import org.dspace.content.DSpaceObject;
|
||||||
import org.dspace.content.MetadataValue;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the base converter from/to objects in the DSpace API data model and
|
* This is the base converter from/to objects in the DSpace API data model and
|
||||||
@@ -26,6 +22,9 @@ public abstract class DSpaceObjectConverter<M extends DSpaceObject, R extends or
|
|||||||
.DSpaceObjectRest>
|
.DSpaceObjectRest>
|
||||||
extends DSpaceConverter<M, R> {
|
extends DSpaceConverter<M, R> {
|
||||||
|
|
||||||
|
@Autowired(required = true)
|
||||||
|
private MetadataConverter metadataConverter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public R fromModel(M obj) {
|
public R fromModel(M obj) {
|
||||||
R resource = newInstance();
|
R resource = newInstance();
|
||||||
@@ -34,27 +33,10 @@ public abstract class DSpaceObjectConverter<M extends DSpaceObject, R extends or
|
|||||||
resource.setUuid(obj.getID().toString());
|
resource.setUuid(obj.getID().toString());
|
||||||
}
|
}
|
||||||
resource.setName(obj.getName());
|
resource.setName(obj.getName());
|
||||||
List<MetadataValue> fullList = obj.getMetadata();
|
resource.setMetadata(metadataConverter.convert(obj.getMetadata()));
|
||||||
List<MetadataEntryRest> metadata = convertMetadataToRest(fullList);
|
|
||||||
resource.setMetadata(metadata);
|
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MetadataEntryRest> convertMetadataToRest(List<MetadataValue> fullList) {
|
|
||||||
List<MetadataEntryRest> metadata = new ArrayList<MetadataEntryRest>();
|
|
||||||
for (MetadataValue mv : fullList) {
|
|
||||||
MetadataEntryRest me = new MetadataEntryRest();
|
|
||||||
me.setKey(mv.getMetadataField().toString('.'));
|
|
||||||
me.setValue(mv.getValue());
|
|
||||||
me.setLanguage(mv.getLanguage());
|
|
||||||
me.setPlace(mv.getPlace());
|
|
||||||
me.setAuthority(mv.getAuthority());
|
|
||||||
me.setConfidence(mv.getConfidence());
|
|
||||||
metadata.add(me);
|
|
||||||
}
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public M toModel(R obj) {
|
public M toModel(R obj) {
|
||||||
return null;
|
return null;
|
||||||
|
@@ -15,7 +15,6 @@ import java.util.List;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.dspace.app.rest.model.BitstreamRest;
|
import org.dspace.app.rest.model.BitstreamRest;
|
||||||
import org.dspace.app.rest.model.ItemRest;
|
import org.dspace.app.rest.model.ItemRest;
|
||||||
import org.dspace.app.rest.model.MetadataEntryRest;
|
|
||||||
import org.dspace.app.rest.model.RelationshipRest;
|
import org.dspace.app.rest.model.RelationshipRest;
|
||||||
import org.dspace.content.Bitstream;
|
import org.dspace.content.Bitstream;
|
||||||
import org.dspace.content.Bundle;
|
import org.dspace.content.Bundle;
|
||||||
@@ -47,6 +46,8 @@ public class ItemConverter extends DSpaceObjectConverter<org.dspace.content.Item
|
|||||||
private RelationshipConverter relationshipConverter;
|
private RelationshipConverter relationshipConverter;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ItemService itemService;
|
private ItemService itemService;
|
||||||
|
@Autowired
|
||||||
|
private MetadataConverter metadataConverter;
|
||||||
|
|
||||||
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemConverter.class);
|
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemConverter.class);
|
||||||
|
|
||||||
@@ -97,8 +98,7 @@ public class ItemConverter extends DSpaceObjectConverter<org.dspace.content.Item
|
|||||||
List<MetadataValue> fullList = new LinkedList<>();
|
List<MetadataValue> fullList = new LinkedList<>();
|
||||||
fullList = itemService.getMetadata(obj, Item.ANY, Item.ANY, Item.ANY, Item.ANY, true);
|
fullList = itemService.getMetadata(obj, Item.ANY, Item.ANY, Item.ANY, Item.ANY, true);
|
||||||
|
|
||||||
List<MetadataEntryRest> metadata = super.convertMetadataToRest(fullList);
|
item.setMetadata(metadataConverter.convert(fullList));
|
||||||
item.setMetadata(metadata);
|
|
||||||
|
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
|
@@ -116,7 +116,8 @@ public class JsonPatchConverter implements PatchConverter<JsonNode> {
|
|||||||
Object value = operation.getValue();
|
Object value = operation.getValue();
|
||||||
|
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
opNode.set("value", mapper.valueToTree(value));
|
opNode.set("value", value instanceof JsonValueEvaluator ? ((JsonValueEvaluator) value).getValueNode()
|
||||||
|
: mapper.valueToTree(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
patchNode.add(opNode);
|
patchNode.add(opNode);
|
||||||
|
@@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.converter;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.model.MetadataRest;
|
||||||
|
import org.dspace.app.rest.model.MetadataValueRest;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.content.DSpaceObject;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.MetadataValue;
|
||||||
|
import org.dspace.content.factory.ContentServiceFactory;
|
||||||
|
import org.dspace.content.service.DSpaceObjectService;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converter to translate between lists of domain {@link MetadataValue}s and {@link MetadataRest} representations.
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class MetadataConverter implements Converter<List<MetadataValue>, MetadataRest> {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ContentServiceFactory contentServiceFactory;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MetadataValueConverter valueConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a rest representation of the given list of domain metadata values.
|
||||||
|
*
|
||||||
|
* @param metadataValueList the domain values.
|
||||||
|
* @return the rest representation.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public MetadataRest convert(List<MetadataValue> metadataValueList) {
|
||||||
|
// Convert each value to a DTO while retaining place order in a map of key -> SortedSet
|
||||||
|
Map<String, SortedSet<MetadataValueRest>> mapOfSortedSets = new HashMap<>();
|
||||||
|
for (MetadataValue metadataValue : metadataValueList) {
|
||||||
|
String key = metadataValue.getMetadataField().toString('.');
|
||||||
|
SortedSet<MetadataValueRest> set = mapOfSortedSets.get(key);
|
||||||
|
if (set == null) {
|
||||||
|
set = new TreeSet<>(Comparator.comparingInt(MetadataValueRest::getPlace));
|
||||||
|
mapOfSortedSets.put(key, set);
|
||||||
|
}
|
||||||
|
set.add(valueConverter.convert(metadataValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
MetadataRest metadataRest = new MetadataRest();
|
||||||
|
|
||||||
|
// Populate MetadataRest's map of key -> List while respecting SortedSet's order
|
||||||
|
Map<String, List<MetadataValueRest>> mapOfLists = metadataRest.getMap();
|
||||||
|
for (Map.Entry<String, SortedSet<MetadataValueRest>> entry : mapOfSortedSets.entrySet()) {
|
||||||
|
mapOfLists.put(entry.getKey(), entry.getValue().stream().collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadataRest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a DSpace object's domain metadata values from a rest representation.
|
||||||
|
*
|
||||||
|
* @param context the context to use.
|
||||||
|
* @param dso the DSpace object.
|
||||||
|
* @param metadataRest the rest representation of the new metadata.
|
||||||
|
* @throws SQLException if a database error occurs.
|
||||||
|
* @throws AuthorizeException if an authorization error occurs.
|
||||||
|
*/
|
||||||
|
public void setMetadata(Context context, DSpaceObject dso, MetadataRest metadataRest)
|
||||||
|
throws SQLException, AuthorizeException {
|
||||||
|
DSpaceObjectService dsoService = contentServiceFactory.getDSpaceObjectService(dso);
|
||||||
|
dsoService.clearMetadata(context, dso, Item.ANY, Item.ANY, Item.ANY, Item.ANY);
|
||||||
|
for (Map.Entry<String, List<MetadataValueRest>> entry: metadataRest.getMap().entrySet()) {
|
||||||
|
String[] seq = entry.getKey().split("\\.");
|
||||||
|
String schema = seq[0];
|
||||||
|
String element = seq[1];
|
||||||
|
String qualifier = seq.length == 3 ? seq[2] : null;
|
||||||
|
for (MetadataValueRest mvr: entry.getValue()) {
|
||||||
|
dsoService.addMetadata(context, dso, schema, element, qualifier, mvr.getLanguage(),
|
||||||
|
mvr.getValue(), mvr.getAuthority(), mvr.getConfidence());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dsoService.update(context, dso);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.converter;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.model.MetadataValueRest;
|
||||||
|
import org.dspace.content.MetadataValue;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converter to translate between domain {@link MetadataValue}s and {@link MetadataValueRest} representations.
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class MetadataValueConverter implements Converter<MetadataValue, MetadataValueRest> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a rest representation of the given domain metadata value.
|
||||||
|
*
|
||||||
|
* @param metadataValue the domain value.
|
||||||
|
* @return the rest representation.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public MetadataValueRest convert(MetadataValue metadataValue) {
|
||||||
|
MetadataValueRest metadataValueRest = new MetadataValueRest();
|
||||||
|
metadataValueRest.setValue(metadataValue.getValue());
|
||||||
|
metadataValueRest.setLanguage(metadataValue.getLanguage());
|
||||||
|
metadataValueRest.setAuthority(metadataValue.getAuthority());
|
||||||
|
metadataValueRest.setConfidence(metadataValue.getConfidence());
|
||||||
|
metadataValueRest.setPlace(metadataValue.getPlace());
|
||||||
|
return metadataValueRest;
|
||||||
|
}
|
||||||
|
}
|
@@ -22,7 +22,10 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
|||||||
public class PatchBadRequestException extends RuntimeException {
|
public class PatchBadRequestException extends RuntimeException {
|
||||||
|
|
||||||
public PatchBadRequestException(String message) {
|
public PatchBadRequestException(String message) {
|
||||||
super(message);
|
this(message, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PatchBadRequestException(String message, Exception e) {
|
||||||
|
super(message, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -27,6 +27,10 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
|||||||
@ResponseStatus(value = HttpStatus.UNPROCESSABLE_ENTITY, reason = "Unprocessable request")
|
@ResponseStatus(value = HttpStatus.UNPROCESSABLE_ENTITY, reason = "Unprocessable request")
|
||||||
public class UnprocessableEntityException extends RuntimeException {
|
public class UnprocessableEntityException extends RuntimeException {
|
||||||
|
|
||||||
|
public UnprocessableEntityException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
public UnprocessableEntityException(String message) {
|
public UnprocessableEntityException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@ package org.dspace.app.rest.model;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Collection REST Resource
|
* The Collection REST Resource
|
||||||
@@ -45,6 +46,7 @@ public class CollectionRest extends DSpaceObjectRest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
@@ -58,4 +60,5 @@ public class CollectionRest extends DSpaceObjectRest {
|
|||||||
public void setDefaultAccessConditions(List<ResourcePolicyRest> defaultAccessConditions) {
|
public void setDefaultAccessConditions(List<ResourcePolicyRest> defaultAccessConditions) {
|
||||||
this.defaultAccessConditions = defaultAccessConditions;
|
this.defaultAccessConditions = defaultAccessConditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -7,8 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest.model;
|
package org.dspace.app.rest.model;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.dspace.app.rest.RestResourceController;
|
import org.dspace.app.rest.RestResourceController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22,7 +20,7 @@ public abstract class DSpaceObjectRest extends BaseObjectRest<String> {
|
|||||||
private String name;
|
private String name;
|
||||||
private String handle;
|
private String handle;
|
||||||
|
|
||||||
List<MetadataEntryRest> metadata;
|
MetadataRest metadata = new MetadataRest();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
@@ -53,11 +51,16 @@ public abstract class DSpaceObjectRest extends BaseObjectRest<String> {
|
|||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MetadataEntryRest> getMetadata() {
|
/**
|
||||||
|
* Gets the rest representation of all metadata of the DSpace object.
|
||||||
|
*
|
||||||
|
* @return the metadata.
|
||||||
|
*/
|
||||||
|
public MetadataRest getMetadata() {
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMetadata(List<MetadataEntryRest> metadata) {
|
public void setMetadata(MetadataRest metadata) {
|
||||||
this.metadata = metadata;
|
this.metadata = metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,6 +11,7 @@ import java.util.Date;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Item REST Resource
|
* The Item REST Resource
|
||||||
@@ -28,8 +29,6 @@ public class ItemRest extends DSpaceObjectRest {
|
|||||||
private CollectionRest owningCollection;
|
private CollectionRest owningCollection;
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private CollectionRest templateItemOf;
|
private CollectionRest templateItemOf;
|
||||||
//private EPerson submitter;
|
|
||||||
|
|
||||||
List<BitstreamRest> bitstreams;
|
List<BitstreamRest> bitstreams;
|
||||||
|
|
||||||
List<RelationshipRest> relationships;
|
List<RelationshipRest> relationships;
|
||||||
@@ -40,6 +39,7 @@ public class ItemRest extends DSpaceObjectRest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
@@ -1,73 +0,0 @@
|
|||||||
/**
|
|
||||||
* The contents of this file are subject to the license and copyright
|
|
||||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
|
||||||
* tree and available online at
|
|
||||||
*
|
|
||||||
* http://www.dspace.org/license/
|
|
||||||
*/
|
|
||||||
package org.dspace.app.rest.model;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An embeddable representation of the Metadata to use in with DSpace REST
|
|
||||||
* Resource
|
|
||||||
*
|
|
||||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
|
||||||
*/
|
|
||||||
public class MetadataEntryRest {
|
|
||||||
String key;
|
|
||||||
|
|
||||||
String value;
|
|
||||||
|
|
||||||
String language;
|
|
||||||
int place;
|
|
||||||
String authority;
|
|
||||||
int confidence;
|
|
||||||
|
|
||||||
public String getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKey(String key) {
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(String value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLanguage() {
|
|
||||||
return language;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLanguage(String language) {
|
|
||||||
this.language = language;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPlace() {
|
|
||||||
return place;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPlace(int place) {
|
|
||||||
this.place = place;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAuthority() {
|
|
||||||
return authority;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAuthority(String authority) {
|
|
||||||
this.authority = authority;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getConfidence() {
|
|
||||||
return confidence;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setConfidence(int confidence) {
|
|
||||||
this.confidence = confidence;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -8,6 +8,7 @@
|
|||||||
package org.dspace.app.rest.model;
|
package org.dspace.app.rest.model;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import org.dspace.app.rest.RestResourceController;
|
import org.dspace.app.rest.RestResourceController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,6 +62,7 @@ public class MetadataFieldRest extends BaseObjectRest<Integer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.model;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.SortedMap;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rest representation of a map of metadata keys to ordered lists of values.
|
||||||
|
*/
|
||||||
|
public class MetadataRest {
|
||||||
|
|
||||||
|
@JsonAnySetter
|
||||||
|
private SortedMap<String, List<MetadataValueRest>> map = new TreeMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the map.
|
||||||
|
*
|
||||||
|
* @return the map of keys to ordered values.
|
||||||
|
*/
|
||||||
|
@JsonAnyGetter
|
||||||
|
public SortedMap<String, List<MetadataValueRest>> getMap() {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the metadata values for a given key.
|
||||||
|
*
|
||||||
|
* @param key the key.
|
||||||
|
* @param values the values. The values will be ordered according to their {@code place} value, if
|
||||||
|
* nonnegative. Values that are negative (the default is -1) are assumed to be non-explicitly
|
||||||
|
* set and will will be ordered at the end of any explicitly ordered values, in the order
|
||||||
|
* they are passed to this method.
|
||||||
|
* @return this instance, to support chaining calls for easy initialization.
|
||||||
|
*/
|
||||||
|
public MetadataRest put(String key, MetadataValueRest... values) {
|
||||||
|
// determine highest explicitly ordered value
|
||||||
|
int highest = -1;
|
||||||
|
for (MetadataValueRest value : values) {
|
||||||
|
if (value.getPlace() > highest) {
|
||||||
|
highest = value.getPlace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add any non-explicitly ordered values after highest
|
||||||
|
for (MetadataValueRest value : values) {
|
||||||
|
if (value.getPlace() < 0) {
|
||||||
|
highest++;
|
||||||
|
value.setPlace(highest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map.put(key, Arrays.asList(values));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
return object instanceof MetadataRest && ((MetadataRest) object).getMap().equals(map);
|
||||||
|
}
|
||||||
|
}
|
@@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest.model;
|
package org.dspace.app.rest.model;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import org.dspace.app.rest.RestResourceController;
|
import org.dspace.app.rest.RestResourceController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,6 +40,7 @@ public class MetadataSchemaRest extends BaseObjectRest<Integer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
@@ -7,8 +7,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest.model;
|
package org.dspace.app.rest.model;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import java.util.List;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty.Access;
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import org.dspace.app.rest.converter.MetadataConverter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An embeddable representation of the Metadata to use in with DSpace REST
|
* An embeddable representation of the Metadata to use in with DSpace REST
|
||||||
@@ -26,8 +28,25 @@ public class MetadataValueRest {
|
|||||||
|
|
||||||
int confidence;
|
int confidence;
|
||||||
|
|
||||||
@JsonProperty(access = Access.READ_ONLY)
|
/**
|
||||||
int place;
|
* The order of this metadata value with respect to others in the same DSO with the same key.
|
||||||
|
*
|
||||||
|
* In the REST representation, all values of the same key are given as a json array that expresses
|
||||||
|
* their relative order, so there is no need to expose the exact numeric value publicly. The numeric
|
||||||
|
* value is only used at this level to ensure the intended order is respected when converting to/from json.
|
||||||
|
*
|
||||||
|
* @see MetadataConverter#convert(List)
|
||||||
|
* @see MetadataRest#put(String, MetadataValueRest...)
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
int place = -1;
|
||||||
|
|
||||||
|
public MetadataValueRest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public MetadataValueRest(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
return value;
|
return value;
|
||||||
@@ -68,5 +87,4 @@ public class MetadataValueRest {
|
|||||||
public void setPlace(int place) {
|
public void setPlace(int place) {
|
||||||
this.place = place;
|
this.place = place;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -45,4 +45,7 @@ public class JsonValueEvaluator implements LateObjectEvaluator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JsonNode getValueNode() {
|
||||||
|
return this.valueNode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,11 +14,14 @@ import java.util.ArrayList;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.dspace.app.rest.converter.BitstreamConverter;
|
import org.dspace.app.rest.converter.BitstreamConverter;
|
||||||
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||||
import org.dspace.app.rest.model.BitstreamRest;
|
import org.dspace.app.rest.model.BitstreamRest;
|
||||||
import org.dspace.app.rest.model.hateoas.BitstreamResource;
|
import org.dspace.app.rest.model.hateoas.BitstreamResource;
|
||||||
|
import org.dspace.app.rest.model.patch.Patch;
|
||||||
|
import org.dspace.app.rest.repository.patch.DSpaceObjectPatch;
|
||||||
import org.dspace.authorize.AuthorizeException;
|
import org.dspace.authorize.AuthorizeException;
|
||||||
import org.dspace.content.Bitstream;
|
import org.dspace.content.Bitstream;
|
||||||
import org.dspace.content.service.BitstreamService;
|
import org.dspace.content.service.BitstreamService;
|
||||||
@@ -39,16 +42,15 @@ import org.springframework.stereotype.Component;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@Component(BitstreamRest.CATEGORY + "." + BitstreamRest.NAME)
|
@Component(BitstreamRest.CATEGORY + "." + BitstreamRest.NAME)
|
||||||
public class BitstreamRestRepository extends DSpaceRestRepository<BitstreamRest, UUID> {
|
public class BitstreamRestRepository extends DSpaceObjectRestRepository<Bitstream, BitstreamRest> {
|
||||||
|
|
||||||
|
private final BitstreamService bs;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
BitstreamService bs;
|
public BitstreamRestRepository(BitstreamService dsoService,
|
||||||
|
BitstreamConverter dsoConverter) {
|
||||||
@Autowired
|
super(dsoService, dsoConverter, new DSpaceObjectPatch<BitstreamRest>() { });
|
||||||
BitstreamConverter converter;
|
this.bs = dsoService;
|
||||||
|
|
||||||
public BitstreamRestRepository() {
|
|
||||||
System.out.println("Repository initialized by Spring");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -70,7 +72,7 @@ public class BitstreamRestRepository extends DSpaceRestRepository<BitstreamRest,
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
return converter.fromModel(bit);
|
return dsoConverter.fromModel(bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -88,10 +90,17 @@ public class BitstreamRestRepository extends DSpaceRestRepository<BitstreamRest,
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
Page<BitstreamRest> page = new PageImpl<Bitstream>(bit, pageable, total).map(converter);
|
Page<BitstreamRest> page = new PageImpl<Bitstream>(bit, pageable, total).map(dsoConverter);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasPermission(#id, 'BITSTREAM', 'WRITE')")
|
||||||
|
protected void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID id,
|
||||||
|
Patch patch) throws AuthorizeException, SQLException {
|
||||||
|
patchDSpaceObject(apiCategory, model, id, patch);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<BitstreamRest> getDomainClass() {
|
public Class<BitstreamRest> getDomainClass() {
|
||||||
return BitstreamRest.class;
|
return BitstreamRest.class;
|
||||||
|
@@ -7,23 +7,38 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest.repository;
|
package org.dspace.app.rest.repository;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import javax.servlet.ServletInputStream;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.ws.rs.BadRequestException;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.dspace.app.rest.Parameter;
|
import org.dspace.app.rest.Parameter;
|
||||||
import org.dspace.app.rest.SearchRestMethod;
|
import org.dspace.app.rest.SearchRestMethod;
|
||||||
import org.dspace.app.rest.converter.CollectionConverter;
|
import org.dspace.app.rest.converter.CollectionConverter;
|
||||||
|
import org.dspace.app.rest.converter.MetadataConverter;
|
||||||
|
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
|
||||||
|
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||||
import org.dspace.app.rest.model.CollectionRest;
|
import org.dspace.app.rest.model.CollectionRest;
|
||||||
import org.dspace.app.rest.model.CommunityRest;
|
import org.dspace.app.rest.model.CommunityRest;
|
||||||
import org.dspace.app.rest.model.hateoas.CollectionResource;
|
import org.dspace.app.rest.model.hateoas.CollectionResource;
|
||||||
|
import org.dspace.app.rest.model.patch.Patch;
|
||||||
|
import org.dspace.app.rest.repository.patch.DSpaceObjectPatch;
|
||||||
|
import org.dspace.app.rest.utils.CollectionRestEqualityUtils;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
import org.dspace.content.Collection;
|
import org.dspace.content.Collection;
|
||||||
import org.dspace.content.Community;
|
import org.dspace.content.Community;
|
||||||
import org.dspace.content.service.CollectionService;
|
import org.dspace.content.service.CollectionService;
|
||||||
import org.dspace.content.service.CommunityService;
|
import org.dspace.content.service.CommunityService;
|
||||||
import org.dspace.core.Constants;
|
import org.dspace.core.Constants;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.util.UUIDUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageImpl;
|
import org.springframework.data.domain.PageImpl;
|
||||||
@@ -39,20 +54,27 @@ import org.springframework.stereotype.Component;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@Component(CollectionRest.CATEGORY + "." + CollectionRest.NAME)
|
@Component(CollectionRest.CATEGORY + "." + CollectionRest.NAME)
|
||||||
public class CollectionRestRepository extends DSpaceRestRepository<CollectionRest, UUID> {
|
public class CollectionRestRepository extends DSpaceObjectRestRepository<Collection, CollectionRest> {
|
||||||
|
|
||||||
|
private final CollectionService cs;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
CommunityService communityService;
|
CommunityService communityService;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
CollectionService cs;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
CollectionConverter converter;
|
CollectionConverter converter;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MetadataConverter metadataConverter;
|
||||||
|
|
||||||
public CollectionRestRepository() {
|
@Autowired
|
||||||
System.out.println("Repository initialized by Spring");
|
CollectionRestEqualityUtils collectionRestEqualityUtils;
|
||||||
|
|
||||||
|
|
||||||
|
public CollectionRestRepository(CollectionService dsoService,
|
||||||
|
CollectionConverter dsoConverter) {
|
||||||
|
super(dsoService, dsoConverter, new DSpaceObjectPatch<CollectionRest>() {});
|
||||||
|
this.cs = dsoService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -67,7 +89,7 @@ public class CollectionRestRepository extends DSpaceRestRepository<CollectionRes
|
|||||||
if (collection == null) {
|
if (collection == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return converter.fromModel(collection);
|
return dsoConverter.fromModel(collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -84,7 +106,7 @@ public class CollectionRestRepository extends DSpaceRestRepository<CollectionRes
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
Page<CollectionRest> page = new PageImpl<Collection>(collections, pageable, total).map(converter);
|
Page<CollectionRest> page = new PageImpl<Collection>(collections, pageable, total).map(dsoConverter);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +130,7 @@ public class CollectionRestRepository extends DSpaceRestRepository<CollectionRes
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
Page<CollectionRest> page = utils.getPage(collections, pageable).map(converter);
|
Page<CollectionRest> page = utils.getPage(collections, pageable).map(dsoConverter);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,10 +147,17 @@ public class CollectionRestRepository extends DSpaceRestRepository<CollectionRes
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
Page<CollectionRest> page = utils.getPage(collections, pageable).map(converter);
|
Page<CollectionRest> page = utils.getPage(collections, pageable).map(dsoConverter);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasPermission(#id, 'COLLECTION', 'WRITE')")
|
||||||
|
protected void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID id,
|
||||||
|
Patch patch) throws AuthorizeException, SQLException {
|
||||||
|
patchDSpaceObject(apiCategory, model, id, patch);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<CollectionRest> getDomainClass() {
|
public Class<CollectionRest> getDomainClass() {
|
||||||
return CollectionRest.class;
|
return CollectionRest.class;
|
||||||
@@ -139,4 +168,97 @@ public class CollectionRestRepository extends DSpaceRestRepository<CollectionRes
|
|||||||
return new CollectionResource(collection, utils, rels);
|
return new CollectionResource(collection, utils, rels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
protected CollectionRest createAndReturn(Context context) throws AuthorizeException {
|
||||||
|
HttpServletRequest req = getRequestService().getCurrentRequest().getHttpServletRequest();
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
CollectionRest collectionRest;
|
||||||
|
try {
|
||||||
|
ServletInputStream input = req.getInputStream();
|
||||||
|
collectionRest = mapper.readValue(input, CollectionRest.class);
|
||||||
|
} catch (IOException e1) {
|
||||||
|
throw new UnprocessableEntityException("Error parsing request body: " + e1.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection collection;
|
||||||
|
|
||||||
|
|
||||||
|
String parentCommunityString = req.getParameter("parent");
|
||||||
|
try {
|
||||||
|
Community parent = null;
|
||||||
|
if (StringUtils.isNotBlank(parentCommunityString)) {
|
||||||
|
|
||||||
|
UUID parentCommunityUuid = UUIDUtils.fromString(parentCommunityString);
|
||||||
|
if (parentCommunityUuid == null) {
|
||||||
|
throw new BadRequestException("The given parent was invalid: "
|
||||||
|
+ parentCommunityString);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent = communityService.find(context, parentCommunityUuid);
|
||||||
|
if (parent == null) {
|
||||||
|
throw new UnprocessableEntityException("Parent community for id: "
|
||||||
|
+ parentCommunityUuid + " not found");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new BadRequestException("The parent parameter cannot be left empty," +
|
||||||
|
"collections require a parent community.");
|
||||||
|
}
|
||||||
|
collection = cs.create(context, parent);
|
||||||
|
cs.update(context, collection);
|
||||||
|
metadataConverter.setMetadata(context, collection, collectionRest.getMetadata());
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException("Unable to create new Collection under parent Community " +
|
||||||
|
parentCommunityString, e);
|
||||||
|
}
|
||||||
|
return converter.convert(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasPermission(#id, 'COLLECTION', 'WRITE')")
|
||||||
|
protected CollectionRest put(Context context, HttpServletRequest request, String apiCategory, String model, UUID id,
|
||||||
|
JsonNode jsonNode)
|
||||||
|
throws RepositoryMethodNotImplementedException, SQLException, AuthorizeException {
|
||||||
|
CollectionRest collectionRest;
|
||||||
|
try {
|
||||||
|
collectionRest = new ObjectMapper().readValue(jsonNode.toString(), CollectionRest.class);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UnprocessableEntityException("Error parsing collection json: " + e.getMessage());
|
||||||
|
}
|
||||||
|
Collection collection = cs.find(context, id);
|
||||||
|
if (collection == null) {
|
||||||
|
throw new ResourceNotFoundException(apiCategory + "." + model + " with id: " + id + " not found");
|
||||||
|
}
|
||||||
|
CollectionRest originalCollectionRest = converter.fromModel(collection);
|
||||||
|
if (collectionRestEqualityUtils.isCollectionRestEqualWithoutMetadata(originalCollectionRest, collectionRest)) {
|
||||||
|
metadataConverter.setMetadata(context, collection, collectionRest.getMetadata());
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("The UUID in the Json and the UUID in the url do not match: "
|
||||||
|
+ id + ", "
|
||||||
|
+ collectionRest.getId());
|
||||||
|
}
|
||||||
|
return converter.fromModel(collection);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasPermission(#id, 'COLLECTION', 'DELETE')")
|
||||||
|
protected void delete(Context context, UUID id) throws AuthorizeException {
|
||||||
|
Collection collection = null;
|
||||||
|
try {
|
||||||
|
collection = cs.find(context, id);
|
||||||
|
if (collection == null) {
|
||||||
|
throw new ResourceNotFoundException(
|
||||||
|
CollectionRest.CATEGORY + "." + CollectionRest.NAME + " with id: " + id + " not found");
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException("Unable to find Collection with id = " + id, e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
cs.delete(context, collection);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException("Unable to delete Collection with id = " + id, e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Unable to delete collection because the logo couldn't be deleted", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -12,22 +12,29 @@ import java.sql.SQLException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import javax.servlet.ServletInputStream;
|
import javax.servlet.ServletInputStream;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.ws.rs.BadRequestException;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.dspace.app.rest.Parameter;
|
import org.dspace.app.rest.Parameter;
|
||||||
import org.dspace.app.rest.SearchRestMethod;
|
import org.dspace.app.rest.SearchRestMethod;
|
||||||
import org.dspace.app.rest.converter.CommunityConverter;
|
import org.dspace.app.rest.converter.CommunityConverter;
|
||||||
|
import org.dspace.app.rest.converter.MetadataConverter;
|
||||||
|
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
|
||||||
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||||
import org.dspace.app.rest.model.CommunityRest;
|
import org.dspace.app.rest.model.CommunityRest;
|
||||||
import org.dspace.app.rest.model.MetadataEntryRest;
|
|
||||||
import org.dspace.app.rest.model.hateoas.CommunityResource;
|
import org.dspace.app.rest.model.hateoas.CommunityResource;
|
||||||
|
import org.dspace.app.rest.model.patch.Patch;
|
||||||
|
import org.dspace.app.rest.repository.patch.DSpaceObjectPatch;
|
||||||
|
import org.dspace.app.rest.utils.CommunityRestEqualityUtils;
|
||||||
import org.dspace.authorize.AuthorizeException;
|
import org.dspace.authorize.AuthorizeException;
|
||||||
import org.dspace.content.Community;
|
import org.dspace.content.Community;
|
||||||
import org.dspace.content.service.CommunityService;
|
import org.dspace.content.service.CommunityService;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.util.UUIDUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageImpl;
|
import org.springframework.data.domain.PageImpl;
|
||||||
@@ -43,16 +50,23 @@ import org.springframework.stereotype.Component;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@Component(CommunityRest.CATEGORY + "." + CommunityRest.NAME)
|
@Component(CommunityRest.CATEGORY + "." + CommunityRest.NAME)
|
||||||
public class CommunityRestRepository extends DSpaceRestRepository<CommunityRest, UUID> {
|
public class CommunityRestRepository extends DSpaceObjectRestRepository<Community, CommunityRest> {
|
||||||
|
|
||||||
@Autowired
|
private final CommunityService cs;
|
||||||
CommunityService cs;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
CommunityConverter converter;
|
CommunityConverter converter;
|
||||||
|
|
||||||
public CommunityRestRepository() {
|
@Autowired
|
||||||
System.out.println("Repository initialized by Spring");
|
MetadataConverter metadataConverter;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
CommunityRestEqualityUtils communityRestEqualityUtils;
|
||||||
|
|
||||||
|
public CommunityRestRepository(CommunityService dsoService,
|
||||||
|
CommunityConverter dsoConverter) {
|
||||||
|
super(dsoService, dsoConverter, new DSpaceObjectPatch<CommunityRest>() {});
|
||||||
|
this.cs = dsoService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -60,7 +74,7 @@ public class CommunityRestRepository extends DSpaceRestRepository<CommunityRest,
|
|||||||
protected CommunityRest createAndReturn(Context context) throws AuthorizeException {
|
protected CommunityRest createAndReturn(Context context) throws AuthorizeException {
|
||||||
HttpServletRequest req = getRequestService().getCurrentRequest().getHttpServletRequest();
|
HttpServletRequest req = getRequestService().getCurrentRequest().getHttpServletRequest();
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
CommunityRest communityRest = null;
|
CommunityRest communityRest;
|
||||||
try {
|
try {
|
||||||
ServletInputStream input = req.getInputStream();
|
ServletInputStream input = req.getInputStream();
|
||||||
communityRest = mapper.readValue(input, CommunityRest.class);
|
communityRest = mapper.readValue(input, CommunityRest.class);
|
||||||
@@ -68,23 +82,34 @@ public class CommunityRestRepository extends DSpaceRestRepository<CommunityRest,
|
|||||||
throw new UnprocessableEntityException("Error parsing request body: " + e1.toString());
|
throw new UnprocessableEntityException("Error parsing request body: " + e1.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
Community community = null;
|
Community community;
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
community = cs.create(null, context);
|
Community parent = null;
|
||||||
|
String parentCommunityString = req.getParameter("parent");
|
||||||
|
if (StringUtils.isNotBlank(parentCommunityString)) {
|
||||||
|
|
||||||
|
UUID parentCommunityUuid = UUIDUtils.fromString(parentCommunityString);
|
||||||
|
if (parentCommunityUuid == null) {
|
||||||
|
throw new BadRequestException("The given parent parameter was invalid: "
|
||||||
|
+ parentCommunityString);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent = cs.find(context, parentCommunityUuid);
|
||||||
|
if (parent == null) {
|
||||||
|
throw new UnprocessableEntityException("Parent community for id: "
|
||||||
|
+ parentCommunityUuid + " not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
community = cs.create(parent, context);
|
||||||
cs.update(context, community);
|
cs.update(context, community);
|
||||||
if (communityRest.getMetadata() != null) {
|
metadataConverter.setMetadata(context, community, communityRest.getMetadata());
|
||||||
for (MetadataEntryRest mer : communityRest.getMetadata()) {
|
|
||||||
String[] metadatakey = mer.getKey().split("\\.");
|
|
||||||
cs.addMetadata(context, community, metadatakey[0], metadatakey[1],
|
|
||||||
metadatakey.length == 3 ? metadatakey[2] : null, mer.getLanguage(), mer.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return converter.convert(community);
|
return dsoConverter.convert(community);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -99,7 +124,7 @@ public class CommunityRestRepository extends DSpaceRestRepository<CommunityRest,
|
|||||||
if (community == null) {
|
if (community == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return converter.fromModel(community);
|
return dsoConverter.fromModel(community);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -116,7 +141,7 @@ public class CommunityRestRepository extends DSpaceRestRepository<CommunityRest,
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
Page<CommunityRest> page = new PageImpl<Community>(communities, pageable, total).map(converter);
|
Page<CommunityRest> page = new PageImpl<Community>(communities, pageable, total).map(dsoConverter);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +155,7 @@ public class CommunityRestRepository extends DSpaceRestRepository<CommunityRest,
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
Page<CommunityRest> page = utils.getPage(topCommunities, pageable).map(converter);
|
Page<CommunityRest> page = utils.getPage(topCommunities, pageable).map(dsoConverter);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,10 +176,17 @@ public class CommunityRestRepository extends DSpaceRestRepository<CommunityRest,
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
Page<CommunityRest> page = utils.getPage(subCommunities, pageable).map(converter);
|
Page<CommunityRest> page = utils.getPage(subCommunities, pageable).map(dsoConverter);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasPermission(#id, 'COMMUNITY', 'WRITE')")
|
||||||
|
protected void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID id,
|
||||||
|
Patch patch) throws AuthorizeException, SQLException {
|
||||||
|
patchDSpaceObject(apiCategory, model, id, patch);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<CommunityRest> getDomainClass() {
|
public Class<CommunityRest> getDomainClass() {
|
||||||
return CommunityRest.class;
|
return CommunityRest.class;
|
||||||
@@ -165,4 +197,49 @@ public class CommunityRestRepository extends DSpaceRestRepository<CommunityRest,
|
|||||||
return new CommunityResource(community, utils, rels);
|
return new CommunityResource(community, utils, rels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasPermission(#id, 'COMMUNITY', 'WRITE')")
|
||||||
|
protected CommunityRest put(Context context, HttpServletRequest request, String apiCategory, String model, UUID id,
|
||||||
|
JsonNode jsonNode)
|
||||||
|
throws RepositoryMethodNotImplementedException, SQLException, AuthorizeException {
|
||||||
|
CommunityRest communityRest;
|
||||||
|
try {
|
||||||
|
communityRest = new ObjectMapper().readValue(jsonNode.toString(), CommunityRest.class);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UnprocessableEntityException("Error parsing community json: " + e.getMessage());
|
||||||
|
}
|
||||||
|
Community community = cs.find(context, id);
|
||||||
|
if (community == null) {
|
||||||
|
throw new ResourceNotFoundException(apiCategory + "." + model + " with id: " + id + " not found");
|
||||||
|
}
|
||||||
|
CommunityRest originalCommunityRest = converter.fromModel(community);
|
||||||
|
if (communityRestEqualityUtils.isCommunityRestEqualWithoutMetadata(originalCommunityRest, communityRest)) {
|
||||||
|
metadataConverter.setMetadata(context, community, communityRest.getMetadata());
|
||||||
|
} else {
|
||||||
|
throw new UnprocessableEntityException("The given JSON and the original Community differ more " +
|
||||||
|
"than just the metadata");
|
||||||
|
}
|
||||||
|
return converter.fromModel(community);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasPermission(#id, 'COMMUNITY', 'DELETE')")
|
||||||
|
protected void delete(Context context, UUID id) throws AuthorizeException {
|
||||||
|
Community community = null;
|
||||||
|
try {
|
||||||
|
community = cs.find(context, id);
|
||||||
|
if (community == null) {
|
||||||
|
throw new ResourceNotFoundException(
|
||||||
|
CommunityRest.CATEGORY + "." + CommunityRest.NAME + " with id: " + id + " not found");
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException("Unable to find Community with id = " + id, e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
cs.delete(context, community);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException("Unable to delete Community with id = " + id, e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Unable to delete community because the logo couldn't be deleted", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.repository;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.converter.DSpaceObjectConverter;
|
||||||
|
import org.dspace.app.rest.converter.MetadataConverter;
|
||||||
|
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||||
|
import org.dspace.app.rest.model.DSpaceObjectRest;
|
||||||
|
import org.dspace.app.rest.model.patch.Patch;
|
||||||
|
import org.dspace.app.rest.repository.patch.DSpaceObjectPatch;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.content.DSpaceObject;
|
||||||
|
import org.dspace.content.service.DSpaceObjectService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for DSpaceObject-based Rest Repositories, providing common functionality.
|
||||||
|
*
|
||||||
|
* @param <M> the specific type of DSpaceObject.
|
||||||
|
* @param <R> the corresponding DSpaceObjectRest.
|
||||||
|
*/
|
||||||
|
public abstract class DSpaceObjectRestRepository<M extends DSpaceObject, R extends DSpaceObjectRest>
|
||||||
|
extends DSpaceRestRepository<R, UUID> {
|
||||||
|
|
||||||
|
final DSpaceObjectService<M> dsoService;
|
||||||
|
final DSpaceObjectPatch<R> dsoPatch;
|
||||||
|
final DSpaceObjectConverter<M, R> dsoConverter;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MetadataConverter metadataConverter;
|
||||||
|
|
||||||
|
DSpaceObjectRestRepository(DSpaceObjectService<M> dsoService,
|
||||||
|
DSpaceObjectConverter<M, R> dsoConverter,
|
||||||
|
DSpaceObjectPatch<R> dsoPatch) {
|
||||||
|
this.dsoService = dsoService;
|
||||||
|
this.dsoPatch = dsoPatch;
|
||||||
|
this.dsoConverter = dsoConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the DSpaceObject according to the given Patch.
|
||||||
|
*
|
||||||
|
* @param apiCategory the api category.
|
||||||
|
* @param model the api model.
|
||||||
|
* @param id the id of the DSpaceObject.
|
||||||
|
* @param patch the patch to apply.
|
||||||
|
* @throws AuthorizeException if the action is unauthorized.
|
||||||
|
* @throws ResourceNotFoundException if the DSpace object was not found.
|
||||||
|
* @throws SQLException if a database error occurs.
|
||||||
|
* @throws UnprocessableEntityException if the patch attempts to modify an unmodifiable attribute of the object.
|
||||||
|
*/
|
||||||
|
protected void patchDSpaceObject(String apiCategory, String model, UUID id, Patch patch)
|
||||||
|
throws AuthorizeException, ResourceNotFoundException, SQLException, UnprocessableEntityException {
|
||||||
|
M dso = dsoService.find(obtainContext(), id);
|
||||||
|
if (dso == null) {
|
||||||
|
throw new ResourceNotFoundException(apiCategory + "." + model + " with id: " + id + " not found");
|
||||||
|
}
|
||||||
|
R dsoRest = dsoPatch.patch(findOne(id), patch.getOperations());
|
||||||
|
updateDSpaceObject(dso, dsoRest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the changes in the given rest DSpace object to the model DSpace object.
|
||||||
|
* The default implementation updates metadata if needed. Subclasses should extend
|
||||||
|
* to support updates of additional properties.
|
||||||
|
*
|
||||||
|
* @param dso the dso to apply changes to.
|
||||||
|
* @param dsoRest the rest representation of the new desired state.
|
||||||
|
*/
|
||||||
|
protected void updateDSpaceObject(M dso, R dsoRest)
|
||||||
|
throws AuthorizeException, SQLException {
|
||||||
|
R origDsoRest = dsoConverter.fromModel(dso);
|
||||||
|
if (!origDsoRest.getMetadata().equals(dsoRest.getMetadata())) {
|
||||||
|
metadataConverter.setMetadata(obtainContext(), dso, dsoRest.getMetadata());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -27,6 +27,7 @@ import org.dspace.app.rest.model.patch.Patch;
|
|||||||
import org.dspace.app.util.DCInputsReaderException;
|
import org.dspace.app.util.DCInputsReaderException;
|
||||||
import org.dspace.authorize.AuthorizeException;
|
import org.dspace.authorize.AuthorizeException;
|
||||||
import org.dspace.content.DSpaceObject;
|
import org.dspace.content.DSpaceObject;
|
||||||
|
import org.dspace.content.service.MetadataFieldService;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
@@ -56,6 +57,9 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
|
|||||||
@Autowired
|
@Autowired
|
||||||
private DSpaceRestRepository<T, ID> thisRepository;
|
private DSpaceRestRepository<T, ID> thisRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MetadataFieldService metadataFieldService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <S extends T> S save(S entity) {
|
public <S extends T> S save(S entity) {
|
||||||
Context context = null;
|
Context context = null;
|
||||||
@@ -451,24 +455,28 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply an update to the REST object via JSON PUT
|
* This method will fully replace the REST object with the given UUID with the REST object that is described
|
||||||
|
* in the JsonNode parameter
|
||||||
*
|
*
|
||||||
* @param request the http request
|
* @param request the http request
|
||||||
* @param apiCategory the API category e.g. "api"
|
* @param apiCategory the API category e.g. "api"
|
||||||
* @param model the DSpace model e.g. "metadatafield"
|
* @param model the DSpace model e.g. "metadatafield"
|
||||||
* @param id the ID of the target REST object
|
* @param uuid the ID of the target REST object
|
||||||
* @param jsonNode the part of the request body representing the updated rest object
|
* @param jsonNode the part of the request body representing the updated rest object
|
||||||
* @return the updated REST object
|
* @return the updated REST object
|
||||||
*/
|
*/
|
||||||
public T put(HttpServletRequest request, String apiCategory, String model, ID id, JsonNode jsonNode) {
|
public T put(HttpServletRequest request, String apiCategory, String model, ID uuid, JsonNode jsonNode) {
|
||||||
Context context = obtainContext();
|
Context context = obtainContext();
|
||||||
try {
|
try {
|
||||||
thisRepository.put(context, request, apiCategory, model, id, jsonNode);
|
thisRepository.put(context, request, apiCategory, model, uuid, jsonNode);
|
||||||
context.commit();
|
context.commit();
|
||||||
} catch (SQLException | AuthorizeException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException("Unable to update DSpace object " + model + " with id=" + uuid, e);
|
||||||
|
} catch (AuthorizeException e) {
|
||||||
|
throw new RuntimeException("Unable to perform PUT request as the " +
|
||||||
|
"current user does not have sufficient rights", e);
|
||||||
}
|
}
|
||||||
return findOne(id);
|
return findOne(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -12,7 +12,6 @@ import java.sql.SQLException;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
@@ -20,26 +19,22 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import org.dspace.app.rest.Parameter;
|
import org.dspace.app.rest.Parameter;
|
||||||
import org.dspace.app.rest.SearchRestMethod;
|
import org.dspace.app.rest.SearchRestMethod;
|
||||||
import org.dspace.app.rest.converter.EPersonConverter;
|
import org.dspace.app.rest.converter.EPersonConverter;
|
||||||
import org.dspace.app.rest.exception.PatchBadRequestException;
|
import org.dspace.app.rest.converter.MetadataConverter;
|
||||||
import org.dspace.app.rest.exception.RESTAuthorizationException;
|
import org.dspace.app.rest.exception.RESTAuthorizationException;
|
||||||
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||||
import org.dspace.app.rest.model.EPersonRest;
|
import org.dspace.app.rest.model.EPersonRest;
|
||||||
import org.dspace.app.rest.model.MetadataEntryRest;
|
|
||||||
import org.dspace.app.rest.model.hateoas.EPersonResource;
|
import org.dspace.app.rest.model.hateoas.EPersonResource;
|
||||||
import org.dspace.app.rest.model.patch.Operation;
|
|
||||||
import org.dspace.app.rest.model.patch.Patch;
|
import org.dspace.app.rest.model.patch.Patch;
|
||||||
import org.dspace.app.rest.repository.patch.EPersonPatch;
|
import org.dspace.app.rest.repository.patch.EPersonPatch;
|
||||||
import org.dspace.authorize.AuthorizeException;
|
import org.dspace.authorize.AuthorizeException;
|
||||||
import org.dspace.authorize.service.AuthorizeService;
|
import org.dspace.authorize.service.AuthorizeService;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
import org.dspace.eperson.EPerson;
|
import org.dspace.eperson.EPerson;
|
||||||
import org.dspace.eperson.factory.EPersonServiceFactory;
|
|
||||||
import org.dspace.eperson.service.EPersonService;
|
import org.dspace.eperson.service.EPersonService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageImpl;
|
import org.springframework.data.domain.PageImpl;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@@ -51,18 +46,26 @@ import org.springframework.stereotype.Component;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@Component(EPersonRest.CATEGORY + "." + EPersonRest.NAME)
|
@Component(EPersonRest.CATEGORY + "." + EPersonRest.NAME)
|
||||||
public class EPersonRestRepository extends DSpaceRestRepository<EPersonRest, UUID> {
|
public class EPersonRestRepository extends DSpaceObjectRestRepository<EPerson, EPersonRest> {
|
||||||
EPersonService es = EPersonServiceFactory.getInstance().getEPersonService();
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
AuthorizeService authorizeService;
|
AuthorizeService authorizeService;
|
||||||
|
|
||||||
|
private final EPersonService es;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
EPersonConverter converter;
|
MetadataConverter metadataConverter;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
EPersonPatch epersonPatch;
|
EPersonPatch epersonPatch;
|
||||||
|
|
||||||
|
public EPersonRestRepository(EPersonService dsoService,
|
||||||
|
EPersonConverter dsoConverter,
|
||||||
|
EPersonPatch dsoPatch) {
|
||||||
|
super(dsoService, dsoConverter, dsoPatch);
|
||||||
|
this.es = dsoService;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected EPersonRest createAndReturn(Context context)
|
protected EPersonRest createAndReturn(Context context)
|
||||||
throws AuthorizeException {
|
throws AuthorizeException {
|
||||||
@@ -89,18 +92,12 @@ public class EPersonRestRepository extends DSpaceRestRepository<EPersonRest, UUI
|
|||||||
es.setPassword(eperson, epersonRest.getPassword());
|
es.setPassword(eperson, epersonRest.getPassword());
|
||||||
}
|
}
|
||||||
es.update(context, eperson);
|
es.update(context, eperson);
|
||||||
if (epersonRest.getMetadata() != null) {
|
metadataConverter.setMetadata(context, eperson, epersonRest.getMetadata());
|
||||||
for (MetadataEntryRest mer : epersonRest.getMetadata()) {
|
|
||||||
String[] metadatakey = mer.getKey().split("\\.");
|
|
||||||
es.addMetadata(context, eperson, metadatakey[0], metadatakey[1],
|
|
||||||
metadatakey.length == 3 ? metadatakey[2] : null, mer.getLanguage(), mer.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return converter.convert(eperson);
|
return dsoConverter.convert(eperson);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -115,7 +112,7 @@ public class EPersonRestRepository extends DSpaceRestRepository<EPersonRest, UUI
|
|||||||
if (eperson == null) {
|
if (eperson == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return converter.fromModel(eperson);
|
return dsoConverter.fromModel(eperson);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -133,7 +130,7 @@ public class EPersonRestRepository extends DSpaceRestRepository<EPersonRest, UUI
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
Page<EPersonRest> page = new PageImpl<EPerson>(epersons, pageable, total).map(converter);
|
Page<EPersonRest> page = new PageImpl<EPerson>(epersons, pageable, total).map(dsoConverter);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +156,7 @@ public class EPersonRestRepository extends DSpaceRestRepository<EPersonRest, UUI
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
Page<EPersonRest> page = new PageImpl<EPerson>(epersons, pageable, total).map(converter);
|
Page<EPersonRest> page = new PageImpl<EPerson>(epersons, pageable, total).map(dsoConverter);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,42 +182,22 @@ public class EPersonRestRepository extends DSpaceRestRepository<EPersonRest, UUI
|
|||||||
if (eperson == null) {
|
if (eperson == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return converter.fromModel(eperson);
|
return dsoConverter.fromModel(eperson);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@PreAuthorize("hasAuthority('ADMIN')")
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
public void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID uuid,
|
protected void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID uuid,
|
||||||
Patch patch)
|
Patch patch) throws AuthorizeException, SQLException {
|
||||||
throws UnprocessableEntityException, PatchBadRequestException, AuthorizeException,
|
patchDSpaceObject(apiCategory, model, uuid, patch);
|
||||||
ResourceNotFoundException {
|
|
||||||
|
|
||||||
try {
|
|
||||||
EPerson eperson = es.find(context, uuid);
|
|
||||||
if (eperson == null) {
|
|
||||||
throw new ResourceNotFoundException(apiCategory + "." + model + " with id: " + uuid + " not found");
|
|
||||||
}
|
|
||||||
List<Operation> operations = patch.getOperations();
|
|
||||||
EPersonRest ePersonRest = findOne(context, uuid);
|
|
||||||
EPersonRest patchedModel = (EPersonRest) epersonPatch.patch(ePersonRest, operations);
|
|
||||||
updatePatchedValues(context, patchedModel, eperson);
|
|
||||||
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Applies changes in the rest model.
|
protected void updateDSpaceObject(EPerson ePerson, EPersonRest ePersonRest)
|
||||||
* @param context
|
throws AuthorizeException, SQLException {
|
||||||
* @param ePersonRest the updated eperson rest
|
super.updateDSpaceObject(ePerson, ePersonRest);
|
||||||
* @param ePerson the eperson content object
|
|
||||||
* @throws SQLException
|
|
||||||
* @throws AuthorizeException
|
|
||||||
*/
|
|
||||||
private void updatePatchedValues(Context context, EPersonRest ePersonRest, EPerson ePerson)
|
|
||||||
throws SQLException, AuthorizeException {
|
|
||||||
|
|
||||||
|
Context context = obtainContext();
|
||||||
if (ePersonRest.getPassword() != null) {
|
if (ePersonRest.getPassword() != null) {
|
||||||
es.setPassword(ePerson, ePersonRest.getPassword());
|
es.setPassword(ePerson, ePersonRest.getPassword());
|
||||||
}
|
}
|
||||||
@@ -235,7 +212,6 @@ public class EPersonRestRepository extends DSpaceRestRepository<EPersonRest, UUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
es.update(context, ePerson);
|
es.update(context, ePerson);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -16,11 +16,13 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.dspace.app.rest.converter.GroupConverter;
|
import org.dspace.app.rest.converter.GroupConverter;
|
||||||
|
import org.dspace.app.rest.converter.MetadataConverter;
|
||||||
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
|
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
|
||||||
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||||
import org.dspace.app.rest.model.GroupRest;
|
import org.dspace.app.rest.model.GroupRest;
|
||||||
import org.dspace.app.rest.model.MetadataEntryRest;
|
|
||||||
import org.dspace.app.rest.model.hateoas.GroupResource;
|
import org.dspace.app.rest.model.hateoas.GroupResource;
|
||||||
|
import org.dspace.app.rest.model.patch.Patch;
|
||||||
|
import org.dspace.app.rest.repository.patch.DSpaceObjectPatch;
|
||||||
import org.dspace.authorize.AuthorizeException;
|
import org.dspace.authorize.AuthorizeException;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
import org.dspace.eperson.Group;
|
import org.dspace.eperson.Group;
|
||||||
@@ -39,12 +41,19 @@ import org.springframework.stereotype.Component;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@Component(GroupRest.CATEGORY + "." + GroupRest.NAME)
|
@Component(GroupRest.CATEGORY + "." + GroupRest.NAME)
|
||||||
public class GroupRestRepository extends DSpaceRestRepository<GroupRest, UUID> {
|
public class GroupRestRepository extends DSpaceObjectRestRepository<Group, GroupRest> {
|
||||||
@Autowired
|
@Autowired
|
||||||
GroupService gs;
|
GroupService gs;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
GroupConverter converter;
|
GroupRestRepository(GroupService dsoService,
|
||||||
|
GroupConverter dsoConverter) {
|
||||||
|
super(dsoService, dsoConverter, new DSpaceObjectPatch<GroupRest>() {});
|
||||||
|
this.gs = dsoService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MetadataConverter metadataConverter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@PreAuthorize("hasAuthority('ADMIN')")
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
@@ -65,19 +74,12 @@ public class GroupRestRepository extends DSpaceRestRepository<GroupRest, UUID> {
|
|||||||
group = gs.create(context);
|
group = gs.create(context);
|
||||||
gs.setName(group, groupRest.getName());
|
gs.setName(group, groupRest.getName());
|
||||||
gs.update(context, group);
|
gs.update(context, group);
|
||||||
|
metadataConverter.setMetadata(context, group, groupRest.getMetadata());
|
||||||
if (groupRest.getMetadata() != null) {
|
|
||||||
for (MetadataEntryRest mer: groupRest.getMetadata()) {
|
|
||||||
String[] metadatakey = mer.getKey().split("\\.");
|
|
||||||
gs.addMetadata(context, group, metadatakey[0], metadatakey[1],
|
|
||||||
metadatakey.length == 3 ? metadatakey[2] : null, mer.getLanguage(), mer.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SQLException excSQL) {
|
} catch (SQLException excSQL) {
|
||||||
throw new RuntimeException(excSQL.getMessage(), excSQL);
|
throw new RuntimeException(excSQL.getMessage(), excSQL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return converter.convert(group);
|
return dsoConverter.convert(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -92,7 +94,7 @@ public class GroupRestRepository extends DSpaceRestRepository<GroupRest, UUID> {
|
|||||||
if (group == null) {
|
if (group == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return converter.fromModel(group);
|
return dsoConverter.fromModel(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('ADMIN')")
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
@@ -106,10 +108,17 @@ public class GroupRestRepository extends DSpaceRestRepository<GroupRest, UUID> {
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
Page<GroupRest> page = new PageImpl<Group>(groups, pageable, total).map(converter);
|
Page<GroupRest> page = new PageImpl<Group>(groups, pageable, total).map(dsoConverter);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasPermission(#id, 'GROUP', 'WRITE')")
|
||||||
|
protected void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID id,
|
||||||
|
Patch patch) throws AuthorizeException, SQLException {
|
||||||
|
patchDSpaceObject(apiCategory, model, id, patch);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<GroupRest> getDomainClass() {
|
public Class<GroupRest> getDomainClass() {
|
||||||
return GroupRest.class;
|
return GroupRest.class;
|
||||||
|
@@ -13,21 +13,32 @@ import java.util.ArrayList;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import javax.servlet.ServletInputStream;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.ws.rs.BadRequestException;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
import org.dspace.app.rest.converter.ItemConverter;
|
import org.dspace.app.rest.converter.ItemConverter;
|
||||||
import org.dspace.app.rest.exception.PatchBadRequestException;
|
import org.dspace.app.rest.converter.MetadataConverter;
|
||||||
|
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
|
||||||
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||||
import org.dspace.app.rest.model.ItemRest;
|
import org.dspace.app.rest.model.ItemRest;
|
||||||
import org.dspace.app.rest.model.hateoas.ItemResource;
|
import org.dspace.app.rest.model.hateoas.ItemResource;
|
||||||
import org.dspace.app.rest.model.patch.Operation;
|
|
||||||
import org.dspace.app.rest.model.patch.Patch;
|
import org.dspace.app.rest.model.patch.Patch;
|
||||||
import org.dspace.app.rest.repository.patch.ItemPatch;
|
import org.dspace.app.rest.repository.patch.ItemPatch;
|
||||||
import org.dspace.authorize.AuthorizeException;
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.content.Collection;
|
||||||
import org.dspace.content.Item;
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.WorkspaceItem;
|
||||||
|
import org.dspace.content.service.CollectionService;
|
||||||
|
import org.dspace.content.service.InstallItemService;
|
||||||
import org.dspace.content.service.ItemService;
|
import org.dspace.content.service.ItemService;
|
||||||
|
import org.dspace.content.service.WorkspaceItemService;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.util.UUIDUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageImpl;
|
import org.springframework.data.domain.PageImpl;
|
||||||
@@ -43,22 +54,35 @@ import org.springframework.stereotype.Component;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@Component(ItemRest.CATEGORY + "." + ItemRest.NAME)
|
@Component(ItemRest.CATEGORY + "." + ItemRest.NAME)
|
||||||
public class ItemRestRepository extends DSpaceRestRepository<ItemRest, UUID> {
|
public class ItemRestRepository extends DSpaceObjectRestRepository<Item, ItemRest> {
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger(ItemRestRepository.class);
|
private static final Logger log = Logger.getLogger(ItemRestRepository.class);
|
||||||
|
|
||||||
@Autowired
|
private final ItemService is;
|
||||||
ItemService is;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
ItemConverter converter;
|
MetadataConverter metadataConverter;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
ItemPatch itemPatch;
|
ItemPatch itemPatch;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
WorkspaceItemService workspaceItemService;
|
||||||
|
|
||||||
public ItemRestRepository() {
|
@Autowired
|
||||||
System.out.println("Repository initialized by Spring");
|
ItemService itemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
CollectionService collectionService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
InstallItemService installItemService;
|
||||||
|
|
||||||
|
public ItemRestRepository(ItemService dsoService,
|
||||||
|
ItemConverter dsoConverter,
|
||||||
|
ItemPatch dsoPatch) {
|
||||||
|
super(dsoService, dsoConverter, dsoPatch);
|
||||||
|
this.is = dsoService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -73,7 +97,7 @@ public class ItemRestRepository extends DSpaceRestRepository<ItemRest, UUID> {
|
|||||||
if (item == null) {
|
if (item == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return converter.fromModel(item);
|
return dsoConverter.fromModel(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -92,41 +116,23 @@ public class ItemRestRepository extends DSpaceRestRepository<ItemRest, UUID> {
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
Page<ItemRest> page = new PageImpl<Item>(items, pageable, total).map(converter);
|
Page<ItemRest> page = new PageImpl<Item>(items, pageable, total).map(dsoConverter);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID uuid,
|
@PreAuthorize("hasPermission(#id, 'ITEM', 'WRITE')")
|
||||||
Patch patch)
|
protected void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID id,
|
||||||
throws UnprocessableEntityException, PatchBadRequestException, SQLException, AuthorizeException,
|
Patch patch) throws AuthorizeException, SQLException {
|
||||||
ResourceNotFoundException {
|
patchDSpaceObject(apiCategory, model, id, patch);
|
||||||
|
|
||||||
Item item = is.find(context, uuid);
|
|
||||||
|
|
||||||
if (item == null) {
|
|
||||||
throw new ResourceNotFoundException(apiCategory + "." + model + " with id: " + uuid + " not found");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Operation> operations = patch.getOperations();
|
@Override
|
||||||
ItemRest itemRest = findOne(uuid);
|
protected void updateDSpaceObject(Item item, ItemRest itemRest)
|
||||||
|
throws AuthorizeException, SQLException {
|
||||||
|
super.updateDSpaceObject(item, itemRest);
|
||||||
|
|
||||||
ItemRest patchedModel = (ItemRest) itemPatch.patch(itemRest, operations);
|
Context context = obtainContext();
|
||||||
updatePatchedValues(context, patchedModel, item);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Persists changes to the rest model.
|
|
||||||
* @param context
|
|
||||||
* @param itemRest the updated item rest resource
|
|
||||||
* @param item the item content object
|
|
||||||
* @throws SQLException
|
|
||||||
* @throws AuthorizeException
|
|
||||||
*/
|
|
||||||
private void updatePatchedValues(Context context, ItemRest itemRest, Item item)
|
|
||||||
throws SQLException, AuthorizeException {
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (itemRest.getWithdrawn() != item.isWithdrawn()) {
|
if (itemRest.getWithdrawn() != item.isWithdrawn()) {
|
||||||
if (itemRest.getWithdrawn()) {
|
if (itemRest.getWithdrawn()) {
|
||||||
is.withdraw(context, item);
|
is.withdraw(context, item);
|
||||||
@@ -138,10 +144,6 @@ public class ItemRestRepository extends DSpaceRestRepository<ItemRest, UUID> {
|
|||||||
item.setDiscoverable(itemRest.getDiscoverable());
|
item.setDiscoverable(itemRest.getDiscoverable());
|
||||||
is.update(context, item);
|
is.update(context, item);
|
||||||
}
|
}
|
||||||
} catch (SQLException | AuthorizeException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -155,10 +157,15 @@ public class ItemRestRepository extends DSpaceRestRepository<ItemRest, UUID> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
protected void delete(Context context, UUID id) throws AuthorizeException {
|
protected void delete(Context context, UUID id) throws AuthorizeException {
|
||||||
Item item = null;
|
Item item = null;
|
||||||
try {
|
try {
|
||||||
item = is.find(context, id);
|
item = is.find(context, id);
|
||||||
|
if (item == null) {
|
||||||
|
throw new ResourceNotFoundException(ItemRest.CATEGORY + "." + ItemRest.NAME +
|
||||||
|
" with id: " + id + " not found");
|
||||||
|
}
|
||||||
if (is.isInProgressSubmission(context, item)) {
|
if (is.isInProgressSubmission(context, item)) {
|
||||||
throw new UnprocessableEntityException("The item cannot be deleted. "
|
throw new UnprocessableEntityException("The item cannot be deleted. "
|
||||||
+ "It's part of a in-progress submission.");
|
+ "It's part of a in-progress submission.");
|
||||||
@@ -177,4 +184,68 @@ public class ItemRestRepository extends DSpaceRestRepository<ItemRest, UUID> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
protected ItemRest createAndReturn(Context context) throws AuthorizeException, SQLException {
|
||||||
|
HttpServletRequest req = getRequestService().getCurrentRequest().getHttpServletRequest();
|
||||||
|
String owningCollectionUuidString = req.getParameter("owningCollection");
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
ItemRest itemRest = null;
|
||||||
|
try {
|
||||||
|
ServletInputStream input = req.getInputStream();
|
||||||
|
itemRest = mapper.readValue(input, ItemRest.class);
|
||||||
|
} catch (IOException e1) {
|
||||||
|
throw new UnprocessableEntityException("Error parsing request body", e1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemRest.getInArchive() == false) {
|
||||||
|
throw new BadRequestException("InArchive attribute should not be set to false for the create");
|
||||||
|
}
|
||||||
|
UUID owningCollectionUuid = UUIDUtils.fromString(owningCollectionUuidString);
|
||||||
|
Collection collection = collectionService.find(context, owningCollectionUuid);
|
||||||
|
if (collection == null) {
|
||||||
|
throw new BadRequestException("The given owningCollection parameter is invalid: "
|
||||||
|
+ owningCollectionUuid);
|
||||||
|
}
|
||||||
|
WorkspaceItem workspaceItem = workspaceItemService.create(context, collection, false);
|
||||||
|
Item item = workspaceItem.getItem();
|
||||||
|
item.setArchived(true);
|
||||||
|
item.setOwningCollection(collection);
|
||||||
|
item.setDiscoverable(itemRest.getDiscoverable());
|
||||||
|
item.setLastModified(itemRest.getLastModified());
|
||||||
|
metadataConverter.setMetadata(context, item, itemRest.getMetadata());
|
||||||
|
|
||||||
|
Item itemToReturn = installItemService.installItem(context, workspaceItem);
|
||||||
|
|
||||||
|
return dsoConverter.fromModel(itemToReturn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasPermission(#id, 'ITEM', 'WRITE')")
|
||||||
|
protected ItemRest put(Context context, HttpServletRequest request, String apiCategory, String model, UUID uuid,
|
||||||
|
JsonNode jsonNode)
|
||||||
|
throws RepositoryMethodNotImplementedException, SQLException, AuthorizeException {
|
||||||
|
HttpServletRequest req = getRequestService().getCurrentRequest().getHttpServletRequest();
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
ItemRest itemRest = null;
|
||||||
|
try {
|
||||||
|
itemRest = mapper.readValue(jsonNode.toString(), ItemRest.class);
|
||||||
|
} catch (IOException e1) {
|
||||||
|
throw new UnprocessableEntityException("Error parsing request body", e1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Item item = itemService.find(context, uuid);
|
||||||
|
if (item == null) {
|
||||||
|
throw new ResourceNotFoundException(apiCategory + "." + model + " with id: " + uuid + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.equals(uuid.toString(), itemRest.getId())) {
|
||||||
|
metadataConverter.setMetadata(context, item, itemRest.getMetadata());
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("The UUID in the Json and the UUID in the url do not match: "
|
||||||
|
+ uuid + ", "
|
||||||
|
+ itemRest.getId());
|
||||||
|
}
|
||||||
|
return dsoConverter.fromModel(item);
|
||||||
|
}
|
||||||
}
|
}
|
@@ -7,22 +7,37 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest.repository;
|
package org.dspace.app.rest.repository;
|
||||||
|
|
||||||
|
import static java.lang.Integer.parseInt;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.google.gson.Gson;
|
||||||
import org.dspace.app.rest.Parameter;
|
import org.dspace.app.rest.Parameter;
|
||||||
import org.dspace.app.rest.SearchRestMethod;
|
import org.dspace.app.rest.SearchRestMethod;
|
||||||
import org.dspace.app.rest.converter.MetadataFieldConverter;
|
import org.dspace.app.rest.converter.MetadataFieldConverter;
|
||||||
|
import org.dspace.app.rest.exception.PatchBadRequestException;
|
||||||
|
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||||
import org.dspace.app.rest.model.MetadataFieldRest;
|
import org.dspace.app.rest.model.MetadataFieldRest;
|
||||||
import org.dspace.app.rest.model.hateoas.MetadataFieldResource;
|
import org.dspace.app.rest.model.hateoas.MetadataFieldResource;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
import org.dspace.content.MetadataField;
|
import org.dspace.content.MetadataField;
|
||||||
import org.dspace.content.MetadataSchema;
|
import org.dspace.content.MetadataSchema;
|
||||||
|
import org.dspace.content.NonUniqueMetadataException;
|
||||||
import org.dspace.content.service.MetadataFieldService;
|
import org.dspace.content.service.MetadataFieldService;
|
||||||
import org.dspace.content.service.MetadataSchemaService;
|
import org.dspace.content.service.MetadataSchemaService;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,7 +49,7 @@ import org.springframework.stereotype.Component;
|
|||||||
public class MetadataFieldRestRepository extends DSpaceRestRepository<MetadataFieldRest, Integer> {
|
public class MetadataFieldRestRepository extends DSpaceRestRepository<MetadataFieldRest, Integer> {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
MetadataFieldService metaFieldService;
|
MetadataFieldService metadataFieldService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
MetadataSchemaService metadataSchemaService;
|
MetadataSchemaService metadataSchemaService;
|
||||||
@@ -49,7 +64,7 @@ public class MetadataFieldRestRepository extends DSpaceRestRepository<MetadataFi
|
|||||||
public MetadataFieldRest findOne(Context context, Integer id) {
|
public MetadataFieldRest findOne(Context context, Integer id) {
|
||||||
MetadataField metadataField = null;
|
MetadataField metadataField = null;
|
||||||
try {
|
try {
|
||||||
metadataField = metaFieldService.find(context, id);
|
metadataField = metadataFieldService.find(context, id);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
@@ -63,7 +78,7 @@ public class MetadataFieldRestRepository extends DSpaceRestRepository<MetadataFi
|
|||||||
public Page<MetadataFieldRest> findAll(Context context, Pageable pageable) {
|
public Page<MetadataFieldRest> findAll(Context context, Pageable pageable) {
|
||||||
List<MetadataField> metadataField = null;
|
List<MetadataField> metadataField = null;
|
||||||
try {
|
try {
|
||||||
metadataField = metaFieldService.findAll(context);
|
metadataField = metadataFieldService.findAll(context);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
@@ -81,7 +96,7 @@ public class MetadataFieldRestRepository extends DSpaceRestRepository<MetadataFi
|
|||||||
if (schema == null) {
|
if (schema == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
metadataFields = metaFieldService.findAllInSchema(context, schema);
|
metadataFields = metadataFieldService.findAllInSchema(context, schema);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
@@ -98,4 +113,112 @@ public class MetadataFieldRestRepository extends DSpaceRestRepository<MetadataFi
|
|||||||
public MetadataFieldResource wrapResource(MetadataFieldRest bs, String... rels) {
|
public MetadataFieldResource wrapResource(MetadataFieldRest bs, String... rels) {
|
||||||
return new MetadataFieldResource(bs, utils, rels);
|
return new MetadataFieldResource(bs, utils, rels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
protected MetadataFieldRest createAndReturn(Context context)
|
||||||
|
throws AuthorizeException, SQLException {
|
||||||
|
|
||||||
|
// parse request body
|
||||||
|
MetadataFieldRest metadataFieldRest;
|
||||||
|
try {
|
||||||
|
metadataFieldRest = new ObjectMapper().readValue(
|
||||||
|
getRequestService().getCurrentRequest().getHttpServletRequest().getInputStream(),
|
||||||
|
MetadataFieldRest.class
|
||||||
|
);
|
||||||
|
} catch (IOException excIO) {
|
||||||
|
throw new PatchBadRequestException("error parsing request body", excIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate fields
|
||||||
|
String schemaId = getRequestService().getCurrentRequest().getHttpServletRequest().getParameter("schemaId");
|
||||||
|
if (isBlank(schemaId)) {
|
||||||
|
throw new UnprocessableEntityException("metadata schema ID cannot be blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
MetadataSchema schema = metadataSchemaService.find(context, parseInt(schemaId));
|
||||||
|
if (schema == null) {
|
||||||
|
throw new UnprocessableEntityException("metadata schema with ID " + schemaId + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBlank(metadataFieldRest.getElement())) {
|
||||||
|
throw new UnprocessableEntityException("metadata element (in request body) cannot be blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
// create
|
||||||
|
MetadataField metadataField;
|
||||||
|
try {
|
||||||
|
metadataField = metadataFieldService.create(context, schema,
|
||||||
|
metadataFieldRest.getElement(), metadataFieldRest.getQualifier(), metadataFieldRest.getScopeNote());
|
||||||
|
metadataFieldService.update(context, metadataField);
|
||||||
|
} catch (NonUniqueMetadataException e) {
|
||||||
|
throw new UnprocessableEntityException(
|
||||||
|
"metadata field "
|
||||||
|
+ schema.getName() + "." + metadataFieldRest.getElement()
|
||||||
|
+ (metadataFieldRest.getQualifier() != null ? "." + metadataFieldRest.getQualifier() : "")
|
||||||
|
+ " already exists"
|
||||||
|
);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return
|
||||||
|
return converter.convert(metadataField);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
protected void delete(Context context, Integer id) throws AuthorizeException {
|
||||||
|
|
||||||
|
try {
|
||||||
|
MetadataField metadataField = metadataFieldService.find(context, id);
|
||||||
|
|
||||||
|
if (metadataField == null) {
|
||||||
|
throw new ResourceNotFoundException("metadata field with id: " + id + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
metadataFieldService.delete(context, metadataField);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException("error while trying to delete " + MetadataFieldRest.NAME + " with id: " + id, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
protected MetadataFieldRest put(Context context, HttpServletRequest request, String apiCategory, String model,
|
||||||
|
Integer id, JsonNode jsonNode) throws SQLException, AuthorizeException {
|
||||||
|
|
||||||
|
MetadataFieldRest metadataFieldRest = new Gson().fromJson(jsonNode.toString(), MetadataFieldRest.class);
|
||||||
|
|
||||||
|
if (isBlank(metadataFieldRest.getElement())) {
|
||||||
|
throw new UnprocessableEntityException("metadata element (in request body) cannot be blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Objects.equals(id, metadataFieldRest.getId())) {
|
||||||
|
throw new UnprocessableEntityException("ID in request body doesn't match path ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
MetadataField metadataField = metadataFieldService.find(context, id);
|
||||||
|
if (metadataField == null) {
|
||||||
|
throw new ResourceNotFoundException("metadata field with id: " + id + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
metadataField.setElement(metadataFieldRest.getElement());
|
||||||
|
metadataField.setQualifier(metadataFieldRest.getQualifier());
|
||||||
|
metadataField.setScopeNote(metadataFieldRest.getScopeNote());
|
||||||
|
|
||||||
|
try {
|
||||||
|
metadataFieldService.update(context, metadataField);
|
||||||
|
context.commit();
|
||||||
|
} catch (NonUniqueMetadataException e) {
|
||||||
|
throw new UnprocessableEntityException("metadata field "
|
||||||
|
+ metadataField.getMetadataSchema().getName() + "." + metadataFieldRest.getElement()
|
||||||
|
+ (metadataFieldRest.getQualifier() != null ? "." + metadataFieldRest.getQualifier() : "")
|
||||||
|
+ " already exists");
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return converter.fromModel(metadataField);
|
||||||
|
}
|
||||||
}
|
}
|
@@ -7,18 +7,32 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest.repository;
|
package org.dspace.app.rest.repository;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.google.gson.Gson;
|
||||||
import org.dspace.app.rest.converter.MetadataSchemaConverter;
|
import org.dspace.app.rest.converter.MetadataSchemaConverter;
|
||||||
|
import org.dspace.app.rest.exception.PatchBadRequestException;
|
||||||
|
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||||
import org.dspace.app.rest.model.MetadataSchemaRest;
|
import org.dspace.app.rest.model.MetadataSchemaRest;
|
||||||
import org.dspace.app.rest.model.hateoas.MetadataSchemaResource;
|
import org.dspace.app.rest.model.hateoas.MetadataSchemaResource;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
import org.dspace.content.MetadataSchema;
|
import org.dspace.content.MetadataSchema;
|
||||||
|
import org.dspace.content.NonUniqueMetadataException;
|
||||||
import org.dspace.content.service.MetadataSchemaService;
|
import org.dspace.content.service.MetadataSchemaService;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,7 +44,7 @@ import org.springframework.stereotype.Component;
|
|||||||
public class MetadataSchemaRestRepository extends DSpaceRestRepository<MetadataSchemaRest, Integer> {
|
public class MetadataSchemaRestRepository extends DSpaceRestRepository<MetadataSchemaRest, Integer> {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
MetadataSchemaService metaScemaService;
|
MetadataSchemaService metadataSchemaService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
MetadataSchemaConverter converter;
|
MetadataSchemaConverter converter;
|
||||||
@@ -42,7 +56,7 @@ public class MetadataSchemaRestRepository extends DSpaceRestRepository<MetadataS
|
|||||||
public MetadataSchemaRest findOne(Context context, Integer id) {
|
public MetadataSchemaRest findOne(Context context, Integer id) {
|
||||||
MetadataSchema metadataSchema = null;
|
MetadataSchema metadataSchema = null;
|
||||||
try {
|
try {
|
||||||
metadataSchema = metaScemaService.find(context, id);
|
metadataSchema = metadataSchemaService.find(context, id);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
@@ -56,7 +70,7 @@ public class MetadataSchemaRestRepository extends DSpaceRestRepository<MetadataS
|
|||||||
public Page<MetadataSchemaRest> findAll(Context context, Pageable pageable) {
|
public Page<MetadataSchemaRest> findAll(Context context, Pageable pageable) {
|
||||||
List<MetadataSchema> metadataSchema = null;
|
List<MetadataSchema> metadataSchema = null;
|
||||||
try {
|
try {
|
||||||
metadataSchema = metaScemaService.findAll(context);
|
metadataSchema = metadataSchemaService.findAll(context);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
@@ -73,4 +87,100 @@ public class MetadataSchemaRestRepository extends DSpaceRestRepository<MetadataS
|
|||||||
public MetadataSchemaResource wrapResource(MetadataSchemaRest bs, String... rels) {
|
public MetadataSchemaResource wrapResource(MetadataSchemaRest bs, String... rels) {
|
||||||
return new MetadataSchemaResource(bs, utils, rels);
|
return new MetadataSchemaResource(bs, utils, rels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
protected MetadataSchemaRest createAndReturn(Context context)
|
||||||
|
throws AuthorizeException, SQLException {
|
||||||
|
|
||||||
|
// parse request body
|
||||||
|
MetadataSchemaRest metadataSchemaRest;
|
||||||
|
try {
|
||||||
|
metadataSchemaRest = new ObjectMapper().readValue(
|
||||||
|
getRequestService().getCurrentRequest().getHttpServletRequest().getInputStream(),
|
||||||
|
MetadataSchemaRest.class
|
||||||
|
);
|
||||||
|
} catch (IOException excIO) {
|
||||||
|
throw new PatchBadRequestException("error parsing request body", excIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate fields
|
||||||
|
if (isBlank(metadataSchemaRest.getPrefix())) {
|
||||||
|
throw new UnprocessableEntityException("metadata schema name cannot be blank");
|
||||||
|
}
|
||||||
|
if (isBlank(metadataSchemaRest.getNamespace())) {
|
||||||
|
throw new UnprocessableEntityException("metadata schema namespace cannot be blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
// create
|
||||||
|
MetadataSchema metadataSchema;
|
||||||
|
try {
|
||||||
|
metadataSchema = metadataSchemaService.create(
|
||||||
|
context, metadataSchemaRest.getPrefix(), metadataSchemaRest.getNamespace()
|
||||||
|
);
|
||||||
|
metadataSchemaService.update(context, metadataSchema);
|
||||||
|
} catch (NonUniqueMetadataException e) {
|
||||||
|
throw new UnprocessableEntityException("metadata schema "
|
||||||
|
+ metadataSchemaRest.getPrefix() + "." + metadataSchemaRest.getNamespace() + " already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
// return
|
||||||
|
return converter.convert(metadataSchema);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
protected void delete(Context context, Integer id) throws AuthorizeException {
|
||||||
|
|
||||||
|
try {
|
||||||
|
MetadataSchema metadataSchema = metadataSchemaService.find(context, id);
|
||||||
|
|
||||||
|
if (metadataSchema == null) {
|
||||||
|
throw new ResourceNotFoundException("metadata schema with id: " + id + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
metadataSchemaService.delete(context, metadataSchema);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"error while trying to delete " + MetadataSchemaRest.NAME + " with id: " + id, e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
protected MetadataSchemaRest put(Context context, HttpServletRequest request, String apiCategory, String model,
|
||||||
|
Integer id, JsonNode jsonNode) throws SQLException, AuthorizeException {
|
||||||
|
|
||||||
|
MetadataSchemaRest metadataSchemaRest = new Gson().fromJson(jsonNode.toString(), MetadataSchemaRest.class);
|
||||||
|
|
||||||
|
if (isBlank(metadataSchemaRest.getPrefix())) {
|
||||||
|
throw new UnprocessableEntityException("metadata schema name cannot be blank");
|
||||||
|
}
|
||||||
|
if (isBlank(metadataSchemaRest.getNamespace())) {
|
||||||
|
throw new UnprocessableEntityException("metadata schema namespace cannot be blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Objects.equals(id, metadataSchemaRest.getId())) {
|
||||||
|
throw new UnprocessableEntityException("ID in request doesn't match path ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
MetadataSchema metadataSchema = metadataSchemaService.find(context, id);
|
||||||
|
if (metadataSchema == null) {
|
||||||
|
throw new ResourceNotFoundException("metadata schema with id: " + id + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
metadataSchema.setName(metadataSchemaRest.getPrefix());
|
||||||
|
metadataSchema.setNamespace(metadataSchemaRest.getNamespace());
|
||||||
|
|
||||||
|
try {
|
||||||
|
metadataSchemaService.update(context, metadataSchema);
|
||||||
|
context.commit();
|
||||||
|
} catch (NonUniqueMetadataException e) {
|
||||||
|
throw new UnprocessableEntityException("metadata schema "
|
||||||
|
+ metadataSchemaRest.getPrefix() + "." + metadataSchemaRest.getNamespace() + " already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
return converter.fromModel(metadataSchema);
|
||||||
|
}
|
||||||
}
|
}
|
@@ -11,10 +11,14 @@ import java.sql.SQLException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.dspace.app.rest.converter.SiteConverter;
|
import org.dspace.app.rest.converter.SiteConverter;
|
||||||
import org.dspace.app.rest.model.SiteRest;
|
import org.dspace.app.rest.model.SiteRest;
|
||||||
import org.dspace.app.rest.model.hateoas.SiteResource;
|
import org.dspace.app.rest.model.hateoas.SiteResource;
|
||||||
|
import org.dspace.app.rest.model.patch.Patch;
|
||||||
|
import org.dspace.app.rest.repository.patch.DSpaceObjectPatch;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
import org.dspace.content.Site;
|
import org.dspace.content.Site;
|
||||||
import org.dspace.content.service.SiteService;
|
import org.dspace.content.service.SiteService;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
@@ -22,6 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageImpl;
|
import org.springframework.data.domain.PageImpl;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,16 +36,15 @@ import org.springframework.stereotype.Component;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@Component(SiteRest.CATEGORY + "." + SiteRest.NAME)
|
@Component(SiteRest.CATEGORY + "." + SiteRest.NAME)
|
||||||
public class SiteRestRepository extends DSpaceRestRepository<SiteRest, UUID> {
|
public class SiteRestRepository extends DSpaceObjectRestRepository<Site, SiteRest> {
|
||||||
|
|
||||||
|
private final SiteService sitesv;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
SiteService sitesv;
|
public SiteRestRepository(SiteService dsoService,
|
||||||
|
SiteConverter dsoConverter) {
|
||||||
@Autowired
|
super(dsoService, dsoConverter, new DSpaceObjectPatch<SiteRest>() {});
|
||||||
SiteConverter converter;
|
this.sitesv = dsoService;
|
||||||
|
|
||||||
|
|
||||||
public SiteRestRepository() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -54,7 +58,7 @@ public class SiteRestRepository extends DSpaceRestRepository<SiteRest, UUID> {
|
|||||||
if (site == null) {
|
if (site == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return converter.fromModel(site);
|
return dsoConverter.fromModel(site);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -66,10 +70,17 @@ public class SiteRestRepository extends DSpaceRestRepository<SiteRest, UUID> {
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
Page<SiteRest> page = new PageImpl<Site>(sites, pageable, total).map(converter);
|
Page<SiteRest> page = new PageImpl<Site>(sites, pageable, total).map(dsoConverter);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
protected void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID id,
|
||||||
|
Patch patch) throws AuthorizeException, SQLException {
|
||||||
|
patchDSpaceObject(apiCategory, model, id, patch);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<SiteRest> getDomainClass() {
|
public Class<SiteRest> getDomainClass() {
|
||||||
return SiteRest.class;
|
return SiteRest.class;
|
||||||
|
@@ -30,7 +30,7 @@ public abstract class AbstractResourcePatch<R extends RestModel> {
|
|||||||
* @throws UnprocessableEntityException
|
* @throws UnprocessableEntityException
|
||||||
* @throws PatchBadRequestException
|
* @throws PatchBadRequestException
|
||||||
*/
|
*/
|
||||||
public RestModel patch(R restModel, List<Operation> operations) {
|
public R patch(R restModel, List<Operation> operations) {
|
||||||
|
|
||||||
// Note: the list of possible operations is taken from JsonPatchConverter class. Does not implement
|
// Note: the list of possible operations is taken from JsonPatchConverter class. Does not implement
|
||||||
// test https://tools.ietf.org/html/rfc6902#section-4.6
|
// test https://tools.ietf.org/html/rfc6902#section-4.6
|
||||||
|
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.repository.patch;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import com.flipkart.zjsonpatch.JsonPatch;
|
||||||
|
import org.dspace.app.rest.converter.JsonPatchConverter;
|
||||||
|
import org.dspace.app.rest.model.DSpaceObjectRest;
|
||||||
|
import org.dspace.app.rest.model.MetadataRest;
|
||||||
|
import org.dspace.app.rest.model.patch.Operation;
|
||||||
|
import org.dspace.app.rest.model.patch.Patch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for DSpaceObject-based PATCH operations, providing common functionality.
|
||||||
|
*
|
||||||
|
* @param <R> the type of DSpaceObjectRest object the class is applicable to.
|
||||||
|
*/
|
||||||
|
public abstract class DSpaceObjectPatch<R extends DSpaceObjectRest> extends AbstractResourcePatch<R> {
|
||||||
|
|
||||||
|
private static final String METADATA_PATH = "/metadata";
|
||||||
|
|
||||||
|
private ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
private JsonPatchConverter jsonPatchConverter = new JsonPatchConverter(objectMapper);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the given patch operations to the given DSpaceObjectRest instance.
|
||||||
|
*
|
||||||
|
* This extends the default implementation by first applying metadata-based patch operations,
|
||||||
|
* then applying any others.
|
||||||
|
*
|
||||||
|
* @param dsoRest the instance to apply the changes to.
|
||||||
|
* @param operations the list of patch operations.
|
||||||
|
* @return the modified DSpaceObectRest instance.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public R patch(R dsoRest, List<Operation> operations) {
|
||||||
|
List<Operation> metadataOperations = new ArrayList<>();
|
||||||
|
List<Operation> otherOperations = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Operation operation : operations) {
|
||||||
|
String path = operation.getPath();
|
||||||
|
if (path.equals(METADATA_PATH) || path.startsWith(METADATA_PATH + "/")) {
|
||||||
|
metadataOperations.add(operation);
|
||||||
|
} else {
|
||||||
|
otherOperations.add(operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!metadataOperations.isEmpty()) {
|
||||||
|
dsoRest.setMetadata(applyMetadataPatch(
|
||||||
|
jsonPatchConverter.convert(new Patch(metadataOperations)),
|
||||||
|
dsoRest.getMetadata()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.patch(dsoRest, otherOperations);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MetadataRest applyMetadataPatch(JsonNode patch, MetadataRest metadataRest) {
|
||||||
|
try {
|
||||||
|
ObjectNode objectNode = objectMapper.createObjectNode();
|
||||||
|
JsonNode metadataNode = objectMapper.valueToTree(metadataRest);
|
||||||
|
objectNode.replace("metadata", metadataNode);
|
||||||
|
JsonPatch.applyInPlace(patch, objectNode);
|
||||||
|
return objectMapper.treeToValue(objectNode.get("metadata"), MetadataRest.class);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -20,7 +20,7 @@ import org.springframework.stereotype.Component;
|
|||||||
* Provides patch operations for eperson updates.
|
* Provides patch operations for eperson updates.
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class EPersonPatch extends AbstractResourcePatch<EPersonRest> {
|
public class EPersonPatch extends DSpaceObjectPatch<EPersonRest> {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
EPersonOperationFactory patchFactory;
|
EPersonOperationFactory patchFactory;
|
||||||
|
@@ -20,7 +20,7 @@ import org.springframework.stereotype.Component;
|
|||||||
* Provides PATCH operations for item updates.
|
* Provides PATCH operations for item updates.
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class ItemPatch extends AbstractResourcePatch<ItemRest> {
|
public class ItemPatch extends DSpaceObjectPatch<ItemRest> {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
ItemOperationFactory patchFactory;
|
ItemOperationFactory patchFactory;
|
||||||
|
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.utils;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dspace.app.rest.model.CollectionRest;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class will contain methods that can define in what way CollectionRest objects are equal
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class CollectionRestEqualityUtils extends DSpaceObjectRestEqualityUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will return a boolean indicating whether the given CollectionRest objects are equal
|
||||||
|
* through comparing their attributes
|
||||||
|
* @param original The original CollectionRest object
|
||||||
|
* @param updated The CollectionRest object that has to be checked for equality
|
||||||
|
* @return A boolean indicating whether they're equal or not
|
||||||
|
*/
|
||||||
|
public boolean isCollectionRestEqualWithoutMetadata(CollectionRest original, CollectionRest updated) {
|
||||||
|
return super.isDSpaceObjectEqualsWithoutMetadata(original, updated) &&
|
||||||
|
StringUtils.equals(original.getCategory(), updated.getCategory()) &&
|
||||||
|
StringUtils.equals(original.getType(), updated.getType());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.utils;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dspace.app.rest.model.CommunityRest;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class will contain methods that can define in what way CommunityRest objects are equal
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class CommunityRestEqualityUtils extends DSpaceObjectRestEqualityUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will return a boolean indicating whether the given CommunityRest objects are equal
|
||||||
|
* through comparing their attributes
|
||||||
|
* @param original The original CommunityRest object
|
||||||
|
* @param updated The CommunityRest object that has to be checked for equality
|
||||||
|
* @return A boolean indicating whether they're equal or not
|
||||||
|
*/
|
||||||
|
public boolean isCommunityRestEqualWithoutMetadata(CommunityRest original, CommunityRest updated) {
|
||||||
|
return super.isDSpaceObjectEqualsWithoutMetadata(original, updated) &&
|
||||||
|
StringUtils.equals(original.getCategory(), updated.getCategory()) &&
|
||||||
|
StringUtils.equals(original.getType(), updated.getType());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.utils;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dspace.app.rest.model.DSpaceObjectRest;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class will contain methods that can define in what way DSpaceObjectRest objects are equal
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class DSpaceObjectRestEqualityUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will return a boolean indicating whether the given DSpaceObjectRest objects are equal
|
||||||
|
* through comparing their attributes
|
||||||
|
* @param original The original DSpaceObjectRest object
|
||||||
|
* @param updated The DSpaceObjectRest object that has to be checked for equality
|
||||||
|
* @return A boolean indicating whether they're equal or not
|
||||||
|
*/
|
||||||
|
public boolean isDSpaceObjectEqualsWithoutMetadata(DSpaceObjectRest original, DSpaceObjectRest updated) {
|
||||||
|
return StringUtils.equals(original.getId(), updated.getId()) &&
|
||||||
|
StringUtils.equals(original.getCategory(), updated.getCategory()) &&
|
||||||
|
StringUtils.equals(original.getHandle(), updated.getHandle());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -27,11 +27,13 @@ import org.dspace.app.rest.builder.ItemBuilder;
|
|||||||
import org.dspace.app.rest.matcher.BitstreamFormatMatcher;
|
import org.dspace.app.rest.matcher.BitstreamFormatMatcher;
|
||||||
import org.dspace.app.rest.matcher.BitstreamMatcher;
|
import org.dspace.app.rest.matcher.BitstreamMatcher;
|
||||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||||
|
import org.dspace.app.rest.test.MetadataPatchSuite;
|
||||||
import org.dspace.content.Bitstream;
|
import org.dspace.content.Bitstream;
|
||||||
import org.dspace.content.Collection;
|
import org.dspace.content.Collection;
|
||||||
import org.dspace.content.Community;
|
import org.dspace.content.Community;
|
||||||
import org.dspace.content.Item;
|
import org.dspace.content.Item;
|
||||||
import org.dspace.content.service.BitstreamService;
|
import org.dspace.content.service.BitstreamService;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -587,4 +589,25 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest
|
|||||||
getClient(token).perform(delete("/api/core/bitstreams/" + col.getLogo().getID()))
|
getClient(token).perform(delete("/api/core/bitstreams/" + col.getLogo().getID()))
|
||||||
.andExpect(status().is(422));
|
.andExpect(status().is(422));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void patchBitstreamMetadataAuthorized() throws Exception {
|
||||||
|
runPatchMetadataTests(admin, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void patchBitstreamMetadataUnauthorized() throws Exception {
|
||||||
|
runPatchMetadataTests(eperson, 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runPatchMetadataTests(EPerson asUser, int expectedStatus) throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Community").withLogo("logo_community")
|
||||||
|
.build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String token = getAuthToken(asUser.getEmail(), password);
|
||||||
|
|
||||||
|
new MetadataPatchSuite().runWith(getClient(token), "/api/core/bitstreams/"
|
||||||
|
+ parentCommunity.getLogo().getID(), expectedStatus);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,10 +7,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest;
|
package org.dspace.app.rest;
|
||||||
|
|
||||||
|
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadata;
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.hasItem;
|
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
@@ -31,6 +31,7 @@ import org.dspace.content.Collection;
|
|||||||
import org.dspace.content.Community;
|
import org.dspace.content.Community;
|
||||||
import org.dspace.content.Item;
|
import org.dspace.content.Item;
|
||||||
import org.dspace.eperson.Group;
|
import org.dspace.eperson.Group;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
|
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
|
||||||
|
|
||||||
@@ -380,14 +381,10 @@ public class BrowsesResourceControllerIT extends AbstractControllerIntegrationTe
|
|||||||
"An embargoed publication",
|
"An embargoed publication",
|
||||||
"2017-08-10"))))
|
"2017-08-10"))))
|
||||||
|
|
||||||
//The private item must not be present
|
//The private and internal items must not be present
|
||||||
.andExpect(jsonPath("$._embedded.items[*].metadata[?(@.key=='dc.title')].value",
|
.andExpect(jsonPath("$._embedded.items[*].metadata", Matchers.allOf(
|
||||||
not(hasItem("This is a private item"))))
|
not(matchMetadata("dc.title", "This is a private item")),
|
||||||
|
not(matchMetadata("dc.title", "Internal publication")))));
|
||||||
//The internal item must not be present
|
|
||||||
.andExpect(jsonPath("$._embedded.items[*].metadata[?(@.key=='dc.title')].value",
|
|
||||||
not(hasItem("Internal publication"))))
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@@ -7,25 +7,48 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest;
|
package org.dspace.app.rest;
|
||||||
|
|
||||||
|
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
||||||
|
import static org.hamcrest.Matchers.empty;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.Matchers.not;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.dspace.app.rest.builder.CollectionBuilder;
|
import org.dspace.app.rest.builder.CollectionBuilder;
|
||||||
import org.dspace.app.rest.builder.CommunityBuilder;
|
import org.dspace.app.rest.builder.CommunityBuilder;
|
||||||
|
import org.dspace.app.rest.converter.CollectionConverter;
|
||||||
import org.dspace.app.rest.matcher.CollectionMatcher;
|
import org.dspace.app.rest.matcher.CollectionMatcher;
|
||||||
|
import org.dspace.app.rest.matcher.MetadataMatcher;
|
||||||
|
import org.dspace.app.rest.model.CollectionRest;
|
||||||
|
import org.dspace.app.rest.model.MetadataRest;
|
||||||
|
import org.dspace.app.rest.model.MetadataValueRest;
|
||||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||||
|
import org.dspace.app.rest.test.MetadataPatchSuite;
|
||||||
|
import org.dspace.authorize.service.AuthorizeService;
|
||||||
import org.dspace.content.Collection;
|
import org.dspace.content.Collection;
|
||||||
import org.dspace.content.Community;
|
import org.dspace.content.Community;
|
||||||
|
import org.dspace.core.Constants;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
|
||||||
public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTest {
|
public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
CollectionConverter collectionConverter;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
AuthorizeService authorizeService;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void findAllTest() throws Exception {
|
public void findAllTest() throws Exception {
|
||||||
@@ -276,4 +299,351 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
|
|||||||
.andExpect(status().isNotFound());
|
.andExpect(status().isNotFound());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findCollectionWithParentCommunity() throws Exception {
|
||||||
|
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and one collection.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community")
|
||||||
|
.build();
|
||||||
|
Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community Two")
|
||||||
|
.build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
|
||||||
|
Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build();
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/collections/" + col1.getID()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", is(
|
||||||
|
CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle())
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$", Matchers.not(
|
||||||
|
is(
|
||||||
|
CollectionMatcher.matchCollectionEntry(col2.getName(), col2.getID(), col2.getHandle())
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateTest() throws Exception {
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and one collection.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community")
|
||||||
|
.build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/collections/" + col1.getID().toString()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", Matchers.is(
|
||||||
|
CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle())
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/collections")))
|
||||||
|
;
|
||||||
|
|
||||||
|
String token = getAuthToken(admin.getEmail(), password);
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
CollectionRest collectionRest = collectionConverter.fromModel(col1);
|
||||||
|
|
||||||
|
collectionRest.setMetadata(new MetadataRest()
|
||||||
|
.put("dc.title", new MetadataValueRest("Electronic theses and dissertations")));
|
||||||
|
|
||||||
|
getClient(token).perform(put("/api/core/collections/" + col1.getID().toString())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(mapper.writeValueAsBytes(collectionRest)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
;
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/collections/" + col1.getID().toString()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", Matchers.is(
|
||||||
|
CollectionMatcher.matchCollectionEntry("Electronic theses and dissertations",
|
||||||
|
col1.getID(), col1.getHandle())
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$._links.self.href",
|
||||||
|
Matchers.containsString("/api/core/collections")))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteTest() throws Exception {
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and one collection.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.withLogo("ThisIsSomeDummyText")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Community parentCommunity2 = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community 2")
|
||||||
|
.withLogo("SomeTest")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Community parentCommunityChild1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunityChild1)
|
||||||
|
.withName("Collection 1")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
String token = getAuthToken(admin.getEmail(), password);
|
||||||
|
|
||||||
|
getClient(token).perform(get("/api/core/collections/" + col1.getID().toString()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", Matchers.is(
|
||||||
|
CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle())
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$._links.self.href",
|
||||||
|
Matchers.containsString("/api/core/collections"))) ;
|
||||||
|
getClient(token).perform(delete("/api/core/collections/" + col1.getID().toString()))
|
||||||
|
.andExpect(status().isNoContent())
|
||||||
|
;
|
||||||
|
getClient(token).perform(get("/api/core/collections/" + col1.getID().toString()))
|
||||||
|
.andExpect(status().isNotFound())
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteTestUnAuthorized() throws Exception {
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and one collection.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.withLogo("ThisIsSomeDummyText")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Community parentCommunity2 = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community 2")
|
||||||
|
.withLogo("SomeTest")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Community parentCommunityChild1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunityChild1)
|
||||||
|
.withName("Collection 1")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/collections/" + col1.getID().toString()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", Matchers.is(
|
||||||
|
CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle())
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$._links.self.href",
|
||||||
|
Matchers.containsString("/api/core/collections"))) ;
|
||||||
|
getClient().perform(delete("/api/core/collections/" + col1.getID().toString()))
|
||||||
|
.andExpect(status().isUnauthorized())
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and one collection.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.withLogo("ThisIsSomeDummyText")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
CollectionRest collectionRest = new CollectionRest();
|
||||||
|
// We send a name but the created collection should set this to the title
|
||||||
|
collectionRest.setName("Collection");
|
||||||
|
|
||||||
|
collectionRest.setMetadata(new MetadataRest()
|
||||||
|
.put("dc.description",
|
||||||
|
new MetadataValueRest("<p>Some cool HTML code here</p>"))
|
||||||
|
.put("dc.description.abstract",
|
||||||
|
new MetadataValueRest("Sample top-level community created via the REST API"))
|
||||||
|
.put("dc.description.tableofcontents",
|
||||||
|
new MetadataValueRest("<p>HTML News</p>"))
|
||||||
|
.put("dc.rights",
|
||||||
|
new MetadataValueRest("Custom Copyright Text"))
|
||||||
|
.put("dc.title",
|
||||||
|
new MetadataValueRest("Title Text")));
|
||||||
|
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(authToken).perform(post("/api/core/collections")
|
||||||
|
.content(mapper.writeValueAsBytes(collectionRest))
|
||||||
|
.param("parent", parentCommunity.getID().toString())
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isCreated())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", Matchers.allOf(
|
||||||
|
hasJsonPath("$.id", not(empty())),
|
||||||
|
hasJsonPath("$.uuid", not(empty())),
|
||||||
|
hasJsonPath("$.name", is("Title Text")),
|
||||||
|
hasJsonPath("$.handle", not(empty())),
|
||||||
|
hasJsonPath("$.type", is("collection")),
|
||||||
|
hasJsonPath("$.metadata", Matchers.allOf(
|
||||||
|
MetadataMatcher.matchMetadata("dc.description",
|
||||||
|
"<p>Some cool HTML code here</p>"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.description.abstract",
|
||||||
|
"Sample top-level community created via the REST API"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.description.tableofcontents",
|
||||||
|
"<p>HTML News</p>"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.rights",
|
||||||
|
"Custom Copyright Text"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.title",
|
||||||
|
"Title Text")
|
||||||
|
)))));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteCollectionEpersonWithDeleteRightsTest() throws Exception {
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and one collection.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.withLogo("ThisIsSomeDummyText")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Community parentCommunity2 = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community 2")
|
||||||
|
.withLogo("SomeTest")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Community parentCommunityChild1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunityChild1)
|
||||||
|
.withName("Collection 1")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
context.setCurrentUser(eperson);
|
||||||
|
authorizeService.addPolicy(context, col1, Constants.DELETE, eperson);
|
||||||
|
authorizeService.addPolicy(context, col1, Constants.WRITE, eperson);
|
||||||
|
|
||||||
|
String token = getAuthToken(eperson.getEmail(), password);
|
||||||
|
|
||||||
|
getClient(token).perform(get("/api/core/collections/" + col1.getID().toString()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", Matchers.is(
|
||||||
|
CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle())
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$._links.self.href",
|
||||||
|
Matchers.containsString("/api/core/collections"))) ;
|
||||||
|
getClient(token).perform(delete("/api/core/collections/" + col1.getID().toString()))
|
||||||
|
.andExpect(status().isNoContent())
|
||||||
|
;
|
||||||
|
getClient(token).perform(get("/api/core/collections/" + col1.getID().toString()))
|
||||||
|
.andExpect(status().isNotFound())
|
||||||
|
;
|
||||||
|
|
||||||
|
authorizeService.removePoliciesActionFilter(context, eperson, Constants.DELETE);
|
||||||
|
authorizeService.removePoliciesActionFilter(context, eperson, Constants.WRITE);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateCollectionEpersonWithWriteRightsTest() throws Exception {
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and one collection.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community")
|
||||||
|
.build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/collections/" + col1.getID().toString()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", Matchers.is(
|
||||||
|
CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle())
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/collections")))
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
context.setCurrentUser(eperson);
|
||||||
|
authorizeService.addPolicy(context, col1, Constants.WRITE, eperson);
|
||||||
|
|
||||||
|
String token = getAuthToken(eperson.getEmail(), password);
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
CollectionRest collectionRest = collectionConverter.fromModel(col1);
|
||||||
|
|
||||||
|
collectionRest.setMetadata(new MetadataRest()
|
||||||
|
.put("dc.title", new MetadataValueRest("Electronic theses and dissertations")));
|
||||||
|
|
||||||
|
getClient(token).perform(put("/api/core/collections/" + col1.getID().toString())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(mapper.writeValueAsBytes(collectionRest)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
;
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/collections/" + col1.getID().toString()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", Matchers.is(
|
||||||
|
CollectionMatcher.matchCollectionEntry("Electronic theses and dissertations",
|
||||||
|
col1.getID(), col1.getHandle())
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$._links.self.href",
|
||||||
|
Matchers.containsString("/api/core/collections")))
|
||||||
|
;
|
||||||
|
|
||||||
|
authorizeService.removePoliciesActionFilter(context, eperson, Constants.WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void patchCollectionMetadataAuthorized() throws Exception {
|
||||||
|
runPatchMetadataTests(admin, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void patchCollectionMetadataUnauthorized() throws Exception {
|
||||||
|
runPatchMetadataTests(eperson, 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runPatchMetadataTests(EPerson asUser, int expectedStatus) throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Community").build();
|
||||||
|
Collection col = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String token = getAuthToken(asUser.getEmail(), password);
|
||||||
|
|
||||||
|
new MetadataPatchSuite().runWith(getClient(token), "/api/core/collections/" + col.getID(), expectedStatus);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,34 +8,50 @@
|
|||||||
package org.dspace.app.rest;
|
package org.dspace.app.rest;
|
||||||
|
|
||||||
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
||||||
|
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadata;
|
||||||
import static org.hamcrest.Matchers.empty;
|
import static org.hamcrest.Matchers.empty;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import org.dspace.app.rest.builder.CollectionBuilder;
|
import org.dspace.app.rest.builder.CollectionBuilder;
|
||||||
import org.dspace.app.rest.builder.CommunityBuilder;
|
import org.dspace.app.rest.builder.CommunityBuilder;
|
||||||
|
import org.dspace.app.rest.converter.CommunityConverter;
|
||||||
import org.dspace.app.rest.matcher.CommunityMatcher;
|
import org.dspace.app.rest.matcher.CommunityMatcher;
|
||||||
import org.dspace.app.rest.matcher.CommunityMetadataMatcher;
|
import org.dspace.app.rest.matcher.MetadataMatcher;
|
||||||
import org.dspace.app.rest.model.CommunityRest;
|
import org.dspace.app.rest.model.CommunityRest;
|
||||||
import org.dspace.app.rest.model.MetadataEntryRest;
|
import org.dspace.app.rest.model.MetadataRest;
|
||||||
|
import org.dspace.app.rest.model.MetadataValueRest;
|
||||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||||
|
import org.dspace.app.rest.test.MetadataPatchSuite;
|
||||||
|
import org.dspace.authorize.service.AuthorizeService;
|
||||||
import org.dspace.content.Collection;
|
import org.dspace.content.Collection;
|
||||||
import org.dspace.content.Community;
|
import org.dspace.content.Community;
|
||||||
|
import org.dspace.core.Constants;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
|
||||||
public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest {
|
public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
CommunityConverter communityConverter;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
AuthorizeService authorizeService;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createTest() throws Exception {
|
public void createTest() throws Exception {
|
||||||
context.turnOffAuthorisationSystem();
|
context.turnOffAuthorisationSystem();
|
||||||
@@ -45,31 +61,29 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
|
|||||||
// We send a name but the created community should set this to the title
|
// We send a name but the created community should set this to the title
|
||||||
comm.setName("Test Top-Level Community");
|
comm.setName("Test Top-Level Community");
|
||||||
|
|
||||||
MetadataEntryRest description = new MetadataEntryRest();
|
MetadataRest metadataRest = new MetadataRest();
|
||||||
description.setKey("dc.description");
|
|
||||||
|
MetadataValueRest description = new MetadataValueRest();
|
||||||
description.setValue("<p>Some cool HTML code here</p>");
|
description.setValue("<p>Some cool HTML code here</p>");
|
||||||
|
metadataRest.put("dc.description", description);
|
||||||
|
|
||||||
MetadataEntryRest abs = new MetadataEntryRest();
|
MetadataValueRest abs = new MetadataValueRest();
|
||||||
abs.setKey("dc.description.abstract");
|
|
||||||
abs.setValue("Sample top-level community created via the REST API");
|
abs.setValue("Sample top-level community created via the REST API");
|
||||||
|
metadataRest.put("dc.description.abstract", abs);
|
||||||
|
|
||||||
MetadataEntryRest contents = new MetadataEntryRest();
|
MetadataValueRest contents = new MetadataValueRest();
|
||||||
contents.setKey("dc.description.tableofcontents");
|
|
||||||
contents.setValue("<p>HTML News</p>");
|
contents.setValue("<p>HTML News</p>");
|
||||||
|
metadataRest.put("dc.description.tableofcontents", contents);
|
||||||
|
|
||||||
MetadataEntryRest copyright = new MetadataEntryRest();
|
MetadataValueRest copyright = new MetadataValueRest();
|
||||||
copyright.setKey("dc.rights");
|
|
||||||
copyright.setValue("Custom Copyright Text");
|
copyright.setValue("Custom Copyright Text");
|
||||||
|
metadataRest.put("dc.rights", copyright);
|
||||||
|
|
||||||
MetadataEntryRest title = new MetadataEntryRest();
|
MetadataValueRest title = new MetadataValueRest();
|
||||||
title.setKey("dc.title");
|
|
||||||
title.setValue("Title Text");
|
title.setValue("Title Text");
|
||||||
|
metadataRest.put("dc.title", title);
|
||||||
|
|
||||||
comm.setMetadata(Arrays.asList(description,
|
comm.setMetadata(metadataRest);
|
||||||
abs,
|
|
||||||
contents,
|
|
||||||
copyright,
|
|
||||||
title));
|
|
||||||
|
|
||||||
String authToken = getAuthToken(admin.getEmail(), password);
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
getClient(authToken).perform(post("/api/core/communities")
|
getClient(authToken).perform(post("/api/core/communities")
|
||||||
@@ -87,21 +101,79 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
|
|||||||
hasJsonPath("$._links.logo.href", not(empty())),
|
hasJsonPath("$._links.logo.href", not(empty())),
|
||||||
hasJsonPath("$._links.subcommunities.href", not(empty())),
|
hasJsonPath("$._links.subcommunities.href", not(empty())),
|
||||||
hasJsonPath("$._links.self.href", not(empty())),
|
hasJsonPath("$._links.self.href", not(empty())),
|
||||||
hasJsonPath("$.metadata", Matchers.containsInAnyOrder(
|
hasJsonPath("$.metadata", Matchers.allOf(
|
||||||
CommunityMetadataMatcher.matchMetadata("dc.description",
|
matchMetadata("dc.description", "<p>Some cool HTML code here</p>"),
|
||||||
"<p>Some cool HTML code here</p>"),
|
matchMetadata("dc.description.abstract",
|
||||||
CommunityMetadataMatcher.matchMetadata("dc.description.abstract",
|
|
||||||
"Sample top-level community created via the REST API"),
|
"Sample top-level community created via the REST API"),
|
||||||
CommunityMetadataMatcher.matchMetadata("dc.description.tableofcontents",
|
matchMetadata("dc.description.tableofcontents", "<p>HTML News</p>"),
|
||||||
|
matchMetadata("dc.rights", "Custom Copyright Text"),
|
||||||
|
matchMetadata("dc.title", "Title Text")
|
||||||
|
)))));
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void createWithParentTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and one collection.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
CommunityRest comm = new CommunityRest();
|
||||||
|
// We send a name but the created community should set this to the title
|
||||||
|
comm.setName("Test Sub-Level Community");
|
||||||
|
|
||||||
|
comm.setMetadata(new MetadataRest()
|
||||||
|
.put("dc.description",
|
||||||
|
new MetadataValueRest("<p>Some cool HTML code here</p>"))
|
||||||
|
.put("dc.description.abstract",
|
||||||
|
new MetadataValueRest("Sample top-level community created via the REST API"))
|
||||||
|
.put("dc.description.tableofcontents",
|
||||||
|
new MetadataValueRest("<p>HTML News</p>"))
|
||||||
|
.put("dc.rights",
|
||||||
|
new MetadataValueRest("Custom Copyright Text"))
|
||||||
|
.put("dc.title",
|
||||||
|
new MetadataValueRest("Title Text")));
|
||||||
|
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(authToken).perform(post("/api/core/communities")
|
||||||
|
.content(mapper.writeValueAsBytes(comm))
|
||||||
|
.param("parent", parentCommunity.getID().toString())
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isCreated())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", Matchers.allOf(
|
||||||
|
hasJsonPath("$.id", not(empty())),
|
||||||
|
hasJsonPath("$.uuid", not(empty())),
|
||||||
|
hasJsonPath("$.name", is("Title Text")),
|
||||||
|
hasJsonPath("$.handle", not(empty())),
|
||||||
|
hasJsonPath("$.type", is("community")),
|
||||||
|
hasJsonPath("$._links.collections.href", not(empty())),
|
||||||
|
hasJsonPath("$._links.logo.href", not(empty())),
|
||||||
|
hasJsonPath("$._links.subcommunities.href", not(empty())),
|
||||||
|
hasJsonPath("$._links.self.href", not(empty())),
|
||||||
|
hasJsonPath("$.metadata", Matchers.allOf(
|
||||||
|
MetadataMatcher.matchMetadata("dc.description",
|
||||||
|
"<p>Some cool HTML code here</p>"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.description.abstract",
|
||||||
|
"Sample top-level community created via the REST API"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.description.tableofcontents",
|
||||||
"<p>HTML News</p>"),
|
"<p>HTML News</p>"),
|
||||||
CommunityMetadataMatcher.matchMetadata("dc.rights",
|
MetadataMatcher.matchMetadata("dc.rights",
|
||||||
"Custom Copyright Text"),
|
"Custom Copyright Text"),
|
||||||
CommunityMetadataMatcher.matchMetadata("dc.title",
|
MetadataMatcher.matchMetadata("dc.title",
|
||||||
"Title Text")
|
"Title Text")
|
||||||
)))));
|
)))));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createUnauthorizedTest() throws Exception {
|
public void createUnauthorizedTest() throws Exception {
|
||||||
context.turnOffAuthorisationSystem();
|
context.turnOffAuthorisationSystem();
|
||||||
@@ -110,11 +182,13 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
|
|||||||
CommunityRest comm = new CommunityRest();
|
CommunityRest comm = new CommunityRest();
|
||||||
comm.setName("Test Top-Level Community");
|
comm.setName("Test Top-Level Community");
|
||||||
|
|
||||||
MetadataEntryRest title = new MetadataEntryRest();
|
MetadataRest metadataRest = new MetadataRest();
|
||||||
title.setKey("dc.title");
|
|
||||||
title.setValue("Title Text");
|
|
||||||
|
|
||||||
comm.setMetadata(Arrays.asList(title));
|
MetadataValueRest title = new MetadataValueRest();
|
||||||
|
title.setValue("Title Text");
|
||||||
|
metadataRest.put("dc.title", title);
|
||||||
|
|
||||||
|
comm.setMetadata(metadataRest);
|
||||||
|
|
||||||
// Anonymous user tries to create a community.
|
// Anonymous user tries to create a community.
|
||||||
// Should fail because user is not authenticated. Error 401.
|
// Should fail because user is not authenticated. Error 401.
|
||||||
@@ -511,4 +585,291 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
|
|||||||
|
|
||||||
getClient().perform(get("/api/core/communities/" + UUID.randomUUID())).andExpect(status().isNotFound());
|
getClient().perform(get("/api/core/communities/" + UUID.randomUUID())).andExpect(status().isNotFound());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateTest() throws Exception {
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and one collection.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community")
|
||||||
|
.build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", Matchers.is(
|
||||||
|
CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), parentCommunity.getID(),
|
||||||
|
parentCommunity.getHandle())
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$", Matchers.not(
|
||||||
|
Matchers.is(
|
||||||
|
CommunityMatcher.matchCommunityEntry(child1.getName(), child1.getID(), child1.getHandle())
|
||||||
|
)
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities")))
|
||||||
|
;
|
||||||
|
|
||||||
|
String token = getAuthToken(admin.getEmail(), password);
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
CommunityRest communityRest = communityConverter.fromModel(parentCommunity);
|
||||||
|
|
||||||
|
communityRest.setMetadata(new MetadataRest()
|
||||||
|
.put("dc.title", new MetadataValueRest("Electronic theses and dissertations")));
|
||||||
|
|
||||||
|
getClient(token).perform(put("/api/core/communities/" + parentCommunity.getID().toString())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(mapper.writeValueAsBytes(communityRest)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
;
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", Matchers.is(
|
||||||
|
CommunityMatcher.matchCommunityEntry("Electronic theses and dissertations",
|
||||||
|
parentCommunity.getID(),
|
||||||
|
parentCommunity.getHandle())
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities")))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteTest() throws Exception {
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and one collection.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.withLogo("ThisIsSomeDummyText")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Community parentCommunity2 = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community 2")
|
||||||
|
.withLogo("SomeTest")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Community parentCommunityChild1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Community parentCommunityChild2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community2")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Community parentCommunityChild2Child1 = CommunityBuilder.createSubCommunity(context, parentCommunityChild2)
|
||||||
|
.withName("Sub Sub Community")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
Community parentCommunity2Child1 = CommunityBuilder.createSubCommunity(context, parentCommunity2)
|
||||||
|
.withName("Sub2 Community")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunityChild1)
|
||||||
|
.withName("Collection 1")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
String token = getAuthToken(admin.getEmail(), password);
|
||||||
|
|
||||||
|
getClient(token).perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", Matchers.is(
|
||||||
|
CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), parentCommunity.getID(),
|
||||||
|
parentCommunity.getHandle())
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$._links.self.href",
|
||||||
|
Matchers.containsString("/api/core/communities"))) ;
|
||||||
|
getClient(token).perform(delete("/api/core/communities/" + parentCommunity.getID().toString()))
|
||||||
|
.andExpect(status().isNoContent())
|
||||||
|
;
|
||||||
|
getClient(token).perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
|
||||||
|
.andExpect(status().isNotFound())
|
||||||
|
;
|
||||||
|
|
||||||
|
getClient(token).perform(get("/api/core/communities/" + parentCommunityChild1.getID().toString()))
|
||||||
|
.andExpect(status().isNotFound())
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteTestUnAuthorized() throws Exception {
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and one collection.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.withLogo("ThisIsSomeDummyText")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Community parentCommunity2 = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community 2")
|
||||||
|
.withLogo("SomeTest")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Community parentCommunityChild1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Community parentCommunityChild2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community2")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Community parentCommunityChild2Child1 = CommunityBuilder.createSubCommunity(context, parentCommunityChild2)
|
||||||
|
.withName("Sub Sub Community")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
Community parentCommunity2Child1 = CommunityBuilder.createSubCommunity(context, parentCommunity2)
|
||||||
|
.withName("Sub2 Community")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunityChild1)
|
||||||
|
.withName("Collection 1")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", Matchers.is(
|
||||||
|
CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), parentCommunity.getID(),
|
||||||
|
parentCommunity.getHandle())
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$._links.self.href",
|
||||||
|
Matchers.containsString("/api/core/communities"))) ;
|
||||||
|
getClient().perform(delete("/api/core/communities/" + parentCommunity.getID().toString()))
|
||||||
|
.andExpect(status().isUnauthorized())
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteCommunityEpersonWithDeleteRightsTest() throws Exception {
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and one collection.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.setCurrentUser(eperson);
|
||||||
|
authorizeService.addPolicy(context, parentCommunity, Constants.DELETE, eperson);
|
||||||
|
|
||||||
|
String token = getAuthToken(eperson.getEmail(), password);
|
||||||
|
|
||||||
|
getClient(token).perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", Matchers.is(
|
||||||
|
CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), parentCommunity.getID(),
|
||||||
|
parentCommunity.getHandle())
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$._links.self.href",
|
||||||
|
Matchers.containsString("/api/core/communities"))) ;
|
||||||
|
getClient(token).perform(delete("/api/core/communities/" + parentCommunity.getID().toString()))
|
||||||
|
.andExpect(status().isNoContent())
|
||||||
|
;
|
||||||
|
getClient(token).perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
|
||||||
|
.andExpect(status().isNotFound())
|
||||||
|
;
|
||||||
|
|
||||||
|
authorizeService.removePoliciesActionFilter(context, eperson, Constants.DELETE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateCommunityEpersonWithWriteRightsTest() throws Exception {
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and one collection.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", Matchers.is(
|
||||||
|
CommunityMatcher.matchCommunityEntry(parentCommunity.getName(), parentCommunity.getID(),
|
||||||
|
parentCommunity.getHandle())
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$", Matchers.not(
|
||||||
|
Matchers.is(
|
||||||
|
CommunityMatcher.matchCommunityEntry(child1.getName(), child1.getID(), child1.getHandle())
|
||||||
|
)
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities")))
|
||||||
|
;
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
CommunityRest communityRest = communityConverter.fromModel(parentCommunity);
|
||||||
|
|
||||||
|
communityRest.setMetadata(new MetadataRest()
|
||||||
|
.put("dc.title", new MetadataValueRest("Electronic theses and dissertations")));
|
||||||
|
|
||||||
|
context.setCurrentUser(eperson);
|
||||||
|
authorizeService.addPolicy(context, parentCommunity, Constants.WRITE, eperson);
|
||||||
|
|
||||||
|
String token = getAuthToken(eperson.getEmail(), password);
|
||||||
|
|
||||||
|
getClient(token).perform(put("/api/core/communities/" + parentCommunity.getID().toString())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(mapper.writeValueAsBytes(communityRest)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
;
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", Matchers.is(
|
||||||
|
CommunityMatcher.matchCommunityEntry("Electronic theses and dissertations",
|
||||||
|
parentCommunity.getID(),
|
||||||
|
parentCommunity.getHandle())
|
||||||
|
)))
|
||||||
|
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities")))
|
||||||
|
;
|
||||||
|
|
||||||
|
authorizeService.removePoliciesActionFilter(context, eperson, Constants.DELETE);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void patchCommunityMetadataAuthorized() throws Exception {
|
||||||
|
runPatchMetadataTests(admin, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void patchCommunityMetadataUnauthorized() throws Exception {
|
||||||
|
runPatchMetadataTests(eperson, 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runPatchMetadataTests(EPerson asUser, int expectedStatus) throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Community").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String token = getAuthToken(asUser.getEmail(), password);
|
||||||
|
|
||||||
|
new MetadataPatchSuite().runWith(getClient(token), "/api/core/communities/"
|
||||||
|
+ parentCommunity.getID(), expectedStatus);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
package org.dspace.app.rest;
|
package org.dspace.app.rest;
|
||||||
|
|
||||||
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
||||||
|
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadata;
|
||||||
import static org.hamcrest.Matchers.empty;
|
import static org.hamcrest.Matchers.empty;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
@@ -21,7 +22,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
@@ -32,12 +32,13 @@ import org.dspace.app.rest.builder.CommunityBuilder;
|
|||||||
import org.dspace.app.rest.builder.EPersonBuilder;
|
import org.dspace.app.rest.builder.EPersonBuilder;
|
||||||
import org.dspace.app.rest.builder.ItemBuilder;
|
import org.dspace.app.rest.builder.ItemBuilder;
|
||||||
import org.dspace.app.rest.matcher.EPersonMatcher;
|
import org.dspace.app.rest.matcher.EPersonMatcher;
|
||||||
import org.dspace.app.rest.matcher.EPersonMetadataMatcher;
|
|
||||||
import org.dspace.app.rest.model.EPersonRest;
|
import org.dspace.app.rest.model.EPersonRest;
|
||||||
import org.dspace.app.rest.model.MetadataEntryRest;
|
import org.dspace.app.rest.model.MetadataRest;
|
||||||
|
import org.dspace.app.rest.model.MetadataValueRest;
|
||||||
import org.dspace.app.rest.model.patch.Operation;
|
import org.dspace.app.rest.model.patch.Operation;
|
||||||
import org.dspace.app.rest.model.patch.ReplaceOperation;
|
import org.dspace.app.rest.model.patch.ReplaceOperation;
|
||||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||||
|
import org.dspace.app.rest.test.MetadataPatchSuite;
|
||||||
import org.dspace.content.Collection;
|
import org.dspace.content.Collection;
|
||||||
import org.dspace.content.Item;
|
import org.dspace.content.Item;
|
||||||
import org.dspace.eperson.EPerson;
|
import org.dspace.eperson.EPerson;
|
||||||
@@ -54,15 +55,16 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest {
|
|||||||
// we should check how to get it from Spring
|
// we should check how to get it from Spring
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
EPersonRest data = new EPersonRest();
|
EPersonRest data = new EPersonRest();
|
||||||
|
MetadataRest metadataRest = new MetadataRest();
|
||||||
data.setEmail("createtest@fake-email.com");
|
data.setEmail("createtest@fake-email.com");
|
||||||
data.setCanLogIn(true);
|
data.setCanLogIn(true);
|
||||||
MetadataEntryRest surname = new MetadataEntryRest();
|
MetadataValueRest surname = new MetadataValueRest();
|
||||||
surname.setKey("eperson.lastname");
|
|
||||||
surname.setValue("Doe");
|
surname.setValue("Doe");
|
||||||
MetadataEntryRest firstname = new MetadataEntryRest();
|
metadataRest.put("eperson.lastname", surname);
|
||||||
firstname.setKey("eperson.firstname");
|
MetadataValueRest firstname = new MetadataValueRest();
|
||||||
firstname.setValue("John");
|
firstname.setValue("John");
|
||||||
data.setMetadata(Arrays.asList(surname, firstname));
|
metadataRest.put("eperson.firstname", firstname);
|
||||||
|
data.setMetadata(metadataRest);
|
||||||
|
|
||||||
String authToken = getAuthToken(admin.getEmail(), password);
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
getClient(authToken).perform(post("/api/eperson/epersons")
|
getClient(authToken).perform(post("/api/eperson/epersons")
|
||||||
@@ -78,9 +80,9 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest {
|
|||||||
hasJsonPath("$.canLogIn", is(true)),
|
hasJsonPath("$.canLogIn", is(true)),
|
||||||
hasJsonPath("$.requireCertificate", is(false)),
|
hasJsonPath("$.requireCertificate", is(false)),
|
||||||
hasJsonPath("$._links.self.href", not(empty())),
|
hasJsonPath("$._links.self.href", not(empty())),
|
||||||
hasJsonPath("$.metadata", Matchers.containsInAnyOrder(
|
hasJsonPath("$.metadata", Matchers.allOf(
|
||||||
EPersonMetadataMatcher.matchFirstName("John"),
|
matchMetadata("eperson.firstname", "John"),
|
||||||
EPersonMetadataMatcher.matchLastName("Doe")
|
matchMetadata("eperson.lastname", "Doe")
|
||||||
)))));
|
)))));
|
||||||
// TODO cleanup the context!!!
|
// TODO cleanup the context!!!
|
||||||
}
|
}
|
||||||
@@ -1030,4 +1032,23 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void patchEPersonMetadataAuthorized() throws Exception {
|
||||||
|
runPatchMetadataTests(admin, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void patchEPersonMetadataUnauthorized() throws Exception {
|
||||||
|
runPatchMetadataTests(eperson, 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runPatchMetadataTests(EPerson asUser, int expectedStatus) throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("user@test.com").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String token = getAuthToken(asUser.getEmail(), password);
|
||||||
|
|
||||||
|
new MetadataPatchSuite().runWith(getClient(token), "/api/eperson/epersons/" + ePerson.getID(), expectedStatus);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,8 @@ import org.dspace.app.rest.builder.GroupBuilder;
|
|||||||
import org.dspace.app.rest.matcher.GroupMatcher;
|
import org.dspace.app.rest.matcher.GroupMatcher;
|
||||||
import org.dspace.app.rest.model.GroupRest;
|
import org.dspace.app.rest.model.GroupRest;
|
||||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||||
|
import org.dspace.app.rest.test.MetadataPatchSuite;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
import org.dspace.eperson.Group;
|
import org.dspace.eperson.Group;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -234,4 +236,22 @@ public class GroupRestRepositoryIT extends AbstractControllerIntegrationTest {
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void patchGroupMetadataAuthorized() throws Exception {
|
||||||
|
runPatchMetadataTests(admin, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void patchGroupMetadataUnauthorized() throws Exception {
|
||||||
|
runPatchMetadataTests(eperson, 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runPatchMetadataTests(EPerson asUser, int expectedStatus) throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
Group group = GroupBuilder.createGroup(context).withName("Group").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String token = getAuthToken(asUser.getEmail(), password);
|
||||||
|
|
||||||
|
new MetadataPatchSuite().runWith(getClient(token), "/api/eperson/groups/" + group.getID(), expectedStatus);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,10 +7,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest;
|
package org.dspace.app.rest;
|
||||||
|
|
||||||
|
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
|
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.request.MockMvcRequestBuilders.put;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
@@ -18,9 +21,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.CharEncoding;
|
import org.apache.commons.lang3.CharEncoding;
|
||||||
import org.dspace.app.rest.builder.BitstreamBuilder;
|
import org.dspace.app.rest.builder.BitstreamBuilder;
|
||||||
@@ -31,9 +36,14 @@ import org.dspace.app.rest.builder.GroupBuilder;
|
|||||||
import org.dspace.app.rest.builder.ItemBuilder;
|
import org.dspace.app.rest.builder.ItemBuilder;
|
||||||
import org.dspace.app.rest.builder.WorkspaceItemBuilder;
|
import org.dspace.app.rest.builder.WorkspaceItemBuilder;
|
||||||
import org.dspace.app.rest.matcher.ItemMatcher;
|
import org.dspace.app.rest.matcher.ItemMatcher;
|
||||||
|
import org.dspace.app.rest.matcher.MetadataMatcher;
|
||||||
|
import org.dspace.app.rest.model.ItemRest;
|
||||||
|
import org.dspace.app.rest.model.MetadataRest;
|
||||||
|
import org.dspace.app.rest.model.MetadataValueRest;
|
||||||
import org.dspace.app.rest.model.patch.Operation;
|
import org.dspace.app.rest.model.patch.Operation;
|
||||||
import org.dspace.app.rest.model.patch.ReplaceOperation;
|
import org.dspace.app.rest.model.patch.ReplaceOperation;
|
||||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||||
|
import org.dspace.app.rest.test.MetadataPatchSuite;
|
||||||
import org.dspace.content.Bitstream;
|
import org.dspace.content.Bitstream;
|
||||||
import org.dspace.content.Collection;
|
import org.dspace.content.Collection;
|
||||||
import org.dspace.content.Community;
|
import org.dspace.content.Community;
|
||||||
@@ -43,6 +53,7 @@ import org.dspace.eperson.EPerson;
|
|||||||
import org.dspace.eperson.Group;
|
import org.dspace.eperson.Group;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
|
||||||
public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
|
public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||||
|
|
||||||
@@ -909,7 +920,6 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void useStringForBooleanTest() throws Exception {
|
public void useStringForBooleanTest() throws Exception {
|
||||||
context.turnOffAuthorisationSystem();
|
context.turnOffAuthorisationSystem();
|
||||||
@@ -1414,4 +1424,348 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
|
|||||||
Matchers.containsString("/api/core/items")));
|
Matchers.containsString("/api/core/items")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateItem() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and two collections.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community")
|
||||||
|
.build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
ItemRest itemRest = new ItemRest();
|
||||||
|
itemRest.setName("Practices of research data curation in institutional repositories:" +
|
||||||
|
" A qualitative view from repository staff");
|
||||||
|
itemRest.setInArchive(true);
|
||||||
|
itemRest.setDiscoverable(true);
|
||||||
|
itemRest.setWithdrawn(false);
|
||||||
|
|
||||||
|
itemRest.setMetadata(new MetadataRest()
|
||||||
|
.put("dc.description", new MetadataValueRest("<p>Some cool HTML code here</p>"))
|
||||||
|
.put("dc.description.abstract", new MetadataValueRest("Sample item created via the REST API"))
|
||||||
|
.put("dc.description.tableofcontents", new MetadataValueRest("<p>HTML News</p>"))
|
||||||
|
.put("dc.rights", new MetadataValueRest("Custom Copyright Text"))
|
||||||
|
.put("dc.title", new MetadataValueRest("Title Text")));
|
||||||
|
|
||||||
|
String token = getAuthToken(admin.getEmail(), password);
|
||||||
|
MvcResult mvcResult = getClient(token).perform(post("/api/core/items?owningCollection=" +
|
||||||
|
col1.getID().toString())
|
||||||
|
.content(mapper.writeValueAsBytes(itemRest)).contentType(contentType))
|
||||||
|
.andExpect(status().isCreated())
|
||||||
|
.andReturn();
|
||||||
|
|
||||||
|
String content = mvcResult.getResponse().getContentAsString();
|
||||||
|
Map<String,Object> map = mapper.readValue(content, Map.class);
|
||||||
|
String itemUuidString = String.valueOf(map.get("uuid"));
|
||||||
|
String itemHandleString = String.valueOf(map.get("handle"));
|
||||||
|
|
||||||
|
//TODO Refactor this to use the converter to Item instead of checking every property separately
|
||||||
|
getClient(token).perform(get("/api/core/items/" + itemUuidString))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", Matchers.allOf(
|
||||||
|
hasJsonPath("$.id", is(itemUuidString)),
|
||||||
|
hasJsonPath("$.uuid", is(itemUuidString)),
|
||||||
|
hasJsonPath("$.name", is("Title Text")),
|
||||||
|
hasJsonPath("$.handle", is(itemHandleString)),
|
||||||
|
hasJsonPath("$.type", is("item")),
|
||||||
|
hasJsonPath("$.metadata", Matchers.allOf(
|
||||||
|
MetadataMatcher.matchMetadata("dc.description",
|
||||||
|
"<p>Some cool HTML code here</p>"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.description.abstract",
|
||||||
|
"Sample item created via the REST API"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.description.tableofcontents",
|
||||||
|
"<p>HTML News</p>"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.rights",
|
||||||
|
"Custom Copyright Text"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.title",
|
||||||
|
"Title Text")
|
||||||
|
)))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateTest() throws Exception {
|
||||||
|
//We turn off the authorization system in order to create the structure as defined below
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and two collections.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community")
|
||||||
|
.build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
ItemRest itemRest = new ItemRest();
|
||||||
|
itemRest.setName("Practices of research data curation in institutional repositories:" +
|
||||||
|
" A qualitative view from repository staff");
|
||||||
|
itemRest.setInArchive(true);
|
||||||
|
itemRest.setDiscoverable(true);
|
||||||
|
itemRest.setWithdrawn(false);
|
||||||
|
|
||||||
|
|
||||||
|
String token = getAuthToken(admin.getEmail(), password);
|
||||||
|
MvcResult mvcResult = getClient(token).perform(post("/api/core/items?owningCollection=" +
|
||||||
|
col1.getID().toString())
|
||||||
|
.content(mapper.writeValueAsBytes(itemRest))
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isCreated())
|
||||||
|
.andReturn();
|
||||||
|
|
||||||
|
String content = mvcResult.getResponse().getContentAsString();
|
||||||
|
Map<String,Object> map = mapper.readValue(content, Map.class);
|
||||||
|
String itemUuidString = String.valueOf(map.get("uuid"));
|
||||||
|
String itemHandleString = String.valueOf(map.get("handle"));
|
||||||
|
|
||||||
|
itemRest.setMetadata(new MetadataRest()
|
||||||
|
.put("dc.description", new MetadataValueRest("<p>Some cool HTML code here</p>"))
|
||||||
|
.put("dc.description.abstract", new MetadataValueRest("Sample item created via the REST API"))
|
||||||
|
.put("dc.description.tableofcontents", new MetadataValueRest("<p>HTML News</p>"))
|
||||||
|
.put("dc.rights", new MetadataValueRest("New Custom Copyright Text"))
|
||||||
|
.put("dc.title", new MetadataValueRest("New title")));
|
||||||
|
|
||||||
|
itemRest.setUuid(itemUuidString);
|
||||||
|
itemRest.setHandle(itemHandleString);
|
||||||
|
|
||||||
|
mvcResult = getClient(token).perform(put("/api/core/items/" + itemUuidString)
|
||||||
|
.content(mapper.writeValueAsBytes(itemRest))
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andReturn();
|
||||||
|
map = mapper.readValue(content, Map.class);
|
||||||
|
itemUuidString = String.valueOf(map.get("uuid"));
|
||||||
|
itemHandleString = String.valueOf(map.get("handle"));
|
||||||
|
|
||||||
|
//TODO Refactor this to use the converter to Item instead of checking every property separately
|
||||||
|
getClient(token).perform(get("/api/core/items/" + itemUuidString))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", Matchers.allOf(
|
||||||
|
hasJsonPath("$.id", is(itemUuidString)),
|
||||||
|
hasJsonPath("$.uuid", is(itemUuidString)),
|
||||||
|
hasJsonPath("$.name", is("New title")),
|
||||||
|
hasJsonPath("$.handle", is(itemHandleString)),
|
||||||
|
hasJsonPath("$.type", is("item")),
|
||||||
|
hasJsonPath("$.metadata", Matchers.allOf(
|
||||||
|
MetadataMatcher.matchMetadata("dc.description",
|
||||||
|
"<p>Some cool HTML code here</p>"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.description.abstract",
|
||||||
|
"Sample item created via the REST API"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.description.tableofcontents",
|
||||||
|
"<p>HTML News</p>"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.rights",
|
||||||
|
"New Custom Copyright Text"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.title",
|
||||||
|
"New title")
|
||||||
|
)))));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteItem() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and two collections.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community")
|
||||||
|
.build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
ItemRest itemRest = new ItemRest();
|
||||||
|
itemRest.setName("Practices of research data curation in institutional repositories:" +
|
||||||
|
" A qualitative view from repository staff");
|
||||||
|
itemRest.setInArchive(true);
|
||||||
|
itemRest.setDiscoverable(true);
|
||||||
|
itemRest.setWithdrawn(false);
|
||||||
|
|
||||||
|
itemRest.setMetadata(new MetadataRest()
|
||||||
|
.put("dc.description", new MetadataValueRest("<p>Some cool HTML code here</p>"))
|
||||||
|
.put("dc.description.abstract", new MetadataValueRest("Sample item created via the REST API"))
|
||||||
|
.put("dc.description.tableofcontents", new MetadataValueRest("<p>HTML News</p>"))
|
||||||
|
.put("dc.rights", new MetadataValueRest("Custom Copyright Text"))
|
||||||
|
.put("dc.title", new MetadataValueRest("Title Text")));
|
||||||
|
|
||||||
|
String token = getAuthToken(admin.getEmail(), password);
|
||||||
|
MvcResult mvcResult = getClient(token).perform(post("/api/core/items?owningCollection=" +
|
||||||
|
col1.getID().toString())
|
||||||
|
.content(mapper.writeValueAsBytes(itemRest))
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isCreated())
|
||||||
|
.andReturn();
|
||||||
|
|
||||||
|
String content = mvcResult.getResponse().getContentAsString();
|
||||||
|
Map<String,Object> map = mapper.readValue(content, Map.class);
|
||||||
|
String itemUuidString = String.valueOf(map.get("uuid"));
|
||||||
|
String itemHandleString = String.valueOf(map.get("handle"));
|
||||||
|
|
||||||
|
//TODO Refactor this to use the converter to Item instead of checking every property separately
|
||||||
|
getClient(token).perform(get("/api/core/items/" + itemUuidString))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", Matchers.allOf(
|
||||||
|
hasJsonPath("$.id", is(itemUuidString)),
|
||||||
|
hasJsonPath("$.uuid", is(itemUuidString)),
|
||||||
|
hasJsonPath("$.name", is("Title Text")),
|
||||||
|
hasJsonPath("$.handle", is(itemHandleString)),
|
||||||
|
hasJsonPath("$.type", is("item")),
|
||||||
|
hasJsonPath("$.metadata", Matchers.allOf(
|
||||||
|
MetadataMatcher.matchMetadata("dc.description",
|
||||||
|
"<p>Some cool HTML code here</p>"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.description.abstract",
|
||||||
|
"Sample item created via the REST API"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.description.tableofcontents",
|
||||||
|
"<p>HTML News</p>"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.rights",
|
||||||
|
"Custom Copyright Text"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.title",
|
||||||
|
"Title Text")
|
||||||
|
)))));
|
||||||
|
|
||||||
|
getClient(token).perform(delete("/api/core/items/" + itemUuidString))
|
||||||
|
.andExpect(status().isNoContent());
|
||||||
|
|
||||||
|
getClient(token).perform(get("/api/core/items/" + itemUuidString))
|
||||||
|
.andExpect(status().isNotFound());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteItemUnauthorized() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community-collection structure with one parent community with sub-community and two collections.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community")
|
||||||
|
.build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
ItemRest itemRest = new ItemRest();
|
||||||
|
itemRest.setName("Practices of research data curation in institutional repositories:" +
|
||||||
|
" A qualitative view from repository staff");
|
||||||
|
itemRest.setInArchive(true);
|
||||||
|
itemRest.setDiscoverable(true);
|
||||||
|
itemRest.setWithdrawn(false);
|
||||||
|
|
||||||
|
itemRest.setMetadata(new MetadataRest()
|
||||||
|
.put("dc.description", new MetadataValueRest("<p>Some cool HTML code here</p>"))
|
||||||
|
.put("dc.description.abstract", new MetadataValueRest("Sample item created via the REST API"))
|
||||||
|
.put("dc.description.tableofcontents", new MetadataValueRest("<p>HTML News</p>"))
|
||||||
|
.put("dc.rights", new MetadataValueRest("Custom Copyright Text"))
|
||||||
|
.put("dc.title", new MetadataValueRest("Title Text")));
|
||||||
|
|
||||||
|
String token = getAuthToken(admin.getEmail(), password);
|
||||||
|
MvcResult mvcResult = getClient(token).perform(post("/api/core/items?owningCollection=" +
|
||||||
|
col1.getID().toString())
|
||||||
|
.content(mapper.writeValueAsBytes(itemRest))
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isCreated())
|
||||||
|
.andReturn();
|
||||||
|
|
||||||
|
String content = mvcResult.getResponse().getContentAsString();
|
||||||
|
Map<String,Object> map = mapper.readValue(content, Map.class);
|
||||||
|
String itemUuidString = String.valueOf(map.get("uuid"));
|
||||||
|
String itemHandleString = String.valueOf(map.get("handle"));
|
||||||
|
|
||||||
|
//TODO Refactor this to use the converter to Item instead of checking every property separately
|
||||||
|
getClient(token).perform(get("/api/core/items/" + itemUuidString))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", Matchers.allOf(
|
||||||
|
hasJsonPath("$.id", is(itemUuidString)),
|
||||||
|
hasJsonPath("$.uuid", is(itemUuidString)),
|
||||||
|
hasJsonPath("$.name", is("Title Text")),
|
||||||
|
hasJsonPath("$.handle", is(itemHandleString)),
|
||||||
|
hasJsonPath("$.type", is("item")),
|
||||||
|
hasJsonPath("$.metadata", Matchers.allOf(
|
||||||
|
MetadataMatcher.matchMetadata("dc.description",
|
||||||
|
"<p>Some cool HTML code here</p>"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.description.abstract",
|
||||||
|
"Sample item created via the REST API"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.description.tableofcontents",
|
||||||
|
"<p>HTML News</p>"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.rights",
|
||||||
|
"Custom Copyright Text"),
|
||||||
|
MetadataMatcher.matchMetadata("dc.title",
|
||||||
|
"Title Text")
|
||||||
|
)))));
|
||||||
|
|
||||||
|
getClient().perform(delete("/api/core/items/" + itemUuidString))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
|
||||||
|
getClient(token).perform(get("/api/core/items/" + itemUuidString))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteOneWrongUuidResourceNotFoundTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
//** GIVEN **
|
||||||
|
//1. A community with one collection.
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Collection col1 = CollectionBuilder
|
||||||
|
.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
|
||||||
|
//2. One public item, one workspace item and one template item.
|
||||||
|
Item publicItem = ItemBuilder.createItem(context, col1)
|
||||||
|
.withTitle("Public item 1")
|
||||||
|
.withIssueDate("2017-10-17")
|
||||||
|
.withAuthor("Smith, Donald").withAuthor("Doe, John")
|
||||||
|
.withSubject("ExtraEntry")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
String token = getAuthToken(admin.getEmail(), password);
|
||||||
|
|
||||||
|
//Delete public item
|
||||||
|
getClient(token).perform(delete("/api/core/items/" + parentCommunity.getID()))
|
||||||
|
.andExpect(status().is(404));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void patchItemMetadataAuthorized() throws Exception {
|
||||||
|
runPatchMetadataTests(admin, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void patchItemMetadataUnauthorized() throws Exception {
|
||||||
|
runPatchMetadataTests(eperson, 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runPatchMetadataTests(EPerson asUser, int expectedStatus) throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
|
||||||
|
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||||
|
.withName("Sub Community").build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
|
||||||
|
Item item = ItemBuilder.createItem(context, col1).build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String token = getAuthToken(asUser.getEmail(), password);
|
||||||
|
|
||||||
|
new MetadataPatchSuite().runWith(getClient(token), "/api/core/items/" + item.getID(), expectedStatus);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -7,50 +7,258 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest;
|
package org.dspace.app.rest;
|
||||||
|
|
||||||
|
import static com.jayway.jsonpath.JsonPath.read;
|
||||||
|
import static org.dspace.app.rest.matcher.MetadataschemaMatcher.matchEntry;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.dspace.app.rest.builder.MetadataSchemaBuilder;
|
import org.dspace.app.rest.builder.MetadataSchemaBuilder;
|
||||||
|
import org.dspace.app.rest.converter.MetadataSchemaConverter;
|
||||||
import org.dspace.app.rest.matcher.MetadataschemaMatcher;
|
import org.dspace.app.rest.matcher.MetadataschemaMatcher;
|
||||||
|
import org.dspace.app.rest.model.MetadataSchemaRest;
|
||||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||||
import org.dspace.content.MetadataSchema;
|
import org.dspace.content.MetadataSchema;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for the {@link org.dspace.app.rest.repository.MetadataSchemaRestRepository}
|
||||||
|
* This class will include all the tests for the logic with regards to the
|
||||||
|
* {@link org.dspace.app.rest.repository.MetadataSchemaRestRepository}
|
||||||
|
*/
|
||||||
public class MetadataSchemaRestRepositoryIT extends AbstractControllerIntegrationTest {
|
public class MetadataSchemaRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||||
|
|
||||||
|
private static final String TEST_NAME = "testSchemaName";
|
||||||
|
private static final String TEST_NAMESPACE = "testSchemaNameSpace";
|
||||||
|
|
||||||
|
private static final String TEST_NAME_UPDATED = "testSchemaNameUpdated";
|
||||||
|
private static final String TEST_NAMESPACE_UPDATED = "testSchemaNameSpaceUpdated";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MetadataSchemaConverter metadataSchemaConverter;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void findAll() throws Exception {
|
public void findAll() throws Exception {
|
||||||
|
|
||||||
context.turnOffAuthorisationSystem();
|
context.turnOffAuthorisationSystem();
|
||||||
MetadataSchema metadataSchema = MetadataSchemaBuilder.createMetadataSchema(context, "ATest", "ANamespace")
|
MetadataSchema metadataSchema = MetadataSchemaBuilder.createMetadataSchema(context, "ATest", "ANamespace")
|
||||||
.build();
|
.build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
getClient().perform(get("/api/core/metadataschemas"))
|
getClient().perform(get("/api/core/metadataschemas"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(content().contentType(contentType))
|
.andExpect(content().contentType(contentType))
|
||||||
.andExpect(jsonPath("$._embedded.metadataschemas", Matchers.hasItem(
|
.andExpect(jsonPath("$._embedded.metadataschemas", Matchers.hasItem(
|
||||||
MetadataschemaMatcher.matchEntry()
|
matchEntry()
|
||||||
)))
|
)))
|
||||||
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/metadataschemas")))
|
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/metadataschemas")))
|
||||||
.andExpect(jsonPath("$.page.size", is(20)));
|
.andExpect(jsonPath("$.page.size", is(20)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void findOne() throws Exception {
|
public void findOne() throws Exception {
|
||||||
|
|
||||||
context.turnOffAuthorisationSystem();
|
context.turnOffAuthorisationSystem();
|
||||||
MetadataSchema metadataSchema = MetadataSchemaBuilder.createMetadataSchema(context, "ATest", "ANamespace")
|
MetadataSchema metadataSchema = MetadataSchemaBuilder.createMetadataSchema(context, "ATest", "ANamespace")
|
||||||
.build();
|
.build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
getClient().perform(get("/api/core/metadataschemas/" + metadataSchema.getID()))
|
getClient().perform(get("/api/core/metadataschemas/" + metadataSchema.getID()))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$", is(
|
.andExpect(jsonPath("$", is(
|
||||||
MetadataschemaMatcher.matchEntry(metadataSchema)
|
matchEntry(metadataSchema)
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSuccess() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
MetadataSchema metadataSchema = MetadataSchemaBuilder.createMetadataSchema(context, "ATest", "ANamespace")
|
||||||
|
.build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
MetadataSchemaRest metadataSchemaRest = metadataSchemaConverter.fromModel(metadataSchema);
|
||||||
|
metadataSchemaRest.setPrefix(TEST_NAME);
|
||||||
|
metadataSchemaRest.setNamespace(TEST_NAMESPACE);
|
||||||
|
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
AtomicReference<Integer> idRef = new AtomicReference<>();
|
||||||
|
|
||||||
|
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(post("/api/core/metadataschemas")
|
||||||
|
.content(new ObjectMapper().writeValueAsBytes(metadataSchemaRest))
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isCreated())
|
||||||
|
.andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id")));
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadataschemas/" + idRef.get()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", MetadataschemaMatcher.matchEntry(TEST_NAME, TEST_NAMESPACE)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createUnauthorizedTest()
|
||||||
|
throws Exception {
|
||||||
|
MetadataSchemaRest metadataSchemaRest = new MetadataSchemaRest();
|
||||||
|
metadataSchemaRest.setPrefix(TEST_NAME);
|
||||||
|
metadataSchemaRest.setNamespace(TEST_NAMESPACE);
|
||||||
|
|
||||||
|
getClient()
|
||||||
|
.perform(post("/api/core/metadataschemas")
|
||||||
|
.content(new ObjectMapper().writeValueAsBytes(metadataSchemaRest))
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteSuccess() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
MetadataSchema metadataSchema = MetadataSchemaBuilder.createMetadataSchema(context, "ATest", "A namespace")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadataschemas/" + metadataSchema.getID()))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
|
||||||
|
getClient(getAuthToken(admin.getEmail(), password))
|
||||||
|
.perform(delete("/api/core/metadataschemas/" + metadataSchema.getID()))
|
||||||
|
.andExpect(status().isNoContent());
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadataschemas/" + metadataSchema.getID()))
|
||||||
|
.andExpect(status().isNotFound());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteUnauthorized() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
MetadataSchema metadataSchema = MetadataSchemaBuilder.createMetadataSchema(context, TEST_NAME, TEST_NAMESPACE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadataschemas/" + metadataSchema.getID())).andExpect(status().isOk());
|
||||||
|
|
||||||
|
getClient()
|
||||||
|
.perform(delete("/api/core/metadataschemas/" + metadataSchema.getID()))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadataschemas/" + metadataSchema.getID())).andExpect(status().isOk());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteNonExisting() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
MetadataSchema metadataSchema = MetadataSchemaBuilder.createMetadataSchema(context, "A name", "A namespace")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
Integer id = metadataSchema.getID();
|
||||||
|
|
||||||
|
getClient(getAuthToken(admin.getEmail(), password))
|
||||||
|
.perform(delete("/api/core/metadataschemas/" + id))
|
||||||
|
.andExpect(status().isNoContent());
|
||||||
|
|
||||||
|
getClient(getAuthToken(admin.getEmail(), password))
|
||||||
|
.perform(delete("/api/core/metadataschemas/" + id))
|
||||||
|
.andExpect(status().isNotFound());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void update() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
MetadataSchema metadataSchema = MetadataSchemaBuilder.createMetadataSchema(context, TEST_NAME, TEST_NAMESPACE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
MetadataSchemaRest metadataSchemaRest = new MetadataSchemaRest();
|
||||||
|
metadataSchemaRest.setId(metadataSchema.getID());
|
||||||
|
metadataSchemaRest.setPrefix(TEST_NAME_UPDATED);
|
||||||
|
metadataSchemaRest.setNamespace(TEST_NAMESPACE_UPDATED);
|
||||||
|
|
||||||
|
getClient(getAuthToken(admin.getEmail(), password))
|
||||||
|
.perform(put("/api/core/metadataschemas/" + metadataSchema.getID())
|
||||||
|
.content(new ObjectMapper().writeValueAsBytes(metadataSchemaRest))
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadataschemas/" + metadataSchema.getID()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", MetadataschemaMatcher
|
||||||
|
.matchEntry(TEST_NAME_UPDATED, TEST_NAMESPACE_UPDATED)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateUnauthorized() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
MetadataSchema metadataSchema = MetadataSchemaBuilder.createMetadataSchema(context, TEST_NAME, TEST_NAMESPACE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
MetadataSchemaRest metadataSchemaRest = new MetadataSchemaRest();
|
||||||
|
metadataSchemaRest.setId(metadataSchema.getID());
|
||||||
|
metadataSchemaRest.setPrefix(TEST_NAME_UPDATED);
|
||||||
|
metadataSchemaRest.setNamespace(TEST_NAMESPACE_UPDATED);
|
||||||
|
|
||||||
|
getClient()
|
||||||
|
.perform(put("/api/core/metadataschemas/" + metadataSchema.getID())
|
||||||
|
.content(new ObjectMapper().writeValueAsBytes(metadataSchemaRest))
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadataschemas/" + metadataSchema.getID()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", MetadataschemaMatcher
|
||||||
|
.matchEntry(TEST_NAME, TEST_NAMESPACE)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateWrongRights() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
MetadataSchema metadataSchema = MetadataSchemaBuilder.createMetadataSchema(context, TEST_NAME, TEST_NAMESPACE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
MetadataSchemaRest metadataSchemaRest = new MetadataSchemaRest();
|
||||||
|
metadataSchemaRest.setId(metadataSchema.getID());
|
||||||
|
metadataSchemaRest.setPrefix(TEST_NAME_UPDATED);
|
||||||
|
metadataSchemaRest.setNamespace(TEST_NAMESPACE_UPDATED);
|
||||||
|
|
||||||
|
getClient(getAuthToken(eperson.getEmail(), password))
|
||||||
|
.perform(put("/api/core/metadataschemas/" + metadataSchema.getID())
|
||||||
|
.content(new ObjectMapper().writeValueAsBytes(metadataSchemaRest))
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadataschemas/" + metadataSchema.getID()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", MetadataschemaMatcher
|
||||||
|
.matchEntry(TEST_NAME, TEST_NAMESPACE)));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -7,23 +7,63 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest;
|
package org.dspace.app.rest;
|
||||||
|
|
||||||
|
import static com.jayway.jsonpath.JsonPath.read;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.dspace.app.rest.builder.MetadataFieldBuilder;
|
import org.dspace.app.rest.builder.MetadataFieldBuilder;
|
||||||
import org.dspace.app.rest.builder.MetadataSchemaBuilder;
|
import org.dspace.app.rest.builder.MetadataSchemaBuilder;
|
||||||
import org.dspace.app.rest.matcher.MetadataFieldMatcher;
|
import org.dspace.app.rest.matcher.MetadataFieldMatcher;
|
||||||
|
import org.dspace.app.rest.model.MetadataFieldRest;
|
||||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||||
import org.dspace.content.MetadataField;
|
import org.dspace.content.MetadataField;
|
||||||
|
import org.dspace.content.MetadataFieldServiceImpl;
|
||||||
import org.dspace.content.MetadataSchema;
|
import org.dspace.content.MetadataSchema;
|
||||||
|
import org.dspace.content.service.MetadataSchemaService;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for the {@link org.dspace.app.rest.repository.MetadataFieldRestRepository}
|
||||||
|
* This class will include all the tests for the logic with regards to the
|
||||||
|
* {@link org.dspace.app.rest.repository.MetadataFieldRestRepository}
|
||||||
|
*/
|
||||||
public class MetadatafieldRestRepositoryIT extends AbstractControllerIntegrationTest {
|
public class MetadatafieldRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||||
|
|
||||||
|
private static final String ELEMENT = "test element";
|
||||||
|
private static final String QUALIFIER = "test qualifier";
|
||||||
|
private static final String SCOPE_NOTE = "test scope_note";
|
||||||
|
|
||||||
|
private static final String ELEMENT_UPDATED = "test element updated";
|
||||||
|
private static final String QUALIFIER_UPDATED = "test qualifier updated";
|
||||||
|
private static final String SCOPE_NOTE_UPDATED = "test scope_note updated";
|
||||||
|
|
||||||
|
private MetadataSchema metadataSchema;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MetadataSchemaService metadataSchemaService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MetadataFieldServiceImpl metadataFieldService;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() throws Exception {
|
||||||
|
metadataSchema = metadataSchemaService.findAll(context).get(0);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void findAll() throws Exception {
|
public void findAll() throws Exception {
|
||||||
@@ -31,6 +71,7 @@ public class MetadatafieldRestRepositoryIT extends AbstractControllerIntegration
|
|||||||
context.turnOffAuthorisationSystem();
|
context.turnOffAuthorisationSystem();
|
||||||
MetadataField metadataField = MetadataFieldBuilder
|
MetadataField metadataField = MetadataFieldBuilder
|
||||||
.createMetadataField(context, "AnElement", "AQualifier", "AScopeNote").build();
|
.createMetadataField(context, "AnElement", "AQualifier", "AScopeNote").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
getClient().perform(get("/api/core/metadatafields")
|
getClient().perform(get("/api/core/metadatafields")
|
||||||
.param("size", String.valueOf(100)))
|
.param("size", String.valueOf(100)))
|
||||||
@@ -54,6 +95,7 @@ public class MetadatafieldRestRepositoryIT extends AbstractControllerIntegration
|
|||||||
context.turnOffAuthorisationSystem();
|
context.turnOffAuthorisationSystem();
|
||||||
MetadataField metadataField = MetadataFieldBuilder
|
MetadataField metadataField = MetadataFieldBuilder
|
||||||
.createMetadataField(context, "AnElement", "AQualifier", "AScopeNote").build();
|
.createMetadataField(context, "AnElement", "AQualifier", "AScopeNote").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
getClient().perform(get("/api/core/metadatafields/" + metadataField.getID()))
|
getClient().perform(get("/api/core/metadatafields/" + metadataField.getID()))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
@@ -65,13 +107,14 @@ public class MetadatafieldRestRepositoryIT extends AbstractControllerIntegration
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void searchMethodsExist() throws Exception {
|
public void searchMethodsExist() throws Exception {
|
||||||
|
|
||||||
getClient().perform(get("/api/core/metadatafields"))
|
getClient().perform(get("/api/core/metadatafields"))
|
||||||
.andExpect(jsonPath("$._links.search.href", Matchers.notNullValue()));
|
.andExpect(jsonPath("$._links.search.href", notNullValue()));
|
||||||
|
|
||||||
getClient().perform(get("/api/core/metadatafields/search"))
|
getClient().perform(get("/api/core/metadatafields/search"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(content().contentType(contentType))
|
.andExpect(content().contentType(contentType))
|
||||||
.andExpect(jsonPath("$._links.bySchema", Matchers.notNullValue()));
|
.andExpect(jsonPath("$._links.bySchema", notNullValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -83,6 +126,7 @@ public class MetadatafieldRestRepositoryIT extends AbstractControllerIntegration
|
|||||||
|
|
||||||
MetadataField metadataField = MetadataFieldBuilder
|
MetadataField metadataField = MetadataFieldBuilder
|
||||||
.createMetadataField(context, schema, "AnElement", "AQualifier", "AScopeNote").build();
|
.createMetadataField(context, schema, "AnElement", "AQualifier", "AScopeNote").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
getClient().perform(get("/api/core/metadatafields/search/bySchema")
|
getClient().perform(get("/api/core/metadatafields/search/bySchema")
|
||||||
.param("schema", "dc")
|
.param("schema", "dc")
|
||||||
@@ -124,4 +168,236 @@ public class MetadatafieldRestRepositoryIT extends AbstractControllerIntegration
|
|||||||
.andExpect(status().isUnprocessableEntity());
|
.andExpect(status().isUnprocessableEntity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSuccess() throws Exception {
|
||||||
|
|
||||||
|
MetadataFieldRest metadataFieldRest = new MetadataFieldRest();
|
||||||
|
metadataFieldRest.setElement("testElementForCreate");
|
||||||
|
metadataFieldRest.setQualifier("testQualifierForCreate");
|
||||||
|
metadataFieldRest.setScopeNote(SCOPE_NOTE);
|
||||||
|
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
AtomicReference<Integer> idRef = new AtomicReference<>();
|
||||||
|
|
||||||
|
assertThat(metadataFieldService.findByElement(context, metadataSchema, ELEMENT, QUALIFIER), nullValue());
|
||||||
|
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(post("/api/core/metadatafields")
|
||||||
|
.param("schemaId", metadataSchema.getID() + "")
|
||||||
|
.content(new ObjectMapper().writeValueAsBytes(metadataFieldRest))
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isCreated())
|
||||||
|
.andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id")));
|
||||||
|
|
||||||
|
getClient(authToken).perform(get("/api/core/metadatafields/" + idRef.get()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", MetadataFieldMatcher.matchMetadataFieldByKeys(
|
||||||
|
metadataSchema.getName(), "testElementForCreate", "testQualifierForCreate")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createUnauthorized() throws Exception {
|
||||||
|
|
||||||
|
MetadataFieldRest metadataFieldRest = new MetadataFieldRest();
|
||||||
|
metadataFieldRest.setElement(ELEMENT);
|
||||||
|
metadataFieldRest.setQualifier(QUALIFIER);
|
||||||
|
metadataFieldRest.setScopeNote(SCOPE_NOTE);
|
||||||
|
|
||||||
|
getClient()
|
||||||
|
.perform(post("/api/core/metadatafields")
|
||||||
|
.param("schemaId", metadataSchema.getID() + "")
|
||||||
|
.content(new ObjectMapper().writeValueAsBytes(metadataFieldRest))
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createUnauthorizedEPersonNoAdminRights() throws Exception {
|
||||||
|
|
||||||
|
MetadataFieldRest metadataFieldRest = new MetadataFieldRest();
|
||||||
|
metadataFieldRest.setElement(ELEMENT);
|
||||||
|
metadataFieldRest.setQualifier(QUALIFIER);
|
||||||
|
metadataFieldRest.setScopeNote(SCOPE_NOTE);
|
||||||
|
|
||||||
|
String token = getAuthToken(eperson.getEmail(), password);
|
||||||
|
|
||||||
|
getClient(token)
|
||||||
|
.perform(post("/api/core/metadatafields")
|
||||||
|
.param("schemaId", metadataSchema.getID() + "")
|
||||||
|
.content(new ObjectMapper().writeValueAsBytes(metadataFieldRest))
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteSuccess() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
MetadataField metadataField = MetadataFieldBuilder.createMetadataField(context, ELEMENT, QUALIFIER, SCOPE_NOTE)
|
||||||
|
.build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadatafields/" + metadataField.getID()))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
getClient(getAuthToken(admin.getEmail(), password))
|
||||||
|
.perform(delete("/api/core/metadatafields/" + metadataField.getID()))
|
||||||
|
.andExpect(status().isNoContent());
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadatafields/" + metadataField.getID()))
|
||||||
|
.andExpect(status().isNotFound());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteUnauthorized() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
MetadataField metadataField = MetadataFieldBuilder.createMetadataField(context, ELEMENT, QUALIFIER, SCOPE_NOTE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadatafields/" + metadataField.getID()))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
getClient()
|
||||||
|
.perform(delete("/api/core/metadatafields/" + metadataField.getID()))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadatafields/" + metadataField.getID()))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteUnauthorizedEPersonNoAdminRights() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
MetadataField metadataField = MetadataFieldBuilder.createMetadataField(context, ELEMENT, QUALIFIER, SCOPE_NOTE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
String token = getAuthToken(eperson.getEmail(), password);
|
||||||
|
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadatafields/" + metadataField.getID()))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
getClient(token)
|
||||||
|
.perform(delete("/api/core/metadatafields/" + metadataField.getID()))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadatafields/" + metadataField.getID()))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteNonExisting() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
MetadataField metadataField = MetadataFieldBuilder.createMetadataField(context, ELEMENT, QUALIFIER, SCOPE_NOTE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
Integer id = metadataField.getID();
|
||||||
|
getClient(getAuthToken(admin.getEmail(), password))
|
||||||
|
.perform(delete("/api/core/metadatafields/" + id))
|
||||||
|
.andExpect(status().isNoContent());
|
||||||
|
|
||||||
|
assertThat(metadataFieldService.find(context, id), nullValue());
|
||||||
|
|
||||||
|
getClient(getAuthToken(admin.getEmail(), password))
|
||||||
|
.perform(delete("/api/core/metadatafields/" + id))
|
||||||
|
.andExpect(status().isNotFound());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void update() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
MetadataField metadataField = MetadataFieldBuilder.createMetadataField(context, ELEMENT, QUALIFIER, SCOPE_NOTE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
MetadataFieldRest metadataFieldRest = new MetadataFieldRest();
|
||||||
|
metadataFieldRest.setId(metadataField.getID());
|
||||||
|
metadataFieldRest.setElement(ELEMENT_UPDATED);
|
||||||
|
metadataFieldRest.setQualifier(QUALIFIER_UPDATED);
|
||||||
|
metadataFieldRest.setScopeNote(SCOPE_NOTE_UPDATED);
|
||||||
|
|
||||||
|
getClient(getAuthToken(admin.getEmail(), password))
|
||||||
|
.perform(put("/api/core/metadatafields/" + metadataField.getID())
|
||||||
|
.content(new ObjectMapper().writeValueAsBytes(metadataFieldRest))
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadatafields/" + metadataField.getID()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", MetadataFieldMatcher.matchMetadataFieldByKeys(
|
||||||
|
metadataSchema.getName(), ELEMENT_UPDATED, QUALIFIER_UPDATED)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateUnauthorized() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
MetadataField metadataField = MetadataFieldBuilder.createMetadataField(context, ELEMENT, QUALIFIER, SCOPE_NOTE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
MetadataFieldRest metadataFieldRest = new MetadataFieldRest();
|
||||||
|
metadataFieldRest.setId(metadataField.getID());
|
||||||
|
metadataFieldRest.setElement(ELEMENT_UPDATED);
|
||||||
|
metadataFieldRest.setQualifier(QUALIFIER_UPDATED);
|
||||||
|
metadataFieldRest.setScopeNote(SCOPE_NOTE_UPDATED);
|
||||||
|
|
||||||
|
getClient()
|
||||||
|
.perform(put("/api/core/metadatafields/" + metadataField.getID())
|
||||||
|
.content(new ObjectMapper().writeValueAsBytes(metadataFieldRest))
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadatafields/" + metadataField.getID()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", MetadataFieldMatcher.matchMetadataFieldByKeys(
|
||||||
|
metadataSchema.getName(), ELEMENT, QUALIFIER)
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateWrongRights() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
MetadataField metadataField = MetadataFieldBuilder.createMetadataField(context, ELEMENT, QUALIFIER, SCOPE_NOTE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
MetadataFieldRest metadataFieldRest = new MetadataFieldRest();
|
||||||
|
metadataFieldRest.setId(metadataField.getID());
|
||||||
|
metadataFieldRest.setElement(ELEMENT_UPDATED);
|
||||||
|
metadataFieldRest.setQualifier(QUALIFIER_UPDATED);
|
||||||
|
metadataFieldRest.setScopeNote(SCOPE_NOTE_UPDATED);
|
||||||
|
|
||||||
|
getClient(getAuthToken(eperson.getEmail(), password))
|
||||||
|
.perform(put("/api/core/metadatafields/" + metadataField.getID())
|
||||||
|
.content(new ObjectMapper().writeValueAsBytes(metadataFieldRest))
|
||||||
|
.contentType(contentType))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
|
||||||
|
getClient().perform(get("/api/core/metadatafields/" + metadataField.getID()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", MetadataFieldMatcher.matchMetadataFieldByKeys(
|
||||||
|
metadataSchema.getName(), ELEMENT, QUALIFIER)
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The contents of this file are subject to the license and copyright
|
* The contents of this file are subject to the license and copyright
|
||||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
@@ -17,7 +17,9 @@ import java.util.UUID;
|
|||||||
import org.dspace.app.rest.builder.SiteBuilder;
|
import org.dspace.app.rest.builder.SiteBuilder;
|
||||||
import org.dspace.app.rest.matcher.SiteMatcher;
|
import org.dspace.app.rest.matcher.SiteMatcher;
|
||||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||||
|
import org.dspace.app.rest.test.MetadataPatchSuite;
|
||||||
import org.dspace.content.Site;
|
import org.dspace.content.Site;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -67,4 +69,23 @@ public class SiteRestRepositoryIT extends AbstractControllerIntegrationTest {
|
|||||||
.andExpect(status().isNotFound());
|
.andExpect(status().isNotFound());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void patchSiteMetadataAuthorized() throws Exception {
|
||||||
|
runPatchMetadataTests(admin, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void patchSiteMetadataUnauthorized() throws Exception {
|
||||||
|
runPatchMetadataTests(eperson, 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runPatchMetadataTests(EPerson asUser, int expectedStatus) throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
Site site = SiteBuilder.createSite(context).build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String token = getAuthToken(asUser.getEmail(), password);
|
||||||
|
|
||||||
|
new MetadataPatchSuite().runWith(getClient(token), "/api/core/sites/" + site.getID(), expectedStatus);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -239,7 +239,8 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
|
|||||||
getClient().perform(get("/api/submission/workspaceitems/" + witem.getID() + "/collection"))
|
getClient().perform(get("/api/submission/workspaceitems/" + witem.getID() + "/collection"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$", Matchers
|
.andExpect(jsonPath("$", Matchers
|
||||||
.is(CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle()))));
|
.is(CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle()))
|
||||||
|
));
|
||||||
|
|
||||||
getClient().perform(get("/api/submission/workspaceitems/" + witem.getID() + "/item")).andExpect(status().isOk())
|
getClient().perform(get("/api/submission/workspaceitems/" + witem.getID() + "/item")).andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$", Matchers.is(ItemMatcher.matchItemWithTitleAndDateIssued(witem.getItem(),
|
.andExpect(jsonPath("$", Matchers.is(ItemMatcher.matchItemWithTitleAndDateIssued(witem.getItem(),
|
||||||
|
@@ -8,8 +8,8 @@
|
|||||||
package org.dspace.app.rest.matcher;
|
package org.dspace.app.rest.matcher;
|
||||||
|
|
||||||
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
||||||
|
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadata;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.empty;
|
import static org.hamcrest.Matchers.empty;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
@@ -30,9 +30,9 @@ public class BitstreamMatcher {
|
|||||||
hasJsonPath("$.uuid", is(bitstream.getID().toString())),
|
hasJsonPath("$.uuid", is(bitstream.getID().toString())),
|
||||||
hasJsonPath("$.name", is(bitstream.getName())),
|
hasJsonPath("$.name", is(bitstream.getName())),
|
||||||
hasJsonPath("$.bundleName", is("ORIGINAL")),
|
hasJsonPath("$.bundleName", is("ORIGINAL")),
|
||||||
hasJsonPath("$.metadata", containsInAnyOrder(
|
hasJsonPath("$.metadata", allOf(
|
||||||
BitstreamMetadataMatcher.matchTitle(bitstream.getName()),
|
matchMetadata("dc.title", bitstream.getName()),
|
||||||
BitstreamMetadataMatcher.matchDescription(bitstream.getDescription())
|
matchMetadata("dc.description", bitstream.getDescription())
|
||||||
)),
|
)),
|
||||||
hasJsonPath("$.sizeBytes", is((int) bitstream.getSizeBytes())),
|
hasJsonPath("$.sizeBytes", is((int) bitstream.getSizeBytes())),
|
||||||
hasJsonPath("$.checkSum", matchChecksum()),
|
hasJsonPath("$.checkSum", matchChecksum()),
|
||||||
|
@@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* The contents of this file are subject to the license and copyright
|
|
||||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
|
||||||
* tree and available online at
|
|
||||||
*
|
|
||||||
* http://www.dspace.org/license/
|
|
||||||
*/
|
|
||||||
package org.dspace.app.rest.matcher;
|
|
||||||
|
|
||||||
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
|
||||||
import static org.hamcrest.Matchers.allOf;
|
|
||||||
import static org.hamcrest.Matchers.is;
|
|
||||||
|
|
||||||
import org.hamcrest.Matcher;
|
|
||||||
|
|
||||||
public class BitstreamMetadataMatcher {
|
|
||||||
|
|
||||||
private BitstreamMetadataMatcher() { }
|
|
||||||
|
|
||||||
public static Matcher<? super Object> matchTitle(String title) {
|
|
||||||
return allOf(
|
|
||||||
hasJsonPath("$.key", is("dc.title")),
|
|
||||||
hasJsonPath("$.value", is(title))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Matcher<? super Object> matchDescription(String description) {
|
|
||||||
return allOf(
|
|
||||||
hasJsonPath("$.key", is("dc.description")),
|
|
||||||
hasJsonPath("$.value", is(description))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -32,8 +32,8 @@ public class CollectionMatcher {
|
|||||||
hasJsonPath("$.name", is(name)),
|
hasJsonPath("$.name", is(name)),
|
||||||
hasJsonPath("$.handle", is(handle)),
|
hasJsonPath("$.handle", is(handle)),
|
||||||
hasJsonPath("$.type", is("collection")),
|
hasJsonPath("$.type", is("collection")),
|
||||||
hasJsonPath("$.metadata", Matchers.contains(
|
hasJsonPath("$.metadata", Matchers.allOf(
|
||||||
CollectionMetadataMatcher.matchTitle(name)
|
MetadataMatcher.matchMetadata("dc.title", name)
|
||||||
)),
|
)),
|
||||||
matchLinks(uuid),
|
matchLinks(uuid),
|
||||||
matchLogo(logo)
|
matchLogo(logo)
|
||||||
|
@@ -1,26 +0,0 @@
|
|||||||
/**
|
|
||||||
* The contents of this file are subject to the license and copyright
|
|
||||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
|
||||||
* tree and available online at
|
|
||||||
*
|
|
||||||
* http://www.dspace.org/license/
|
|
||||||
*/
|
|
||||||
package org.dspace.app.rest.matcher;
|
|
||||||
|
|
||||||
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
|
||||||
import static org.hamcrest.Matchers.allOf;
|
|
||||||
|
|
||||||
import org.hamcrest.Matcher;
|
|
||||||
|
|
||||||
public class CollectionMetadataMatcher {
|
|
||||||
|
|
||||||
private CollectionMetadataMatcher() { }
|
|
||||||
|
|
||||||
public static Matcher<? super Object> matchTitle(String title) {
|
|
||||||
return allOf(
|
|
||||||
hasJsonPath("$.key", is("dc.title")),
|
|
||||||
hasJsonPath("$.value", is(title))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -36,8 +36,8 @@ public class CommunityMatcher {
|
|||||||
hasJsonPath("$.name", is(name)),
|
hasJsonPath("$.name", is(name)),
|
||||||
hasJsonPath("$.handle", is(handle)),
|
hasJsonPath("$.handle", is(handle)),
|
||||||
hasJsonPath("$.type", is("community")),
|
hasJsonPath("$.type", is("community")),
|
||||||
hasJsonPath("$.metadata", Matchers.contains(
|
hasJsonPath("$.metadata", Matchers.allOf(
|
||||||
CommunityMetadataMatcher.matchMetadata("dc.title", name)
|
MetadataMatcher.matchMetadata("dc.title", name)
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,26 +0,0 @@
|
|||||||
/**
|
|
||||||
* The contents of this file are subject to the license and copyright
|
|
||||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
|
||||||
* tree and available online at
|
|
||||||
*
|
|
||||||
* http://www.dspace.org/license/
|
|
||||||
*/
|
|
||||||
package org.dspace.app.rest.matcher;
|
|
||||||
|
|
||||||
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
|
||||||
import static org.hamcrest.Matchers.allOf;
|
|
||||||
import static org.hamcrest.Matchers.is;
|
|
||||||
|
|
||||||
import org.hamcrest.Matcher;
|
|
||||||
|
|
||||||
public class CommunityMetadataMatcher {
|
|
||||||
|
|
||||||
private CommunityMetadataMatcher() { }
|
|
||||||
|
|
||||||
public static Matcher<? super Object> matchMetadata(String key, String value) {
|
|
||||||
return allOf(
|
|
||||||
hasJsonPath("$.key", is(key)),
|
|
||||||
hasJsonPath("$.value", is(value))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -29,9 +29,9 @@ public class EPersonMatcher {
|
|||||||
hasJsonPath("$.type", is("eperson")),
|
hasJsonPath("$.type", is("eperson")),
|
||||||
hasJsonPath("$.canLogIn", not(empty())),
|
hasJsonPath("$.canLogIn", not(empty())),
|
||||||
hasJsonPath("$._links.self.href", containsString("/api/eperson/epersons/" + ePerson.getID().toString())),
|
hasJsonPath("$._links.self.href", containsString("/api/eperson/epersons/" + ePerson.getID().toString())),
|
||||||
hasJsonPath("$.metadata", Matchers.hasItems(
|
hasJsonPath("$.metadata", Matchers.allOf(
|
||||||
EPersonMetadataMatcher.matchFirstName(ePerson.getFirstName()),
|
MetadataMatcher.matchMetadata("eperson.firstname", ePerson.getFirstName()),
|
||||||
EPersonMetadataMatcher.matchLastName(ePerson.getLastName())
|
MetadataMatcher.matchMetadata("eperson.lastname", ePerson.getLastName())
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,40 +0,0 @@
|
|||||||
/**
|
|
||||||
* The contents of this file are subject to the license and copyright
|
|
||||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
|
||||||
* tree and available online at
|
|
||||||
*
|
|
||||||
* http://www.dspace.org/license/
|
|
||||||
*/
|
|
||||||
package org.dspace.app.rest.matcher;
|
|
||||||
|
|
||||||
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
|
||||||
import static org.hamcrest.Matchers.allOf;
|
|
||||||
import static org.hamcrest.Matchers.is;
|
|
||||||
|
|
||||||
import org.hamcrest.Matcher;
|
|
||||||
|
|
||||||
public class EPersonMetadataMatcher {
|
|
||||||
|
|
||||||
private EPersonMetadataMatcher() { }
|
|
||||||
|
|
||||||
public static Matcher<? super Object> matchFirstName(String firstName) {
|
|
||||||
return allOf(
|
|
||||||
hasJsonPath("$.key", is("eperson.firstname")),
|
|
||||||
hasJsonPath("$.value", is(firstName))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Matcher<? super Object> matchLastName(String lastName) {
|
|
||||||
return allOf(
|
|
||||||
hasJsonPath("$.key", is("eperson.lastname")),
|
|
||||||
hasJsonPath("$.value", is(lastName))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Matcher<? super Object> matchLanguage(String language) {
|
|
||||||
return allOf(
|
|
||||||
hasJsonPath("$.key", is("eperson.language")),
|
|
||||||
hasJsonPath("$.value", is(language))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -8,9 +8,9 @@
|
|||||||
package org.dspace.app.rest.matcher;
|
package org.dspace.app.rest.matcher;
|
||||||
|
|
||||||
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
||||||
|
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadata;
|
||||||
import static org.dspace.app.rest.test.AbstractControllerIntegrationTest.REST_SERVER_URL;
|
import static org.dspace.app.rest.test.AbstractControllerIntegrationTest.REST_SERVER_URL;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
import static org.hamcrest.Matchers.contains;
|
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
@@ -34,8 +34,9 @@ public class ItemMatcher {
|
|||||||
matchItemProperties(item),
|
matchItemProperties(item),
|
||||||
|
|
||||||
//Check core metadata (the JSON Path expression evaluates to a collection so we have to use contains)
|
//Check core metadata (the JSON Path expression evaluates to a collection so we have to use contains)
|
||||||
hasJsonPath("$.metadata[?(@.key=='dc.title')].value", contains(title)),
|
hasJsonPath("$.metadata", allOf(
|
||||||
hasJsonPath("$.metadata[?(@.key=='dc.date.issued')].value", contains(dateIssued)),
|
matchMetadata("dc.title", title),
|
||||||
|
matchMetadata("dc.date.issued", dateIssued))),
|
||||||
|
|
||||||
//Check links
|
//Check links
|
||||||
matchItemLinks(item)
|
matchItemLinks(item)
|
||||||
|
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.matcher;
|
||||||
|
|
||||||
|
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
||||||
|
import static org.hamcrest.Matchers.contains;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class to provide convenient matchers for metadata.
|
||||||
|
*/
|
||||||
|
public class MetadataMatcher {
|
||||||
|
|
||||||
|
private MetadataMatcher() { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a matcher to ensure a given value is present among all values for a given metadata key.
|
||||||
|
*
|
||||||
|
* @param key the metadata key.
|
||||||
|
* @param value the value that must be present.
|
||||||
|
* @return the matcher.
|
||||||
|
*/
|
||||||
|
public static Matcher<? super Object> matchMetadata(String key, String value) {
|
||||||
|
return hasJsonPath("$.['" + key + "'][*].value", contains(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a matcher to ensure a given value is present at a specific position in the list of values for a given key.
|
||||||
|
*
|
||||||
|
* @param key the metadata key.
|
||||||
|
* @param value the value that must be present.
|
||||||
|
* @param position the position it must be present at.
|
||||||
|
* @return the matcher.
|
||||||
|
*/
|
||||||
|
public static Matcher<? super Object> matchMetadata(String key, String value, int position) {
|
||||||
|
return hasJsonPath("$.['" + key + "'][" + position + "].value", is(value));
|
||||||
|
}
|
||||||
|
}
|
@@ -29,9 +29,13 @@ public class MetadataschemaMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Matcher<? super Object> matchEntry(MetadataSchema metadataSchema) {
|
public static Matcher<? super Object> matchEntry(MetadataSchema metadataSchema) {
|
||||||
|
return matchEntry(metadataSchema.getName(), metadataSchema.getNamespace());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Matcher<? super Object> matchEntry(String name, String nameSpace) {
|
||||||
return allOf(
|
return allOf(
|
||||||
hasJsonPath("$.prefix", is(metadataSchema.getName())),
|
hasJsonPath("$.prefix", is(name)),
|
||||||
hasJsonPath("$.namespace", is(metadataSchema.getNamespace())),
|
hasJsonPath("$.namespace", is(nameSpace)),
|
||||||
hasJsonPath("$.type", is("metadataschema")),
|
hasJsonPath("$.type", is("metadataschema")),
|
||||||
hasJsonPath("$._links.self.href", Matchers.containsString("/api/core/metadataschemas"))
|
hasJsonPath("$._links.self.href", Matchers.containsString("/api/core/metadataschemas"))
|
||||||
);
|
);
|
||||||
|
@@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.test;
|
||||||
|
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.ResultActions;
|
||||||
|
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for performing metadata patch tests sourced from a common json file (see constructor).
|
||||||
|
*/
|
||||||
|
public class MetadataPatchSuite {
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
private final JsonNode suite;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the suite by parsing the json file of tests.
|
||||||
|
*
|
||||||
|
* @throws Exception if there is an error reading the file.
|
||||||
|
*/
|
||||||
|
public MetadataPatchSuite() throws Exception {
|
||||||
|
suite = objectMapper.readTree(getClass().getResourceAsStream("metadata-patch-suite.json"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs all tests in the file using the given client and url, expecting the given status.
|
||||||
|
*
|
||||||
|
* @param client the client to use.
|
||||||
|
* @param url the url to issue the patch against.
|
||||||
|
* @param expectedStatus the expected http status code. If this does not match the actual code, the test fails.
|
||||||
|
*/
|
||||||
|
public void runWith(MockMvc client, String url, int expectedStatus) {
|
||||||
|
for (JsonNode testNode: suite.get("tests")) {
|
||||||
|
String requestBody = testNode.get("patch").toString();
|
||||||
|
String expectedMetadata = testNode.get("expect").toString();
|
||||||
|
try {
|
||||||
|
System.out.println("Running patch test: " + testNode.get("name") + "\nRequest: " + requestBody);
|
||||||
|
checkResponse("PATCH", client, patch(url).content(requestBody), expectedMetadata, expectedStatus);
|
||||||
|
if (expectedStatus >= 200 && expectedStatus < 300) {
|
||||||
|
checkResponse("GET", client, get(url), expectedMetadata, expectedStatus);
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
Assert.fail("Metadata patch test '" + testNode.get("name") + "' failed.\n" + "Request body: "
|
||||||
|
+ requestBody + "\n" + "Error: " + (t instanceof AssertionError ? "" : t.getClass().getName())
|
||||||
|
+ t.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issues a PATCH or GET request and checks that the body and response code match what is expected.
|
||||||
|
*
|
||||||
|
* @param verb the http verb (PATCH or GET).
|
||||||
|
* @param client the client to use.
|
||||||
|
* @param requestBuilder the request builder that has been pre-seeded with the request url and request body.
|
||||||
|
* @param expectedMetadata the expected metadata as a minimal (no extra spaces) json string. Note: This will
|
||||||
|
* only be checked if the expectedStatus is in the 200 range.
|
||||||
|
* @param expectedStatus the expected http response status.
|
||||||
|
* @throws Exception if any checked error occurs, signifying test failure.
|
||||||
|
*/
|
||||||
|
private void checkResponse(String verb, MockMvc client, MockHttpServletRequestBuilder requestBuilder,
|
||||||
|
String expectedMetadata, int expectedStatus) throws Exception {
|
||||||
|
ResultActions resultActions = client.perform(requestBuilder
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().is(expectedStatus));
|
||||||
|
if (expectedStatus >= 200 && expectedStatus < 300) {
|
||||||
|
String responseBody = resultActions.andReturn().getResponse().getContentAsString();
|
||||||
|
JsonNode responseJson = objectMapper.readTree(responseBody);
|
||||||
|
String responseMetadata = responseJson.get("metadata").toString();
|
||||||
|
if (!responseMetadata.equals(expectedMetadata)) {
|
||||||
|
Assert.fail("Expected metadata in " + verb + " response: " + expectedMetadata
|
||||||
|
+ "\nGot metadata in " + verb + " response: " + responseMetadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,152 @@
|
|||||||
|
{
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"name": "clear metadata",
|
||||||
|
"patch": [
|
||||||
|
{ "op": "replace",
|
||||||
|
"path": "/metadata",
|
||||||
|
"value": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expect": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "add first title",
|
||||||
|
"patch": [
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/metadata/dc.title",
|
||||||
|
"value": [
|
||||||
|
{ "value": "title 1" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expect": {
|
||||||
|
"dc.title": [
|
||||||
|
{ "value": "title 1", "language": null, "authority": null, "confidence": -1 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "add second title",
|
||||||
|
"patch": [
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/metadata/dc.title/-",
|
||||||
|
"value": { "value": "最後のタイトル", "language": "ja_JP" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expect": {
|
||||||
|
"dc.title": [
|
||||||
|
{ "value": "title 1", "language": null, "authority": null, "confidence": -1 },
|
||||||
|
{ "value": "最後のタイトル", "language": "ja_JP", "authority": null, "confidence": -1 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "insert zeroth title",
|
||||||
|
"patch": [
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/metadata/dc.title/0",
|
||||||
|
"value": {
|
||||||
|
"value": "title 0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expect": {
|
||||||
|
"dc.title": [
|
||||||
|
{ "value": "title 0", "language": null, "authority": null, "confidence": -1 },
|
||||||
|
{ "value": "title 1", "language": null, "authority": null, "confidence": -1 },
|
||||||
|
{ "value": "最後のタイトル", "language": "ja_JP", "authority": null, "confidence": -1 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "move last title up one",
|
||||||
|
"patch": [
|
||||||
|
{
|
||||||
|
"op": "move",
|
||||||
|
"from": "/metadata/dc.title/2",
|
||||||
|
"path": "/metadata/dc.title/1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expect": {
|
||||||
|
"dc.title": [
|
||||||
|
{ "value": "title 0", "language": null, "authority": null, "confidence": -1 },
|
||||||
|
{ "value": "最後のタイトル", "language": "ja_JP", "authority": null, "confidence": -1 },
|
||||||
|
{ "value": "title 1", "language": null, "authority": null, "confidence": -1 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "replace title 2 value and language in two operations",
|
||||||
|
"patch": [
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/metadata/dc.title/1/value",
|
||||||
|
"value": "title A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/metadata/dc.title/1/language",
|
||||||
|
"value": "en_US"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expect": {
|
||||||
|
"dc.title": [
|
||||||
|
{ "value": "title 0", "language": null, "authority": null, "confidence": -1 },
|
||||||
|
{ "value": "title A", "language": "en_US", "authority": null, "confidence": -1 },
|
||||||
|
{ "value": "title 1", "language": null, "authority": null, "confidence": -1 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "copy title A to end of list",
|
||||||
|
"patch": [
|
||||||
|
{
|
||||||
|
"op": "copy",
|
||||||
|
"from": "/metadata/dc.title/1",
|
||||||
|
"path": "/metadata/dc.title/-"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expect": {
|
||||||
|
"dc.title": [
|
||||||
|
{ "value": "title 0", "language": null, "authority": null, "confidence": -1 },
|
||||||
|
{ "value": "title A", "language": "en_US", "authority": null, "confidence": -1 },
|
||||||
|
{ "value": "title 1", "language": null, "authority": null, "confidence": -1 },
|
||||||
|
{ "value": "title A", "language": "en_US", "authority": null, "confidence": -1 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "remove both title A copies",
|
||||||
|
"patch": [
|
||||||
|
{
|
||||||
|
"op": "remove",
|
||||||
|
"path": "/metadata/dc.title/1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "remove",
|
||||||
|
"path": "/metadata/dc.title/2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expect": {
|
||||||
|
"dc.title": [
|
||||||
|
{ "value": "title 0", "language": null, "authority": null, "confidence": -1 },
|
||||||
|
{ "value": "title 1", "language": null, "authority": null, "confidence": -1 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "remove all titles",
|
||||||
|
"patch": [
|
||||||
|
{
|
||||||
|
"op": "remove",
|
||||||
|
"path": "/metadata/dc.title"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expect": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -37,7 +37,7 @@ dspace.url = ${dspace.baseUrl}
|
|||||||
|
|
||||||
# This is the URL that will be used for the REST endpoints to be served on.
|
# This is the URL that will be used for the REST endpoints to be served on.
|
||||||
# This will typically be followed by /api to determine the root endpoints.
|
# This will typically be followed by /api to determine the root endpoints.
|
||||||
dspace.restUrl = ${dspace.baseUrl}/rest
|
dspace.restUrl = ${dspace.baseUrl}/spring-rest
|
||||||
|
|
||||||
# Optional: DSpace URL for mobile access
|
# Optional: DSpace URL for mobile access
|
||||||
# This
|
# This
|
||||||
|
@@ -31,6 +31,13 @@ oai.cache.enabled = true
|
|||||||
# Base Cache Directory
|
# Base Cache Directory
|
||||||
oai.cache.dir = ${dspace.dir}/var/oai
|
oai.cache.dir = ${dspace.dir}/var/oai
|
||||||
|
|
||||||
|
#---------------------------------------------------------------#
|
||||||
|
#--------------OAI IMPORT CONFIGURATION ------------------------#
|
||||||
|
#---------------------------------------------------------------#
|
||||||
|
|
||||||
|
# Size of batches to commit to solr at a time
|
||||||
|
oai.import.batch.size = 1000
|
||||||
|
|
||||||
#---------------------------------------------------------------#
|
#---------------------------------------------------------------#
|
||||||
#--------------OAI HARVESTING CONFIGURATIONS--------------------#
|
#--------------OAI HARVESTING CONFIGURATIONS--------------------#
|
||||||
#---------------------------------------------------------------#
|
#---------------------------------------------------------------#
|
||||||
|
@@ -2,7 +2,15 @@
|
|||||||
#------------SUBMISSION CURATION CONFIGURATIONS-----------------#
|
#------------SUBMISSION CURATION CONFIGURATIONS-----------------#
|
||||||
#---------------------------------------------------------------#
|
#---------------------------------------------------------------#
|
||||||
# This file contains configuration properties solely relating #
|
# This file contains configuration properties solely relating #
|
||||||
# to the scheduling of curation tasks during submission. #
|
# to the scheduling of curation tasks during submission -- that #
|
||||||
|
# is: when tasks are attached to a workflow. #
|
||||||
#---------------------------------------------------------------#
|
#---------------------------------------------------------------#
|
||||||
# Scan for viruses
|
# Scan for viruses
|
||||||
submission-curation.virus-scan = false
|
submission-curation.virus-scan = false
|
||||||
|
|
||||||
|
# Report serializer plugin, to capture submission task reports.
|
||||||
|
# Uncomment exactly one, or configure your own.
|
||||||
|
# FileReporter writes reports to ${report.dir}/curation-yyyyMMddThhmmssSSS.report
|
||||||
|
plugin.single.org.dspace.curate.Reporter = org.dspace.curate.FileReporter
|
||||||
|
# LogReporter writes report lines to the DSpace log.
|
||||||
|
#plugin.single.org.dspace.curate.Reporter = org.dspace.curate.LogReporter
|
||||||
|
@@ -1,33 +0,0 @@
|
|||||||
# DSpace Database Now Upgrades Automatically
|
|
||||||
|
|
||||||
AS OF DSPACE 5, the DSpace database now upgrades itself AUTOMATICALLY.
|
|
||||||
|
|
||||||
Therefore, all `database_schema*.sql` files have been removed. Starting
|
|
||||||
with DSpace 4.x -> 5.0 upgrade, you will no longer need to manually run any
|
|
||||||
SQL scripts to upgrade your database.
|
|
||||||
|
|
||||||
Please see the [5.0 Upgrade Instructions](https://wiki.duraspace.org/display/DSDOC5x/Upgrading+DSpace)
|
|
||||||
for more information on upgrading to DSpace 5.
|
|
||||||
|
|
||||||
|
|
||||||
## More info on automatic database upgrades
|
|
||||||
|
|
||||||
As of DSpace 5.0, we now use [Flyway DB](http://flywaydb.org/) along with the
|
|
||||||
SQL scripts embedded in the `dspace-api.jar` to automatically keep your DSpace
|
|
||||||
database up-to-date. These scripts are now located in the source code at:
|
|
||||||
`[dspace-src]/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle`
|
|
||||||
|
|
||||||
As Flyway automates the upgrade process, you should NEVER run these SQL scripts
|
|
||||||
manually. For more information, please see the `README.md` in the scripts directory.
|
|
||||||
|
|
||||||
## Using the update-sequences.sql script
|
|
||||||
|
|
||||||
The `update-sequences.sql` script in this directory may still be used to update
|
|
||||||
your internal database counts if you feel they have gotten out of "sync". This
|
|
||||||
may sometimes occur after large restores of content (e.g. when using the DSpace
|
|
||||||
[AIP Backup and Restore](https://wiki.duraspace.org/display/DSDOC5x/AIP+Backup+and+Restore)
|
|
||||||
feature).
|
|
||||||
|
|
||||||
This `update-sequences.sql` script can be run manually. It will not harm your
|
|
||||||
database (or its contents) in any way. It just ensures all database counts (i.e.
|
|
||||||
sequences) are properly set to the next available value.
|
|
@@ -1,75 +0,0 @@
|
|||||||
--
|
|
||||||
-- update-sequences.sql
|
|
||||||
--
|
|
||||||
-- Copyright (c) 2002-2016, The DSpace Foundation. All rights reserved.
|
|
||||||
--
|
|
||||||
-- Redistribution and use in source and binary forms, with or without
|
|
||||||
-- modification, are permitted provided that the following conditions are
|
|
||||||
-- met:
|
|
||||||
--
|
|
||||||
-- - Redistributions of source code must retain the above copyright
|
|
||||||
-- notice, this list of conditions and the following disclaimer.
|
|
||||||
--
|
|
||||||
-- - Redistributions in binary form must reproduce the above copyright
|
|
||||||
-- notice, this list of conditions and the following disclaimer in the
|
|
||||||
-- documentation and/or other materials provided with the distribution.
|
|
||||||
--
|
|
||||||
-- - Neither the name of the DSpace Foundation nor the names of its
|
|
||||||
-- contributors may be used to endorse or promote products derived from
|
|
||||||
-- this software without specific prior written permission.
|
|
||||||
--
|
|
||||||
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
-- ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
-- HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
-- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
||||||
-- BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
||||||
-- OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
|
||||||
-- TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
||||||
-- USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
|
||||||
-- DAMAGE.
|
|
||||||
|
|
||||||
-- SQL code to update the ID (primary key) generating sequences, if some
|
|
||||||
-- import operation has set explicit IDs.
|
|
||||||
--
|
|
||||||
-- Sequences are used to generate IDs for new rows in the database. If a
|
|
||||||
-- bulk import operation, such as an SQL dump, specifies primary keys for
|
|
||||||
-- imported data explicitly, the sequences are out of sync and need updating.
|
|
||||||
-- This SQL code does just that.
|
|
||||||
--
|
|
||||||
-- This should rarely be needed; any bulk import should be performed using the
|
|
||||||
-- org.dspace.content API which is safe to use concurrently and in multiple
|
|
||||||
-- JVMs. The SQL code below will typically only be required after a direct
|
|
||||||
-- SQL data dump from a backup or somesuch.
|
|
||||||
|
|
||||||
-- Depends on being run from sqlplus with incseq.sql in the current path
|
|
||||||
-- you can find incseq.sql at: http://www.akadia.com/services/scripts/incseq.sql
|
|
||||||
-- Here that script was renamed to updateseq.sql.
|
|
||||||
|
|
||||||
@updateseq.sql bitstreamformatregistry_seq bitstreamformatregistry bitstream_format_id ""
|
|
||||||
@updateseq.sql fileextension_seq fileextension file_extension_id ""
|
|
||||||
@updateseq.sql resourcepolicy_seq resourcepolicy policy_id ""
|
|
||||||
@updateseq.sql workspaceitem_seq workspaceitem workspace_item_id ""
|
|
||||||
@updateseq.sql workflowitem_seq workflowitem workflow_id ""
|
|
||||||
@updateseq.sql tasklistitem_seq tasklistitem tasklist_id ""
|
|
||||||
@updateseq.sql registrationdata_seq registrationdata registrationdata_id ""
|
|
||||||
@updateseq.sql subscription_seq subscription subscription_id ""
|
|
||||||
@updateseq.sql metadatafieldregistry_seq metadatafieldregistry metadata_field_id ""
|
|
||||||
@updateseq.sql metadatavalue_seq metadatavalue metadata_value_id ""
|
|
||||||
@updateseq.sql metadataschemaregistry_seq metadataschemaregistry metadata_schema_id ""
|
|
||||||
@updateseq.sql harvested_collection_seq harvested_collection id ""
|
|
||||||
@updateseq.sql harvested_item_seq harvested_item id ""
|
|
||||||
@updateseq.sql webapp_seq webapp webapp_id ""
|
|
||||||
@updateseq.sql requestitem_seq requestitem requestitem_id ""
|
|
||||||
@updateseq.sql handle_id_seq handle handle_id ""
|
|
||||||
|
|
||||||
-- Handle Sequence is a special case. Since Handles minted by DSpace use the 'handle_seq',
|
|
||||||
-- we need to ensure the next assigned handle will *always* be unique. So, 'handle_seq'
|
|
||||||
-- always needs to be set to the value of the *largest* handle suffix. That way when the
|
|
||||||
-- next handle is assigned, it will use the next largest number. This query does the following:
|
|
||||||
-- For all 'handle' values which have a number in their suffix (after '/'), find the maximum
|
|
||||||
-- suffix value, convert it to a number, and set the 'handle_seq' to start at the next value
|
|
||||||
-- (see updateseq.sql script for more)
|
|
||||||
@updateseq.sql handle_seq handle "to_number(regexp_replace(handle, '.*/', ''), '999999999999')" "WHERE REGEXP_LIKE(handle, '^.*/[0123456789]*$')"
|
|
@@ -1,30 +0,0 @@
|
|||||||
-- #############################################################################################
|
|
||||||
--
|
|
||||||
-- %Purpose: Set a sequence to the max value of a given attribute
|
|
||||||
--
|
|
||||||
-- #############################################################################################
|
|
||||||
--
|
|
||||||
-- Paramters:
|
|
||||||
-- 1: sequence name
|
|
||||||
-- 2: table name
|
|
||||||
-- 3: attribute name
|
|
||||||
--
|
|
||||||
-- Sample usage:
|
|
||||||
-- @updateseq.sql my_sequence my_table my_attribute where-clause
|
|
||||||
--
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
--
|
|
||||||
SET SERVEROUTPUT ON SIZE 1000000;
|
|
||||||
--
|
|
||||||
DECLARE
|
|
||||||
curr NUMBER := 0;
|
|
||||||
BEGIN
|
|
||||||
SELECT max(&3) INTO curr FROM &2 &4;
|
|
||||||
|
|
||||||
curr := curr + 1;
|
|
||||||
|
|
||||||
EXECUTE IMMEDIATE 'DROP SEQUENCE &1';
|
|
||||||
|
|
||||||
EXECUTE IMMEDIATE 'CREATE SEQUENCE &1 START WITH ' || NVL(curr,1);
|
|
||||||
END;
|
|
||||||
/
|
|
@@ -1,33 +0,0 @@
|
|||||||
# DSpace Database Now Upgrades Automatically
|
|
||||||
|
|
||||||
AS OF DSPACE 5, the DSpace database now upgrades itself AUTOMATICALLY.
|
|
||||||
|
|
||||||
Therefore, all `database_schema*.sql` files have been removed. Starting
|
|
||||||
with DSpace 4.x -> 5.0 upgrade, you will no longer need to manually run any
|
|
||||||
SQL scripts to upgrade your database.
|
|
||||||
|
|
||||||
Please see the [5.0 Upgrade Instructions](https://wiki.duraspace.org/display/DSDOC5x/Upgrading+DSpace)
|
|
||||||
for more information on upgrading to DSpace 5.
|
|
||||||
|
|
||||||
|
|
||||||
## More info on automatic database upgrades
|
|
||||||
|
|
||||||
As of DSpace 5.0, we now use [Flyway DB](http://flywaydb.org/) along with the
|
|
||||||
SQL scripts embedded in the `dspace-api.jar` to automatically keep your DSpace
|
|
||||||
database up-to-date. These scripts are now located in the source code at:
|
|
||||||
`[dspace-src]/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres`
|
|
||||||
|
|
||||||
As Flyway automates the upgrade process, you should NEVER run these SQL scripts
|
|
||||||
manually. For more information, please see the `README.md` in the scripts directory.
|
|
||||||
|
|
||||||
## Using the update-sequences.sql script
|
|
||||||
|
|
||||||
The `update-sequences.sql` script in this directory may still be used to update
|
|
||||||
your internal database counts if you feel they have gotten out of "sync". This
|
|
||||||
may sometimes occur after large restores of content (e.g. when using the DSpace
|
|
||||||
[AIP Backup and Restore](https://wiki.duraspace.org/display/DSDOC5x/AIP+Backup+and+Restore)
|
|
||||||
feature).
|
|
||||||
|
|
||||||
This `update-sequences.sql` script can be run manually. It will not harm your
|
|
||||||
database (or its contents) in any way. It just ensures all database counts (i.e.
|
|
||||||
sequences) are properly set to the next available value.
|
|
@@ -569,16 +569,6 @@ Common usage:
|
|||||||
<fileset dir="lib" />
|
<fileset dir="lib" />
|
||||||
</copy>
|
</copy>
|
||||||
|
|
||||||
<move todir="${dspace.dir}/etc.bak-${build.date}" failonerror="no">
|
|
||||||
<fileset dir="${dspace.dir}/etc">
|
|
||||||
<include name="**/*" />
|
|
||||||
</fileset>
|
|
||||||
</move>
|
|
||||||
|
|
||||||
<copy todir="${dspace.dir}/etc" preservelastmodified="true">
|
|
||||||
<fileset dir="etc" />
|
|
||||||
</copy>
|
|
||||||
|
|
||||||
<copy todir="${dspace.dir}/solr" preservelastmodified="true">
|
<copy todir="${dspace.dir}/solr" preservelastmodified="true">
|
||||||
<fileset dir="solr"/>
|
<fileset dir="solr"/>
|
||||||
</copy>
|
</copy>
|
||||||
@@ -593,10 +583,6 @@ Common usage:
|
|||||||
|
|
||||||
${dspace.dir}/lib.bak-${build.date}
|
${dspace.dir}/lib.bak-${build.date}
|
||||||
|
|
||||||
${dspace.dir}/etc was backed up to
|
|
||||||
|
|
||||||
${dspace.dir}/etc.bak-${build.date}
|
|
||||||
|
|
||||||
Please review these directories and delete if no longer needed.
|
Please review these directories and delete if no longer needed.
|
||||||
====================================================================
|
====================================================================
|
||||||
</echo>
|
</echo>
|
||||||
@@ -728,8 +714,6 @@ Common usage:
|
|||||||
|
|
||||||
<mkdir dir="${dspace.dir}/lib" />
|
<mkdir dir="${dspace.dir}/lib" />
|
||||||
|
|
||||||
<mkdir dir="${dspace.dir}/etc" />
|
|
||||||
|
|
||||||
<mkdir dir="${dspace.dir}/webapps" />
|
<mkdir dir="${dspace.dir}/webapps" />
|
||||||
|
|
||||||
<mkdir dir="${dspace.dir}/triplestore" />
|
<mkdir dir="${dspace.dir}/triplestore" />
|
||||||
@@ -821,17 +805,6 @@ Common usage:
|
|||||||
<fileset dir="lib" />
|
<fileset dir="lib" />
|
||||||
</copy>
|
</copy>
|
||||||
|
|
||||||
<!-- NB: no regular use is made of etc/ files in an installed
|
|
||||||
- system, so this step is 'deprecated', and will eventually
|
|
||||||
- be removed. -->
|
|
||||||
<delete failonerror="no">
|
|
||||||
<fileset dir="${dspace.dir}/etc" includes="**/*" />
|
|
||||||
</delete>
|
|
||||||
|
|
||||||
<copy todir="${dspace.dir}/etc" preservelastmodified="true">
|
|
||||||
<fileset dir="etc" />
|
|
||||||
</copy>
|
|
||||||
|
|
||||||
<copy todir="${dspace.dir}/solr" preservelastmodified="true">
|
<copy todir="${dspace.dir}/solr" preservelastmodified="true">
|
||||||
<fileset dir="solr" />
|
<fileset dir="solr" />
|
||||||
</copy>
|
</copy>
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
|
|
||||||
"http://java.sun.com/dtd/web-app_2_3.dtd">
|
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
The contents of this file are subject to the license and copyright
|
The contents of this file are subject to the license and copyright
|
||||||
@@ -15,7 +13,11 @@
|
|||||||
This file overrides the solr localhost restriction.
|
This file overrides the solr localhost restriction.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<web-app>
|
<web-app version='2.5'
|
||||||
|
xmlns='http://java.sun.com/xml/ns/javaee'
|
||||||
|
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
|
||||||
|
xsi:schemaLocation='http://java.sun.com/xml/ns/javaee
|
||||||
|
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd'>
|
||||||
|
|
||||||
<!-- Uncomment if you are trying to use a Resin version before 3.0.19.
|
<!-- Uncomment if you are trying to use a Resin version before 3.0.19.
|
||||||
Their XML implementation isn't entirely compatible with Xerces.
|
Their XML implementation isn't entirely compatible with Xerces.
|
||||||
@@ -28,32 +30,35 @@
|
|||||||
"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"/>
|
"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"/>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- People who want to hardcode their "Solr Home" directly into the
|
|
||||||
WAR File can set the JNDI property here...
|
|
||||||
-->
|
|
||||||
<!--
|
|
||||||
<env-entry>
|
|
||||||
<env-entry-name>solr/home</env-entry-name>
|
|
||||||
<env-entry-value>/put/your/solr/home/here</env-entry-value>
|
|
||||||
<env-entry-type>java.lang.String</env-entry-type>
|
|
||||||
</env-entry>
|
|
||||||
-->
|
|
||||||
<env-entry>
|
<env-entry>
|
||||||
|
<description>Solr home: configuration, cores etc.</description>
|
||||||
<env-entry-name>solr/home</env-entry-name>
|
<env-entry-name>solr/home</env-entry-name>
|
||||||
<env-entry-value>${dspace.dir}/solr</env-entry-value>
|
<env-entry-value>${dspace.dir}/solr</env-entry-value>
|
||||||
<env-entry-type>java.lang.String</env-entry-type>
|
<env-entry-type>java.lang.String</env-entry-type>
|
||||||
</env-entry>
|
</env-entry>
|
||||||
|
|
||||||
<!-- Tell Solr where its log4j configuration is located -->
|
<!-- Tell Solr where its log4j configuration is located -->
|
||||||
<!-- NOTE: Solr cannot use the default DSpace log4j configuration as it isn't
|
<!-- NOTE: Solr cannot use the default DSpace log4j configuration as it
|
||||||
initialized until the DSpace Kernel starts up, and we don't want Solr to
|
isn't initialized until the DSpace Kernel starts up, and we don't want
|
||||||
depend on the DSpace Kernel/API -->
|
Solr to depend on the DSpace Kernel/API -->
|
||||||
<context-param>
|
<context-param>
|
||||||
<param-name>log4j.configuration</param-name>
|
<description>
|
||||||
<param-value>${dspace.dir}/config/log4j-solr.properties</param-value>
|
URL locating a Log4J configuration file (properties or XML).
|
||||||
<description>URL locating a Log4J configuration file (properties or XML).</description>
|
</description>
|
||||||
|
<param-name>log4jConfiguration</param-name>
|
||||||
|
<param-value>${dspace.dir}/config/log4j-solr.xml</param-value>
|
||||||
</context-param>
|
</context-param>
|
||||||
|
|
||||||
|
<listener>
|
||||||
|
<listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class>
|
||||||
|
</listener>
|
||||||
|
|
||||||
|
<filter>
|
||||||
|
<description>Activate logging</description>
|
||||||
|
<filter-name>log4jServletFilter</filter-name>
|
||||||
|
<filter-class>org.apache.logging.log4j.web.Log4jServletFilter</filter-class>
|
||||||
|
</filter>
|
||||||
|
|
||||||
<!-- Any path (name) registered in solrconfig.xml will be sent to that filter -->
|
<!-- Any path (name) registered in solrconfig.xml will be sent to that filter -->
|
||||||
<filter>
|
<filter>
|
||||||
<filter-name>LocalHostRestrictionFilter</filter-name>
|
<filter-name>LocalHostRestrictionFilter</filter-name>
|
||||||
@@ -87,6 +92,15 @@
|
|||||||
-->
|
-->
|
||||||
</filter>
|
</filter>
|
||||||
|
|
||||||
|
<filter-mapping>
|
||||||
|
<filter-name>log4jServletFilter</filter-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
<dispatcher>REQUEST</dispatcher>
|
||||||
|
<dispatcher>FORWARD</dispatcher>
|
||||||
|
<dispatcher>INCLUDE</dispatcher>
|
||||||
|
<dispatcher>ERROR</dispatcher>
|
||||||
|
</filter-mapping>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
<filter-mapping>
|
<filter-mapping>
|
||||||
<filter-name>LocalHostRestrictionFilter</filter-name>
|
<filter-name>LocalHostRestrictionFilter</filter-name>
|
||||||
@@ -111,10 +125,6 @@
|
|||||||
|
|
||||||
<!-- Otherwise it will continue to the old servlets -->
|
<!-- Otherwise it will continue to the old servlets -->
|
||||||
|
|
||||||
<listener>
|
|
||||||
<listener-class>org.dspace.solr.filters.ConfigureLog4jListener</listener-class>
|
|
||||||
</listener>
|
|
||||||
|
|
||||||
<servlet>
|
<servlet>
|
||||||
<servlet-name>Zookeeper</servlet-name>
|
<servlet-name>Zookeeper</servlet-name>
|
||||||
<servlet-class>org.apache.solr.servlet.ZookeeperInfoServlet</servlet-class>
|
<servlet-class>org.apache.solr.servlet.ZookeeperInfoServlet</servlet-class>
|
||||||
|
Reference in New Issue
Block a user