Merge remote-tracking branch 'dspace/master' into w2p-65568_bugs-place-management-creating-relationships

Conflicts:
	dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RelationshipRestRepository.java
This commit is contained in:
Raf Ponsaerts
2019-10-15 12:03:07 +02:00
100 changed files with 4628 additions and 1714 deletions

View File

@@ -3,4 +3,9 @@
.settings/ .settings/
*/target/ */target/
dspace/modules/*/target/ dspace/modules/*/target/
Dockerfile.* Dockerfile.*
dspace/src/main/docker/dspace-postgres-pgcrypto
dspace/src/main/docker/dspace-postgres-pgcrypto-curl
dspace/src/main/docker/solr
dspace/src/main/docker/README.md
dspace/src/main/docker-compose/

View File

@@ -3,12 +3,12 @@
[![Build Status](https://travis-ci.org/DSpace/DSpace.png?branch=master)](https://travis-ci.org/DSpace/DSpace) [![Build Status](https://travis-ci.org/DSpace/DSpace.png?branch=master)](https://travis-ci.org/DSpace/DSpace)
[DSpace Documentation](https://wiki.duraspace.org/display/DSDOC/) | [DSpace Documentation](https://wiki.duraspace.org/display/DSDOC/) |
[DSpace Releases](https://github.com/DSpace/DSpace/releases) | [DSpace Releases](https://github.com/DSpace/DSpace/releases) |
[DSpace Wiki](https://wiki.duraspace.org/display/DSPACE/Home) | [DSpace Wiki](https://wiki.duraspace.org/display/DSPACE/Home) |
[Support](https://wiki.duraspace.org/display/DSPACE/Support) [Support](https://wiki.duraspace.org/display/DSPACE/Support)
DSpace open source software is a turnkey repository application used by more than DSpace open source software is a turnkey repository application used by more than
2,000 organizations and institutions worldwide to provide durable access to digital resources. 2,000 organizations and institutions worldwide to provide durable access to digital resources.
For more information, visit http://www.dspace.org/ For more information, visit http://www.dspace.org/
@@ -17,7 +17,7 @@ For more information, visit http://www.dspace.org/
* DSpace 7 REST API work is occurring on the [`master` branch](https://github.com/DSpace/DSpace/tree/master/dspace-server-webapp) of this repository. * DSpace 7 REST API work is occurring on the [`master` branch](https://github.com/DSpace/DSpace/tree/master/dspace-server-webapp) of this repository.
* The REST Contract is being documented at https://github.com/DSpace/Rest7Contract * The REST Contract is being documented at https://github.com/DSpace/Rest7Contract
* DSpace 7 Angular UI work is occurring at https://github.com/DSpace/dspace-angular * DSpace 7 Angular UI work is occurring at https://github.com/DSpace/dspace-angular
**If you would like to get involved in our DSpace 7 development effort, we welcome new contributors.** Just join one of our meetings or get in touch via Slack. See the [DSpace 7 UI Working Group](https://wiki.duraspace.org/display/DSPACE/DSpace+7+UI+Working+Group) wiki page for more info. **If you would like to get involved in our DSpace 7 development effort, we welcome new contributors.** Just join one of our meetings or get in touch via Slack. See the [DSpace 7 UI Working Group](https://wiki.duraspace.org/display/DSPACE/DSpace+7+UI+Working+Group) wiki page for more info.
**If you are looking for the ongoing maintenance work for DSpace 6 (or prior releases)**, you can find that work on the corresponding maintenance branch (e.g. [`dspace-6_x`](https://github.com/DSpace/DSpace/tree/dspace-6_x)) in this repository. **If you are looking for the ongoing maintenance work for DSpace 6 (or prior releases)**, you can find that work on the corresponding maintenance branch (e.g. [`dspace-6_x`](https://github.com/DSpace/DSpace/tree/dspace-6_x)) in this repository.
@@ -31,21 +31,21 @@ Past releases are all available via GitHub at https://github.com/DSpace/DSpace/r
## Documentation / Installation ## Documentation / Installation
Documentation for each release may be viewed online or downloaded via our [Documentation Wiki](https://wiki.duraspace.org/display/DSDOC/). Documentation for each release may be viewed online or downloaded via our [Documentation Wiki](https://wiki.duraspace.org/display/DSDOC/).
The latest DSpace Installation instructions are available at: The latest DSpace Installation instructions are available at:
https://wiki.duraspace.org/display/DSDOC6x/Installing+DSpace https://wiki.duraspace.org/display/DSDOC6x/Installing+DSpace
Please be aware that, as a Java web application, DSpace requires a database (PostgreSQL or Oracle) Please be aware that, as a Java web application, DSpace requires a database (PostgreSQL or Oracle)
and a servlet container (usually Tomcat) in order to function. and a servlet container (usually Tomcat) in order to function.
More information about these and all other prerequisites can be found in the Installation instructions above. More information about these and all other prerequisites can be found in the Installation instructions above.
## Dockerfile Usage ## Running DSpace 7 in Docker
See the [DSpace Docker Tutorial](https://dspace-labs.github.io/DSpace-Docker-Images/). See [Running DSpace 7 with Docker Compose](dspace/src/main/docker-compose/README.md)
## Contributing ## Contributing
DSpace is a community built and supported project. We do not have a centralized development or support team, DSpace is a community built and supported project. We do not have a centralized development or support team,
but have a dedicated group of volunteers who help us improve the software, documentation, resources, etc. but have a dedicated group of volunteers who help us improve the software, documentation, resources, etc.
We welcome contributions of any type. Here's a few basic guides that provide suggestions for contributing to DSpace: We welcome contributions of any type. Here's a few basic guides that provide suggestions for contributing to DSpace:
@@ -71,8 +71,8 @@ Great Q&A is also available under the [DSpace tag on Stackoverflow](http://stack
Additional support options are listed at https://wiki.duraspace.org/display/DSPACE/Support Additional support options are listed at https://wiki.duraspace.org/display/DSPACE/Support
DSpace also has an active service provider network. If you'd rather hire a service provider to DSpace also has an active service provider network. If you'd rather hire a service provider to
install, upgrade, customize or host DSpace, then we recommend getting in touch with one of our install, upgrade, customize or host DSpace, then we recommend getting in touch with one of our
[Registered Service Providers](http://www.dspace.org/service-providers). [Registered Service Providers](http://www.dspace.org/service-providers).
## Issue Tracker ## Issue Tracker
@@ -101,7 +101,7 @@ run automatically by [Travis CI](https://travis-ci.org/DSpace/DSpace/) for all P
# Run all tests in a specific test class # Run all tests in a specific test class
# NOTE: testClassName is just the class name, do not include package # NOTE: testClassName is just the class name, do not include package
mvn clean test -Dmaven.test.skip=false -Dtest=[testClassName] mvn clean test -Dmaven.test.skip=false -Dtest=[testClassName]
# Run one test method in a specific test class # Run one test method in a specific test class
mvn clean test -Dmaven.test.skip=false -Dtest=[testClassName]#[testMethodName] mvn clean test -Dmaven.test.skip=false -Dtest=[testClassName]#[testMethodName]
``` ```
@@ -115,7 +115,7 @@ run automatically by [Travis CI](https://travis-ci.org/DSpace/DSpace/) for all P
# NOTE: Integration Tests only run for "verify" or "install" phases # NOTE: Integration Tests only run for "verify" or "install" phases
# NOTE: testClassName is just the class name, do not include package # NOTE: testClassName is just the class name, do not include package
mvn clean verify -Dmaven.test.skip=false -DskipITs=false -Dit.test=[testClassName] mvn clean verify -Dmaven.test.skip=false -DskipITs=false -Dit.test=[testClassName]
# Run one test method in a specific test class # Run one test method in a specific test class
mvn clean verify -Dmaven.test.skip=false -DskipITs=false -Dit.test=[testClassName]#[testMethodName] mvn clean verify -Dmaven.test.skip=false -DskipITs=false -Dit.test=[testClassName]#[testMethodName]
``` ```
@@ -124,7 +124,7 @@ run automatically by [Travis CI](https://travis-ci.org/DSpace/DSpace/) for all P
# Before you can run only one module's tests, other modules may need installing into your ~/.m2 # Before you can run only one module's tests, other modules may need installing into your ~/.m2
cd [dspace-src] cd [dspace-src]
mvn clean install mvn clean install
# Then, move into a module subdirectory, and run the test command # Then, move into a module subdirectory, and run the test command
cd [dspace-src]/dspace-server-webapp cd [dspace-src]/dspace-server-webapp
# Choose your test command from the lists above # Choose your test command from the lists above

25
docker-compose-cli.yml Normal file
View File

@@ -0,0 +1,25 @@
version: "3.7"
services:
dspace-cli:
image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-dspace-7_x}"
container_name: dspace-cli
build:
context: .
dockerfile: Dockerfile.cli.jdk8
#environment:
volumes:
- ./dspace/src/main/docker-compose/local.cfg:/dspace/config/local.cfg
- assetstore:/dspace/assetstore
entrypoint: /dspace/bin/dspace
command: help
networks:
- dspacenet
tty: true
stdin_open: true
volumes:
assetstore:
networks:
dspacenet:

62
docker-compose.yml Normal file
View File

@@ -0,0 +1,62 @@
version: '3.7'
networks:
dspacenet:
services:
dspace:
container_name: dspace
image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-dspace-7_x-jdk8-test}"
build:
context: .
dockerfile: Dockerfile.jdk8-test
depends_on:
- dspacedb
networks:
dspacenet:
ports:
- published: 8080
target: 8080
stdin_open: true
tty: true
volumes:
- assetstore:/dspace/assetstore
- ./dspace/src/main/docker-compose/local.cfg:/dspace/config/local.cfg
# Ensure that the database is ready before starting tomcat
entrypoint:
- /bin/bash
- '-c'
- |
/dspace/bin/dspace database migrate
catalina.sh run
dspacedb:
container_name: dspacedb
environment:
PGDATA: /pgdata
image: dspace/dspace-postgres-pgcrypto
networks:
dspacenet:
stdin_open: true
tty: true
volumes:
- pgdata:/pgdata
dspacesolr:
container_name: dspacesolr
image: dspace/dspace-solr
networks:
dspacenet:
ports:
- published: 8983
target: 8983
stdin_open: true
tty: true
volumes:
- solr_authority:/opt/solr/server/solr/authority/data
- solr_oai:/opt/solr/server/solr/oai/data
- solr_search:/opt/solr/server/solr/search/data
- solr_statistics:/opt/solr/server/solr/statistics/data
volumes:
assetstore:
pgdata:
solr_authority:
solr_oai:
solr_search:
solr_statistics:

View File

@@ -666,7 +666,7 @@ public class MetadataImport {
if (StringUtils.equals(schema, MetadataSchemaEnum.RELATION.getName())) { if (StringUtils.equals(schema, MetadataSchemaEnum.RELATION.getName())) {
List<RelationshipType> relationshipTypeList = relationshipTypeService List<RelationshipType> relationshipTypeList = relationshipTypeService
.findByLeftOrRightLabel(c, element); .findByLeftwardOrRightwardTypeName(c, element);
for (RelationshipType relationshipType : relationshipTypeList) { for (RelationshipType relationshipType : relationshipTypeList) {
for (Relationship relationship : relationshipService for (Relationship relationship : relationshipService
.findByItemAndRelationshipType(c, item, relationshipType)) { .findByItemAndRelationshipType(c, item, relationshipType)) {
@@ -762,17 +762,22 @@ public class MetadataImport {
List<RelationshipType> leftRelationshipTypesForEntity = entityService.getLeftRelationshipTypes(c, entity); List<RelationshipType> leftRelationshipTypesForEntity = entityService.getLeftRelationshipTypes(c, entity);
List<RelationshipType> rightRelationshipTypesForEntity = entityService.getRightRelationshipTypes(c, entity); List<RelationshipType> rightRelationshipTypesForEntity = entityService.getRightRelationshipTypes(c, entity);
//Identify which RelationshipType objects match the combination of:
// * the left entity type
// * the right entity type
// * the name of the relationship type (based on the expected direction of the relationship)
//The matches are included in the acceptableRelationshipTypes
for (RelationshipType relationshipType : entityService.getAllRelationshipTypes(c, entity)) { for (RelationshipType relationshipType : entityService.getAllRelationshipTypes(c, entity)) {
if (StringUtils.equalsIgnoreCase(relationshipType.getLeftLabel(), element)) { if (StringUtils.equalsIgnoreCase(relationshipType.getLeftwardType(), element)) {
left = handleLeftLabelEqualityRelationshipTypeElement(c, entity, relationEntity, left, left = verifyValidLeftwardRelationshipType(c, entity, relationEntity, left,
acceptableRelationshipTypes, acceptableRelationshipTypes,
leftRelationshipTypesForEntity, leftRelationshipTypesForEntity,
relationshipType); relationshipType);
} else if (StringUtils.equalsIgnoreCase(relationshipType.getRightLabel(), element)) { } else if (StringUtils.equalsIgnoreCase(relationshipType.getRightwardType(), element)) {
left = handleRightLabelEqualityRelationshipTypeElement(c, entity, relationEntity, left, left = verifyValidRightwardRelationshipType(c, entity, relationEntity, left,
acceptableRelationshipTypes, acceptableRelationshipTypes,
rightRelationshipTypesForEntity, rightRelationshipTypesForEntity,
relationshipType); relationshipType);
} }
} }
@@ -832,16 +837,20 @@ public class MetadataImport {
* for the right entity * for the right entity
* @param relationshipType The RelationshipType object that we want to check whether it's * @param relationshipType The RelationshipType object that we want to check whether it's
* valid to be added or not * valid to be added or not
* @return A boolean indicating whether the relationship is left or right, will * @return A boolean indicating whether the relationship is left or right.
* be false in this case * Will be set to false if the relationship is valid.
* Will remain unmodified from the left parameter if the
* relationship is not valid.
* @throws SQLException If something goes wrong * @throws SQLException If something goes wrong
*/ */
private boolean handleRightLabelEqualityRelationshipTypeElement(Context c, Entity entity, Entity relationEntity, private boolean verifyValidRightwardRelationshipType(Context c, Entity entity,
boolean left, Entity relationEntity,
List<RelationshipType> acceptableRelationshipTypes, boolean left,
List<RelationshipType> List<RelationshipType>
rightRelationshipTypesForEntity, acceptableRelationshipTypes,
RelationshipType relationshipType) List<RelationshipType>
rightRelationshipTypesForEntity,
RelationshipType relationshipType)
throws SQLException { throws SQLException {
if (StringUtils.equalsIgnoreCase(entityService.getType(c, entity).getLabel(), if (StringUtils.equalsIgnoreCase(entityService.getType(c, entity).getLabel(),
relationshipType.getRightType().getLabel()) && relationshipType.getRightType().getLabel()) &&
@@ -874,16 +883,20 @@ public class MetadataImport {
* for the left entity * for the left entity
* @param relationshipType The RelationshipType object that we want to check whether it's * @param relationshipType The RelationshipType object that we want to check whether it's
* valid to be added or not * valid to be added or not
* @return A boolean indicating whether the relationship is left or right, will * @return A boolean indicating whether the relationship is left or right.
* be true in this case * Will be set to true if the relationship is valid.
* Will remain unmodified from the left parameter if the
* relationship is not valid.
* @throws SQLException If something goes wrong * @throws SQLException If something goes wrong
*/ */
private boolean handleLeftLabelEqualityRelationshipTypeElement(Context c, Entity entity, Entity relationEntity, private boolean verifyValidLeftwardRelationshipType(Context c, Entity entity,
boolean left, Entity relationEntity,
List<RelationshipType> acceptableRelationshipTypes, boolean left,
List<RelationshipType> List<RelationshipType>
leftRelationshipTypesForEntity, acceptableRelationshipTypes,
RelationshipType relationshipType) List<RelationshipType>
leftRelationshipTypesForEntity,
RelationshipType relationshipType)
throws SQLException { throws SQLException {
if (StringUtils.equalsIgnoreCase(entityService.getType(c, entity).getLabel(), if (StringUtils.equalsIgnoreCase(entityService.getType(c, entity).getLabel(),
relationshipType.getLeftType().getLabel()) && relationshipType.getLeftType().getLabel()) &&

View File

@@ -272,9 +272,8 @@ public class Harvest {
targetCollection = (Collection) dso; targetCollection = (Collection) dso;
} }
} else { } else {
// not a handle, try and treat it as an integer collection database ID // not a handle, try and treat it as an collection database UUID
System.out.println("Looking up by id: " + collectionID + ", parsed as '" + Integer System.out.println("Looking up by UUID: " + collectionID + ", " + "in context: " + context);
.parseInt(collectionID) + "', " + "in context: " + context);
targetCollection = collectionService.find(context, UUID.fromString(collectionID)); targetCollection = collectionService.find(context, UUID.fromString(collectionID));
} }
} }
@@ -460,7 +459,7 @@ public class Harvest {
List<String> errors; List<String> errors;
System.out.print("Testing basic PMH access: "); System.out.print("Testing basic PMH access: ");
errors = OAIHarvester.verifyOAIharvester(server, set, errors = harvestedCollectionService.verifyOAIharvester(server, set,
(null != metadataFormat) ? metadataFormat : "dc", false); (null != metadataFormat) ? metadataFormat : "dc", false);
if (errors.isEmpty()) { if (errors.isEmpty()) {
System.out.println("OK"); System.out.println("OK");
@@ -471,7 +470,7 @@ public class Harvest {
} }
System.out.print("Testing ORE support: "); System.out.print("Testing ORE support: ");
errors = OAIHarvester.verifyOAIharvester(server, set, errors = harvestedCollectionService.verifyOAIharvester(server, set,
(null != metadataFormat) ? metadataFormat : "dc", true); (null != metadataFormat) ? metadataFormat : "dc", true);
if (errors.isEmpty()) { if (errors.isEmpty()) {
System.out.println("OK"); System.out.println("OK");

View File

@@ -128,8 +128,8 @@ public class InitializeEntities {
String leftType = eElement.getElementsByTagName("leftType").item(0).getTextContent(); String leftType = eElement.getElementsByTagName("leftType").item(0).getTextContent();
String rightType = eElement.getElementsByTagName("rightType").item(0).getTextContent(); String rightType = eElement.getElementsByTagName("rightType").item(0).getTextContent();
String leftLabel = eElement.getElementsByTagName("leftLabel").item(0).getTextContent(); String leftwardType = eElement.getElementsByTagName("leftwardType").item(0).getTextContent();
String rightLabel = eElement.getElementsByTagName("rightLabel").item(0).getTextContent(); String rightwardType = eElement.getElementsByTagName("rightwardType").item(0).getTextContent();
NodeList leftCardinalityList = eElement.getElementsByTagName("leftCardinality"); NodeList leftCardinalityList = eElement.getElementsByTagName("leftCardinality");
@@ -154,7 +154,7 @@ public class InitializeEntities {
rightCardinalityMax = getString(rightCardinalityMax,(Element) node, "max"); rightCardinalityMax = getString(rightCardinalityMax,(Element) node, "max");
} }
populateRelationshipType(context, leftType, rightType, leftLabel, rightLabel, populateRelationshipType(context, leftType, rightType, leftwardType, rightwardType,
leftCardinalityMin, leftCardinalityMax, leftCardinalityMin, leftCardinalityMax,
rightCardinalityMin, rightCardinalityMax); rightCardinalityMin, rightCardinalityMax);
@@ -173,8 +173,8 @@ public class InitializeEntities {
return leftCardinalityMin; return leftCardinalityMin;
} }
private void populateRelationshipType(Context context, String leftType, String rightType, String leftLabel, private void populateRelationshipType(Context context, String leftType, String rightType, String leftwardType,
String rightLabel, String leftCardinalityMin, String leftCardinalityMax, String rightwardType, String leftCardinalityMin, String leftCardinalityMax,
String rightCardinalityMin, String rightCardinalityMax) String rightCardinalityMin, String rightCardinalityMax)
throws SQLException, AuthorizeException { throws SQLException, AuthorizeException {
@@ -211,9 +211,9 @@ public class InitializeEntities {
rightCardinalityMaxInteger = null; rightCardinalityMaxInteger = null;
} }
RelationshipType relationshipType = relationshipTypeService RelationshipType relationshipType = relationshipTypeService
.findbyTypesAndLabels(context, leftEntityType, rightEntityType, leftLabel, rightLabel); .findbyTypesAndLabels(context, leftEntityType, rightEntityType, leftwardType, rightwardType);
if (relationshipType == null) { if (relationshipType == null) {
relationshipTypeService.create(context, leftEntityType, rightEntityType, leftLabel, rightLabel, relationshipTypeService.create(context, leftEntityType, rightEntityType, leftwardType, rightwardType,
leftCardinalityMinInteger, leftCardinalityMaxInteger, leftCardinalityMinInteger, leftCardinalityMaxInteger,
rightCardinalityMinInteger, rightCardinalityMaxInteger); rightCardinalityMinInteger, rightCardinalityMaxInteger);
} else { } else {

View File

@@ -8,6 +8,7 @@
package org.dspace.content; package org.dspace.content;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@@ -83,8 +84,8 @@ public class EntityServiceImpl implements EntityService {
List<Relationship> relationshipList = relationshipService.findAll(context); List<Relationship> relationshipList = relationshipService.findAll(context);
for (Relationship relationship : relationshipList) { for (Relationship relationship : relationshipList) {
RelationshipType relationshipType = relationship.getRelationshipType(); RelationshipType relationshipType = relationship.getRelationshipType();
if (StringUtils.equals(relationshipType.getLeftLabel(),label) || if (StringUtils.equals(relationshipType.getLeftwardType(),label) ||
StringUtils.equals(relationshipType.getRightLabel(),label)) { StringUtils.equals(relationshipType.getRightwardType(),label)) {
listToReturn.add(relationship); listToReturn.add(relationship);
} }
} }
@@ -94,6 +95,9 @@ public class EntityServiceImpl implements EntityService {
@Override @Override
public List<RelationshipType> getAllRelationshipTypes(Context context, Entity entity) throws SQLException { public List<RelationshipType> getAllRelationshipTypes(Context context, Entity entity) throws SQLException {
EntityType entityType = this.getType(context, entity); EntityType entityType = this.getType(context, entity);
if (entityType == null) {
return Collections.emptyList();
}
List<RelationshipType> listToReturn = new LinkedList<>(); List<RelationshipType> listToReturn = new LinkedList<>();
for (RelationshipType relationshipType : relationshipTypeService.findAll(context)) { for (RelationshipType relationshipType : relationshipTypeService.findAll(context)) {
if (relationshipType.getLeftType().getID() == entityType.getID() || if (relationshipType.getLeftType().getID() == entityType.getID() ||
@@ -129,11 +133,11 @@ public class EntityServiceImpl implements EntityService {
} }
@Override @Override
public List<RelationshipType> getRelationshipTypesByLabel(Context context, String label) throws SQLException { public List<RelationshipType> getRelationshipTypesByTypeName(Context context, String label) throws SQLException {
List<RelationshipType> listToReturn = new LinkedList<>(); List<RelationshipType> listToReturn = new LinkedList<>();
for (RelationshipType relationshipType : relationshipTypeService.findAll(context)) { for (RelationshipType relationshipType : relationshipTypeService.findAll(context)) {
if (StringUtils.equals(relationshipType.getLeftLabel(),label) || if (StringUtils.equals(relationshipType.getLeftwardType(),label) ||
StringUtils.equals(relationshipType.getRightLabel(),label)) { StringUtils.equals(relationshipType.getRightwardType(),label)) {
listToReturn.add(relationshipType); listToReturn.add(relationshipType);
} }
} }

View File

@@ -13,11 +13,9 @@ import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -44,7 +42,6 @@ import org.dspace.content.service.ItemService;
import org.dspace.content.service.MetadataSchemaService; import org.dspace.content.service.MetadataSchemaService;
import org.dspace.content.service.RelationshipService; import org.dspace.content.service.RelationshipService;
import org.dspace.content.service.WorkspaceItemService; import org.dspace.content.service.WorkspaceItemService;
import org.dspace.content.virtual.VirtualMetadataConfiguration;
import org.dspace.content.virtual.VirtualMetadataPopulator; import org.dspace.content.virtual.VirtualMetadataPopulator;
import org.dspace.core.Constants; import org.dspace.core.Constants;
import org.dspace.core.Context; import org.dspace.core.Context;
@@ -117,6 +114,9 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
@Autowired(required = true) @Autowired(required = true)
protected VirtualMetadataPopulator virtualMetadataPopulator; protected VirtualMetadataPopulator virtualMetadataPopulator;
@Autowired(required = true)
private RelationshipMetadataService relationshipMetadataService;
protected ItemServiceImpl() { protected ItemServiceImpl() {
super(); super();
} }
@@ -1344,27 +1344,6 @@ prevent the generation of resource policy entry values with null dspace_object a
return this.getMetadata(item, schema, element, qualifier, lang, true); return this.getMetadata(item, schema, element, qualifier, lang, true);
} }
@Override
public List<RelationshipMetadataValue> getRelationshipMetadata(Item item, boolean enableVirtualMetadata) {
Context context = new Context();
List<RelationshipMetadataValue> fullMetadataValueList = new LinkedList<>();
try {
List<MetadataValue> list = item.getMetadata();
String entityType = getEntityTypeStringFromMetadata(list);
if (StringUtils.isNotBlank(entityType)) {
List<Relationship> relationships = relationshipService.findByItem(context, item);
for (Relationship relationship : relationships) {
fullMetadataValueList
.addAll(handleItemRelationship(context, item, entityType, relationship, enableVirtualMetadata));
}
}
} catch (SQLException e) {
log.error("Lookup for Relationships for item with uuid: " + item.getID() + " caused DSpace to crash", e);
}
return fullMetadataValueList;
}
@Override @Override
public List<MetadataValue> getMetadata(Item item, String schema, String element, String qualifier, String lang, public List<MetadataValue> getMetadata(Item item, String schema, String element, String qualifier, String lang,
boolean enableVirtualMetadata) { boolean enableVirtualMetadata) {
@@ -1372,7 +1351,8 @@ prevent the generation of resource policy entry values with null dspace_object a
//except for relation.type which is the type of item in the model //except for relation.type which is the type of item in the model
if (StringUtils.equals(schema, MetadataSchemaEnum.RELATION.getName()) && !StringUtils.equals(element, "type")) { if (StringUtils.equals(schema, MetadataSchemaEnum.RELATION.getName()) && !StringUtils.equals(element, "type")) {
List<RelationshipMetadataValue> relationMetadata = getRelationshipMetadata(item, false); List<RelationshipMetadataValue> relationMetadata = relationshipMetadataService
.getRelationshipMetadata(item, false);
List<MetadataValue> listToReturn = new LinkedList<>(); List<MetadataValue> listToReturn = new LinkedList<>();
for (MetadataValue metadataValue : relationMetadata) { for (MetadataValue metadataValue : relationMetadata) {
if (StringUtils.equals(metadataValue.getMetadataField().getElement(), element)) { if (StringUtils.equals(metadataValue.getMetadataField().getElement(), element)) {
@@ -1388,7 +1368,7 @@ prevent the generation of resource policy entry values with null dspace_object a
List<MetadataValue> fullMetadataValueList = new LinkedList<>(); List<MetadataValue> fullMetadataValueList = new LinkedList<>();
if (enableVirtualMetadata) { if (enableVirtualMetadata) {
fullMetadataValueList.addAll(getRelationshipMetadata(item, true)); fullMetadataValueList.addAll(relationshipMetadataService.getRelationshipMetadata(item, true));
} }
fullMetadataValueList.addAll(dbMetadataValues); fullMetadataValueList.addAll(dbMetadataValues);
@@ -1409,7 +1389,7 @@ prevent the generation of resource policy entry values with null dspace_object a
* This method will sort the List of MetadataValue objects based on the MetadataSchema, MetadataField Element, * This method will sort the List of MetadataValue objects based on the MetadataSchema, MetadataField Element,
* MetadataField Qualifier and MetadataField Place in that order. * MetadataField Qualifier and MetadataField Place in that order.
* @param listToReturn The list to be sorted * @param listToReturn The list to be sorted
* @return The list sorted on those criteria * @return The list sorted on those criteria
*/ */
private List<MetadataValue> sortMetadataValueList(List<MetadataValue> listToReturn) { private List<MetadataValue> sortMetadataValueList(List<MetadataValue> listToReturn) {
Comparator<MetadataValue> comparator = Comparator.comparing( Comparator<MetadataValue> comparator = Comparator.comparing(
@@ -1427,137 +1407,5 @@ prevent the generation of resource policy entry values with null dspace_object a
return listToReturn; return listToReturn;
} }
//This method processes the Relationship of an Item and will return a list of RelationshipMetadataValue objects
//that are generated for this specfic relationship for the item through the config in VirtualMetadataPopulator
private List<RelationshipMetadataValue> handleItemRelationship(Context context, Item item, String entityType,
Relationship relationship,
boolean enableVirtualMetadata)
throws SQLException {
List<RelationshipMetadataValue> resultingMetadataValueList = new LinkedList<>();
RelationshipType relationshipType = relationship.getRelationshipType();
HashMap<String, VirtualMetadataConfiguration> hashMaps;
String relationName = "";
Item otherItem = null;
int place = 0;
if (StringUtils.equals(relationshipType.getLeftType().getLabel(), entityType)) {
hashMaps = virtualMetadataPopulator.getMap().get(relationshipType.getLeftLabel());
otherItem = relationship.getRightItem();
relationName = relationship.getRelationshipType().getLeftLabel();
place = relationship.getLeftPlace();
} else if (StringUtils.equals(relationshipType.getRightType().getLabel(), entityType)) {
hashMaps = virtualMetadataPopulator.getMap().get(relationshipType.getRightLabel());
otherItem = relationship.getLeftItem();
relationName = relationship.getRelationshipType().getRightLabel();
place = relationship.getRightPlace();
} else {
//No virtual metadata can be created
return resultingMetadataValueList;
}
if (hashMaps != null && enableVirtualMetadata) {
resultingMetadataValueList.addAll(handleRelationshipTypeMetadataMapping(context, item, hashMaps,
otherItem, relationName,
relationship.getID(), place));
}
RelationshipMetadataValue relationMetadataFromOtherItem =
getRelationMetadataFromOtherItem(context, otherItem, relationName, relationship.getID(), place);
if (relationMetadataFromOtherItem != null) {
resultingMetadataValueList.add(relationMetadataFromOtherItem);
}
return resultingMetadataValueList;
}
//This method will retrieve a list of RelationshipMetadataValue objects based on the config passed along in the
//hashmaps parameter. The beans will be used to retrieve the values for the RelationshipMetadataValue objects
//and the keys of the hashmap will be used to construct the RelationshipMetadataValue object.
private List<RelationshipMetadataValue> handleRelationshipTypeMetadataMapping(Context context, Item item,
HashMap<String, VirtualMetadataConfiguration> hashMaps,
Item otherItem, String relationName,
Integer relationshipId, int place)
throws SQLException {
List<RelationshipMetadataValue> resultingMetadataValueList = new LinkedList<>();
for (Map.Entry<String, VirtualMetadataConfiguration> entry : hashMaps.entrySet()) {
String key = entry.getKey();
VirtualMetadataConfiguration virtualBean = entry.getValue();
for (String value : virtualBean.getValues(context, otherItem)) {
RelationshipMetadataValue metadataValue = constructMetadataValue(context, key);
if (metadataValue != null) {
metadataValue = constructResultingMetadataValue(item, value, metadataValue, relationshipId);
metadataValue.setUseForPlace(virtualBean.getUseForPlace());
metadataValue.setPlace(place);
if (StringUtils.isNotBlank(metadataValue.getValue())) {
resultingMetadataValueList.add(metadataValue);
}
}
}
}
return resultingMetadataValueList;
}
private RelationshipMetadataValue getRelationMetadataFromOtherItem(Context context, Item otherItem,
String relationName,
Integer relationshipId, int place) {
RelationshipMetadataValue metadataValue = constructMetadataValue(context,
MetadataSchemaEnum.RELATION
.getName() + "." + relationName);
if (metadataValue != null) {
metadataValue.setAuthority(Constants.VIRTUAL_AUTHORITY_PREFIX + relationshipId);
metadataValue.setValue(otherItem.getID().toString());
metadataValue.setPlace(place);
return metadataValue;
}
return null;
}
private String getEntityTypeStringFromMetadata(List<MetadataValue> list) {
for (MetadataValue mdv : list) {
if (StringUtils.equals(mdv.getMetadataField().getMetadataSchema().getName(),
"relationship")
&& StringUtils.equals(mdv.getMetadataField().getElement(),
"type")) {
return mdv.getValue();
}
}
return null;
}
private RelationshipMetadataValue constructResultingMetadataValue(Item item, String value,
RelationshipMetadataValue metadataValue,
Integer relationshipId) {
metadataValue.setValue(value);
metadataValue.setAuthority(Constants.VIRTUAL_AUTHORITY_PREFIX + relationshipId);
metadataValue.setConfidence(-1);
metadataValue.setDSpaceObject(item);
return metadataValue;
}
//This method will construct a RelationshipMetadataValue object with proper schema, element and qualifier based
//on the key String parameter passed along to it
private RelationshipMetadataValue constructMetadataValue(Context context, String key) {
String[] splittedKey = key.split("\\.");
RelationshipMetadataValue metadataValue = new RelationshipMetadataValue();
String metadataSchema = splittedKey.length > 0 ? splittedKey[0] : null;
String metadataElement = splittedKey.length > 1 ? splittedKey[1] : null;
String metadataQualifier = splittedKey.length > 2 ? splittedKey[2] : null;
MetadataField metadataField = null;
try {
metadataField = metadataFieldService
.findByElement(context, metadataSchema, metadataElement, metadataQualifier);
} catch (SQLException e) {
log.error("Could not find element with MetadataSchema: " + metadataSchema +
", MetadataElement: " + metadataElement + " and MetadataQualifier: " + metadataQualifier, e);
return null;
}
if (metadataField == null) {
log.error("A MetadataValue was attempted to construct with MetadataField for parameters: " +
"metadataschema: {}, metadataelement: {}, metadataqualifier: {}",
metadataSchema, metadataElement, metadataQualifier);
return null;
}
metadataValue.setMetadataField(metadataField);
metadataValue.setLanguage(Item.ANY);
return metadataValue;
}
} }

View File

@@ -77,6 +77,18 @@ public class Relationship implements ReloadableEntity<Integer> {
@Column(name = "right_place") @Column(name = "right_place")
private int rightPlace; private int rightPlace;
/**
* A String containing an alternative value (name variant) for the left side
*/
@Column(name = "leftward_value")
private String leftwardValue;
/**
* A String containing an alternative value (name variant) for the right side
*/
@Column(name = "rightward_value")
private String rightwardValue;
/** /**
* Protected constructor, create object using: * Protected constructor, create object using:
* {@link org.dspace.content.service.RelationshipService#create(Context)} } * {@link org.dspace.content.service.RelationshipService#create(Context)} }
@@ -170,6 +182,38 @@ public class Relationship implements ReloadableEntity<Integer> {
this.rightPlace = rightPlace; this.rightPlace = rightPlace;
} }
/**
* Standard getter for the leftwardValue String in this Relationship
* @return the leftwardValue String for this relationship
*/
public String getLeftwardValue() {
return leftwardValue;
}
/**
* Standard setter for the leftwardValue String in this Relationship
* @param leftwardValue the leftwardValue String that will be used in this relationship
*/
public void setLeftwardValue(String leftwardValue) {
this.leftwardValue = leftwardValue;
}
/**
* Standard getter for the rightwardValue String in this Relationship
* @return the rightwardValue string for this relationship
*/
public String getRightwardValue() {
return rightwardValue;
}
/**
* Standard setter for the rightwardValue String in this Relationship
* @param rightwardValue the rightwardValue String that will be used in this relationship
*/
public void setRightwardValue(String rightwardValue) {
this.rightwardValue = rightwardValue;
}
/** /**
* Standard getter for the ID for this Relationship * Standard getter for the ID for this Relationship
* @return The ID of this relationship * @return The ID of this relationship

View File

@@ -0,0 +1,30 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.content;
import java.util.List;
import org.dspace.content.virtual.VirtualMetadataPopulator;
/**
* Interface used for the {@link RelationshipMetadataServiceImpl}
* This will define methods regarding the RelationshipMetadata
*/
public interface RelationshipMetadataService {
/**
* This method retrieves a list of MetadataValue objects that get constructed from processing
* the given Item's Relationships through the config given to the {@link VirtualMetadataPopulator}
* @param item The Item that will be processed through it's Relationships
* @param enableVirtualMetadata This parameter will determine whether the list of Relationship metadata
* should be populated with metadata that is being generated through the
* VirtualMetadataPopulator functionality or not
* @return The list of MetadataValue objects constructed through the Relationships
*/
public List<RelationshipMetadataValue> getRelationshipMetadata(Item item, boolean enableVirtualMetadata);
}

View File

@@ -0,0 +1,290 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.content;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.content.service.MetadataFieldService;
import org.dspace.content.service.RelationshipService;
import org.dspace.content.virtual.VirtualMetadataConfiguration;
import org.dspace.content.virtual.VirtualMetadataPopulator;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
public class RelationshipMetadataServiceImpl implements RelationshipMetadataService {
/**
* log4j category
*/
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger();
@Autowired(required = true)
protected RelationshipService relationshipService;
@Autowired(required = true)
protected VirtualMetadataPopulator virtualMetadataPopulator;
@Autowired(required = true)
protected MetadataFieldService metadataFieldService;
@Override
public List<RelationshipMetadataValue> getRelationshipMetadata(Item item, boolean enableVirtualMetadata) {
Context context = new Context();
List<RelationshipMetadataValue> fullMetadataValueList = new LinkedList<>();
try {
List<MetadataValue> list = item.getMetadata();
String entityType = getEntityTypeStringFromMetadata(list);
if (StringUtils.isNotBlank(entityType)) {
List<Relationship> relationships = relationshipService.findByItem(context, item);
for (Relationship relationship : relationships) {
fullMetadataValueList
.addAll(findRelationshipMetadataValueForItemRelationship(context, item, entityType,
relationship, enableVirtualMetadata));
}
}
} catch (SQLException e) {
log.error("Lookup for Relationships for item with uuid: " + item.getID() + " caused DSpace to crash", e);
}
return fullMetadataValueList;
}
private String getEntityTypeStringFromMetadata(List<MetadataValue> list) {
for (MetadataValue mdv : list) {
if (StringUtils.equals(mdv.getMetadataField().getMetadataSchema().getName(),
"relationship")
&& StringUtils.equals(mdv.getMetadataField().getElement(),
"type")) {
return mdv.getValue();
}
}
return null;
}
/**
* This method processes one Relationship of an Item and will return a list of RelationshipMetadataValue objects
* that are generated for this specific relationship for the item through the config in VirtualMetadataPopulator
*
* It returns a combination of the output of the findVirtualMetadataFromConfiguration method and
*
* @param context The context
* @param item The item whose virtual metadata is requested
* @param entityType The entity type of the given item
* @param relationship The relationship whose virtual metadata is requested
* @param enableVirtualMetadata Determines whether the VirtualMetadataPopulator should be used.
* If false, only the relation."relationname" metadata is populated
* If true, fields from the spring config virtual metadata is included as well
* @return The list of virtual metadata values
*/
private List<RelationshipMetadataValue> findRelationshipMetadataValueForItemRelationship(
Context context, Item item, String entityType, Relationship relationship, boolean enableVirtualMetadata)
throws SQLException {
List<RelationshipMetadataValue> resultingMetadataValueList = new LinkedList<>();
RelationshipType relationshipType = relationship.getRelationshipType();
HashMap<String, VirtualMetadataConfiguration> hashMaps;
String relationName;
Item otherItem;
int place = 0;
boolean isLeftwards;
if (StringUtils.equals(relationshipType.getLeftType().getLabel(), entityType)) {
hashMaps = virtualMetadataPopulator.getMap().get(relationshipType.getLeftwardType());
otherItem = relationship.getRightItem();
relationName = relationship.getRelationshipType().getLeftwardType();
place = relationship.getLeftPlace();
isLeftwards = false; //if the current item is stored on the left,
// the name variant is retrieved from the rightwards label
} else if (StringUtils.equals(relationshipType.getRightType().getLabel(), entityType)) {
hashMaps = virtualMetadataPopulator.getMap().get(relationshipType.getRightwardType());
otherItem = relationship.getLeftItem();
relationName = relationship.getRelationshipType().getRightwardType();
place = relationship.getRightPlace();
isLeftwards = true; //if the current item is stored on the right,
// the name variant is retrieved from the leftwards label
} else {
//No virtual metadata can be created
return resultingMetadataValueList;
}
if (hashMaps != null && enableVirtualMetadata) {
resultingMetadataValueList.addAll(findVirtualMetadataFromConfiguration(context, item, hashMaps,
otherItem, relationName,
relationship, place, isLeftwards));
}
RelationshipMetadataValue relationMetadataFromOtherItem =
getRelationMetadataFromOtherItem(context, otherItem, relationName, relationship.getID(), place);
if (relationMetadataFromOtherItem != null) {
resultingMetadataValueList.add(relationMetadataFromOtherItem);
}
return resultingMetadataValueList;
}
/**
* This method will retrieve a list of RelationshipMetadataValue objects based on the config passed along in the
* hashmaps parameter. The beans will be used to retrieve the values for the RelationshipMetadataValue objects
* and the keys of the hashmap will be used to construct the RelationshipMetadataValue object.
*
* @param context The context
* @param item The item whose virtual metadata is requested
* @param hashMaps The list of VirtualMetadataConfiguration objects which will generate the
* virtual metadata. These configurations are applicable for a relationship
* between both items
* @param otherItem The related item whose actual metadata is requested
* @param relationName The name of the relationship
* @param relationship The relationship whose virtual metadata is requested
* @param place The place to use in the virtual metadata
* @param isLeftwards Determines the direction of the virtual metadata
* @return The list of virtual metadata values
*/
private List<RelationshipMetadataValue> findVirtualMetadataFromConfiguration(Context context, Item item,
HashMap<String, VirtualMetadataConfiguration> hashMaps, Item otherItem, String relationName,
Relationship relationship, int place, boolean isLeftwards) throws SQLException {
List<RelationshipMetadataValue> resultingMetadataValueList = new LinkedList<>();
for (Map.Entry<String, VirtualMetadataConfiguration> entry : hashMaps.entrySet()) {
String key = entry.getKey();
VirtualMetadataConfiguration virtualBean = entry.getValue();
if (virtualBean.getPopulateWithNameVariant()) {
String wardLabel = isLeftwards ? relationship.getLeftwardValue() : relationship.getRightwardValue();
if (wardLabel != null) {
resultingMetadataValueList.add(
constructRelationshipMetadataValue(context, item, relationship.getID(), place, key, virtualBean,
wardLabel));
} else {
resultingMetadataValueList.addAll(
findRelationshipMetadataValueFromBean(context, item, otherItem, relationship, place, key,
virtualBean));
}
} else {
resultingMetadataValueList.addAll(
findRelationshipMetadataValueFromBean(context, item, otherItem, relationship, place, key,
virtualBean));
}
}
return resultingMetadataValueList;
}
/**
* This method will retrieve a list of RelationshipMetadataValue objects based on the config passed along in the
* hashmaps parameter. The beans will be used to retrieve the values for the RelationshipMetadataValue objects
* and the keys of the hashmap will be used to construct the RelationshipMetadataValue object.
*
* @param context The context
* @param item The item whose virtual metadata is requested
* @param otherItem The related item whose actual metadata is requested
* @param relationship The relationship whose virtual metadata is requested
* @param place The place to use in the virtual metadata
* @param key The key corresponding to the VirtualMetadataConfiguration
* @param virtualBean The VirtualMetadataConfiguration object which will generate the
* virtual metadata. This configuration is applicable for a relationship
* between both items
* @return The list of virtual metadata values
*/
private List<RelationshipMetadataValue> findRelationshipMetadataValueFromBean(
Context context, Item item, Item otherItem, Relationship relationship, int place,
String key, VirtualMetadataConfiguration virtualBean) throws SQLException {
List<RelationshipMetadataValue> resultingMetadataValueList = new LinkedList<>();
for (String value : virtualBean.getValues(context, otherItem)) {
RelationshipMetadataValue relationshipMetadataValue = constructRelationshipMetadataValue(context, item,
relationship
.getID(),
place,
key, virtualBean,
value);
if (relationshipMetadataValue != null) {
resultingMetadataValueList.add(relationshipMetadataValue);
}
}
return resultingMetadataValueList;
}
//This method will construct a RelationshipMetadataValue object with proper schema, element, qualifier,
//authority, item, place and useForPlace based on the key String parameter passed along to it
private RelationshipMetadataValue constructRelationshipMetadataValue(Context context, Item item,
Integer relationshipId, int place,
String key,
VirtualMetadataConfiguration virtualBean,
String value) {
RelationshipMetadataValue metadataValue = constructMetadataValue(context, key);
if (metadataValue != null) {
metadataValue = constructResultingMetadataValue(item, value, metadataValue, relationshipId);
metadataValue.setUseForPlace(virtualBean.getUseForPlace());
metadataValue.setPlace(place);
if (StringUtils.isNotBlank(metadataValue.getValue())) {
return metadataValue;
}
}
return null;
}
//This method will construct a RelationshipMetadataValue object with proper schema, element and qualifier based
//on the key String parameter passed along to it
private RelationshipMetadataValue constructMetadataValue(Context context, String key) {
String[] splittedKey = key.split("\\.");
RelationshipMetadataValue metadataValue = new RelationshipMetadataValue();
String metadataSchema = splittedKey.length > 0 ? splittedKey[0] : null;
String metadataElement = splittedKey.length > 1 ? splittedKey[1] : null;
String metadataQualifier = splittedKey.length > 2 ? splittedKey[2] : null;
MetadataField metadataField = null;
try {
metadataField = metadataFieldService
.findByElement(context, metadataSchema, metadataElement, metadataQualifier);
} catch (SQLException e) {
log.error("Could not find element with MetadataSchema: " + metadataSchema +
", MetadataElement: " + metadataElement + " and MetadataQualifier: " + metadataQualifier, e);
return null;
}
if (metadataField == null) {
log.error("A MetadataValue was attempted to construct with MetadataField for parameters: " +
"metadataschema: {}, metadataelement: {}, metadataqualifier: {}",
metadataSchema, metadataElement, metadataQualifier);
return null;
}
metadataValue.setMetadataField(metadataField);
metadataValue.setLanguage(Item.ANY);
return metadataValue;
}
//This method will update a RelationshipMetadataValue object with authority info and relation to the item
private RelationshipMetadataValue constructResultingMetadataValue(Item item, String value,
RelationshipMetadataValue metadataValue,
Integer relationshipId) {
metadataValue.setValue(value);
metadataValue.setAuthority(Constants.VIRTUAL_AUTHORITY_PREFIX + relationshipId);
metadataValue.setConfidence(-1);
metadataValue.setDSpaceObject(item);
return metadataValue;
}
// This method will create the Relationship Metadatavalue that describes the relationship type and has the ID
// of the other item as value
private RelationshipMetadataValue getRelationMetadataFromOtherItem(Context context, Item otherItem,
String relationName,
Integer relationshipId, int place) {
RelationshipMetadataValue metadataValue = constructMetadataValue(context,
MetadataSchemaEnum.RELATION
.getName() + "." + relationName);
if (metadataValue != null) {
metadataValue.setAuthority(Constants.VIRTUAL_AUTHORITY_PREFIX + relationshipId);
metadataValue.setValue(otherItem.getID().toString());
metadataValue.setPlace(place);
return metadataValue;
}
return null;
}
}

View File

@@ -59,12 +59,21 @@ public class RelationshipServiceImpl implements RelationshipService {
@Override @Override
public Relationship create(Context c, Item leftItem, Item rightItem, RelationshipType relationshipType, public Relationship create(Context c, Item leftItem, Item rightItem, RelationshipType relationshipType,
int leftPlace, int rightPlace) throws AuthorizeException, SQLException { int leftPlace, int rightPlace) throws AuthorizeException, SQLException {
return create(c, leftItem, rightItem, relationshipType, leftPlace, rightPlace, null, null);
}
@Override
public Relationship create(Context c, Item leftItem, Item rightItem, RelationshipType relationshipType,
int leftPlace, int rightPlace, String leftwardValue, String rightwardValue)
throws AuthorizeException, SQLException {
Relationship relationship = new Relationship(); Relationship relationship = new Relationship();
relationship.setLeftItem(leftItem); relationship.setLeftItem(leftItem);
relationship.setRightItem(rightItem); relationship.setRightItem(rightItem);
relationship.setRelationshipType(relationshipType); relationship.setRelationshipType(relationshipType);
relationship.setLeftPlace(leftPlace); relationship.setLeftPlace(leftPlace);
relationship.setRightPlace(rightPlace); relationship.setRightPlace(rightPlace);
relationship.setLeftwardValue(leftwardValue);
relationship.setRightwardValue(rightwardValue);
return create(c, relationship); return create(c, relationship);
} }
@@ -113,7 +122,7 @@ public class RelationshipServiceImpl implements RelationshipService {
rightRelationships.remove(relationship); rightRelationships.remove(relationship);
} }
context.turnOffAuthorisationSystem(); context.turnOffAuthorisationSystem();
//If useForPlace for the leftlabel is false for the relationshipType, //If useForPlace for the leftwardType is false for the relationshipType,
// we need to sort the relationships here based on leftplace. // we need to sort the relationships here based on leftplace.
if (!virtualMetadataPopulator.isUseForPlaceTrueForRelationshipType(relationship.getRelationshipType(), true)) { if (!virtualMetadataPopulator.isUseForPlaceTrueForRelationshipType(relationship.getRelationshipType(), true)) {
if (!leftRelationships.isEmpty()) { if (!leftRelationships.isEmpty()) {
@@ -130,7 +139,7 @@ public class RelationshipServiceImpl implements RelationshipService {
} }
//If useForPlace for the rightLabel is false for the relationshipType, //If useForPlace for the rightwardType is false for the relationshipType,
// we need to sort the relationships here based on the rightplace. // we need to sort the relationships here based on the rightplace.
if (!virtualMetadataPopulator.isUseForPlaceTrueForRelationshipType(relationship.getRelationshipType(), false)) { if (!virtualMetadataPopulator.isUseForPlaceTrueForRelationshipType(relationship.getRelationshipType(), false)) {
if (!rightRelationships.isEmpty()) { if (!rightRelationships.isEmpty()) {
@@ -202,8 +211,8 @@ public class RelationshipServiceImpl implements RelationshipService {
private void logRelationshipTypeDetailsForError(RelationshipType relationshipType) { private void logRelationshipTypeDetailsForError(RelationshipType relationshipType) {
log.warn("The relationshipType's ID is: " + relationshipType.getID()); log.warn("The relationshipType's ID is: " + relationshipType.getID());
log.warn("The relationshipType's left label is: " + relationshipType.getLeftLabel()); log.warn("The relationshipType's leftward type is: " + relationshipType.getLeftwardType());
log.warn("The relationshipType's right label is: " + relationshipType.getRightLabel()); log.warn("The relationshipType's rightward type is: " + relationshipType.getRightwardType());
log.warn("The relationshipType's left entityType label is: " + relationshipType.getLeftType().getLabel()); log.warn("The relationshipType's left entityType label is: " + relationshipType.getLeftType().getLabel());
log.warn("The relationshipType's right entityType label is: " + relationshipType.getRightType().getLabel()); log.warn("The relationshipType's right entityType label is: " + relationshipType.getRightType().getLabel());
log.warn("The relationshipType's left min cardinality is: " + relationshipType.getLeftMinCardinality()); log.warn("The relationshipType's left min cardinality is: " + relationshipType.getLeftMinCardinality());
@@ -243,8 +252,8 @@ public class RelationshipServiceImpl implements RelationshipService {
List<Relationship> list = relationshipDAO.findByItem(context, item); List<Relationship> list = relationshipDAO.findByItem(context, item);
list.sort((o1, o2) -> { list.sort((o1, o2) -> {
int relationshipType = o1.getRelationshipType().getLeftLabel() int relationshipType = o1.getRelationshipType().getLeftwardType()
.compareTo(o2.getRelationshipType().getLeftLabel()); .compareTo(o2.getRelationshipType().getLeftwardType());
if (relationshipType != 0) { if (relationshipType != 0) {
return relationshipType; return relationshipType;
} else { } else {
@@ -357,12 +366,16 @@ public class RelationshipServiceImpl implements RelationshipService {
for (Relationship relationship : list) { for (Relationship relationship : list) {
if (isLeft) { if (isLeft) {
if (StringUtils if (StringUtils
.equals(relationship.getRelationshipType().getLeftLabel(), relationshipType.getLeftLabel())) { .equals(
relationship.getRelationshipType().getLeftwardType(), relationshipType.getLeftwardType())
) {
listToReturn.add(relationship); listToReturn.add(relationship);
} }
} else { } else {
if (StringUtils if (StringUtils
.equals(relationship.getRelationshipType().getRightLabel(), relationshipType.getRightLabel())) { .equals(
relationship.getRelationshipType().getRightwardType(), relationshipType.getRightwardType())
) {
listToReturn.add(relationship); listToReturn.add(relationship);
} }
} }

View File

@@ -26,8 +26,8 @@ import org.dspace.core.ReloadableEntity;
* Class representing a RelationshipType * Class representing a RelationshipType
* This class contains an Integer ID that will be the unique value and primary key in the database. * This class contains an Integer ID that will be the unique value and primary key in the database.
* This key is automatically generated * This key is automatically generated
* It also has a leftType and rightType EntityType that describes the relationshipType together with a leftLabel and * It also has a leftType and rightType EntityType that describes the relationshipType together with a leftwardType and
* rightLabel. * rightwardType.
* The cardinality properties describe how many of each relations this relationshipType can support * The cardinality properties describe how many of each relations this relationshipType can support
*/ */
@Entity @Entity
@@ -61,20 +61,20 @@ public class RelationshipType implements ReloadableEntity<Integer> {
private EntityType rightType; private EntityType rightType;
/** /**
* The leftLabel String field for the relationshipType * The leftwardType String field for the relationshipType
* This is stored as a String and cannot be null * This is stored as a String and cannot be null
* This is a textual representation of the name of the relationship that this RelationshipType is connected to * This is a textual representation of the name of the relationship that this RelationshipType is connected to
*/ */
@Column(name = "left_label", nullable = false) @Column(name = "leftward_type", nullable = false)
private String leftLabel; private String leftwardType;
/** /**
* The rightLabel String field for the relationshipType * The rightwardType String field for the relationshipType
* This is stored as a String and cannot be null * This is stored as a String and cannot be null
* This is a textual representation of the name of the relationship that this RelationshipType is connected to * This is a textual representation of the name of the relationship that this RelationshipType is connected to
*/ */
@Column(name = "right_label", nullable = false) @Column(name = "rightward_type", nullable = false)
private String rightLabel; private String rightwardType;
/** /**
* The minimum amount of relations for the leftItem that need to be present at all times * The minimum amount of relations for the leftItem that need to be present at all times
@@ -149,35 +149,35 @@ public class RelationshipType implements ReloadableEntity<Integer> {
} }
/** /**
* Standard getter for the leftLabel String for this RelationshipType * Standard getter for the leftwardType String for this RelationshipType
* @return The leftLabel String of this RelationshipType * @return The leftwardType String of this RelationshipType
*/ */
public String getLeftLabel() { public String getLeftwardType() {
return leftLabel; return leftwardType;
} }
/** /**
* Standard setter for the leftLabel String for this RelationshipType * Standard setter for the leftwardType String for this RelationshipType
* @param leftLabel The leftLabel String that this RelationshipType should receive * @param leftwardType The leftwardType String that this RelationshipType should receive
*/ */
public void setLeftLabel(String leftLabel) { public void setLeftwardType(String leftwardType) {
this.leftLabel = leftLabel; this.leftwardType = leftwardType;
} }
/** /**
* Standard getter for the rightLabel String for this RelationshipType * Standard getter for the rightwardType String for this RelationshipType
* @return The rightLabel String of this RelationshipType * @return The rightwardType String of this RelationshipType
*/ */
public String getRightLabel() { public String getRightwardType() {
return rightLabel; return rightwardType;
} }
/** /**
* Standard setter for the rightLabel String for this RelationshipType * Standard setter for the rightwardType String for this RelationshipType
* @param rightLabel The rightLabel String that this RelationshipType should receive * @param rightwardType The rightwardType String that this RelationshipType should receive
*/ */
public void setRightLabel(String rightLabel) { public void setRightwardType(String rightwardType) {
this.rightLabel = rightLabel; this.rightwardType = rightwardType;
} }
/** /**

View File

@@ -48,8 +48,8 @@ public class RelationshipTypeServiceImpl implements RelationshipTypeService {
@Override @Override
public RelationshipType findbyTypesAndLabels(Context context,EntityType leftType,EntityType rightType, public RelationshipType findbyTypesAndLabels(Context context,EntityType leftType,EntityType rightType,
String leftLabel,String rightLabel) throws SQLException { String leftwardType,String rightwardType) throws SQLException {
return relationshipTypeDAO.findByTypesAndLabels(context, leftType, rightType, leftLabel, rightLabel); return relationshipTypeDAO.findByTypesAndLabels(context, leftType, rightType, leftwardType, rightwardType);
} }
@Override @Override
@@ -58,8 +58,8 @@ public class RelationshipTypeServiceImpl implements RelationshipTypeService {
} }
@Override @Override
public List<RelationshipType> findByLeftOrRightLabel(Context context, String label) throws SQLException { public List<RelationshipType> findByLeftwardOrRightwardTypeName(Context context, String label) throws SQLException {
return relationshipTypeDAO.findByLeftOrRightLabel(context, label); return relationshipTypeDAO.findByLeftwardOrRightwardTypeName(context, label);
} }
@Override @Override
@@ -69,15 +69,15 @@ public class RelationshipTypeServiceImpl implements RelationshipTypeService {
@Override @Override
public RelationshipType create(Context context, EntityType leftEntityType, EntityType rightEntityType, public RelationshipType create(Context context, EntityType leftEntityType, EntityType rightEntityType,
String leftLabel, String rightLabel, Integer leftCardinalityMinInteger, String leftwardType, String rightwardType, Integer leftCardinalityMinInteger,
Integer leftCardinalityMaxInteger, Integer rightCardinalityMinInteger, Integer leftCardinalityMaxInteger, Integer rightCardinalityMinInteger,
Integer rightCardinalityMaxInteger) Integer rightCardinalityMaxInteger)
throws SQLException, AuthorizeException { throws SQLException, AuthorizeException {
RelationshipType relationshipType = new RelationshipType(); RelationshipType relationshipType = new RelationshipType();
relationshipType.setLeftType(leftEntityType); relationshipType.setLeftType(leftEntityType);
relationshipType.setRightType(rightEntityType); relationshipType.setRightType(rightEntityType);
relationshipType.setLeftLabel(leftLabel); relationshipType.setLeftwardType(leftwardType);
relationshipType.setRightLabel(rightLabel); relationshipType.setRightwardType(rightwardType);
relationshipType.setLeftMinCardinality(leftCardinalityMinInteger); relationshipType.setLeftMinCardinality(leftCardinalityMinInteger);
relationshipType.setLeftMaxCardinality(leftCardinalityMaxInteger); relationshipType.setLeftMaxCardinality(leftCardinalityMaxInteger);
relationshipType.setRightMinCardinality(rightCardinalityMinInteger); relationshipType.setRightMinCardinality(rightCardinalityMinInteger);

View File

@@ -25,28 +25,30 @@ public interface RelationshipTypeDAO extends GenericDAO<RelationshipType> {
/** /**
* This method is used to retrieve the RelationshipType object that has the same * This method is used to retrieve the RelationshipType object that has the same
* leftType, rightType, leftLabel and rightLabel as given in the parameters * leftType, rightType, leftwardType and rightwardType as given in the parameters
* @param context The relevant DSpace context * @param context The relevant DSpace context
* @param leftType The leftType EntityType object to be matched in the query * @param leftType The leftType EntityType object to be matched in the query
* @param rightType The rightType EntityType object to be matched in the query * @param rightType The rightType EntityType object to be matched in the query
* @param leftLabel The leftLabel String to be matched in the query * @param leftwardType The leftwardType String to be matched in the query
* @param rightLabel The rightLabel String to be matched in the query * @param rightwardType The rightwardType String to be matched in the query
* @return The RelationshipType object that matches all the given parameters * @return The RelationshipType object that matches all the given parameters
* @throws SQLException If something goes wrong * @throws SQLException If something goes wrong
*/ */
RelationshipType findByTypesAndLabels(Context context, RelationshipType findByTypesAndLabels(Context context, EntityType leftType,EntityType rightType,
EntityType leftType,EntityType rightType,String leftLabel,String rightLabel) String leftwardType,
throws SQLException; String rightwardType)
throws SQLException;
/** /**
* This method will return a list of RelationshipType objects for which the given label is equal to * This method will return a list of RelationshipType objects for which the given label is equal to
* either the leftLabel or rightLabel. * either the leftwardType or rightwardType.
* @param context The relevant DSpace context * @param context The relevant DSpace context
* @param label The label that will be used to check on * @param type The label that will be used to check on
* @return A list of RelationshipType objects that have the given label as either the leftLabel or rightLabel * @return A list of RelationshipType objects that have the given label as either the leftwardType
* or rightwardType
* @throws SQLException If something goes wrong * @throws SQLException If something goes wrong
*/ */
List<RelationshipType> findByLeftOrRightLabel(Context context, String label) throws SQLException; List<RelationshipType> findByLeftwardOrRightwardTypeName(Context context, String type) throws SQLException;
/** /**
* This method will return a list of RelationshipType objects for which the given EntityType object is equal * This method will return a list of RelationshipType objects for which the given EntityType object is equal

View File

@@ -8,8 +8,6 @@
package org.dspace.content.dao.impl; package org.dspace.content.dao.impl;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import javax.persistence.Query; import javax.persistence.Query;
@@ -62,11 +60,20 @@ public class CommunityDAOImpl extends AbstractHibernateDSODAO<Community> impleme
public List<Community> findAll(Context context, MetadataField sortField, Integer limit, Integer offset) public List<Community> findAll(Context context, MetadataField sortField, Integer limit, Integer offset)
throws SQLException { throws SQLException {
StringBuilder queryBuilder = new StringBuilder(); StringBuilder queryBuilder = new StringBuilder();
queryBuilder.append("SELECT ").append(Community.class.getSimpleName()).append(" FROM Community as ")
.append(Community.class.getSimpleName()).append(" ");
addMetadataLeftJoin(queryBuilder, Community.class.getSimpleName(), Arrays.asList(sortField));
addMetadataSortQuery(queryBuilder, Arrays.asList(sortField), Collections.EMPTY_LIST);
// The query has to be rather complex because we want to sort the retrieval of Communities based on the title
// We'll join the Communities with the metadata fields on the sortfield specified in the parameters
// then we'll sort on this metadata field (this is usually the title). We're also making sure that the place
// is the lowest place in the metadata fields list so that we avoid the duplication bug
queryBuilder.append("SELECT c" +
" FROM Community c" +
" left join c.metadata title on title.metadataField = :sortField and" +
" title.dSpaceObject = c.id and" +
" title.place = (select min(internal.place) " +
"from c.metadata internal " +
"where internal.metadataField = :sortField and" +
" internal.dSpaceObject = c.id)" +
" ORDER BY LOWER(title.value)");
Query query = createQuery(context, queryBuilder.toString()); Query query = createQuery(context, queryBuilder.toString());
if (offset != null) { if (offset != null) {
query.setFirstResult(offset); query.setFirstResult(offset);
@@ -74,7 +81,8 @@ public class CommunityDAOImpl extends AbstractHibernateDSODAO<Community> impleme
if (limit != null) { if (limit != null) {
query.setMaxResults(limit); query.setMaxResults(limit);
} }
query.setParameter(sortField.toString(), sortField.getID()); query.setParameter("sortField", sortField);
return list(query); return list(query);
} }
@@ -91,16 +99,26 @@ public class CommunityDAOImpl extends AbstractHibernateDSODAO<Community> impleme
@Override @Override
public List<Community> findAllNoParent(Context context, MetadataField sortField) throws SQLException { public List<Community> findAllNoParent(Context context, MetadataField sortField) throws SQLException {
StringBuilder queryBuilder = new StringBuilder(); StringBuilder queryBuilder = new StringBuilder();
queryBuilder.append("SELECT community FROM Community as community ");
addMetadataLeftJoin(queryBuilder, Community.class.getSimpleName().toLowerCase(), Arrays.asList(sortField));
addMetadataValueWhereQuery(queryBuilder, Collections.EMPTY_LIST, null, " community.parentCommunities IS EMPTY");
addMetadataSortQuery(queryBuilder, Arrays.asList(sortField), Collections.EMPTY_LIST);
// The query has to be rather complex because we want to sort the retrieval of Communities based on the title
// We'll join the Communities with the metadata fields on the sortfield specified in the parameters
// then we'll sort on this metadata field (this is usually the title). We're also making sure that the place
// is the lowest place in the metadata fields list so that we avoid the duplication bug
// This query has the added where clause to enforce that the community cannot have any parent communities
queryBuilder.append("SELECT c" +
" FROM Community c" +
" left join c.metadata title on title.metadataField = :sortField and" +
" title.dSpaceObject = c.id and" +
" title.place = (select min(internal.place) " +
"from c.metadata internal " +
"where internal.metadataField = :sortField and" +
" internal.dSpaceObject = c.id)" +
" WHERE c.parentCommunities IS EMPTY " +
" ORDER BY LOWER(title.value)");
Query query = createQuery(context, queryBuilder.toString()); Query query = createQuery(context, queryBuilder.toString());
query.setParameter(sortField.toString(), sortField.getID()); query.setParameter("sortField", sortField);
query.setHint("org.hibernate.cacheable", Boolean.TRUE); query.setHint("org.hibernate.cacheable", Boolean.TRUE);
return findMany(context, query); return findMany(context, query);
} }

View File

@@ -24,31 +24,31 @@ public class RelationshipTypeDAOImpl extends AbstractHibernateDAO<RelationshipTy
@Override @Override
public RelationshipType findByTypesAndLabels(Context context, EntityType leftType, EntityType rightType, public RelationshipType findByTypesAndLabels(Context context, EntityType leftType, EntityType rightType,
String leftLabel, String rightLabel) String leftwardType, String rightwardType)
throws SQLException { throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context); CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, RelationshipType.class); CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, RelationshipType.class);
Root<RelationshipType> relationshipTypeRoot = criteriaQuery.from(RelationshipType.class); Root<RelationshipType> relationshipTypeRoot = criteriaQuery.from(RelationshipType.class);
criteriaQuery.select(relationshipTypeRoot); criteriaQuery.select(relationshipTypeRoot);
criteriaQuery.where( criteriaQuery.where(
criteriaBuilder.and(criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.leftType), leftType), criteriaBuilder.and(
criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.rightType), rightType), criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.leftType), leftType),
criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.leftLabel), leftLabel), criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.rightType), rightType),
criteriaBuilder criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.leftwardType), leftwardType),
.equal(relationshipTypeRoot.get(RelationshipType_.rightLabel), rightLabel))); criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.rightwardType), rightwardType)));
return uniqueResult(context, criteriaQuery, false, RelationshipType.class, -1, -1); return uniqueResult(context, criteriaQuery, false, RelationshipType.class, -1, -1);
} }
@Override @Override
public List<RelationshipType> findByLeftOrRightLabel(Context context, String label) throws SQLException { public List<RelationshipType> findByLeftwardOrRightwardTypeName(Context context, String type) throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context); CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, RelationshipType.class); CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, RelationshipType.class);
Root<RelationshipType> relationshipTypeRoot = criteriaQuery.from(RelationshipType.class); Root<RelationshipType> relationshipTypeRoot = criteriaQuery.from(RelationshipType.class);
criteriaQuery.select(relationshipTypeRoot); criteriaQuery.select(relationshipTypeRoot);
criteriaQuery.where( criteriaQuery.where(
criteriaBuilder.or( criteriaBuilder.or(
criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.leftLabel), label), criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.leftwardType), type),
criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.rightLabel), label) criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.rightwardType), type)
) )
); );
return list(context, criteriaQuery, true, RelationshipType.class, -1, -1); return list(context, criteriaQuery, true, RelationshipType.class, -1, -1);

View File

@@ -12,6 +12,7 @@ import java.util.List;
import org.dspace.content.DSpaceObject; import org.dspace.content.DSpaceObject;
import org.dspace.content.InProgressSubmission; import org.dspace.content.InProgressSubmission;
import org.dspace.content.RelationshipMetadataService;
import org.dspace.content.WorkspaceItem; import org.dspace.content.WorkspaceItem;
import org.dspace.content.service.BitstreamFormatService; import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.BitstreamService; import org.dspace.content.service.BitstreamService;
@@ -112,6 +113,8 @@ public abstract class ContentServiceFactory {
*/ */
public abstract EntityService getEntityService(); public abstract EntityService getEntityService();
public abstract RelationshipMetadataService getRelationshipMetadataService();
public InProgressSubmissionService getInProgressSubmissionService(InProgressSubmission inProgressSubmission) { public InProgressSubmissionService getInProgressSubmissionService(InProgressSubmission inProgressSubmission) {
if (inProgressSubmission instanceof WorkspaceItem) { if (inProgressSubmission instanceof WorkspaceItem) {
return getWorkspaceItemService(); return getWorkspaceItemService();

View File

@@ -10,6 +10,7 @@ package org.dspace.content.factory;
import java.util.List; import java.util.List;
import org.dspace.content.DSpaceObject; import org.dspace.content.DSpaceObject;
import org.dspace.content.RelationshipMetadataService;
import org.dspace.content.service.BitstreamFormatService; import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.BitstreamService; import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.BundleService; import org.dspace.content.service.BundleService;
@@ -78,6 +79,8 @@ public class ContentServiceFactoryImpl extends ContentServiceFactory {
@Autowired(required = true) @Autowired(required = true)
private RelationshipTypeService relationshipTypeService; private RelationshipTypeService relationshipTypeService;
@Autowired(required = true) @Autowired(required = true)
private RelationshipMetadataService relationshipMetadataService;
@Autowired(required = true)
private EntityTypeService entityTypeService; private EntityTypeService entityTypeService;
@Autowired(required = true) @Autowired(required = true)
private EntityService entityService; private EntityService entityService;
@@ -182,4 +185,8 @@ public class ContentServiceFactoryImpl extends ContentServiceFactory {
return entityService; return entityService;
} }
@Override
public RelationshipMetadataService getRelationshipMetadataService() {
return relationshipMetadataService;
}
} }

View File

@@ -122,6 +122,6 @@ public interface EntityService {
* to the given label parameter * to the given label parameter
* @throws SQLException If something goes wrong * @throws SQLException If something goes wrong
*/ */
List<RelationshipType> getRelationshipTypesByLabel(Context context, String label) throws SQLException; List<RelationshipType> getRelationshipTypesByTypeName(Context context, String label) throws SQLException;
} }

View File

@@ -24,10 +24,8 @@ import org.dspace.content.Community;
import org.dspace.content.Item; import org.dspace.content.Item;
import org.dspace.content.MetadataField; import org.dspace.content.MetadataField;
import org.dspace.content.MetadataValue; import org.dspace.content.MetadataValue;
import org.dspace.content.RelationshipMetadataValue;
import org.dspace.content.Thumbnail; import org.dspace.content.Thumbnail;
import org.dspace.content.WorkspaceItem; import org.dspace.content.WorkspaceItem;
import org.dspace.content.virtual.VirtualMetadataPopulator;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
@@ -40,7 +38,7 @@ import org.dspace.eperson.Group;
* @author kevinvandevelde at atmire.com * @author kevinvandevelde at atmire.com
*/ */
public interface ItemService public interface ItemService
extends DSpaceObjectService<Item>, DSpaceObjectLegacySupportService<Item>, IndexableObjectService<Item, UUID> { extends DSpaceObjectService<Item>, DSpaceObjectLegacySupportService<Item>, IndexableObjectService<Item, UUID> {
public Thumbnail getThumbnail(Context context, Item item, boolean requireOriginal) throws SQLException; public Thumbnail getThumbnail(Context context, Item item, boolean requireOriginal) throws SQLException;
@@ -205,7 +203,7 @@ public interface ItemService
* @throws SQLException if database error * @throws SQLException if database error
*/ */
public Iterator<Item> findInArchiveOrWithdrawnNonDiscoverableModifiedSince(Context context, Date since) public Iterator<Item> findInArchiveOrWithdrawnNonDiscoverableModifiedSince(Context context, Date since)
throws SQLException; throws SQLException;
/** /**
* Get all the items (including private and withdrawn) in this collection. The order is indeterminate. * Get all the items (including private and withdrawn) in this collection. The order is indeterminate.
@@ -677,19 +675,6 @@ public interface ItemService
*/ */
boolean isInProgressSubmission(Context context, Item item) throws SQLException; boolean isInProgressSubmission(Context context, Item item) throws SQLException;
/**
* This method retrieves a list of MetadataValue objects that get constructed from processing
* the given Item's Relationships through the config given to the {@link VirtualMetadataPopulator}
* @param item The Item that will be processed through it's Relationships
* @param enableVirtualMetadata This parameter will determine whether the list of Relationship metadata
* should be populated with metadata that is being generated through the
* VirtualMetadataPopulator functionality or not
* @return The list of MetadataValue objects constructed through the Relationships
*/
public List<RelationshipMetadataValue> getRelationshipMetadata(Item item, boolean enableVirtualMetadata);
/** /**
* Get metadata for the DSpace Object in a chosen schema. * Get metadata for the DSpace Object in a chosen schema.
* See <code>MetadataSchema</code> for more information about schemas. * See <code>MetadataSchema</code> for more information about schemas.

View File

@@ -89,7 +89,7 @@ public interface RelationshipService extends DSpaceCRUDService<Relationship> {
/** /**
* This method will update the place for the Relationship and all other relationships found by the items and * This method will update the place for the Relationship and all other relationships found by the items and
* relationship type of the given Relatonship. It will give this Relationship the last place in both the * relationship type of the given Relationship. It will give this Relationship the last place in both the
* left and right place determined by querying for the list of leftRelationships and rightRelationships * left and right place determined by querying for the list of leftRelationships and rightRelationships
* by the leftItem, rightItem and relationshipType of the given Relationship. * by the leftItem, rightItem and relationshipType of the given Relationship.
* @param context The relevant DSpace context * @param context The relevant DSpace context
@@ -115,7 +115,7 @@ public interface RelationshipService extends DSpaceCRUDService<Relationship> {
/** /**
* This method returns a list of Relationship objets for which the relationshipType property is equal to the given * This method returns a list of Relationship objects for which the relationshipType property is equal to the given
* RelationshipType object * RelationshipType object
* @param context The relevant DSpace context * @param context The relevant DSpace context
* @param relationshipType The RelationshipType object that will be used to check the Relationship on * @param relationshipType The RelationshipType object that will be used to check the Relationship on
@@ -133,11 +133,31 @@ public interface RelationshipService extends DSpaceCRUDService<Relationship> {
* @param relationshipType The RelationshipType object for the relationship * @param relationshipType The RelationshipType object for the relationship
* @param leftPlace The leftPlace integer for the relationship * @param leftPlace The leftPlace integer for the relationship
* @param rightPlace The rightPlace integer for the relationship * @param rightPlace The rightPlace integer for the relationship
* @param leftwardValue The leftwardValue string for the relationship
* @param rightwardValue The rightwardValue string for the relationship
* @return The created Relationship object with the given properties
* @throws AuthorizeException If something goes wrong
* @throws SQLException If something goes wrong
*/
Relationship create(Context c, Item leftItem, Item rightItem, RelationshipType relationshipType,
int leftPlace, int rightPlace, String leftwardValue, String rightwardValue)
throws AuthorizeException, SQLException;
/**
* This method is used to construct a Relationship object with all it's variables,
* except the leftward and rightward labels
* @param c The relevant DSpace context
* @param leftItem The leftItem Item object for the relationship
* @param rightItem The rightItem Item object for the relationship
* @param relationshipType The RelationshipType object for the relationship
* @param leftPlace The leftPlace integer for the relationship
* @param rightPlace The rightPlace integer for the relationship
* @return The created Relationship object with the given properties * @return The created Relationship object with the given properties
* @throws AuthorizeException If something goes wrong * @throws AuthorizeException If something goes wrong
* @throws SQLException If something goes wrong * @throws SQLException If something goes wrong
*/ */
Relationship create(Context c, Item leftItem, Item rightItem, RelationshipType relationshipType, Relationship create(Context c, Item leftItem, Item rightItem, RelationshipType relationshipType,
int leftPlace, int rightPlace) int leftPlace, int rightPlace)
throws AuthorizeException, SQLException; throws AuthorizeException, SQLException;
} }

View File

@@ -23,9 +23,9 @@ public interface RelationshipTypeService extends DSpaceCRUDService<RelationshipT
/** /**
* This method creates the given RelationshipType object in the database and returns it * This method creates the given RelationshipType object in the database and returns it
* @param context The relevant DSpace context * @param context The relevant DSpace context
* @param relationshipType The RelationshipType to be created in the database * @param relationshipType The RelationshipType to be created in the database
* @return The newly created RelationshipType * @return The newly created RelationshipType
* @throws SQLException If something goes wrong * @throws SQLException If something goes wrong
* @throws AuthorizeException If something goes wrong with authorizations * @throws AuthorizeException If something goes wrong with authorizations
*/ */
@@ -33,16 +33,16 @@ public interface RelationshipTypeService extends DSpaceCRUDService<RelationshipT
/** /**
* Retrieves a RelationshipType for which the given parameters all match the one in the returned RelationshipType * Retrieves a RelationshipType for which the given parameters all match the one in the returned RelationshipType
* @param context The relevant DSpace context * @param context The relevant DSpace context
* @param leftType The rightType EntityType that needs to match for the returned RelationshipType * @param leftType The rightType EntityType that needs to match for the returned RelationshipType
* @param rightType The rightType EntityType that needs to match for the returned RelationshipType * @param rightType The rightType EntityType that needs to match for the returned RelationshipType
* @param leftLabel The leftLabel String that needs to match for the returned RelationshipType * @param leftwardType The leftwardType String that needs to match for the returned RelationshipType
* @param rightLabel The rightLabel String that needs to match for the returned RelationshipType * @param rightwardType The rightwardType String that needs to match for the returned RelationshipType
* @return * @return
* @throws SQLException If something goes wrong * @throws SQLException If something goes wrong
*/ */
RelationshipType findbyTypesAndLabels(Context context,EntityType leftType,EntityType rightType, RelationshipType findbyTypesAndLabels(Context context,EntityType leftType,EntityType rightType,
String leftLabel,String rightLabel) String leftwardType,String rightwardType)
throws SQLException; throws SQLException;
/** /**
@@ -54,7 +54,7 @@ public interface RelationshipTypeService extends DSpaceCRUDService<RelationshipT
List<RelationshipType> findAll(Context context) throws SQLException; List<RelationshipType> findAll(Context context) throws SQLException;
/** /**
* Retrieves all RelationshipType objects that have a left or right label that is * Retrieves all RelationshipType objects that have a left or right type that is
* equal to the given String * equal to the given String
* @param context The relevant DSpace context * @param context The relevant DSpace context
* @param label The label that has to match * @param label The label that has to match
@@ -62,7 +62,7 @@ public interface RelationshipTypeService extends DSpaceCRUDService<RelationshipT
* that is equal to the given label param * that is equal to the given label param
* @throws SQLException If something goes wrong * @throws SQLException If something goes wrong
*/ */
List<RelationshipType> findByLeftOrRightLabel(Context context, String label) throws SQLException; List<RelationshipType> findByLeftwardOrRightwardTypeName(Context context, String label) throws SQLException;
/** /**
* Returns a list of RelationshipType objects for which the given EntityType is equal to either the leftType * Returns a list of RelationshipType objects for which the given EntityType is equal to either the leftType
@@ -80,8 +80,8 @@ public interface RelationshipTypeService extends DSpaceCRUDService<RelationshipT
* @param context The relevant DSpace context * @param context The relevant DSpace context
* @param leftEntityType The leftEntityType EntityType object for this relationshipType * @param leftEntityType The leftEntityType EntityType object for this relationshipType
* @param rightEntityType The rightEntityType EntityType object for this relationshipType * @param rightEntityType The rightEntityType EntityType object for this relationshipType
* @param leftLabel The leftLabel String object for this relationshipType * @param leftwardType The leftwardType String object for this relationshipType
* @param rightLabel The rightLabel String object for this relationshipType * @param rightwardType The rightwardType String object for this relationshipType
* @param leftCardinalityMinInteger The leftCardinalityMinInteger Integer object for this relationshipType * @param leftCardinalityMinInteger The leftCardinalityMinInteger Integer object for this relationshipType
* @param leftCardinalityMaxInteger The leftCardinalityMaxInteger Integer object for this relationshipType * @param leftCardinalityMaxInteger The leftCardinalityMaxInteger Integer object for this relationshipType
* @param rightCardinalityMinInteger The rightCardinalityMinInteger Integer object for this relationshipType * @param rightCardinalityMinInteger The rightCardinalityMinInteger Integer object for this relationshipType
@@ -90,8 +90,9 @@ public interface RelationshipTypeService extends DSpaceCRUDService<RelationshipT
* @throws SQLException If something goes wrong * @throws SQLException If something goes wrong
* @throws AuthorizeException If something goes wrong * @throws AuthorizeException If something goes wrong
*/ */
RelationshipType create(Context context, EntityType leftEntityType, EntityType rightEntityType, String leftLabel, RelationshipType create(Context context, EntityType leftEntityType, EntityType rightEntityType,
String rightLabel, Integer leftCardinalityMinInteger, Integer leftCardinalityMaxInteger, String leftwardType, String rightwardType, Integer leftCardinalityMinInteger,
Integer rightCardinalityMinInteger, Integer rightCardinalityMaxInteger) Integer leftCardinalityMaxInteger, Integer rightCardinalityMinInteger,
Integer rightCardinalityMaxInteger)
throws SQLException, AuthorizeException; throws SQLException, AuthorizeException;
} }

View File

@@ -65,6 +65,7 @@ public class Collected implements VirtualMetadataConfiguration {
* Generic setter for the useForPlace property * Generic setter for the useForPlace property
* @param useForPlace The boolean value that the useForPlace property will be set to * @param useForPlace The boolean value that the useForPlace property will be set to
*/ */
@Override
public void setUseForPlace(boolean useForPlace) { public void setUseForPlace(boolean useForPlace) {
this.useForPlace = useForPlace; this.useForPlace = useForPlace;
} }
@@ -73,10 +74,19 @@ public class Collected implements VirtualMetadataConfiguration {
* Generic getter for the useForPlace property * Generic getter for the useForPlace property
* @return The useForPlace to be used by this bean * @return The useForPlace to be used by this bean
*/ */
@Override
public boolean getUseForPlace() { public boolean getUseForPlace() {
return useForPlace; return useForPlace;
} }
@Override
public void setPopulateWithNameVariant(boolean populateWithNameVariant) { }
@Override
public boolean getPopulateWithNameVariant() {
return false;
}
/** /**
* this method will retrieve the metadata values from the given item for all the metadata fields listed * this method will retrieve the metadata values from the given item for all the metadata fields listed
* in the fields property and it'll return all those values as a list * in the fields property and it'll return all those values as a list

View File

@@ -44,6 +44,7 @@ public class Concatenate implements VirtualMetadataConfiguration {
*/ */
private boolean useForPlace = false; private boolean useForPlace = false;
private boolean populateWithNameVariant = false;
/** /**
* Generic getter for the fields property * Generic getter for the fields property
* @return The list of fields to be used in this bean * @return The list of fields to be used in this bean
@@ -80,6 +81,7 @@ public class Concatenate implements VirtualMetadataConfiguration {
* Generic setter for the useForPlace property * Generic setter for the useForPlace property
* @param useForPlace The boolean value that the useForPlace property will be set to * @param useForPlace The boolean value that the useForPlace property will be set to
*/ */
@Override
public void setUseForPlace(boolean useForPlace) { public void setUseForPlace(boolean useForPlace) {
this.useForPlace = useForPlace; this.useForPlace = useForPlace;
} }
@@ -88,10 +90,21 @@ public class Concatenate implements VirtualMetadataConfiguration {
* Generic getter for the useForPlace property * Generic getter for the useForPlace property
* @return The useForPlace to be used by this bean * @return The useForPlace to be used by this bean
*/ */
@Override
public boolean getUseForPlace() { public boolean getUseForPlace() {
return useForPlace; return useForPlace;
} }
@Override
public void setPopulateWithNameVariant(boolean populateWithNameVariant) {
this.populateWithNameVariant = populateWithNameVariant;
}
@Override
public boolean getPopulateWithNameVariant() {
return populateWithNameVariant;
}
/** /**
* this method will retrieve the metadata values from the given item for all the metadata fields listed * this method will retrieve the metadata values from the given item for all the metadata fields listed
* in the fields property and it'll concatenate all those values together with the separator specified * in the fields property and it'll concatenate all those values together with the separator specified
@@ -100,6 +113,7 @@ public class Concatenate implements VirtualMetadataConfiguration {
* @param item The item that will be used to either retrieve metadata values from * @param item The item that will be used to either retrieve metadata values from
* @return The String value for all of the retrieved metadatavalues combined with the separator * @return The String value for all of the retrieved metadatavalues combined with the separator
*/ */
@Override
public List<String> getValues(Context context, Item item) { public List<String> getValues(Context context, Item item) {
List<String> resultValues = new LinkedList<>(); List<String> resultValues = new LinkedList<>();

View File

@@ -120,6 +120,7 @@ public class Related implements VirtualMetadataConfiguration {
* Generic setter for the useForPlace property * Generic setter for the useForPlace property
* @param useForPlace The boolean value that the useForPlace property will be set to * @param useForPlace The boolean value that the useForPlace property will be set to
*/ */
@Override
public void setUseForPlace(boolean useForPlace) { public void setUseForPlace(boolean useForPlace) {
this.useForPlace = useForPlace; this.useForPlace = useForPlace;
} }
@@ -128,10 +129,19 @@ public class Related implements VirtualMetadataConfiguration {
* Generic getter for the useForPlace property * Generic getter for the useForPlace property
* @return The useForPlace to be used by this bean * @return The useForPlace to be used by this bean
*/ */
@Override
public boolean getUseForPlace() { public boolean getUseForPlace() {
return useForPlace; return useForPlace;
} }
@Override
public void setPopulateWithNameVariant(boolean populateWithNameVariant) { }
@Override
public boolean getPopulateWithNameVariant() {
return false;
}
/** /**
* This method will find the correct Relationship from the given item to retrieve the other item from it * This method will find the correct Relationship from the given item to retrieve the other item from it
* and pass this along to the next VirtualBean that's stored in this class. * and pass this along to the next VirtualBean that's stored in this class.
@@ -142,6 +152,7 @@ public class Related implements VirtualMetadataConfiguration {
* Will return an empty list if no relationships are found * Will return an empty list if no relationships are found
* @throws SQLException If something goes wrong * @throws SQLException If something goes wrong
*/ */
@Override
public List<String> getValues(Context context, Item item) throws SQLException { public List<String> getValues(Context context, Item item) throws SQLException {
Entity entity = entityService.findByItemId(context, item.getID()); Entity entity = entityService.findByItemId(context, item.getID());
EntityType entityType = entityService.getType(context, entity); EntityType entityType = entityService.getType(context, entity);
@@ -149,8 +160,8 @@ public class Related implements VirtualMetadataConfiguration {
List<RelationshipType> relationshipTypes = entityService.getAllRelationshipTypes(context, entity); List<RelationshipType> relationshipTypes = entityService.getAllRelationshipTypes(context, entity);
List<RelationshipType> possibleRelationshipTypes = new LinkedList<>(); List<RelationshipType> possibleRelationshipTypes = new LinkedList<>();
for (RelationshipType relationshipType : relationshipTypes) { for (RelationshipType relationshipType : relationshipTypes) {
if (StringUtils.equals(relationshipType.getLeftLabel(), relationshipTypeString) || StringUtils if (StringUtils.equals(relationshipType.getLeftwardType(), relationshipTypeString) || StringUtils
.equals(relationshipType.getRightLabel(), relationshipTypeString)) { .equals(relationshipType.getRightwardType(), relationshipTypeString)) {
possibleRelationshipTypes.add(relationshipType); possibleRelationshipTypes.add(relationshipType);
} }
} }

View File

@@ -38,4 +38,12 @@ public class UUIDValue implements VirtualMetadataConfiguration {
public boolean getUseForPlace() { public boolean getUseForPlace() {
return useForPlace; return useForPlace;
} }
@Override
public void setPopulateWithNameVariant(boolean populateWithNameVariant) { }
@Override
public boolean getPopulateWithNameVariant() {
return false;
}
} }

View File

@@ -42,4 +42,18 @@ public interface VirtualMetadataConfiguration {
* @return The useForPlace to be used by this bean * @return The useForPlace to be used by this bean
*/ */
boolean getUseForPlace(); boolean getUseForPlace();
/**
* Generic setter for the populateWithNameVariant
* This property defines whether the value should be retrieved from the left/rightward on the Relationship (true)
* or through the configuration and usual way (false)
* @param populateWithNameVariant The boolean value that the populateWithNameVariant property will be set to
*/
void setPopulateWithNameVariant(boolean populateWithNameVariant);
/**
* Generic getter for the populateWithNameVariant property
* @return The populatewithNameVariant to be used by this bean
*/
boolean getPopulateWithNameVariant();
} }

View File

@@ -42,17 +42,17 @@ public class VirtualMetadataPopulator {
/** /**
* This method will return a boolean indicating whether the useForPlace is true or false for the given * This method will return a boolean indicating whether the useForPlace is true or false for the given
* RelationshipType for the left or right label as indicated by the second parameter. * RelationshipType for the left or right type as indicated by the second parameter.
* @param relationshipType The relationshipType for which this should be checked * @param relationshipType The relationshipType for which this should be checked
* @param isLeft The boolean indicating whether to check the left or the right label * @param isLeft The boolean indicating whether to check the left or the right type
* @return A boolean indicating whether the useForPlace is true or not for the given parameters * @return A boolean indicating whether the useForPlace is true or not for the given parameters
*/ */
public boolean isUseForPlaceTrueForRelationshipType(RelationshipType relationshipType, boolean isLeft) { public boolean isUseForPlaceTrueForRelationshipType(RelationshipType relationshipType, boolean isLeft) {
HashMap<String, VirtualMetadataConfiguration> hashMaps; HashMap<String, VirtualMetadataConfiguration> hashMaps;
if (isLeft) { if (isLeft) {
hashMaps = this.getMap().get(relationshipType.getLeftLabel()); hashMaps = this.getMap().get(relationshipType.getLeftwardType());
} else { } else {
hashMaps = this.getMap().get(relationshipType.getRightLabel()); hashMaps = this.getMap().get(relationshipType.getRightwardType());
} }
if (hashMaps != null) { if (hashMaps != null) {
for (Map.Entry<String, VirtualMetadataConfiguration> entry : hashMaps.entrySet()) { for (Map.Entry<String, VirtualMetadataConfiguration> entry : hashMaps.entrySet()) {

View File

@@ -7,16 +7,28 @@
*/ */
package org.dspace.harvest; package org.dspace.harvest;
import static org.dspace.harvest.OAIHarvester.OAI_ADDRESS_ERROR;
import static org.dspace.harvest.OAIHarvester.OAI_DMD_ERROR;
import static org.dspace.harvest.OAIHarvester.OAI_ORE_ERROR;
import static org.dspace.harvest.OAIHarvester.OAI_SET_ERROR;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import ORG.oclc.oai.harvester2.verb.Identify;
import ORG.oclc.oai.harvester2.verb.ListIdentifiers;
import org.dspace.content.Collection; import org.dspace.content.Collection;
import org.dspace.core.ConfigurationManager; import org.dspace.core.ConfigurationManager;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.harvest.dao.HarvestedCollectionDAO; import org.dspace.harvest.dao.HarvestedCollectionDAO;
import org.dspace.harvest.service.HarvestedCollectionService; import org.dspace.harvest.service.HarvestedCollectionService;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.input.DOMBuilder;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
/** /**
@@ -27,6 +39,10 @@ import org.springframework.beans.factory.annotation.Autowired;
* @author kevinvandevelde at atmire.com * @author kevinvandevelde at atmire.com
*/ */
public class HarvestedCollectionServiceImpl implements HarvestedCollectionService { public class HarvestedCollectionServiceImpl implements HarvestedCollectionService {
private static final Namespace ORE_NS = Namespace.getNamespace("http://www.openarchives.org/ore/terms/");
private static final Namespace OAI_NS = Namespace.getNamespace("http://www.openarchives.org/OAI/2.0/");
@Autowired(required = true) @Autowired(required = true)
protected HarvestedCollectionDAO harvestedCollectionDAO; protected HarvestedCollectionDAO harvestedCollectionDAO;
@@ -156,5 +172,94 @@ public class HarvestedCollectionServiceImpl implements HarvestedCollectionServic
return 0 < harvestedCollectionDAO.count(context); return 0 < harvestedCollectionDAO.count(context);
} }
/**
* Verify the existence of an OAI server with the specified set and
* supporting the provided metadata formats.
*
* @param oaiSource the address of the OAI-PMH provider
* @param oaiSetId OAI set identifier
* @param metaPrefix OAI metadataPrefix
* @param testORE whether the method should also check the PMH provider for ORE support
* @return list of errors encountered during verification. Empty list indicates a "success" condition.
*/
public List<String> verifyOAIharvester(String oaiSource,
String oaiSetId, String metaPrefix, boolean testORE) {
List<String> errorSet = new ArrayList<String>();
// First, see if we can contact the target server at all.
try {
new Identify(oaiSource);
} catch (Exception ex) {
errorSet.add(OAI_ADDRESS_ERROR + ": OAI server could not be reached.");
return errorSet;
}
// Next, make sure the metadata we need is supported by the target server
Namespace DMD_NS = OAIHarvester.getDMDNamespace(metaPrefix);
if (null == DMD_NS) {
errorSet.add(OAI_DMD_ERROR + ": " + metaPrefix);
return errorSet;
}
String OREOAIPrefix = null;
String DMDOAIPrefix = null;
try {
OREOAIPrefix = OAIHarvester.oaiResolveNamespaceToPrefix(oaiSource, OAIHarvester.getORENamespace().getURI());
DMDOAIPrefix = OAIHarvester.oaiResolveNamespaceToPrefix(oaiSource, DMD_NS.getURI());
} catch (Exception ex) {
errorSet.add(OAI_ADDRESS_ERROR
+ ": OAI did not respond to ListMetadataFormats query ("
+ ORE_NS.getPrefix() + ":" + OREOAIPrefix + " ; "
+ DMD_NS.getPrefix() + ":" + DMDOAIPrefix + "): "
+ ex.getMessage());
return errorSet;
}
if (testORE && OREOAIPrefix == null) {
errorSet.add(OAI_ORE_ERROR + ": The OAI server does not support ORE dissemination");
}
if (DMDOAIPrefix == null) {
errorSet.add(OAI_DMD_ERROR + ": The OAI server does not support dissemination in this format");
}
// Now scan the sets and make sure the one supplied is in the list
boolean foundSet = false;
try {
//If we do not want to harvest from one set, then skip this.
if (!"all".equals(oaiSetId)) {
ListIdentifiers ls = new ListIdentifiers(oaiSource, null, null, oaiSetId, DMDOAIPrefix);
// The only error we can really get here is "noSetHierarchy"
if (ls.getErrors() != null && ls.getErrors().getLength() > 0) {
for (int i = 0; i < ls.getErrors().getLength(); i++) {
String errorCode = ls.getErrors().item(i).getAttributes().getNamedItem("code").getTextContent();
errorSet.add(
OAI_SET_ERROR + ": The OAI server does not have a set with the specified setSpec (" +
errorCode + ")");
}
} else {
// Drilling down to /OAI-PMH/ListSets/set
DOMBuilder db = new DOMBuilder();
Document reply = db.build(ls.getDocument());
Element root = reply.getRootElement();
//Check if we can find items, if so this indicates that we have children and our sets exist
foundSet = 0 < root.getChild("ListIdentifiers", OAI_NS).getChildren().size();
if (!foundSet) {
errorSet.add(OAI_SET_ERROR + ": The OAI server does not have a set with the specified setSpec");
}
}
}
} catch (RuntimeException re) {
throw re;
} catch (Exception e) {
errorSet.add(OAI_ADDRESS_ERROR + ": OAI server could not be reached");
return errorSet;
}
return errorSet;
}
} }

View File

@@ -17,10 +17,14 @@ import java.sql.SQLException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
@@ -28,7 +32,6 @@ import javax.xml.transform.TransformerException;
import ORG.oclc.oai.harvester2.verb.GetRecord; import ORG.oclc.oai.harvester2.verb.GetRecord;
import ORG.oclc.oai.harvester2.verb.Identify; import ORG.oclc.oai.harvester2.verb.Identify;
import ORG.oclc.oai.harvester2.verb.ListIdentifiers;
import ORG.oclc.oai.harvester2.verb.ListMetadataFormats; import ORG.oclc.oai.harvester2.verb.ListMetadataFormats;
import ORG.oclc.oai.harvester2.verb.ListRecords; import ORG.oclc.oai.harvester2.verb.ListRecords;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -102,7 +105,7 @@ public class OAIHarvester {
protected BitstreamFormatService bitstreamFormatService; protected BitstreamFormatService bitstreamFormatService;
protected BundleService bundleService; protected BundleService bundleService;
protected CollectionService collectionService; protected CollectionService collectionService;
protected HarvestedCollectionService harvestedCollection; protected HarvestedCollectionService harvestedCollectionService;
protected InstallItemService installItemService; protected InstallItemService installItemService;
protected ItemService itemService; protected ItemService itemService;
protected HandleService handleService; protected HandleService handleService;
@@ -140,7 +143,7 @@ public class OAIHarvester {
bundleService = ContentServiceFactory.getInstance().getBundleService(); bundleService = ContentServiceFactory.getInstance().getBundleService();
collectionService = ContentServiceFactory.getInstance().getCollectionService(); collectionService = ContentServiceFactory.getInstance().getCollectionService();
handleService = HandleServiceFactory.getInstance().getHandleService(); handleService = HandleServiceFactory.getInstance().getHandleService();
harvestedCollection = HarvestServiceFactory.getInstance().getHarvestedCollectionService(); harvestedCollectionService = HarvestServiceFactory.getInstance().getHarvestedCollectionService();
harvestedItemService = HarvestServiceFactory.getInstance().getHarvestedItemService(); harvestedItemService = HarvestServiceFactory.getInstance().getHarvestedItemService();
itemService = ContentServiceFactory.getInstance().getItemService(); itemService = ContentServiceFactory.getInstance().getItemService();
installItemService = ContentServiceFactory.getInstance().getInstallItemService(); installItemService = ContentServiceFactory.getInstance().getInstallItemService();
@@ -150,7 +153,6 @@ public class OAIHarvester {
configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
if (dso.getType() != Constants.COLLECTION) { if (dso.getType() != Constants.COLLECTION) {
throw new HarvestingException("OAIHarvester can only harvest collections"); throw new HarvestingException("OAIHarvester can only harvest collections");
} }
@@ -159,7 +161,7 @@ public class OAIHarvester {
targetCollection = (Collection) dso; targetCollection = (Collection) dso;
harvestRow = hc; harvestRow = hc;
if (harvestRow == null || !harvestedCollection.isHarvestable(harvestRow)) { if (harvestRow == null || !harvestedCollectionService.isHarvestable(harvestRow)) {
throw new HarvestingException("Provided collection is not set up for harvesting"); throw new HarvestingException("Provided collection is not set up for harvesting");
} }
@@ -188,7 +190,7 @@ public class OAIHarvester {
* *
* @return Namespace of the supported ORE format. Returns null if not found. * @return Namespace of the supported ORE format. Returns null if not found.
*/ */
private static Namespace getORENamespace() { public static Namespace getORENamespace() {
String ORESerializationString = null; String ORESerializationString = null;
String ORESeialKey = null; String ORESeialKey = null;
String oreString = "oai.harvester.oreSerializationFormat"; String oreString = "oai.harvester.oreSerializationFormat";
@@ -213,7 +215,7 @@ public class OAIHarvester {
* @param metadataKey * @param metadataKey
* @return Namespace of the designated metadata format. Returns null of not found. * @return Namespace of the designated metadata format. Returns null of not found.
*/ */
private static Namespace getDMDNamespace(String metadataKey) { public static Namespace getDMDNamespace(String metadataKey) {
String metadataString = null; String metadataString = null;
String metaString = "oai.harvester.metadataformats"; String metaString = "oai.harvester.metadataformats";
@@ -313,7 +315,7 @@ public class OAIHarvester {
harvestRow.setHarvestStatus(HarvestedCollection.STATUS_BUSY); harvestRow.setHarvestStatus(HarvestedCollection.STATUS_BUSY);
harvestRow.setHarvestMessage("Collection harvesting is initializing..."); harvestRow.setHarvestMessage("Collection harvesting is initializing...");
harvestRow.setHarvestStartTime(startTime); harvestRow.setHarvestStartTime(startTime);
harvestedCollection.update(ourContext, harvestRow); harvestedCollectionService.update(ourContext, harvestRow);
intermediateCommit(); intermediateCommit();
// expiration timer starts // expiration timer starts
@@ -354,7 +356,7 @@ public class OAIHarvester {
harvestRow.setHarvestStartTime(new Date()); harvestRow.setHarvestStartTime(new Date());
harvestRow.setHarvestMessage("OAI server did not contain any updates"); harvestRow.setHarvestMessage("OAI server did not contain any updates");
harvestRow.setHarvestStatus(HarvestedCollection.STATUS_READY); harvestRow.setHarvestStatus(HarvestedCollection.STATUS_READY);
harvestedCollection.update(ourContext, harvestRow); harvestedCollectionService.update(ourContext, harvestRow);
return; return;
} else { } else {
throw new HarvestingException(errorSet.toString()); throw new HarvestingException(errorSet.toString());
@@ -411,7 +413,7 @@ public class OAIHarvester {
harvestRow.setHarvestMessage(String harvestRow.setHarvestMessage(String
.format("Collection is currently being harvested (item %d of %d)", .format("Collection is currently being harvested (item %d of %d)",
currentRecord, totalListSize)); currentRecord, totalListSize));
harvestedCollection.update(ourContext, harvestRow); harvestedCollectionService.update(ourContext, harvestRow);
} finally { } finally {
//In case of an exception, make sure to restore our authentication state to the previous state //In case of an exception, make sure to restore our authentication state to the previous state
ourContext.restoreAuthSystemState(); ourContext.restoreAuthSystemState();
@@ -429,19 +431,19 @@ public class OAIHarvester {
alertAdmin(HarvestedCollection.STATUS_OAI_ERROR, hex); alertAdmin(HarvestedCollection.STATUS_OAI_ERROR, hex);
} }
harvestRow.setHarvestStatus(HarvestedCollection.STATUS_OAI_ERROR); harvestRow.setHarvestStatus(HarvestedCollection.STATUS_OAI_ERROR);
harvestedCollection.update(ourContext, harvestRow); harvestedCollectionService.update(ourContext, harvestRow);
ourContext.complete(); ourContext.complete();
return; return;
} catch (Exception ex) { } catch (Exception ex) {
harvestRow.setHarvestMessage("Unknown error occurred while generating an OAI response"); harvestRow.setHarvestMessage("Unknown error occurred while generating an OAI response");
harvestRow.setHarvestStatus(HarvestedCollection.STATUS_UNKNOWN_ERROR); harvestRow.setHarvestStatus(HarvestedCollection.STATUS_UNKNOWN_ERROR);
harvestedCollection.update(ourContext, harvestRow); harvestedCollectionService.update(ourContext, harvestRow);
alertAdmin(HarvestedCollection.STATUS_UNKNOWN_ERROR, ex); alertAdmin(HarvestedCollection.STATUS_UNKNOWN_ERROR, ex);
log.error("Error occurred while generating an OAI response: " + ex.getMessage() + " " + ex.getCause(), ex); log.error("Error occurred while generating an OAI response: " + ex.getMessage() + " " + ex.getCause(), ex);
ourContext.complete(); ourContext.complete();
return; return;
} finally { } finally {
harvestedCollection.update(ourContext, harvestRow); harvestedCollectionService.update(ourContext, harvestRow);
ourContext.turnOffAuthorisationSystem(); ourContext.turnOffAuthorisationSystem();
collectionService.update(ourContext, targetCollection); collectionService.update(ourContext, targetCollection);
ourContext.restoreAuthSystemState(); ourContext.restoreAuthSystemState();
@@ -456,7 +458,7 @@ public class OAIHarvester {
log.info( log.info(
"Harvest from " + oaiSource + " successful. The process took " + timeTaken + " milliseconds. Harvested " "Harvest from " + oaiSource + " successful. The process took " + timeTaken + " milliseconds. Harvested "
+ currentRecord + " items."); + currentRecord + " items.");
harvestedCollection.update(ourContext, harvestRow); harvestedCollectionService.update(ourContext, harvestRow);
ourContext.setMode(originalMode); ourContext.setMode(originalMode);
} }
@@ -900,94 +902,44 @@ public class OAIHarvester {
String oaiSetId = harvestRow.getOaiSetId(); String oaiSetId = harvestRow.getOaiSetId();
String metaPrefix = harvestRow.getHarvestMetadataConfig(); String metaPrefix = harvestRow.getHarvestMetadataConfig();
return verifyOAIharvester(oaiSource, oaiSetId, metaPrefix, true); return harvestedCollectionService.verifyOAIharvester(oaiSource, oaiSetId, metaPrefix, true);
} }
/** /**
* Verify the existence of an OAI server with the specified set and * Return all available metadata formats
* supporting the provided metadata formats.
* *
* @param oaiSource the address of the OAI-PMH provider * @return a list containing a map for each supported metadata format
* @param oaiSetId OAI set identifier
* @param metaPrefix OAI metadataPrefix
* @param testORE whether the method should also check the PMH provider for ORE support
* @return list of errors encountered during verification. Empty list indicates a "success" condition.
*/ */
public static List<String> verifyOAIharvester(String oaiSource, public static List<Map<String,String>> getAvailableMetadataFormats() {
String oaiSetId, String metaPrefix, boolean testORE) { List<Map<String,String>> configs = new ArrayList<>();
List<String> errorSet = new ArrayList<String>(); String metaString = "oai.harvester.metadataformats.";
Enumeration pe = Collections.enumeration(
DSpaceServicesFactory.getInstance().getConfigurationService()
.getPropertyKeys("oai.harvester.metadataformats")
);
while (pe.hasMoreElements()) {
String key = (String) pe.nextElement();
String metadataString = DSpaceServicesFactory.getInstance().getConfigurationService().getProperty(key);
// First, see if we can contact the target server at all. String id = key.substring(metaString.length());
try { String label;
new Identify(oaiSource); String namespace = "";
} catch (Exception ex) {
errorSet.add(OAI_ADDRESS_ERROR + ": OAI server could not be reached.");
return errorSet;
}
// Next, make sure the metadata we need is supported by the target server if (metadataString.indexOf(',') != -1) {
Namespace DMD_NS = OAIHarvester.getDMDNamespace(metaPrefix); label = metadataString.substring(metadataString.indexOf(',') + 2);
if (null == DMD_NS) { namespace = metadataString.substring(0, metadataString.indexOf(','));
errorSet.add(OAI_DMD_ERROR + ": " + metaPrefix); } else {
return errorSet; label = id + "(" + metadataString + ")";
}
String OREOAIPrefix = null;
String DMDOAIPrefix = null;
try {
OREOAIPrefix = OAIHarvester.oaiResolveNamespaceToPrefix(oaiSource, getORENamespace().getURI());
DMDOAIPrefix = OAIHarvester.oaiResolveNamespaceToPrefix(oaiSource, DMD_NS.getURI());
} catch (Exception ex) {
errorSet.add(OAI_ADDRESS_ERROR
+ ": OAI did not respond to ListMetadataFormats query ("
+ ORE_NS.getPrefix() + ":" + OREOAIPrefix + " ; "
+ DMD_NS.getPrefix() + ":" + DMDOAIPrefix + "): "
+ ex.getMessage());
return errorSet;
}
if (testORE && OREOAIPrefix == null) {
errorSet.add(OAI_ORE_ERROR + ": The OAI server does not support ORE dissemination");
}
if (DMDOAIPrefix == null) {
errorSet.add(OAI_DMD_ERROR + ": The OAI server does not support dissemination in this format");
}
// Now scan the sets and make sure the one supplied is in the list
boolean foundSet = false;
try {
//If we do not want to harvest from one set, then skip this.
if (!"all".equals(oaiSetId)) {
ListIdentifiers ls = new ListIdentifiers(oaiSource, null, null, oaiSetId, DMDOAIPrefix);
// The only error we can really get here is "noSetHierarchy"
if (ls.getErrors() != null && ls.getErrors().getLength() > 0) {
for (int i = 0; i < ls.getErrors().getLength(); i++) {
String errorCode = ls.getErrors().item(i).getAttributes().getNamedItem("code").getTextContent();
errorSet.add(
OAI_SET_ERROR + ": The OAI server does not have a set with the specified setSpec (" +
errorCode + ")");
}
} else {
// Drilling down to /OAI-PMH/ListSets/set
Document reply = db.build(ls.getDocument());
Element root = reply.getRootElement();
//Check if we can find items, if so this indicates that we have children and our sets exist
foundSet = 0 < root.getChild("ListIdentifiers", OAI_NS).getChildren().size();
if (!foundSet) {
errorSet.add(OAI_SET_ERROR + ": The OAI server does not have a set with the specified setSpec");
}
}
} }
} catch (RuntimeException re) {
throw re; Map<String,String> config = new HashMap<>();
} catch (Exception e) { config.put("id", id);
errorSet.add(OAI_ADDRESS_ERROR + ": OAI server could not be reached"); config.put("label", label);
return errorSet; config.put("namespace", namespace);
configs.add(config);
} }
return errorSet; return configs;
} }
} }

View File

@@ -137,4 +137,15 @@ public interface HarvestedCollectionService {
public void update(Context context, HarvestedCollection harvestedCollection) throws SQLException; public void update(Context context, HarvestedCollection harvestedCollection) throws SQLException;
public boolean exists(Context context) throws SQLException; public boolean exists(Context context) throws SQLException;
/**
* Test the given harvest settings
* @param oaiSource the address of the OAI-PMH provider
* @param oaiSetId OAI set identifier
* @param metaPrefix OAI metadataPrefix
* @param testORE whether the method should also check the PMH provider for ORE support
* @return list of errors encountered during verification. An empty list indicates a "success" condition.
*/
public List<String> verifyOAIharvester(String oaiSource,
String oaiSetId, String metaPrefix, boolean testORE);
} }

View 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/
--
-----------------------------------------------------------------------------------
-- Create columns leftwardValue and rightwardValue in table relationship
-- Rename columns left_label and right_label to leftward_type and rightward_type
-----------------------------------------------------------------------------------
ALTER TABLE relationship ADD leftward_value VARCHAR;
ALTER TABLE relationship ADD rightward_value VARCHAR;
ALTER TABLE relationship_type ALTER COLUMN left_label RENAME TO leftward_type;
ALTER TABLE relationship_type ALTER COLUMN right_label RENAME TO rightward_type;

View 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/
--
-----------------------------------------------------------------------------------
-- Create columns leftwardValue and rightwardValue in table relationship
-- Rename columns left_label and right_label to leftward_type and rightward_type
-----------------------------------------------------------------------------------
ALTER TABLE relationship ADD leftward_value VARCHAR;
ALTER TABLE relationship ADD rightward_value VARCHAR;
ALTER TABLE relationship_type RENAME left_label TO leftward_type;
ALTER TABLE relationship_type RENAME right_label TO rightward_type;

View 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/
--
-----------------------------------------------------------------------------------
-- Create columns leftwardValue and rightwardValue in table relationship
-- Rename columns left_label and right_label to leftward_type and rightward_type
-----------------------------------------------------------------------------------
ALTER TABLE relationship ADD leftward_value VARCHAR;
ALTER TABLE relationship ADD rightward_value VARCHAR;
ALTER TABLE relationship_type RENAME COLUMN left_label TO leftward_type;
ALTER TABLE relationship_type RENAME COLUMN right_label TO rightward_type;

View File

@@ -5,8 +5,8 @@
<type> <type>
<leftType>Publication</leftType> <leftType>Publication</leftType>
<rightType>Person</rightType> <rightType>Person</rightType>
<leftLabel>isAuthorOfPublication</leftLabel> <leftwardType>isAuthorOfPublication</leftwardType>
<rightLabel>isPublicationOfAuthor</rightLabel> <rightwardType>isPublicationOfAuthor</rightwardType>
<leftCardinality> <leftCardinality>
<min>10</min> <min>10</min>
</leftCardinality> </leftCardinality>
@@ -17,8 +17,8 @@
<type> <type>
<leftType>Publication</leftType> <leftType>Publication</leftType>
<rightType>Project</rightType> <rightType>Project</rightType>
<leftLabel>isProjectOfPublication</leftLabel> <leftwardType>isProjectOfPublication</leftwardType>
<rightLabel>isPublicationOfProject</rightLabel> <rightwardType>isPublicationOfProject</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -29,8 +29,8 @@
<type> <type>
<leftType>Publication</leftType> <leftType>Publication</leftType>
<rightType>OrgUnit</rightType> <rightType>OrgUnit</rightType>
<leftLabel>isOrgUnitOfPublication</leftLabel> <leftwardType>isOrgUnitOfPublication</leftwardType>
<rightLabel>isPublicationOfOrgUnit</rightLabel> <rightwardType>isPublicationOfOrgUnit</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -41,8 +41,8 @@
<type> <type>
<leftType>Person</leftType> <leftType>Person</leftType>
<rightType>Project</rightType> <rightType>Project</rightType>
<leftLabel>isProjectOfPerson</leftLabel> <leftwardType>isProjectOfPerson</leftwardType>
<rightLabel>isPersonOfProject</rightLabel> <rightwardType>isPersonOfProject</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -53,8 +53,8 @@
<type> <type>
<leftType>Person</leftType> <leftType>Person</leftType>
<rightType>OrgUnit</rightType> <rightType>OrgUnit</rightType>
<leftLabel>isOrgUnitOfPerson</leftLabel> <leftwardType>isOrgUnitOfPerson</leftwardType>
<rightLabel>isPersonOfOrgUnit</rightLabel> <rightwardType>isPersonOfOrgUnit</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -65,8 +65,8 @@
<type> <type>
<leftType>Project</leftType> <leftType>Project</leftType>
<rightType>OrgUnit</rightType> <rightType>OrgUnit</rightType>
<leftLabel>isOrgUnitOfProject</leftLabel> <leftwardType>isOrgUnitOfProject</leftwardType>
<rightLabel>isProjectOfOrgUnit</rightLabel> <rightwardType>isProjectOfOrgUnit</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -77,8 +77,8 @@
<type> <type>
<leftType>Journal</leftType> <leftType>Journal</leftType>
<rightType>JournalVolume</rightType> <rightType>JournalVolume</rightType>
<leftLabel>isVolumeOfJournal</leftLabel> <leftwardType>isVolumeOfJournal</leftwardType>
<rightLabel>isJournalOfVolume</rightLabel> <rightwardType>isJournalOfVolume</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -89,8 +89,8 @@
<type> <type>
<leftType>JournalVolume</leftType> <leftType>JournalVolume</leftType>
<rightType>JournalIssue</rightType> <rightType>JournalIssue</rightType>
<leftLabel>isIssueOfJournalVolume</leftLabel> <leftwardType>isIssueOfJournalVolume</leftwardType>
<rightLabel>isJournalVolumeOfIssue</rightLabel> <rightwardType>isJournalVolumeOfIssue</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -102,8 +102,8 @@
<type> <type>
<leftType>Publication</leftType> <leftType>Publication</leftType>
<rightType>OrgUnit</rightType> <rightType>OrgUnit</rightType>
<leftLabel>isAuthorOfPublication</leftLabel> <leftwardType>isAuthorOfPublication</leftwardType>
<rightLabel>isPublicationOfAuthor</rightLabel> <rightwardType>isPublicationOfAuthor</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -114,8 +114,8 @@
<type> <type>
<leftType>JournalIssue</leftType> <leftType>JournalIssue</leftType>
<rightType>Publication</rightType> <rightType>Publication</rightType>
<leftLabel>isPublicationOfJournalIssue</leftLabel> <leftwardType>isPublicationOfJournalIssue</leftwardType>
<rightLabel>isJournalIssueOfPublication</rightLabel> <rightwardType>isJournalIssueOfPublication</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>

View File

@@ -2,11 +2,11 @@
<!ELEMENT relationships (type)*> <!ELEMENT relationships (type)*>
<!ELEMENT type (leftType|rightType|leftLabel|rightLabel|leftCardinality|rightCardinality)*> <!ELEMENT type (leftType|rightType|leftwardType|rightwardType|leftCardinality|rightCardinality)*>
<!ELEMENT leftType (#PCDATA)> <!ELEMENT leftType (#PCDATA)>
<!ELEMENT rightType (#PCDATA)> <!ELEMENT rightType (#PCDATA)>
<!ELEMENT leftLabel (#PCDATA)> <!ELEMENT leftwardType (#PCDATA)>
<!ELEMENT rightLabel (#PCDATA)> <!ELEMENT rightwardType (#PCDATA)>
<!ELEMENT leftCardinality (min|max)*> <!ELEMENT leftCardinality (min|max)*>
<!ELEMENT min (#PCDATA)> <!ELEMENT min (#PCDATA)>
<!ELEMENT rightCardinality (min|max)*> <!ELEMENT rightCardinality (min|max)*>

View File

@@ -5,8 +5,8 @@
<type> <type>
<leftType>Publication</leftType> <leftType>Publication</leftType>
<rightType>Person</rightType> <rightType>Person</rightType>
<leftLabel>isAuthorOfPublication</leftLabel> <leftwardType>isAuthorOfPublication</leftwardType>
<rightLabel>isPublicationOfAuthor</rightLabel> <rightwardType>isPublicationOfAuthor</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -17,8 +17,8 @@
<type> <type>
<leftType>Publication</leftType> <leftType>Publication</leftType>
<rightType>Project</rightType> <rightType>Project</rightType>
<leftLabel>isProjectOfPublication</leftLabel> <leftwardType>isProjectOfPublication</leftwardType>
<rightLabel>isPublicationOfProject</rightLabel> <rightwardType>isPublicationOfProject</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -29,8 +29,8 @@
<type> <type>
<leftType>Publication</leftType> <leftType>Publication</leftType>
<rightType>OrgUnit</rightType> <rightType>OrgUnit</rightType>
<leftLabel>isOrgUnitOfPublication</leftLabel> <leftwardType>isOrgUnitOfPublication</leftwardType>
<rightLabel>isPublicationOfOrgUnit</rightLabel> <rightwardType>isPublicationOfOrgUnit</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -41,8 +41,8 @@
<type> <type>
<leftType>Person</leftType> <leftType>Person</leftType>
<rightType>Project</rightType> <rightType>Project</rightType>
<leftLabel>isProjectOfPerson</leftLabel> <leftwardType>isProjectOfPerson</leftwardType>
<rightLabel>isPersonOfProject</rightLabel> <rightwardType>isPersonOfProject</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -53,8 +53,8 @@
<type> <type>
<leftType>Person</leftType> <leftType>Person</leftType>
<rightType>OrgUnit</rightType> <rightType>OrgUnit</rightType>
<leftLabel>isOrgUnitOfPerson</leftLabel> <leftwardType>isOrgUnitOfPerson</leftwardType>
<rightLabel>isPersonOfOrgUnit</rightLabel> <rightwardType>isPersonOfOrgUnit</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -65,8 +65,8 @@
<type> <type>
<leftType>Project</leftType> <leftType>Project</leftType>
<rightType>OrgUnit</rightType> <rightType>OrgUnit</rightType>
<leftLabel>isOrgUnitOfProject</leftLabel> <leftwardType>isOrgUnitOfProject</leftwardType>
<rightLabel>isProjectOfOrgUnit</rightLabel> <rightwardType>isProjectOfOrgUnit</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -77,8 +77,8 @@
<type> <type>
<leftType>Journal</leftType> <leftType>Journal</leftType>
<rightType>JournalVolume</rightType> <rightType>JournalVolume</rightType>
<leftLabel>isVolumeOfJournal</leftLabel> <leftwardType>isVolumeOfJournal</leftwardType>
<rightLabel>isJournalOfVolume</rightLabel> <rightwardType>isJournalOfVolume</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -89,8 +89,8 @@
<type> <type>
<leftType>JournalVolume</leftType> <leftType>JournalVolume</leftType>
<rightType>JournalIssue</rightType> <rightType>JournalIssue</rightType>
<leftLabel>isIssueOfJournalVolume</leftLabel> <leftwardType>isIssueOfJournalVolume</leftwardType>
<rightLabel>isJournalVolumeOfIssue</rightLabel> <rightwardType>isJournalVolumeOfIssue</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -102,8 +102,8 @@
<type> <type>
<leftType>Publication</leftType> <leftType>Publication</leftType>
<rightType>OrgUnit</rightType> <rightType>OrgUnit</rightType>
<leftLabel>isAuthorOfPublication</leftLabel> <leftwardType>isAuthorOfPublication</leftwardType>
<rightLabel>isPublicationOfAuthor</rightLabel> <rightwardType>isPublicationOfAuthor</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>
@@ -114,8 +114,8 @@
<type> <type>
<leftType>JournalIssue</leftType> <leftType>JournalIssue</leftType>
<rightType>Publication</rightType> <rightType>Publication</rightType>
<leftLabel>isPublicationOfJournalIssue</leftLabel> <leftwardType>isPublicationOfJournalIssue</leftwardType>
<rightLabel>isJournalIssueOfPublication</rightLabel> <rightwardType>isJournalIssueOfPublication</rightwardType>
<leftCardinality> <leftCardinality>
<min>0</min> <min>0</min>
</leftCardinality> </leftCardinality>

View File

@@ -145,15 +145,15 @@ public class EntityServiceImplTest {
List<Relationship> relationshipList = new ArrayList<>(); List<Relationship> relationshipList = new ArrayList<>();
relationshipList.add(relationship); relationshipList.add(relationship);
// Mock the state of objects utilized in getRelationsByLabel() to meet the success criteria of an invocation // Mock the state of objects utilized in getRelationsByType() to meet the success criteria of an invocation
when(relationshipService.findAll(context)).thenReturn(relationshipList); when(relationshipService.findAll(context)).thenReturn(relationshipList);
when(relationship.getRelationshipType()).thenReturn(relationshipType); when(relationship.getRelationshipType()).thenReturn(relationshipType);
when(relationshipType.getLeftLabel()).thenReturn("leftLabel"); when(relationshipType.getLeftwardType()).thenReturn("leftwardType");
when(relationshipType.getRightLabel()).thenReturn("rightLabel"); when(relationshipType.getRightwardType()).thenReturn("rightwardType");
// The relation(s) reported from our defined label should match our relationshipList // The relation(s) reported from our defined type should match our relationshipList
assertEquals("TestGetRelationsByLabel 0", relationshipList, assertEquals("TestGetRelationsByLabel 0", relationshipList,
entityService.getRelationsByLabel(context, "leftLabel")); entityService.getRelationsByLabel(context, "leftwardType"));
} }
@Test @Test
@@ -260,15 +260,15 @@ public class EntityServiceImplTest {
RelationshipType relationshipType = mock(RelationshipType.class); RelationshipType relationshipType = mock(RelationshipType.class);
list.add(relationshipType); list.add(relationshipType);
// Mock the state of objects utilized in getRelationshipTypesByLabel() // Mock the state of objects utilized in getRelationshipTypesByTypeName()
// to meet the success criteria of the invocation // to meet the success criteria of the invocation
when(relationshipTypeService.findAll(context)).thenReturn(list); when(relationshipTypeService.findAll(context)).thenReturn(list);
when(relationshipType.getLeftLabel()).thenReturn("leftLabel"); when(relationshipType.getLeftwardType()).thenReturn("leftwardType");
when(relationshipType.getRightLabel()).thenReturn("rightLabel"); when(relationshipType.getRightwardType()).thenReturn("rightwardType");
// The RelationshipType(s) reported from our mocked Entity should match our list // The RelationshipType(s) reported from our mocked Entity should match our list
assertEquals("TestGetRelationshipTypesByLabel 0", list, assertEquals("TestGetRelationshipTypesByLabel 0", list,
entityService.getRelationshipTypesByLabel(context, "leftLabel")); entityService.getRelationshipTypesByTypeName(context, "leftwardType"));
} }

View File

@@ -0,0 +1,142 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.content;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.sql.SQLException;
import java.util.List;
import org.apache.logging.log4j.Logger;
import org.dspace.AbstractUnitTest;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.CommunityService;
import org.dspace.content.service.EntityService;
import org.dspace.content.service.EntityTypeService;
import org.dspace.content.service.InstallItemService;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.RelationshipService;
import org.dspace.content.service.RelationshipTypeService;
import org.dspace.content.service.WorkspaceItemService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class RelationshipMetadataServiceTest extends AbstractUnitTest {
private static final Logger log = org.apache.logging.log4j.LogManager
.getLogger(RelationshipMetadataServiceTest.class);
protected RelationshipMetadataService relationshipMetadataService = ContentServiceFactory
.getInstance().getRelationshipMetadataService();
protected RelationshipService relationshipService = ContentServiceFactory.getInstance().getRelationshipService();
protected RelationshipTypeService relationshipTypeService = ContentServiceFactory.getInstance()
.getRelationshipTypeService();
protected EntityService entityService = ContentServiceFactory.getInstance().getEntityService();
protected EntityTypeService entityTypeService = ContentServiceFactory.getInstance().getEntityTypeService();
protected CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService();
protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
protected ItemService itemService = ContentServiceFactory.getInstance().getItemService();
protected InstallItemService installItemService = ContentServiceFactory.getInstance().getInstallItemService();
protected WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService();
Item item;
Item authorItem;
/**
* This method will be run before every test as per @Before. It will
* initialize resources required for the tests.
*
* Other methods can be annotated with @Before here or in subclasses
* but no execution order is guaranteed
*/
@Before
@Override
public void init() {
super.init();
try {
context.turnOffAuthorisationSystem();
Community community = communityService.create(null, context);
Collection col = collectionService.create(context, community);
WorkspaceItem is = workspaceItemService.create(context, col, false);
WorkspaceItem authorIs = workspaceItemService.create(context, col, false);
item = installItemService.installItem(context, is);
authorItem = installItemService.installItem(context, authorIs);
context.restoreAuthSystemState();
} catch (AuthorizeException ex) {
log.error("Authorization Error in init", ex);
fail("Authorization Error in init: " + ex.getMessage());
} catch (SQLException ex) {
log.error("SQL Error in init", ex);
fail("SQL Error in init: " + ex.getMessage());
}
}
/**
* This method will be run after every test as per @After. It will
* clean resources initialized by the @Before methods.
*
* Other methods can be annotated with @After here or in subclasses
* but no execution order is guaranteed
*/
@After
@Override
public void destroy() {
context.abort();
super.destroy();
}
@Test
public void testGetRelationshipMetadata() throws Exception {
context.turnOffAuthorisationSystem();
itemService.addMetadata(context, item, "relationship", "type", null, null, "Publication");
itemService.addMetadata(context, authorItem, "relationship", "type", null, null, "Author");
itemService.addMetadata(context, authorItem, "person", "familyName", null, null, "familyName");
itemService.addMetadata(context, authorItem, "person", "givenName", null, null, "firstName");
EntityType publicationEntityType = entityTypeService.create(context, "Publication");
EntityType authorEntityType = entityTypeService.create(context, "Author");
RelationshipType isAuthorOfPublication = relationshipTypeService
.create(context, publicationEntityType, authorEntityType, "isAuthorOfPublication", "isPublicationOfAuthor",
null, null, null, null);
Relationship relationship = relationshipService.create(context, item, authorItem, isAuthorOfPublication, 0, 0);
context.restoreAuthSystemState();
List<MetadataValue> authorList = itemService.getMetadata(item, "dc", "contributor", "author", Item.ANY);
assertThat(authorList.size(), equalTo(1));
assertThat(authorList.get(0).getValue(), equalTo("familyName, firstName"));
List<MetadataValue> relationshipMetadataList = itemService
.getMetadata(item, "relation", "isAuthorOfPublication", null, Item.ANY);
assertThat(relationshipMetadataList.size(), equalTo(1));
assertThat(relationshipMetadataList.get(0).getValue(), equalTo(String.valueOf(authorItem.getID())));
List<RelationshipMetadataValue> list = relationshipMetadataService.getRelationshipMetadata(item, true);
assertThat(list.size(), equalTo(2));
assertThat(list.get(0).getValue(), equalTo("familyName, firstName"));
assertThat(list.get(0).getMetadataField().getMetadataSchema().getName(), equalTo("dc"));
assertThat(list.get(0).getMetadataField().getElement(), equalTo("contributor"));
assertThat(list.get(0).getMetadataField().getQualifier(), equalTo("author"));
assertThat(list.get(0).getAuthority(), equalTo("virtual::" + relationship.getID()));
assertThat(list.get(1).getValue(), equalTo(String.valueOf(authorItem.getID())));
assertThat(list.get(1).getMetadataField().getMetadataSchema().getName(), equalTo("relation"));
assertThat(list.get(1).getMetadataField().getElement(), equalTo("isAuthorOfPublication"));
assertThat(list.get(1).getAuthority(), equalTo("virtual::" + relationship.getID()));
}
}

View File

@@ -84,12 +84,12 @@ public class RelationshipServiceImplTest {
RelationshipType hasDog = new RelationshipType(); RelationshipType hasDog = new RelationshipType();
RelationshipType hasFather = new RelationshipType(); RelationshipType hasFather = new RelationshipType();
RelationshipType hasMother = new RelationshipType(); RelationshipType hasMother = new RelationshipType();
hasDog.setLeftLabel("hasDog"); hasDog.setLeftwardType("hasDog");
hasDog.setRightLabel("isDogOf"); hasDog.setRightwardType("isDogOf");
hasFather.setLeftLabel("hasFather"); hasFather.setLeftwardType("hasFather");
hasFather.setRightLabel("isFatherOf"); hasFather.setRightwardType("isFatherOf");
hasMother.setLeftLabel("hasMother"); hasMother.setLeftwardType("hasMother");
hasMother.setRightLabel("isMotherOf"); hasMother.setRightwardType("isMotherOf");
relationshipTest.add(getRelationship(cindy, spot, hasDog,0,0)); relationshipTest.add(getRelationship(cindy, spot, hasDog,0,0));
relationshipTest.add(getRelationship(cindy, jasper, hasDog,0,1)); relationshipTest.add(getRelationship(cindy, jasper, hasDog,0,1));
@@ -194,8 +194,8 @@ public class RelationshipServiceImplTest {
EntityType rightEntityType = mock(EntityType.class); EntityType rightEntityType = mock(EntityType.class);
testRel.setLeftType(leftEntityType); testRel.setLeftType(leftEntityType);
testRel.setRightType(rightEntityType); testRel.setRightType(rightEntityType);
testRel.setLeftLabel("Entitylabel"); testRel.setLeftwardType("Entitylabel");
testRel.setRightLabel("Entitylabel"); testRel.setRightwardType("Entitylabel");
metsList.add(metVal); metsList.add(metVal);
relationship = getRelationship(leftItem, rightItem, testRel, 0,0); relationship = getRelationship(leftItem, rightItem, testRel, 0,0);
leftTypelist.add(relationship); leftTypelist.add(relationship);
@@ -247,8 +247,8 @@ public class RelationshipServiceImplTest {
EntityType rightEntityType = mock(EntityType.class); EntityType rightEntityType = mock(EntityType.class);
testRel.setLeftType(leftEntityType); testRel.setLeftType(leftEntityType);
testRel.setRightType(rightEntityType); testRel.setRightType(rightEntityType);
testRel.setLeftLabel("Entitylabel"); testRel.setLeftwardType("Entitylabel");
testRel.setRightLabel("Entitylabel"); testRel.setRightwardType("Entitylabel");
testRel.setLeftMinCardinality(0); testRel.setLeftMinCardinality(0);
testRel.setRightMinCardinality(0); testRel.setRightMinCardinality(0);
metsList.add(metVal); metsList.add(metVal);
@@ -299,8 +299,8 @@ public class RelationshipServiceImplTest {
EntityType rightEntityType = mock(EntityType.class); EntityType rightEntityType = mock(EntityType.class);
testRel.setLeftType(leftEntityType); testRel.setLeftType(leftEntityType);
testRel.setRightType(rightEntityType); testRel.setRightType(rightEntityType);
testRel.setLeftLabel("Entitylabel"); testRel.setLeftwardType("Entitylabel");
testRel.setRightLabel("Entitylabel"); testRel.setRightwardType("Entitylabel");
testRel.setLeftMinCardinality(0); testRel.setLeftMinCardinality(0);
testRel.setRightMinCardinality(0); testRel.setRightMinCardinality(0);
metsList.add(metVal); metsList.add(metVal);

View File

@@ -51,8 +51,8 @@ public class RelationshipTypeTest {
firstRelationshipType.setId(1); firstRelationshipType.setId(1);
firstRelationshipType.setLeftType(mock(EntityType.class)); firstRelationshipType.setLeftType(mock(EntityType.class));
firstRelationshipType.setRightType(mock(EntityType.class)); firstRelationshipType.setRightType(mock(EntityType.class));
firstRelationshipType.setLeftLabel("isAuthorOfPublication"); firstRelationshipType.setLeftwardType("isAuthorOfPublication");
firstRelationshipType.setRightLabel("isPublicationOfAuthor"); firstRelationshipType.setRightwardType("isPublicationOfAuthor");
firstRelationshipType.setLeftMinCardinality(0); firstRelationshipType.setLeftMinCardinality(0);
firstRelationshipType.setLeftMaxCardinality(null); firstRelationshipType.setLeftMaxCardinality(null);
firstRelationshipType.setRightMinCardinality(0); firstRelationshipType.setRightMinCardinality(0);
@@ -63,8 +63,8 @@ public class RelationshipTypeTest {
secondRelationshipType.setId(new Random().nextInt()); secondRelationshipType.setId(new Random().nextInt());
secondRelationshipType.setLeftType(mock(EntityType.class)); secondRelationshipType.setLeftType(mock(EntityType.class));
secondRelationshipType.setRightType(mock(EntityType.class)); secondRelationshipType.setRightType(mock(EntityType.class));
secondRelationshipType.setLeftLabel("isProjectOfPerson"); secondRelationshipType.setLeftwardType("isProjectOfPerson");
secondRelationshipType.setRightLabel("isPersonOfProject"); secondRelationshipType.setRightwardType("isPersonOfProject");
secondRelationshipType.setLeftMinCardinality(0); secondRelationshipType.setLeftMinCardinality(0);
secondRelationshipType.setLeftMaxCardinality(null); secondRelationshipType.setLeftMaxCardinality(null);
secondRelationshipType.setRightMinCardinality(0); secondRelationshipType.setRightMinCardinality(0);
@@ -118,16 +118,16 @@ public class RelationshipTypeTest {
} }
@Test @Test
public void testRelationshipTypeFindByLeftOrRightLabel() throws Exception { public void testRelationshipTypeFindByLeftOrRightwardType() throws Exception {
// Declare objects utilized for this test // Declare objects utilized for this test
List<RelationshipType> mockedList = new LinkedList<>(); List<RelationshipType> mockedList = new LinkedList<>();
mockedList.add(firstRelationshipType); mockedList.add(firstRelationshipType);
// Mock DAO to return our mockedList // Mock DAO to return our mockedList
when(relationshipTypeDAO.findByLeftOrRightLabel(any(), any())).thenReturn(mockedList); when(relationshipTypeDAO.findByLeftwardOrRightwardTypeName(any(), any())).thenReturn(mockedList);
// Invoke findByLeftOrRightLabel() // Invoke findByLeftwardOrRightwardTypeName()
List<RelationshipType> found = relationshipTypeService.findByLeftOrRightLabel(context, "mock"); List<RelationshipType> found = relationshipTypeService.findByLeftwardOrRightwardTypeName(context, "mock");
// Assert that our expected list contains our expected RelationshipType and nothing more // Assert that our expected list contains our expected RelationshipType and nothing more
assertThat(found, notNullValue()); assertThat(found, notNullValue());
@@ -160,8 +160,8 @@ public class RelationshipTypeTest {
*/ */
private void checkRelationshipTypeValues(RelationshipType found, RelationshipType original) { private void checkRelationshipTypeValues(RelationshipType found, RelationshipType original) {
assertThat(found, notNullValue()); assertThat(found, notNullValue());
assertThat(found.getLeftLabel(), equalTo(original.getLeftLabel())); assertThat(found.getLeftwardType(), equalTo(original.getLeftwardType()));
assertThat(found.getRightLabel(), equalTo(original.getRightLabel())); assertThat(found.getRightwardType(), equalTo(original.getRightwardType()));
assertThat(found.getLeftType(), equalTo(original.getLeftType())); assertThat(found.getLeftType(), equalTo(original.getLeftType()));
assertThat(found.getRightType(), equalTo(original.getRightType())); assertThat(found.getRightType(), equalTo(original.getRightType()));
assertThat(found.getLeftMinCardinality(), equalTo(original.getLeftMinCardinality())); assertThat(found.getLeftMinCardinality(), equalTo(original.getLeftMinCardinality()));

View File

@@ -124,15 +124,15 @@ public class RelatedTest {
Entity entity = mock(Entity.class); Entity entity = mock(Entity.class);
EntityType entityType = mock(EntityType.class); EntityType entityType = mock(EntityType.class);
RelationshipType relationshipType = mock(RelationshipType.class); RelationshipType relationshipType = mock(RelationshipType.class);
related.setRelationshipTypeString("LeftLabel"); related.setRelationshipTypeString("LeftwardType");
relationshipTypeList.add(relationshipType); relationshipTypeList.add(relationshipType);
relationshipList.add(relationship); relationshipList.add(relationship);
related.setPlace(0); related.setPlace(0);
// Mock the state of objects utilized in getRelationsByLabel() to meet the success criteria of an invocation // Mock the state of objects utilized in getRelationsByLabel() to meet the success criteria of an invocation
when(item.getID()).thenReturn(UUID.randomUUID()); when(item.getID()).thenReturn(UUID.randomUUID());
when(relationshipType.getLeftLabel()).thenReturn("LeftLabel"); when(relationshipType.getLeftwardType()).thenReturn("LeftwardType");
when(relationshipType.getRightLabel()).thenReturn("RightLabel"); when(relationshipType.getRightwardType()).thenReturn("RightwardType");
when(relationshipType.getLeftType()).thenReturn(entityType); when(relationshipType.getLeftType()).thenReturn(entityType);
when(relationshipType.getRightType()).thenReturn(entityType); when(relationshipType.getRightType()).thenReturn(entityType);
when(entityService.getAllRelationshipTypes(context, entity)).thenReturn(relationshipTypeList); when(entityService.getAllRelationshipTypes(context, entity)).thenReturn(relationshipTypeList);

View File

@@ -62,15 +62,15 @@ public class VirtualMetadataPopulatorTest {
HashMap<String, VirtualMetadataConfiguration> mapExt = new HashMap<>(); HashMap<String, VirtualMetadataConfiguration> mapExt = new HashMap<>();
VirtualMetadataConfiguration virtualMetadataConfiguration = mock(VirtualMetadataConfiguration.class); VirtualMetadataConfiguration virtualMetadataConfiguration = mock(VirtualMetadataConfiguration.class);
mapExt.put("hashKey", virtualMetadataConfiguration); mapExt.put("hashKey", virtualMetadataConfiguration);
map.put("LeftLabel", mapExt); map.put("LeftwardType", mapExt);
map.put("NotRightLabel", mapExt); map.put("NotRightwardType", mapExt);
virtualMetadataPopulator.setMap(map); virtualMetadataPopulator.setMap(map);
// Mock the state of objects utilized in isUseForPlaceTrueForRelationshipType() // Mock the state of objects utilized in isUseForPlaceTrueForRelationshipType()
// to meet the success criteria of an invocation // to meet the success criteria of an invocation
when(virtualMetadataConfiguration.getUseForPlace()).thenReturn(true); when(virtualMetadataConfiguration.getUseForPlace()).thenReturn(true);
when(relationshipType.getLeftLabel()).thenReturn("LeftLabel"); when(relationshipType.getLeftwardType()).thenReturn("LeftwardType");
when(relationshipType.getRightLabel()).thenReturn("RightLabel"); when(relationshipType.getRightwardType()).thenReturn("RightwardType");
// Assert that the useForPlace for our mocked relationshipType is false // Assert that the useForPlace for our mocked relationshipType is false
assertEquals("TestGetFields 0", false, assertEquals("TestGetFields 0", false,

View File

@@ -0,0 +1,30 @@
/**
* 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.harvest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* A mock for the HarvestedCollectionService
* @author Jelle Pelgrims (jelle.pelgrims at atmire.com)
*/
public class MockHarvestedCollectionServiceImpl extends HarvestedCollectionServiceImpl {
@Override
public List<String> verifyOAIharvester(String oaiSource,
String oaiSetId, String metaPrefix, boolean testORE) {
if (metaPrefix.equals("dc")) {
return new ArrayList<>();
} else {
return Arrays.asList("(Mock error) Incorrect metadataConfigID");
}
}
}

View File

@@ -0,0 +1,125 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest;
import java.sql.SQLException;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.dspace.app.rest.converter.HarvestedCollectionConverter;
import org.dspace.app.rest.link.HalLinkService;
import org.dspace.app.rest.model.HarvestedCollectionRest;
import org.dspace.app.rest.model.hateoas.HarvestedCollectionResource;
import org.dspace.app.rest.repository.HarvestedCollectionRestRepository;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.content.Collection;
import org.dspace.content.service.CollectionService;
import org.dspace.core.Context;
import org.dspace.harvest.service.HarvestedCollectionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* Rest controller that handles the harvest settings for collections
*
* @author Jelle Pelgrims (jelle.pelgrims at atmire.com)
*/
@RestController
@RequestMapping("/api/core/collections/" +
"{collectionUuid:[0-9a-fxA-FX]{8}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{12" +
"}}/harvester")
public class CollectionHarvestSettingsController {
@Autowired
HarvestedCollectionConverter harvestedCollectionConverter;
@Autowired
CollectionService collectionService;
@Autowired
HarvestedCollectionService harvestedCollectionService;
@Autowired
private HalLinkService halLinkService;
@Autowired
HarvestedCollectionRestRepository harvestedCollectionRestRepository;
@Autowired
private Utils utils;
/**
* GET endpoint that returns the harvest settings of the given collection
* @param request The request object
* @param response The response object
* @return a HarvesterMetadataResource containing all available metadata formats
*/
@PreAuthorize("hasPermission(#collectionUuid, 'COLLECTION', 'WRITE')")
@RequestMapping(method = RequestMethod.GET)
public HarvestedCollectionResource get(@PathVariable UUID collectionUuid,
HttpServletRequest request,
HttpServletResponse response) throws SQLException {
Context context = ContextUtil.obtainContext(request);
Collection collection = collectionService.find(context, collectionUuid);
if (collection == null) {
throw new ResourceNotFoundException("Collection with uuid: " + collectionUuid + " not found");
}
HarvestedCollectionRest harvestedCollectionRest = harvestedCollectionRestRepository.findOne(collection);
HarvestedCollectionResource resource = new HarvestedCollectionResource(harvestedCollectionRest);
halLinkService.addLinks(resource);
return resource;
}
/**
* PUT Endpoint for updating the settings of a collection.
*
* @param collectionUuid The collection whose settings should be changed
* @param response The response object
* @param request The request object
* @throws SQLException
*/
@PreAuthorize("hasPermission(#collectionUuid, 'COLLECTION', 'WRITE')")
@RequestMapping(method = RequestMethod.PUT, consumes = {"application/json"})
public HarvestedCollectionResource updateHarvestSettingsEndpoint(@PathVariable UUID collectionUuid,
HttpServletResponse response,
HttpServletRequest request) throws SQLException {
Context context = ContextUtil.obtainContext(request);
Collection collection = collectionService.find(context, collectionUuid);
HarvestedCollectionResource harvestedCollectionResource = null;
if (collection == null) {
throw new ResourceNotFoundException("Collection with uuid: " + collectionUuid + " not found");
}
HarvestedCollectionRest harvestedCollectionRest =
harvestedCollectionRestRepository.update(context, request, collection);
// Return a harvestedCollectionResource only if a new harvestedCollection was created
if (harvestedCollectionRest != null) {
harvestedCollectionResource = new HarvestedCollectionResource(harvestedCollectionRest);
halLinkService.addLinks(harvestedCollectionResource);
}
context.commit();
return harvestedCollectionResource;
}
}

View 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.app.rest;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.dspace.app.rest.link.HalLinkService;
import org.dspace.app.rest.model.HarvesterMetadataRest;
import org.dspace.app.rest.model.hateoas.HarvesterMetadataResource;
import org.dspace.app.rest.utils.Utils;
import org.dspace.harvest.OAIHarvester;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* Rest controller that handles the harvesting metadata formats
*
* @author Jelle Pelgrims (jelle.pelgrims at atmire.com)
*/
@RestController
@RequestMapping("/api/config/harvestermetadata")
public class HarvesterMetadataController {
@Autowired
Utils utils;
@Autowired
private HalLinkService halLinkService;
/**
* GET endpoint that returns all available metadata formats
* @param request The request object
* @param response The response object
* @return a HarvesterMetadataResource containing all available metadata formats
*/
@RequestMapping(method = RequestMethod.GET)
public HarvesterMetadataResource get(HttpServletRequest request,
HttpServletResponse response) {
List<Map<String,String>> configs = OAIHarvester.getAvailableMetadataFormats();
HarvesterMetadataRest data = new HarvesterMetadataRest();
data.setConfigs(configs);
HarvesterMetadataResource resource = new HarvesterMetadataResource(data, utils);
halLinkService.addLinks(resource);
return resource;
}
}

View File

@@ -51,6 +51,7 @@ import org.dspace.app.rest.repository.LinkRestRepository;
import org.dspace.app.rest.utils.RestRepositoryUtils; import org.dspace.app.rest.utils.RestRepositoryUtils;
import org.dspace.app.rest.utils.Utils; import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.AuthorizeException;
import org.dspace.util.UUIDUtils;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
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;
@@ -390,14 +391,17 @@ public class RestResourceController implements InitializingBean {
* @param request The relevant request * @param request The relevant request
* @param apiCategory The apiCategory to be used * @param apiCategory The apiCategory to be used
* @param model The model to be used * @param model The model to be used
* @param parent Optional parent identifier
* @return The relevant ResponseEntity for this request * @return The relevant ResponseEntity for this request
* @throws HttpRequestMethodNotSupportedException If something goes wrong * @throws HttpRequestMethodNotSupportedException If something goes wrong
*/ */
@RequestMapping(method = RequestMethod.POST, consumes = {"application/json", "application/hal+json"}) @RequestMapping(method = RequestMethod.POST, consumes = {"application/json", "application/hal+json"})
public ResponseEntity<ResourceSupport> post(HttpServletRequest request, @PathVariable String apiCategory, public ResponseEntity<ResourceSupport> post(HttpServletRequest request,
@PathVariable String model) @PathVariable String apiCategory,
@PathVariable String model,
@RequestParam(required = false) String parent)
throws HttpRequestMethodNotSupportedException { throws HttpRequestMethodNotSupportedException {
return postJsonInternal(request, apiCategory, model); return postJsonInternal(request, apiCategory, model, parent);
} }
/** /**
@@ -432,16 +436,24 @@ public class RestResourceController implements InitializingBean {
* @param request The relevant request * @param request The relevant request
* @param apiCategory The apiCategory to be used * @param apiCategory The apiCategory to be used
* @param model The model to be used * @param model The model to be used
* @param parent The parent object id (optional)
* @return The relevant ResponseEntity for this request * @return The relevant ResponseEntity for this request
* @throws HttpRequestMethodNotSupportedException If something goes wrong * @throws HttpRequestMethodNotSupportedException If something goes wrong
*/ */
public <ID extends Serializable> ResponseEntity<ResourceSupport> postJsonInternal(HttpServletRequest request, public <ID extends Serializable> ResponseEntity<ResourceSupport> postJsonInternal(HttpServletRequest request,
String apiCategory, String apiCategory,
String model) String model, String parent)
throws HttpRequestMethodNotSupportedException { throws HttpRequestMethodNotSupportedException {
checkModelPluralForm(apiCategory, model); checkModelPluralForm(apiCategory, model);
DSpaceRestRepository<RestAddressableModel, ID> repository = utils.getResourceRepository(apiCategory, model); DSpaceRestRepository<RestAddressableModel, ID> repository = utils.getResourceRepository(apiCategory, model);
RestAddressableModel modelObject = repository.createAndReturn();
RestAddressableModel modelObject;
if (parent != null) {
UUID parentUuid = UUIDUtils.fromString(parent);
modelObject = repository.createAndReturn(parentUuid);
} else {
modelObject = repository.createAndReturn();
}
if (modelObject == null) { if (modelObject == null) {
return ControllerUtils.toEmptyResponse(HttpStatus.CREATED); return ControllerUtils.toEmptyResponse(HttpStatus.CREATED);
} }

View File

@@ -0,0 +1,82 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.converter;
import java.util.List;
import java.util.Map;
import org.dspace.app.rest.model.HarvestStatusEnum;
import org.dspace.app.rest.model.HarvestTypeEnum;
import org.dspace.app.rest.model.HarvestedCollectionRest;
import org.dspace.app.rest.model.HarvesterMetadataRest;
import org.dspace.content.Collection;
import org.dspace.harvest.HarvestedCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
/**
* This is the converter from/to the HarvestedCollection in the DSpace API data model and the REST data model
*
* @author Jelle Pelgrims (jelle.pelgrims at atmire.com)
*/
@Component
public class HarvestedCollectionConverter implements DSpaceConverter<HarvestedCollection, HarvestedCollectionRest> {
@Autowired
private CollectionConverter collectionConverter;
@Override
public HarvestedCollectionRest fromModel(HarvestedCollection obj) {
HarvestedCollectionRest harvestedCollectionRest = new HarvestedCollectionRest();
if (obj != null) {
HarvestTypeEnum harvestTypeEnum = HarvestTypeEnum.fromInt(obj.getHarvestType());
HarvestStatusEnum harvestStatusEnum = HarvestStatusEnum.fromInt(obj.getHarvestStatus());
harvestedCollectionRest.setId(obj.getID());
harvestedCollectionRest.setCollection(collectionConverter.fromModel(obj.getCollection()));
harvestedCollectionRest.setHarvestType(harvestTypeEnum);
harvestedCollectionRest.setHarvestStatus(harvestStatusEnum);
harvestedCollectionRest.setMetadataConfigId(obj.getHarvestMetadataConfig());
harvestedCollectionRest.setOaiSetId(obj.getOaiSetId());
harvestedCollectionRest.setOaiSource(obj.getOaiSource());
harvestedCollectionRest.setHarvestMessage(obj.getHarvestMessage());
harvestedCollectionRest.setHarvestStartTime(obj.getHarvestStartTime());
harvestedCollectionRest.setLastHarvested(obj.getHarvestDate());
} else {
harvestedCollectionRest.setHarvestType(HarvestTypeEnum.NONE);
}
return harvestedCollectionRest;
}
public HarvestedCollectionRest fromModel(HarvestedCollection obj,
Collection collection,
List<Map<String,String>> metadata_configs) {
HarvestedCollectionRest harvestedCollectionRest = this.fromModel(obj);
// Add collectionRest to the empty HarvestedCollectionRest so that we can use its uuid later in the linkFactory
if (obj == null) {
harvestedCollectionRest.setCollection(collectionConverter.fromModel(collection));
}
HarvesterMetadataRest harvesterMetadataRest = new HarvesterMetadataRest();
harvesterMetadataRest.setConfigs(metadata_configs);
harvestedCollectionRest.setMetadataConfigs(harvesterMetadataRest);
return harvestedCollectionRest;
}
@Override
public HarvestedCollection toModel(HarvestedCollectionRest obj) {
throw new NotImplementedException();
}
}

View File

@@ -38,6 +38,8 @@ public class RelationshipConverter implements DSpaceConverter<Relationship, Rela
relationshipRest.setRightId(obj.getRightItem().getID()); relationshipRest.setRightId(obj.getRightItem().getID());
relationshipRest.setLeftPlace(obj.getLeftPlace()); relationshipRest.setLeftPlace(obj.getLeftPlace());
relationshipRest.setRightPlace(obj.getRightPlace()); relationshipRest.setRightPlace(obj.getRightPlace());
relationshipRest.setLeftwardValue(obj.getLeftwardValue());
relationshipRest.setRightwardValue(obj.getRightwardValue());
return relationshipRest; return relationshipRest;
} }

View File

@@ -32,8 +32,8 @@ public class RelationshipTypeConverter implements DSpaceConverter<RelationshipTy
RelationshipTypeRest relationshipTypeRest = new RelationshipTypeRest(); RelationshipTypeRest relationshipTypeRest = new RelationshipTypeRest();
relationshipTypeRest.setId(obj.getID()); relationshipTypeRest.setId(obj.getID());
relationshipTypeRest.setLeftLabel(obj.getLeftLabel()); relationshipTypeRest.setLeftwardType(obj.getLeftwardType());
relationshipTypeRest.setRightLabel(obj.getRightLabel()); relationshipTypeRest.setRightwardType(obj.getRightwardType());
relationshipTypeRest.setLeftMinCardinality(obj.getLeftMinCardinality()); relationshipTypeRest.setLeftMinCardinality(obj.getLeftMinCardinality());
relationshipTypeRest.setLeftMaxCardinality(obj.getLeftMaxCardinality()); relationshipTypeRest.setLeftMaxCardinality(obj.getLeftMaxCardinality());
relationshipTypeRest.setRightMinCardinality(obj.getRightMinCardinality()); relationshipTypeRest.setRightMinCardinality(obj.getRightMinCardinality());

View File

@@ -0,0 +1,44 @@
/**
* 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.link.harvest;
import java.util.LinkedList;
import java.util.UUID;
import org.dspace.app.rest.model.HarvestedCollectionRest;
import org.dspace.app.rest.model.hateoas.HarvestedCollectionResource;
import org.springframework.data.domain.Pageable;
import org.springframework.hateoas.Link;
import org.springframework.stereotype.Component;
/**
* This class adds links to {@link org.dspace.app.rest.model.hateoas.HarvestedCollectionResource}s
* This builds a link to the collection harvest link
*/
@Component
public class HarvestedCollectionHalLinkFactory
extends HarvestedCollectionRestHalLinkFactory<HarvestedCollectionResource> {
protected void addLinks(HarvestedCollectionResource halResource, Pageable page, LinkedList<Link> list)
throws Exception {
HarvestedCollectionRest data = halResource.getContent();
if (data != null) {
list.add(
buildLink(
Link.REL_SELF,
getMethodOn().get(UUID.fromString(data.getCollectionRest().getUuid()), null, null)
)
);
}
}
protected Class<HarvestedCollectionResource> getResourceClass() {
return HarvestedCollectionResource.class;
}
}

View File

@@ -0,0 +1,20 @@
/**
* 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.link.harvest;
import org.dspace.app.rest.CollectionHarvestSettingsController;
import org.dspace.app.rest.link.HalLinkFactory;
public abstract class HarvestedCollectionRestHalLinkFactory<T>
extends HalLinkFactory<T, CollectionHarvestSettingsController> {
@Override
protected Class<CollectionHarvestSettingsController> getControllerClass() {
return CollectionHarvestSettingsController.class;
}
}

View File

@@ -24,7 +24,7 @@ public class StatisticsSupportHalLinkFactory
list.add(buildLink(Link.REL_SELF, getMethodOn().getStatisticsSupport())); list.add(buildLink(Link.REL_SELF, getMethodOn().getStatisticsSupport()));
list.add(buildLink("viewevents", getMethodOn().getViewEvents())); list.add(buildLink("viewevents", getMethodOn().getViewEvents()));
list.add(buildLink("searchevents", getMethodOn().getViewEvents())); list.add(buildLink("searchevents", getMethodOn().getSearchEvents()));
} }
protected Class<StatisticsRestController> getControllerClass() { protected Class<StatisticsRestController> getControllerClass() {

View File

@@ -25,6 +25,7 @@ public class CollectionRest extends DSpaceObjectRest {
public static final String NAME = "collection"; public static final String NAME = "collection";
public static final String CATEGORY = RestAddressableModel.CORE; public static final String CATEGORY = RestAddressableModel.CORE;
public static final String LICENSE = "license"; public static final String LICENSE = "license";
public static final String HARVEST = "harvester";
public static final String DEFAULT_ACCESS_CONDITIONS = "defaultAccessConditions"; public static final String DEFAULT_ACCESS_CONDITIONS = "defaultAccessConditions";
@JsonIgnore @JsonIgnore
private BitstreamRest logo; private BitstreamRest logo;

View File

@@ -0,0 +1,47 @@
/**
* 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 enum containing all the possible harvest statuses
*
* @author Jelle Pelgrims (jelle.pelgrims at atmire.com)
*/
public enum HarvestStatusEnum {
READY(0),
BUSY(1),
QUEUED(2),
OAI_ERROR(3),
UNKNOWN_ERROR(-1);
private int harvestStatus;
HarvestStatusEnum(int harvestStatus) {
this.harvestStatus = harvestStatus;
}
public int getValue() {
return harvestStatus;
}
public static HarvestStatusEnum fromInt(Integer harvestStatus) {
if (harvestStatus == null) {
return null;
}
switch (harvestStatus) {
case -1: return HarvestStatusEnum.UNKNOWN_ERROR;
case 0: return HarvestStatusEnum.READY;
case 1: return HarvestStatusEnum.BUSY;
case 2: return HarvestStatusEnum.QUEUED;
case 3: return HarvestStatusEnum.OAI_ERROR;
default: throw new IllegalArgumentException("No corresponding enum value for integer");
}
}
}

View File

@@ -0,0 +1,49 @@
/**
* 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 enum containing all the possible harvest types
*
* @author Jelle Pelgrims (jelle.pelgrims at atmire.com)
*/
public enum HarvestTypeEnum {
NONE(0),
METADATA_ONLY(1),
METADATA_AND_REF(2),
METADATA_AND_BITSTREAMS(3);
private int harvestType;
HarvestTypeEnum(int harvestType) {
this.harvestType = harvestType;
}
public int getValue() {
return harvestType;
}
/**
* Creates an enum from the given integer
* @param harvestType The harvest type
* @return a harvestTypeEnum
*/
public static HarvestTypeEnum fromInt(Integer harvestType) {
if (harvestType == null) {
return HarvestTypeEnum.NONE;
}
switch (harvestType) {
case 0: return HarvestTypeEnum.NONE;
case 1: return HarvestTypeEnum.METADATA_ONLY;
case 2: return HarvestTypeEnum.METADATA_AND_REF;
case 3: return HarvestTypeEnum.METADATA_AND_BITSTREAMS;
default: throw new IllegalArgumentException("No corresponding enum value for integer");
}
}
}

View File

@@ -0,0 +1,170 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.model;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* The HarvestCollection REST Resource
*
* @author Jelle Pelgrims (jelle.pelgrims at atmire.com)
*/
public class HarvestedCollectionRest extends BaseObjectRest<Integer> {
public static final String NAME = "collections";
public static final String CATEGORY = "core";
@JsonProperty("harvest_type")
private HarvestTypeEnum harvestType;
@JsonProperty("oai_source")
private String oaiSource;
@JsonProperty("oai_set_id")
private String oaiSetId;
@JsonProperty("harvest_message")
private String harvestMessage;
@JsonProperty("metadata_config_id")
private String metadataConfigId;
@JsonProperty("harvest_status")
private HarvestStatusEnum harvestStatus;
@JsonProperty("harvest_start_time")
private Date harvestStartTime;
@JsonProperty("last_harvested")
private Date lastHarvested;
private HarvesterMetadataRest metadata_configs;
private CollectionRest collectionRest;
@JsonIgnore
@Override
public Integer getId() {
return id;
}
public String getCategory() {
return CATEGORY;
}
public Class getController() {
return HarvestedCollectionRest.class;
}
@JsonIgnore
public String getType() {
return NAME;
}
@JsonIgnore
public CollectionRest getCollectionRest() {
return this.collectionRest;
}
public void setCollection(CollectionRest collectionRest) {
this.collectionRest = collectionRest;
}
@JsonIgnore
public int getHarvestType() {
return harvestType.ordinal();
}
@JsonGetter("harvest_type")
public String getHarvestTypeAsString() {
return harvestType.name();
}
public void setHarvestType(HarvestTypeEnum harvestType) {
this.harvestType = harvestType;
}
public String getOaiSource() {
return oaiSource;
}
public void setOaiSource(String oaiSource) {
this.oaiSource = oaiSource;
}
public String getOaiSetId() {
return oaiSetId;
}
public void setOaiSetId(String oaiSetId) {
this.oaiSetId = oaiSetId;
}
public String getMetadataConfigId() {
return metadataConfigId;
}
public void setMetadataConfigId(String metadataConfigId) {
this.metadataConfigId = metadataConfigId;
}
public String getHarvestMessage() {
return harvestMessage;
}
public void setHarvestMessage(String harvestMessage) {
this.harvestMessage = harvestMessage;
}
@JsonIgnore
public HarvestStatusEnum getHarvestStatus() {
return harvestStatus;
}
@JsonGetter("harvest_status")
public String getHarvestStatusAsString() {
return harvestStatus == null ? null : harvestStatus.name();
}
public void setHarvestStatus(HarvestStatusEnum harvestStatus) {
this.harvestStatus = harvestStatus;
}
public Date getHarvestStartTime() {
return harvestStartTime;
}
public void setHarvestStartTime(Date harvestStartTime) {
this.harvestStartTime = harvestStartTime;
}
public Date getLastHarvested() {
return lastHarvested;
}
public void setLastHarvested(Date lastHarvested) {
this.lastHarvested = lastHarvested;
}
@LinkRest(linkClass = HarvesterMetadataRest.class, name = "harvestermetadata", optional = true)
@JsonIgnore
public HarvesterMetadataRest getMetadataConfigs() {
return metadata_configs;
}
public void setMetadataConfigs(HarvesterMetadataRest metadata_configs) {
this.metadata_configs = metadata_configs;
}
}

View File

@@ -0,0 +1,58 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.model;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.dspace.app.rest.HarvesterMetadataController;
/**
* The rest resource used for harvester metadata
*
* @author Jelle Pelgrims (jelle.pelgrims at atmire.com)
*/
public class HarvesterMetadataRest extends BaseObjectRest {
public static final String CATEGORY = "config";
public static final String NAME = "harvesterMetadata";
private List<Map<String,String>> configs;
@Override
@JsonIgnore
public Serializable getId() {
return id;
}
@JsonIgnore
public String getCategory() {
return CATEGORY;
}
@JsonIgnore
public String getType() {
return NAME;
}
public Class getController() {
return HarvesterMetadataController.class;
}
public List<Map<String,String>> getConfigs() {
return configs;
}
public void setConfigs(List<Map<String,String>> configs) {
this.configs = configs;
}
}

View File

@@ -30,6 +30,8 @@ public class RelationshipRest extends BaseObjectRest<Integer> {
private RelationshipTypeRest relationshipType; private RelationshipTypeRest relationshipType;
private int leftPlace; private int leftPlace;
private int rightPlace; private int rightPlace;
private String leftwardValue;
private String rightwardValue;
public String getType() { public String getType() {
return NAME; return NAME;
@@ -92,4 +94,20 @@ public class RelationshipRest extends BaseObjectRest<Integer> {
public void setRelationshipTypeId(int relationshipTypeId) { public void setRelationshipTypeId(int relationshipTypeId) {
this.relationshipTypeId = relationshipTypeId; this.relationshipTypeId = relationshipTypeId;
} }
public String getRightwardValue() {
return rightwardValue;
}
public void setRightwardValue(String rightwardValue) {
this.rightwardValue = rightwardValue;
}
public String getLeftwardValue() {
return leftwardValue;
}
public void setLeftwardValue(String leftwardValue) {
this.leftwardValue = leftwardValue;
}
} }

View File

@@ -20,8 +20,8 @@ public class RelationshipTypeRest extends BaseObjectRest<Integer> {
public static final String NAME = "relationshiptype"; public static final String NAME = "relationshiptype";
public static final String CATEGORY = "core"; public static final String CATEGORY = "core";
private String leftLabel; private String leftwardType;
private String rightLabel; private String rightwardType;
private Integer leftMinCardinality; private Integer leftMinCardinality;
private Integer leftMaxCardinality; private Integer leftMaxCardinality;
private Integer rightMinCardinality; private Integer rightMinCardinality;
@@ -41,20 +41,20 @@ public class RelationshipTypeRest extends BaseObjectRest<Integer> {
return RestResourceController.class; return RestResourceController.class;
} }
public String getLeftLabel() { public String getLeftwardType() {
return leftLabel; return leftwardType;
} }
public void setLeftLabel(String leftLabel) { public void setLeftwardType(String leftwardType) {
this.leftLabel = leftLabel; this.leftwardType = leftwardType;
} }
public String getRightLabel() { public String getRightwardType() {
return rightLabel; return rightwardType;
} }
public void setRightLabel(String rightLabel) { public void setRightwardType(String rightwardType) {
this.rightLabel = rightLabel; this.rightwardType = rightwardType;
} }
public Integer getLeftMinCardinality() { public Integer getLeftMinCardinality() {

View File

@@ -22,6 +22,7 @@ public class CollectionResource extends DSpaceResource<CollectionRest> {
public CollectionResource(CollectionRest collection, Utils utils, String... rels) { public CollectionResource(CollectionRest collection, Utils utils, String... rels) {
super(collection, utils, rels); super(collection, utils, rels);
add(utils.linkToSubResource(collection, CollectionRest.LICENSE)); add(utils.linkToSubResource(collection, CollectionRest.LICENSE));
add(utils.linkToSubResource(collection, CollectionRest.HARVEST));
add(utils.linkToSubResource(collection, "mappedItems")); add(utils.linkToSubResource(collection, "mappedItems"));
} }
} }

View File

@@ -0,0 +1,39 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.model.hateoas;
import org.dspace.app.rest.model.HarvestedCollectionRest;
import org.dspace.app.rest.model.HarvesterMetadataRest;
import org.dspace.app.rest.utils.Utils;
import org.springframework.beans.factory.annotation.Autowired;
/**
* HarvestedCollection Rest HAL Resource. The HAL Resource wraps the REST Resource
* adding support for the links and embedded resources
*
* @author Jelle Pelgrims (jelle.pelgrims at atmire.com)
*/
public class HarvestedCollectionResource extends HALResource<HarvestedCollectionRest> {
@Autowired
private Utils utils;
public HarvestedCollectionResource(HarvestedCollectionRest data) {
super(data);
embedResource("harvestermetadata", data.getMetadataConfigs());
}
private void embedResource(String relationship, HarvesterMetadataRest harvesterMetadataRest) {
if (harvesterMetadataRest != null) {
HarvesterMetadataResource harvesterMetadataResource =
new HarvesterMetadataResource(harvesterMetadataRest, utils);
embedResource(relationship, harvesterMetadataResource);
}
}
}

View File

@@ -0,0 +1,25 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.model.hateoas;
import org.dspace.app.rest.model.HarvesterMetadataRest;
import org.dspace.app.rest.utils.Utils;
/**
* HarvesterMetadata Rest HAL Resource. The HAL Resource wraps the REST Resource
* adding support for the links and embedded resources
*
* @author Jelle Pelgrims (jelle.pelgrims at atmire.com)
*/
public class HarvesterMetadataResource extends DSpaceResource<HarvesterMetadataRest> {
public HarvesterMetadataResource(HarvesterMetadataRest data, Utils utils, String... rels) {
super(data, utils, rels);
}
}

View File

@@ -17,7 +17,6 @@ import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.JsonNode; 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.CollectionConverter; import org.dspace.app.rest.converter.CollectionConverter;
@@ -38,7 +37,6 @@ 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;
@@ -169,8 +167,19 @@ public class CollectionRestRepository extends DSpaceObjectRestRepository<Collect
} }
@Override @Override
@PreAuthorize("hasAuthority('ADMIN')")
protected CollectionRest createAndReturn(Context context) throws AuthorizeException { protected CollectionRest createAndReturn(Context context) throws AuthorizeException {
throw new DSpaceBadRequestException("Cannot create a Collection without providing a parent Community.");
}
@Override
@PreAuthorize("hasPermission(#id, 'COMMUNITY', 'ADD')")
protected CollectionRest createAndReturn(Context context, UUID id) throws AuthorizeException {
if (id == null) {
throw new DSpaceBadRequestException("Parent Community UUID is null. " +
"Cannot create a Collection without providing a parent Community");
}
HttpServletRequest req = getRequestService().getCurrentRequest().getHttpServletRequest(); HttpServletRequest req = getRequestService().getCurrentRequest().getHttpServletRequest();
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
CollectionRest collectionRest; CollectionRest collectionRest;
@@ -178,38 +187,21 @@ public class CollectionRestRepository extends DSpaceObjectRestRepository<Collect
ServletInputStream input = req.getInputStream(); ServletInputStream input = req.getInputStream();
collectionRest = mapper.readValue(input, CollectionRest.class); collectionRest = mapper.readValue(input, CollectionRest.class);
} catch (IOException e1) { } catch (IOException e1) {
throw new UnprocessableEntityException("Error parsing request body: " + e1.toString()); throw new UnprocessableEntityException("Error parsing request body.", e1);
} }
Collection collection; Collection collection;
String parentCommunityString = req.getParameter("parent");
try { try {
Community parent = null; Community parent = communityService.find(context, id);
if (StringUtils.isNotBlank(parentCommunityString)) { if (parent == null) {
throw new UnprocessableEntityException("Parent community for id: "
UUID parentCommunityUuid = UUIDUtils.fromString(parentCommunityString); + id + " not found");
if (parentCommunityUuid == null) {
throw new DSpaceBadRequestException("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 DSpaceBadRequestException("The parent parameter cannot be left empty," +
"collections require a parent community.");
} }
collection = cs.create(context, parent); collection = cs.create(context, parent);
cs.update(context, collection); cs.update(context, collection);
metadataConverter.setMetadata(context, collection, collectionRest.getMetadata()); metadataConverter.setMetadata(context, collection, collectionRest.getMetadata());
} catch (SQLException e) { } catch (SQLException e) {
throw new RuntimeException("Unable to create new Collection under parent Community " + throw new RuntimeException("Unable to create new Collection under parent Community " + id, e);
parentCommunityString, e);
} }
return converter.convert(collection); return converter.convert(collection);
} }

View File

@@ -17,7 +17,6 @@ import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.JsonNode; 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;
@@ -34,7 +33,6 @@ 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;
@@ -83,25 +81,45 @@ public class CommunityRestRepository extends DSpaceObjectRestRepository<Communit
} }
Community community; Community community;
try { try {
Community parent = null; // top-level community
String parentCommunityString = req.getParameter("parent"); community = cs.create(null, context);
if (StringUtils.isNotBlank(parentCommunityString)) { cs.update(context, community);
metadataConverter.setMetadata(context, community, communityRest.getMetadata());
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
UUID parentCommunityUuid = UUIDUtils.fromString(parentCommunityString); return dsoConverter.convert(community);
if (parentCommunityUuid == null) { }
throw new DSpaceBadRequestException("The given parent parameter was invalid: "
+ parentCommunityString);
}
parent = cs.find(context, parentCommunityUuid); @Override
if (parent == null) { @PreAuthorize("hasPermission(#id, 'COMMUNITY', 'ADD')")
throw new UnprocessableEntityException("Parent community for id: " protected CommunityRest createAndReturn(Context context, UUID id) throws AuthorizeException {
+ parentCommunityUuid + " not found");
} if (id == null) {
throw new DSpaceBadRequestException("Parent Community UUID is null. " +
"Cannot create a SubCommunity without providing a parent Community.");
}
HttpServletRequest req = getRequestService().getCurrentRequest().getHttpServletRequest();
ObjectMapper mapper = new ObjectMapper();
CommunityRest communityRest;
try {
ServletInputStream input = req.getInputStream();
communityRest = mapper.readValue(input, CommunityRest.class);
} catch (IOException e1) {
throw new UnprocessableEntityException("Error parsing request body.", e1);
}
Community community;
try {
Community parent = cs.find(context, id);
if (parent == null) {
throw new UnprocessableEntityException("Parent community for id: "
+ id + " not found");
} }
// sub-community
community = cs.create(parent, context); community = cs.create(parent, context);
cs.update(context, community); cs.update(context, community);
metadataConverter.setMetadata(context, community, communityRest.getMetadata()); metadataConverter.setMetadata(context, community, communityRest.getMetadata());

View File

@@ -12,6 +12,7 @@ import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@@ -233,7 +234,7 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
} }
/** /**
* Method to implement to support scroll of entity instances from the collection resource endpoin * Method to implement to support scroll of entity instances from the collection resource endpoint
* *
* @param context * @param context
* the dspace context * the dspace context
@@ -259,6 +260,46 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
*/ */
public abstract DSpaceResource<T> wrapResource(T model, String... rels); public abstract DSpaceResource<T> wrapResource(T model, String... rels);
/**
* Create and return a new instance. Data are usually retrieved from the thread bound http request
*
* @return the created REST object
*/
public T createAndReturn() {
Context context = null;
try {
context = obtainContext();
T entity = thisRepository.createAndReturn(context);
context.commit();
return entity;
} catch (AuthorizeException e) {
throw new RESTAuthorizationException(e);
} catch (SQLException ex) {
throw new RuntimeException(ex.getMessage(), ex);
}
}
/**
* Create and return a new instance after adding to the parent. Data are usually retrieved from
* the thread bound http request.
*
* @param uuid the id of the parent object
* @return the created REST object
*/
public T createAndReturn(UUID uuid) {
Context context = null;
try {
context = obtainContext();
T entity = thisRepository.createAndReturn(context, uuid);
context.commit();
return entity;
} catch (AuthorizeException e) {
throw new RESTAuthorizationException(e);
} catch (SQLException ex) {
throw new RuntimeException(ex.getMessage(), ex);
}
}
/** /**
* Create and return a new instance. Data is recovered from the thread bound HTTP request and the list * Create and return a new instance. Data is recovered from the thread bound HTTP request and the list
* of DSpaceObjects provided in the uri-list body * of DSpaceObjects provided in the uri-list body
@@ -281,22 +322,22 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
} }
/** /**
* Create and return a new instance. Data are usually retrieved from the thread bound http request * Method to implement to support the creation of a new instance. Usually require to retrieve the http request from
* the thread bound attribute
* *
* @param context
* the dspace context
* @param uuid
* The uuid of the parent object retrieved from the query param.
* @return the created REST object * @return the created REST object
* @throws AuthorizeException
* @throws SQLException
* @throws RepositoryMethodNotImplementedException
* returned by the default implementation when the operation is not supported for the entity
*/ */
public T createAndReturn() { protected T createAndReturn(Context context, UUID uuid)
Context context = null; throws AuthorizeException, SQLException, RepositoryMethodNotImplementedException {
try { throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", "");
context = obtainContext();
T entity = thisRepository.createAndReturn(context);
context.commit();
return entity;
} catch (AuthorizeException e) {
throw new RESTAuthorizationException(e);
} catch (SQLException ex) {
throw new RuntimeException(ex.getMessage(), ex);
}
} }
/** /**

View File

@@ -0,0 +1,190 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.repository;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dspace.app.rest.converter.HarvestedCollectionConverter;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.HarvestTypeEnum;
import org.dspace.app.rest.model.HarvestedCollectionRest;
import org.dspace.content.Collection;
import org.dspace.core.Context;
import org.dspace.harvest.HarvestedCollection;
import org.dspace.harvest.OAIHarvester;
import org.dspace.harvest.service.HarvestedCollectionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* This is the repository responsible for managing the HarvestedCollection Rest object
*
* @author Jelle Pelgrims (jelle.pelgrims at atmire.com)
*/
@Component(HarvestedCollectionRest.CATEGORY + "." + HarvestedCollectionRest.NAME)
public class HarvestedCollectionRestRepository extends AbstractDSpaceRestRepository {
@Autowired
HarvestedCollectionService harvestedCollectionService;
@Autowired
HarvestedCollectionConverter harvestedCollectionConverter;
public HarvestedCollectionRest findOne(Collection collection) throws SQLException {
Context context = obtainContext();
if (collection == null) {
return null;
}
HarvestedCollection harvestedCollection = harvestedCollectionService.find(context, collection);
List<Map<String,String>> configs = OAIHarvester.getAvailableMetadataFormats();
return harvestedCollectionConverter.fromModel(harvestedCollection, collection, configs);
}
/**
* Function to update the harvesting settings of a collection
* @param context The context object
* @param request The incoming put request
* @param collection The collection whose settings should be changed
* @return a harvestedCollection if a new harvestedCollection is created, otherwise null
* @throws SQLException
*/
public HarvestedCollectionRest update(Context context,
HttpServletRequest request,
Collection collection) throws SQLException {
HarvestedCollectionRest harvestedCollectionRest = parseHarvestedCollectionRest(context, request, collection);
HarvestedCollection harvestedCollection = harvestedCollectionService.find(context, collection);
// Delete harvestedCollectionService object if harvest type is not set
if (harvestedCollectionRest.getHarvestType() == HarvestTypeEnum.NONE.getValue()
&& harvestedCollection != null) {
harvestedCollectionService.delete(context, harvestedCollection);
return harvestedCollectionConverter.fromModel(null);
} else if (harvestedCollectionRest.getHarvestType() != HarvestTypeEnum.NONE.getValue()) {
List<String> errors = testHarvestSettings(harvestedCollectionRest);
if (errors.size() == 0) {
if (harvestedCollection == null) {
harvestedCollection = harvestedCollectionService.create(context, collection);
}
updateCollectionHarvestSettings(context, harvestedCollection, harvestedCollectionRest);
harvestedCollection = harvestedCollectionService.find(context, collection);
List<Map<String,String>> configs = OAIHarvester.getAvailableMetadataFormats();
return harvestedCollectionConverter.fromModel(harvestedCollection, collection, configs);
} else {
throw new UnprocessableEntityException(
"Incorrect harvest settings in request. The following errors were found: " + errors.toString()
);
}
}
return null;
}
/**
* Function to parse a harvestedCollectionRest from an incoming put request
* @param context The context object
* @param request The incoming put request
* @param collection The collection to which the harvestedCollection belongs
* @return The harvestedCollectionRest object contained inn the request
*/
private HarvestedCollectionRest parseHarvestedCollectionRest(Context context,
HttpServletRequest request,
Collection collection) throws SQLException {
ObjectMapper mapper = new ObjectMapper();
HarvestedCollectionRest harvestedCollectionRest;
try {
ServletInputStream input = request.getInputStream();
harvestedCollectionRest = mapper.readValue(input, HarvestedCollectionRest.class);
} catch (IOException e) {
throw new UnprocessableEntityException("Error parsing request body: " + e.toString(), e);
}
return harvestedCollectionRest;
}
/**
* Function to update the harvest settings of a collection
* @param context The context object
* @param harvestedCollection The harvestedCollection whose settings should be updated
* @param harvestedCollectionRest An object containing the new harvest settings
* @throws SQLException
*/
private void updateCollectionHarvestSettings(Context context, HarvestedCollection harvestedCollection,
HarvestedCollectionRest harvestedCollectionRest) throws SQLException {
int harvestType = harvestedCollectionRest.getHarvestType();
String oaiSource = harvestedCollectionRest.getOaiSource();
String oaiSetId = harvestedCollectionRest.getOaiSetId();
String metadataConfigId = harvestedCollectionRest.getMetadataConfigId();
harvestedCollection.setHarvestType(harvestType);
harvestedCollection.setOaiSource(oaiSource);
harvestedCollection.setOaiSetId(oaiSetId);
harvestedCollection.setHarvestMetadataConfig(metadataConfigId);
harvestedCollectionService.update(context, harvestedCollection);
}
/**
* Function used to verify that the harvest settings work
* @param collection The collection to which the harvest settings should be aplied
* @param harvestedCollectionRest A object containg the harvest settings to be tested
* @return
*/
private List<String> testHarvestSettings(HarvestedCollectionRest harvestedCollectionRest) {
int harvestType = harvestedCollectionRest.getHarvestType();
String metadataConfigId = harvestedCollectionRest.getMetadataConfigId();
List<String> errors = new ArrayList<>();
// See if metadata config identifier appears in available metadata formats
List<Map<String,String>> metadataFormats = OAIHarvester.getAvailableMetadataFormats();
boolean inAvailableMetadataFormats = metadataFormats.stream()
.filter(x -> x.get("id").equals(metadataConfigId))
.count() >= 1;
if (inAvailableMetadataFormats) {
boolean testORE = Arrays.asList(
HarvestTypeEnum.METADATA_AND_REF.getValue(),
HarvestTypeEnum.METADATA_AND_BITSTREAMS.getValue()
).contains(harvestType);
// Actually verify the harvest settings
List<String> verificationErrors = harvestedCollectionService.verifyOAIharvester(
harvestedCollectionRest.getOaiSource(),
harvestedCollectionRest.getOaiSetId(),
metadataConfigId,
testORE
);
errors = verificationErrors;
} else {
errors.add(
"The metadata format with identifier '" + metadataConfigId + "' is not an available metadata format."
);
}
return errors;
}
}

View File

@@ -7,12 +7,15 @@
*/ */
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.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.dspace.app.rest.Parameter; import org.dspace.app.rest.Parameter;
import org.dspace.app.rest.SearchRestMethod; import org.dspace.app.rest.SearchRestMethod;
@@ -109,11 +112,14 @@ public class RelationshipRestRepository extends DSpaceRestRepository<Relationshi
RelationshipType relationshipType = relationshipTypeService RelationshipType relationshipType = relationshipTypeService
.find(context, Integer.parseInt(req.getParameter("relationshipType"))); .find(context, Integer.parseInt(req.getParameter("relationshipType")));
String leftwardValue = req.getParameter("leftwardValue");
String rightwardValue = req.getParameter("rightwardValue");
EPerson ePerson = context.getCurrentUser(); EPerson ePerson = context.getCurrentUser();
if (authorizeService.authorizeActionBoolean(context, leftItem, Constants.WRITE) || if (authorizeService.authorizeActionBoolean(context, leftItem, Constants.WRITE) ||
authorizeService.authorizeActionBoolean(context, rightItem, Constants.WRITE)) { authorizeService.authorizeActionBoolean(context, rightItem, Constants.WRITE)) {
Relationship relationship = relationshipService.create(context, leftItem, rightItem, Relationship relationship = relationshipService.create(context, leftItem, rightItem,
relationshipType, -1, -1); relationshipType, -1, -1, leftwardValue, rightwardValue);
// The above if check deals with the case that a Relationship can be created if the user has write // The above if check deals with the case that a Relationship can be created if the user has write
// rights on one of the two items. The following updateItem calls can however call the // rights on one of the two items. The following updateItem calls can however call the
// ItemService.update() functions which would fail if the user doesn't have permission on both items. // ItemService.update() functions which would fail if the user doesn't have permission on both items.
@@ -198,6 +204,66 @@ public class RelationshipRestRepository extends DSpaceRestRepository<Relationshi
} }
/**
* Method to replace the metadata of a relationship (the left/right places and the leftward/rightward labels)
* @param context the dspace context
* @param request
* @param apiCategory the API category e.g. "api"
* @param model the DSpace model e.g. "metadatafield"
* @param id the ID of the target REST object
* @param jsonNode the part of the request body representing the updated rest object
* @return
* @throws RepositoryMethodNotImplementedException
* @throws SQLException
* @throws AuthorizeException
*/
@Override
protected RelationshipRest put(Context context, HttpServletRequest request, String apiCategory, String model,
Integer id, JsonNode jsonNode)
throws RepositoryMethodNotImplementedException, SQLException, AuthorizeException {
Relationship relationship;
try {
relationship = relationshipService.find(context, id);
} catch (SQLException e) {
throw new ResourceNotFoundException("Relationship" + " with id: " + id + " not found");
}
if (relationship == null) {
throw new ResourceNotFoundException("Relationship" + " with id: " + id + " not found");
}
try {
RelationshipRest relationshipRest;
try {
relationshipRest = new ObjectMapper().readValue(jsonNode.toString(), RelationshipRest.class);
} catch (IOException e) {
throw new UnprocessableEntityException("Error parsing request body: " + e.toString());
}
relationship.setLeftwardValue(relationshipRest.getLeftwardValue());
relationship.setRightwardValue(relationshipRest.getRightwardValue());
if (jsonNode.hasNonNull("rightPlace")) {
relationship.setRightPlace(relationshipRest.getRightPlace());
}
if (jsonNode.hasNonNull("leftPlace")) {
relationship.setRightPlace(relationshipRest.getLeftPlace());
}
relationshipService.update(context, relationship);
context.commit();
context.reloadEntity(relationship);
return relationshipConverter.fromModel(relationship);
} catch (AuthorizeException e) {
throw new AccessDeniedException("You do not have write rights on this relationship's metadata");
}
}
/** /**
* This method will check with the current user has write rights on both one of the original items and one of the * This method will check with the current user has write rights on both one of the original items and one of the
* new items for the relationship. * new items for the relationship.
@@ -253,7 +319,8 @@ public class RelationshipRestRepository extends DSpaceRestRepository<Relationshi
Context context = obtainContext(); Context context = obtainContext();
List<RelationshipType> relationshipTypeList = relationshipTypeService.findByLeftOrRightLabel(context, label); List<RelationshipType> relationshipTypeList =
relationshipTypeService.findByLeftwardOrRightwardTypeName(context, label);
List<Relationship> relationships = new LinkedList<>(); List<Relationship> relationships = new LinkedList<>();
if (dsoId != null) { if (dsoId != null) {

View File

@@ -30,8 +30,8 @@ import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* An authenicated user is allowed to view, update or delete his or her own data. This {@link RestPermissionEvaluatorPlugin} * An authenticated user is allowed to view, update or delete his or her own data. This {@link RestPermissionEvaluatorPlugin}
* implemenents that requirement. * implements that requirement.
*/ */
@Component @Component
public class EPersonRestPermissionEvaluatorPlugin extends RestObjectPermissionEvaluatorPlugin { public class EPersonRestPermissionEvaluatorPlugin extends RestObjectPermissionEvaluatorPlugin {

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
default-lazy-init="true">
<!-- Replace harvestCollectionService with a mocked service -->
<bean class="org.dspace.harvest.MockHarvestedCollectionServiceImpl" id="org.dspace.harvest.service.HarvestedCollectionService" primary="true"/>
</beans>

View File

@@ -0,0 +1,312 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.builder.EPersonBuilder;
import org.dspace.app.rest.matcher.MetadataConfigsMatcher;
import org.dspace.app.rest.model.HarvestTypeEnum;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.core.Constants;
import org.dspace.eperson.EPerson;
import org.dspace.harvest.HarvestedCollection;
import org.dspace.harvest.OAIHarvester;
import org.dspace.harvest.service.HarvestedCollectionService;
import org.hamcrest.Matchers;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Integration test for collection harvest settings controller
*
* @author Jelle Pelgrims (jelle.pelgrims at atmire.com)
*/
public class CollectionHarvestSettingsControllerIT extends AbstractControllerIntegrationTest {
@Autowired
AuthorizeService authorizeService;
@Autowired
HarvestedCollectionService harvestedCollectionService;
Collection collection;
Collection collectionNoHarvestSettings;
EPerson ePersonWithWriteRights;
@Before
public void SetUp() throws SQLException, AuthorizeException {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community community = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
collection = CollectionBuilder.createCollection(context, community)
.withName("Collection 1")
.build();
collectionNoHarvestSettings = CollectionBuilder.createCollection(context, community)
.withName("Collection 2")
.build();
ePersonWithWriteRights = EPersonBuilder.createEPerson(context)
.withEmail("email@email.com")
.withPassword(password)
.build();
authorizeService.addPolicy(context, collection, Constants.WRITE, ePersonWithWriteRights);
context.restoreAuthSystemState();
}
/**
* Function to create a JSONObject containing the harvest settings
* @param harvestType The harvest type
* @param oaiSource The OAI source
* @param oaiSetId The OAI set id
* @param metadataConfigId The metadata config id
* @return A JSONObject containing the given harvest settings
*/
public JSONObject createHarvestSettingsJson(String harvestType,
String oaiSource, String oaiSetId, String metadataConfigId) {
JSONObject json = new JSONObject();
json.put("harvest_type", harvestType);
json.put("oai_source", oaiSource);
json.put("oai_set_id", oaiSetId);
json.put("metadata_config_id", metadataConfigId);
return json;
}
@Test
public void GetCollectionHarvestSettings() throws Exception {
String token = getAuthToken(admin.getEmail(), password);
List<Map<String,String>> configs = OAIHarvester.getAvailableMetadataFormats();
// Add harvest settings to collection
JSONObject json = createHarvestSettingsJson("METADATA_ONLY", "https://dspace.org/oai/request", "col_1721.1_114174", "dc");
getClient(token).perform(
put("/api/core/collections/" + collection.getID() + "/harvester")
.contentType("application/json")
.content(json.toString()))
.andExpect(status().isOk());
//Retrieve harvest settings
getClient(token).perform(
get("/api/core/collections/" + collection.getID() + "/harvester"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.harvest_type", is("METADATA_ONLY")))
.andExpect(jsonPath("$.oai_source", is("https://dspace.org/oai/request")))
.andExpect(jsonPath("$.oai_set_id", is("col_1721.1_114174")))
.andExpect(jsonPath("$.harvest_message", is(nullValue())))
.andExpect(jsonPath("$.metadata_config_id", is("dc")))
.andExpect(jsonPath("$.harvest_status", is("READY")))
.andExpect(jsonPath("$.harvest_start_time", is(nullValue())))
.andExpect(jsonPath("$.last_harvested", is(nullValue())))
.andExpect(jsonPath("$._links.self.href",
endsWith("api/core/collections/" + collection.getID() + "/harvester")))
.andExpect(jsonPath("$._embedded.harvestermetadata", Matchers.allOf(
MetadataConfigsMatcher.matchMetadataConfigs(configs)
)))
.andExpect(jsonPath("$._embedded.harvestermetadata._links.self.href",
endsWith("/api/config/harvestermetadata")));
}
@Test
public void GetCollectionHarvestSettingsIfNotAdmin() throws Exception {
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(
put("/api/core/collections/" + collection.getID() + "/harvester")
.contentType("application/json"))
.andExpect(status().isForbidden());
}
@Test
public void GetAndPutCollectionHarvestSettingsIfUserHasWriteRights() throws Exception {
context.setCurrentUser(ePersonWithWriteRights);
String token = getAuthToken(ePersonWithWriteRights.getEmail(), password);
JSONObject json = createHarvestSettingsJson("METADATA_ONLY", "https://dspace.org/oai/request", "col_1721.1_114174", "dc");
getClient(token).perform(
put("/api/core/collections/" + collection.getID() + "/harvester")
.contentType("application/json")
.content(json.toString()))
.andExpect(status().isOk());
getClient(token).perform(
get("/api/core/collections/" + collection.getID() + "/harvester")
.contentType("application/json"))
.andExpect(status().isOk());
}
@Test
public void getAndPutCollectionHarvestSettingsAnonymousUserException() throws Exception {
JSONObject json = createHarvestSettingsJson("METADATA_ONLY", "https://dspace.org/oai/request", "col_1721.1_114174", "dc");
getClient().perform(
put("/api/core/collections/" + collection.getID() + "/harvester")
.contentType("application/json")
.content(json.toString()))
.andExpect(status().isUnauthorized());
}
@Test
public void GetAndPutCollectionHarvestSettingsIfUserHasNoWriteRightsException() throws Exception {
context.setCurrentUser(eperson);
String token = getAuthToken(eperson.getEmail(), password);
JSONObject json = createHarvestSettingsJson("METADATA_ONLY", "https://dspace.org/oai/request", "col_1721.1_114174", "dc");
getClient(token).perform(
put("/api/core/collections/" + collection.getID() + "/harvester")
.contentType("application/json")
.content(json.toString()))
.andExpect(status().isForbidden());
}
@Test
public void getCollectionHarvestSettingsIfNotSet() throws Exception {
String token = getAuthToken(admin.getEmail(), password);
List<Map<String,String>> configs = OAIHarvester.getAvailableMetadataFormats();
getClient(token).perform(
get("/api/core/collections/" + collectionNoHarvestSettings.getID() + "/harvester"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.harvest_type", is("NONE")))
.andExpect(jsonPath("$.oai_source", is(nullValue())))
.andExpect(jsonPath("$.oai_set_id", is(nullValue())))
.andExpect(jsonPath("$.harvest_message", is(nullValue())))
.andExpect(jsonPath("$.metadata_config_id", is(nullValue())))
.andExpect(jsonPath("$.harvest_status", is(nullValue())))
.andExpect(jsonPath("$.harvest_start_time", is(nullValue())))
.andExpect(jsonPath("$.last_harvested", is(nullValue())))
.andExpect(jsonPath("$._links.self.href",
endsWith("api/core/collections/" + collectionNoHarvestSettings.getID() + "/harvester")))
.andExpect(jsonPath("$._embedded.harvestermetadata", Matchers.allOf(
MetadataConfigsMatcher.matchMetadataConfigs(configs)
)))
.andExpect(jsonPath("$._embedded.harvestermetadata._links.self.href",
endsWith("/api/config/harvestermetadata")));
}
@Test
public void PutWorksWithStandardSettings() throws Exception {
String token = getAuthToken(admin.getEmail(), password);
JSONObject json = createHarvestSettingsJson("METADATA_ONLY", "https://dspace.org/oai/request", "col_1721.1_114174", "dc");
getClient(token).perform(
put("/api/core/collections/" + collection.getID() + "/harvester")
.contentType("application/json")
.content(json.toString()))
.andExpect(status().isOk());
HarvestedCollection harvestedCollection = harvestedCollectionService.find(context, collection);
assertTrue(harvestedCollection.getHarvestType()
== HarvestTypeEnum.valueOf(json.getString("harvest_type")).getValue());
assertTrue(harvestedCollection.getOaiSource().equals(json.getString("oai_source")));
assertTrue(harvestedCollection.getOaiSetId().equals(json.getString("oai_set_id")));
assertTrue(harvestedCollection.getHarvestMetadataConfig().equals(json.getString("metadata_config_id")));
}
@Test
public void PutUnProcessableEntityIfIncorrectSettings() throws Exception {
String token = getAuthToken(admin.getEmail(), password);
JSONObject json = createHarvestSettingsJson("METADATA_ONLY", "https://mydspace.edu/oai/request", "col_1721.1_114174", "bc");
getClient(token).perform(
put("/api/core/collections/" + collection.getID() + "/harvester")
.contentType("application/json")
.content(json.toString()))
.andExpect(status().isUnprocessableEntity());
}
@Test
public void PutHarvestSettingsDeletedIfHarvestTypeIsNone() throws Exception {
String token = getAuthToken(admin.getEmail(), password);
JSONObject json = createHarvestSettingsJson("NONE", "", "", "dc");
getClient(token).perform(
put("/api/core/collections/" + collection.getID() + "/harvester")
.contentType("application/json")
.content(json.toString()))
.andExpect(status().isOk());
HarvestedCollection harvestedCollection = harvestedCollectionService.find(context, collection);
assertNull(harvestedCollection);
}
@Test
public void PutUnauthorizedIfNotAuthenticated() throws Exception {
getClient().perform(put("/api/core/collections/" + collection.getID() + "/harvester")
.contentType("application/json"))
.andExpect(status().isUnauthorized());
}
@Test
public void PutForbiddenIfNotEnoughpermissions() throws Exception {
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(put("/api/core/collections/" + collection.getID() + "/harvester")
.contentType("application/json"))
.andExpect(status().isForbidden());
}
@Test
public void PutNotFoundIfNoSuchCollection() throws Exception {
String token = getAuthToken(admin.getEmail(), password);
String fakeUuid = "6c9a081e-f2e5-42cd-8cf8-338f64b0841b";
getClient(token).perform(put("/api/core/collections/" + fakeUuid + "/harvester")
.contentType("application/json"))
.andExpect(status().isNotFound());
}
@Test
public void PutUnprocessableEntityIfHarvestTypeIncorrect() throws Exception {
String token = getAuthToken(admin.getEmail(), password);
JSONObject json = createHarvestSettingsJson("INCORRECT_HARVEST_TYPE", "https://mydspace.edu/oai/request", "col_1721.1_114174", "dc");
getClient(token).perform(
put("/api/core/collections/" + collection.getID() + "/harvester")
.contentType("application/json")
.content(json.toString()))
.andExpect(status().isUnprocessableEntity());
}
}

View File

@@ -98,6 +98,7 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build(); Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections") getClient().perform(get("/api/core/collections")
.param("size", "1")) .param("size", "1"))
@@ -147,6 +148,7 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build(); Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections/" + col1.getID())) getClient().perform(get("/api/core/collections/" + col1.getID()))
.andExpect(status().isOk()) .andExpect(status().isOk())
@@ -180,6 +182,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
.withLogo("TestingContentForLogo").build(); .withLogo("TestingContentForLogo").build();
Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build(); Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections/" + col1.getID())) getClient().perform(get("/api/core/collections/" + col1.getID()))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
@@ -225,6 +229,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build(); Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections/search/findAuthorized")) getClient().perform(get("/api/core/collections/search/findAuthorized"))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
@@ -254,6 +260,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build(); Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections/search/findAuthorizedByCommunity") getClient().perform(get("/api/core/collections/search/findAuthorizedByCommunity")
.param("uuid", parentCommunity.getID().toString())) .param("uuid", parentCommunity.getID().toString()))
.andExpect(status().isOk()) .andExpect(status().isOk())
@@ -294,6 +302,7 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build(); Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections/" + UUID.randomUUID())) getClient().perform(get("/api/core/collections/" + UUID.randomUUID()))
.andExpect(status().isNotFound()); .andExpect(status().isNotFound());
@@ -318,6 +327,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build(); Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections/" + col1.getID())) getClient().perform(get("/api/core/collections/" + col1.getID()))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
@@ -363,6 +374,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
collectionRest.setMetadata(new MetadataRest() collectionRest.setMetadata(new MetadataRest()
.put("dc.title", new MetadataValueRest("Electronic theses and dissertations"))); .put("dc.title", new MetadataValueRest("Electronic theses and dissertations")));
context.restoreAuthSystemState();
getClient(token).perform(put("/api/core/collections/" + col1.getID().toString()) getClient(token).perform(put("/api/core/collections/" + col1.getID().toString())
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsBytes(collectionRest))) .content(mapper.writeValueAsBytes(collectionRest)))
@@ -408,6 +421,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
String token = getAuthToken(admin.getEmail(), password); String token = getAuthToken(admin.getEmail(), password);
context.restoreAuthSystemState();
getClient(token).perform(get("/api/core/collections/" + col1.getID().toString())) getClient(token).perform(get("/api/core/collections/" + col1.getID().toString()))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
@@ -449,6 +464,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
.withName("Collection 1") .withName("Collection 1")
.build(); .build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections/" + col1.getID().toString())) getClient().perform(get("/api/core/collections/" + col1.getID().toString()))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
@@ -473,6 +490,7 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
.withName("Parent Community") .withName("Parent Community")
.withLogo("ThisIsSomeDummyText") .withLogo("ThisIsSomeDummyText")
.build(); .build();
context.restoreAuthSystemState();
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
CollectionRest collectionRest = new CollectionRest(); CollectionRest collectionRest = new CollectionRest();
@@ -519,6 +537,111 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
} }
@Test
public void createTestByAuthorizedUser() 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")));
// ADD authorization on parent community
context.setCurrentUser(eperson);
authorizeService.addPolicy(context, parentCommunity, Constants.ADD, eperson);
context.restoreAuthSystemState();
String authToken = getAuthToken(eperson.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 createTestByUnauthorizedUser() 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")));
context.setCurrentUser(eperson);
context.restoreAuthSystemState();
// User doesn't have add permission on the collection.
String authToken = getAuthToken(eperson.getEmail(), password);
getClient(authToken).perform(post("/api/core/collections")
.content(mapper.writeValueAsBytes(collectionRest))
.param("parent", parentCommunity.getID().toString())
.contentType(contentType))
.andExpect(status().isForbidden());
}
@Test @Test
public void deleteCollectionEpersonWithDeleteRightsTest() throws Exception { public void deleteCollectionEpersonWithDeleteRightsTest() throws Exception {
@@ -552,6 +675,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
String token = getAuthToken(eperson.getEmail(), password); String token = getAuthToken(eperson.getEmail(), password);
context.restoreAuthSystemState();
getClient(token).perform(get("/api/core/collections/" + col1.getID().toString())) getClient(token).perform(get("/api/core/collections/" + col1.getID().toString()))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
@@ -600,6 +725,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
context.setCurrentUser(eperson); context.setCurrentUser(eperson);
authorizeService.addPolicy(context, col1, Constants.WRITE, eperson); authorizeService.addPolicy(context, col1, Constants.WRITE, eperson);
context.restoreAuthSystemState();
String token = getAuthToken(eperson.getEmail(), password); String token = getAuthToken(eperson.getEmail(), password);
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
@@ -677,6 +804,9 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
new MetadataValueRest("Title Text"))); new MetadataValueRest("Title Text")));
String authToken = getAuthToken(admin.getEmail(), password); String authToken = getAuthToken(admin.getEmail(), password);
context.restoreAuthSystemState();
getClient(authToken).perform(post("/api/core/collections") getClient(authToken).perform(post("/api/core/collections")
.content(mapper.writeValueAsBytes(collectionRest)) .content(mapper.writeValueAsBytes(collectionRest))
.param("parent", "123") .param("parent", "123")
@@ -716,6 +846,9 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
new MetadataValueRest("Title Text"))); new MetadataValueRest("Title Text")));
String authToken = getAuthToken(admin.getEmail(), password); String authToken = getAuthToken(admin.getEmail(), password);
context.restoreAuthSystemState();
getClient(authToken).perform(post("/api/core/collections") getClient(authToken).perform(post("/api/core/collections")
.content(mapper.writeValueAsBytes(collectionRest)) .content(mapper.writeValueAsBytes(collectionRest))
.contentType(contentType)) .contentType(contentType))

View File

@@ -9,6 +9,7 @@ package org.dspace.app.rest;
import static com.jayway.jsonpath.JsonPath.read; import static com.jayway.jsonpath.JsonPath.read;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static junit.framework.TestCase.assertEquals;
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadata; 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;
@@ -21,8 +22,12 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
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.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@@ -31,6 +36,7 @@ import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.converter.CommunityConverter; 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.MetadataMatcher; import org.dspace.app.rest.matcher.MetadataMatcher;
import org.dspace.app.rest.matcher.PageMatcher;
import org.dspace.app.rest.model.CommunityRest; import org.dspace.app.rest.model.CommunityRest;
import org.dspace.app.rest.model.MetadataRest; import org.dspace.app.rest.model.MetadataRest;
import org.dspace.app.rest.model.MetadataValueRest; import org.dspace.app.rest.model.MetadataValueRest;
@@ -43,9 +49,12 @@ import org.dspace.content.service.CommunityService;
import org.dspace.core.Constants; import org.dspace.core.Constants;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
/** /**
* Integration Tests against the /api/core/communities endpoint (including any subpaths) * Integration Tests against the /api/core/communities endpoint (including any subpaths)
@@ -132,7 +141,7 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
} }
@Test @Test
public void createWithParentTest() throws Exception { public void createSubCommunityUnAuthorizedTest() throws Exception {
//We turn off the authorization system in order to create the structure as defined below //We turn off the authorization system in order to create the structure as defined below
context.turnOffAuthorisationSystem(); context.turnOffAuthorisationSystem();
@@ -140,6 +149,7 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
parentCommunity = CommunityBuilder.createCommunity(context) parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community") .withName("Parent Community")
.build(); .build();
context.restoreAuthSystemState(); context.restoreAuthSystemState();
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
@@ -147,26 +157,65 @@ 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 Sub-Level Community"); comm.setName("Test Sub-Level Community");
comm.setMetadata(new MetadataRest() // Anonymous user tries to create a community.
.put("dc.description", // Should fail because user is not authenticated. Error 401.
new MetadataValueRest("<p>Some cool HTML code here</p>")) getClient().perform(post("/api/core/communities")
.put("dc.description.abstract", .content(mapper.writeValueAsBytes(comm))
new MetadataValueRest("Sample top-level community created via the REST API")) .param("parent", parentCommunity.getID().toString())
.put("dc.description.tableofcontents", .contentType(contentType))
new MetadataValueRest("<p>HTML News</p>")) .andExpect(status().isUnauthorized());
.put("dc.rights",
new MetadataValueRest("Custom Copyright Text"))
.put("dc.title",
new MetadataValueRest("Title Text")));
String authToken = getAuthToken(admin.getEmail(), password); // Non-admin Eperson tries to create a community.
// Capture the UUID of the created Community (see andDo() below) // Should fail because user doesn't have permissions. Error 403.
String authToken = getAuthToken(eperson.getEmail(), password);
getClient(authToken).perform(post("/api/core/communities")
.content(mapper.writeValueAsBytes(comm))
.param("parent", parentCommunity.getID().toString())
.contentType(contentType))
.andExpect(status().isForbidden());
}
@Test
public void createSubCommunityAuthorizedTest() throws Exception {
//We turn off the authorization system in order to create the structure as defined below
context.turnOffAuthorisationSystem();
// Create a parent community to POST a new sub-community to
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
// ADD authorization on parent community
context.setCurrentUser(eperson);
authorizeService.addPolicy(context, parentCommunity, Constants.ADD, eperson);
context.restoreAuthSystemState();
String authToken = getAuthToken(eperson.getEmail(), password);
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")));
// Capture the UUID of the created Community (see andDo() below)
AtomicReference<UUID> idRef = new AtomicReference<UUID>(); AtomicReference<UUID> idRef = new AtomicReference<UUID>();
try { try {
getClient(authToken).perform(post("/api/core/communities") getClient(authToken).perform(post("/api/core/communities")
.content(mapper.writeValueAsBytes(comm)) .content(mapper.writeValueAsBytes(comm))
.param("parent", parentCommunity.getID().toString()) .param("parent", parentCommunity.getID().toString())
.contentType(contentType)) .contentType(contentType))
.andExpect(status().isCreated()) .andExpect(status().isCreated())
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
.andExpect(jsonPath("$", Matchers.allOf( .andExpect(jsonPath("$", Matchers.allOf(
@@ -181,15 +230,15 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
hasJsonPath("$._links.self.href", not(empty())), hasJsonPath("$._links.self.href", not(empty())),
hasJsonPath("$.metadata", Matchers.allOf( hasJsonPath("$.metadata", Matchers.allOf(
MetadataMatcher.matchMetadata("dc.description", MetadataMatcher.matchMetadata("dc.description",
"<p>Some cool HTML code here</p>"), "<p>Some cool HTML code here</p>"),
MetadataMatcher.matchMetadata("dc.description.abstract", MetadataMatcher.matchMetadata("dc.description.abstract",
"Sample top-level community created via the REST API"), "Sample top-level community created via the REST API"),
MetadataMatcher.matchMetadata("dc.description.tableofcontents", MetadataMatcher.matchMetadata("dc.description.tableofcontents",
"<p>HTML News</p>"), "<p>HTML News</p>"),
MetadataMatcher.matchMetadata("dc.rights", MetadataMatcher.matchMetadata("dc.rights",
"Custom Copyright Text"), "Custom Copyright Text"),
MetadataMatcher.matchMetadata("dc.title", MetadataMatcher.matchMetadata("dc.title",
"Title Text") "Title Text")
) )
) )
))) )))
@@ -218,6 +267,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
comm.setMetadata(metadataRest); comm.setMetadata(metadataRest);
context.restoreAuthSystemState();
// 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.
getClient().perform(post("/api/core/communities") getClient().perform(post("/api/core/communities")
@@ -253,6 +304,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
.withLogo("Test Logo") .withLogo("Test Logo")
.build(); .build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities")) getClient().perform(get("/api/core/communities"))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
@@ -269,6 +322,125 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
; ;
} }
@Test
public void findAllNoDuplicatesOnMultipleCommunityTitlesTest() throws Exception {
context.turnOffAuthorisationSystem();
List<String> titles = Arrays.asList("First title", "Second title", "Third title", "Fourth title");
parentCommunity = CommunityBuilder.createCommunity(context)
.withName(titles.get(0))
.withTitle(titles.get(1))
.withTitle(titles.get(2))
.withTitle(titles.get(3))
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
getClient().perform(get("/api/core/communities").param("size", "2"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.communities", Matchers.containsInAnyOrder(
CommunityMatcher.matchCommunityEntryMultipleTitles(titles, parentCommunity.getID(),
parentCommunity.getHandle()),
CommunityMatcher.matchCommunityEntry(child1.getID(), child1.getHandle())
)))
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities")))
.andExpect(jsonPath("$.page.totalElements", is(2)))
.andExpect(jsonPath("$.page.totalPages", is(1)));
}
@Test
public void findAllNoDuplicatesOnMultipleCommunityTitlesPaginationTest() throws Exception {
context.turnOffAuthorisationSystem();
List<String> titles = Arrays.asList("First title", "Second title", "Third title", "Fourth title");
parentCommunity = CommunityBuilder.createCommunity(context)
.withName(titles.get(0))
.withTitle(titles.get(1))
.withTitle(titles.get(2))
.withTitle(titles.get(3))
.build();
Community childCommunity = CommunityBuilder.createSubCommunity(context, parentCommunity).build();
Community secondParentCommunity = CommunityBuilder.createCommunity(context).withName("testing").build();
Community thirdParentCommunity = CommunityBuilder.createCommunity(context).withName("testingTitleTwo").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities").param("size", "2"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.communities", Matchers.containsInAnyOrder(
CommunityMatcher.matchCommunityEntryMultipleTitles(titles, parentCommunity.getID(),
parentCommunity.getHandle()),
CommunityMatcher.matchCommunityEntry(childCommunity.getID(), childCommunity.getHandle())
)))
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities")))
.andExpect(jsonPath("$.page", PageMatcher.pageEntryWithTotalPagesAndElements(0, 2, 2, 4)));
getClient().perform(get("/api/core/communities").param("size", "2").param("page", "1"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.communities", Matchers.containsInAnyOrder(
CommunityMatcher.matchCommunityEntry(secondParentCommunity.getID(),
secondParentCommunity.getHandle()),
CommunityMatcher.matchCommunityEntry(thirdParentCommunity.getID(),
thirdParentCommunity.getHandle())
)))
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities")))
.andExpect(jsonPath("$.page", PageMatcher.pageEntryWithTotalPagesAndElements(1, 2, 2, 4)));
}
@Test
public void findAllNoNameCommunityIsReturned() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context).build();
getClient().perform(get("/api/core/communities"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.communities", Matchers.contains(
CommunityMatcher.matchCommunityEntry(parentCommunity.getID(),
parentCommunity.getHandle())
)))
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/communities")))
.andExpect(jsonPath("$.page.totalElements", is(1)));
}
@Test
public void findAllCommunitiesAreReturnedInCorrectOrder() throws Exception {
// The hibernate query for finding all communities is "SELECT ... ORDER BY STR(dc_title.value)"
// So the communities should be returned in alphabetical order
context.turnOffAuthorisationSystem();
List<String> orderedTitles = Arrays.asList("Abc", "Bcd", "Cde");
Community community1 = CommunityBuilder.createCommunity(context)
.withName(orderedTitles.get(0))
.build();
Community community2 = CommunityBuilder.createCommunity(context)
.withName(orderedTitles.get(1))
.build();
Community community3 = CommunityBuilder.createCommunity(context)
.withName(orderedTitles.get(2))
.build();
ObjectMapper mapper = new ObjectMapper();
MvcResult result = getClient().perform(get("/api/core/communities")).andReturn();
String response = result.getResponse().getContentAsString();
JSONArray communities = new JSONObject(response).getJSONObject("_embedded").getJSONArray("communities");
List<String> responseTitles = StreamSupport.stream(communities.spliterator(), false)
.map(JSONObject.class::cast)
.map(x -> x.getString("name"))
.collect(Collectors.toList());
assertEquals(orderedTitles, responseTitles);
}
@Test @Test
public void findAllPaginationTest() throws Exception { public void findAllPaginationTest() throws Exception {
//We turn off the authorization system in order to create the structure as defined below //We turn off the authorization system in order to create the structure as defined below
@@ -284,6 +456,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
.build(); .build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities") getClient().perform(get("/api/core/communities")
.param("size", "1")) .param("size", "1"))
.andExpect(status().isOk()) .andExpect(status().isOk())
@@ -335,6 +509,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
.build(); .build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString())) getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
@@ -367,6 +543,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
.build(); .build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString())) getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
@@ -444,6 +622,7 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
.build(); .build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities/search/top")) getClient().perform(get("/api/core/communities/search/top"))
.andExpect(status().isOk()) .andExpect(status().isOk())
@@ -504,6 +683,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
.withName("Collection 1") .withName("Collection 1")
.build(); .build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities/search/subCommunities") getClient().perform(get("/api/core/communities/search/subCommunities")
.param("parent", parentCommunity.getID().toString())) .param("parent", parentCommunity.getID().toString()))
.andExpect(status().isOk()) .andExpect(status().isOk())
@@ -611,6 +792,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
.build(); .build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities/" + UUID.randomUUID())).andExpect(status().isNotFound()); getClient().perform(get("/api/core/communities/" + UUID.randomUUID())).andExpect(status().isNotFound());
} }
@@ -653,6 +836,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
communityRest.setMetadata(new MetadataRest() communityRest.setMetadata(new MetadataRest()
.put("dc.title", new MetadataValueRest("Electronic theses and dissertations"))); .put("dc.title", new MetadataValueRest("Electronic theses and dissertations")));
context.restoreAuthSystemState();
getClient(token).perform(put("/api/core/communities/" + parentCommunity.getID().toString()) getClient(token).perform(put("/api/core/communities/" + parentCommunity.getID().toString())
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsBytes(communityRest))) .content(mapper.writeValueAsBytes(communityRest)))
@@ -711,6 +896,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
String token = getAuthToken(admin.getEmail(), password); String token = getAuthToken(admin.getEmail(), password);
context.restoreAuthSystemState();
getClient(token).perform(get("/api/core/communities/" + parentCommunity.getID().toString())) getClient(token).perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
@@ -770,6 +957,7 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
.withName("Collection 1") .withName("Collection 1")
.build(); .build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString())) getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
.andExpect(status().isOk()) .andExpect(status().isOk())
@@ -801,6 +989,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
String token = getAuthToken(eperson.getEmail(), password); String token = getAuthToken(eperson.getEmail(), password);
context.restoreAuthSystemState();
getClient(token).perform(get("/api/core/communities/" + parentCommunity.getID().toString())) getClient(token).perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().contentType(contentType)) .andExpect(content().contentType(contentType))
@@ -859,6 +1049,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
context.setCurrentUser(eperson); context.setCurrentUser(eperson);
authorizeService.addPolicy(context, parentCommunity, Constants.WRITE, eperson); authorizeService.addPolicy(context, parentCommunity, Constants.WRITE, eperson);
context.restoreAuthSystemState();
String token = getAuthToken(eperson.getEmail(), password); String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(put("/api/core/communities/" + parentCommunity.getID().toString()) getClient(token).perform(put("/api/core/communities/" + parentCommunity.getID().toString())
@@ -935,6 +1127,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
comm.setMetadata(metadataRest); comm.setMetadata(metadataRest);
context.restoreAuthSystemState();
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")
.param("parent", "123") .param("parent", "123")

View File

@@ -0,0 +1,64 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest;
import static org.hamcrest.Matchers.endsWith;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.List;
import java.util.Map;
import org.dspace.app.rest.matcher.MetadataConfigsMatcher;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.harvest.OAIHarvester;
import org.hamcrest.Matchers;
import org.junit.Test;
/**
* Integration test for harvester metadata controller
*
* @author Jelle Pelgrims (jelle.pelgrims at atmire.com)
*/
public class HarvesterMetadataControllerIT extends AbstractControllerIntegrationTest {
@Test
public void GetReturnsAllAvailableMetadataFormats() throws Exception {
String token = getAuthToken(admin.getEmail(), password);
List<Map<String,String>> configs = OAIHarvester.getAvailableMetadataFormats();
getClient(token).perform(
get("/api/config/harvestermetadata"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.allOf(
MetadataConfigsMatcher.matchMetadataConfigs(configs)
)))
.andExpect(jsonPath("$._links.self.href", endsWith("/api/config/harvestermetadata")));
getClient().perform(
get("/api/config/harvestermetadata"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.allOf(
MetadataConfigsMatcher.matchMetadataConfigs(configs)
)))
.andExpect(jsonPath("$._links.self.href", endsWith("/api/config/harvestermetadata")));
token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(
get("/api/config/harvestermetadata"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.allOf(
MetadataConfigsMatcher.matchMetadataConfigs(configs)
)))
.andExpect(jsonPath("$._links.self.href", endsWith("/api/config/harvestermetadata")));
}
}

View File

@@ -49,87 +49,87 @@ public class RelationshipTypeRestRepositoryIT extends AbstractEntityIntegrationT
public void findPublicationPersonRelationshipType() throws SQLException { public void findPublicationPersonRelationshipType() throws SQLException {
String leftTypeString = "Publication"; String leftTypeString = "Publication";
String rightTypeString = "Person"; String rightTypeString = "Person";
String leftLabel = "isAuthorOfPublication"; String leftwardType = "isAuthorOfPublication";
String rightLabel = "isPublicationOfAuthor"; String rightwardType = "isPublicationOfAuthor";
checkRelationshipType(leftTypeString, rightTypeString, leftLabel, rightLabel); checkRelationshipType(leftTypeString, rightTypeString, leftwardType, rightwardType);
} }
@Test @Test
public void findPublicationProjectRelationshipType() throws SQLException { public void findPublicationProjectRelationshipType() throws SQLException {
String leftTypeString = "Publication"; String leftTypeString = "Publication";
String rightTypeString = "Project"; String rightTypeString = "Project";
String leftLabel = "isProjectOfPublication"; String leftwardType = "isProjectOfPublication";
String rightLabel = "isPublicationOfProject"; String rightwardType = "isPublicationOfProject";
checkRelationshipType(leftTypeString, rightTypeString, leftLabel, rightLabel); checkRelationshipType(leftTypeString, rightTypeString, leftwardType, rightwardType);
} }
@Test @Test
public void findPublicationOrgUnitRelationshipType() throws SQLException { public void findPublicationOrgUnitRelationshipType() throws SQLException {
String leftTypeString = "Publication"; String leftTypeString = "Publication";
String rightTypeString = "OrgUnit"; String rightTypeString = "OrgUnit";
String leftLabel = "isOrgUnitOfPublication"; String leftwardType = "isOrgUnitOfPublication";
String rightLabel = "isPublicationOfOrgUnit"; String rightwardType = "isPublicationOfOrgUnit";
checkRelationshipType(leftTypeString, rightTypeString, leftLabel, rightLabel); checkRelationshipType(leftTypeString, rightTypeString, leftwardType, rightwardType);
} }
@Test @Test
public void findPersonProjectRelationshipType() throws SQLException { public void findPersonProjectRelationshipType() throws SQLException {
String leftTypeString = "Person"; String leftTypeString = "Person";
String rightTypeString = "Project"; String rightTypeString = "Project";
String leftLabel = "isProjectOfPerson"; String leftwardType = "isProjectOfPerson";
String rightLabel = "isPersonOfProject"; String rightwardType = "isPersonOfProject";
checkRelationshipType(leftTypeString, rightTypeString, leftLabel, rightLabel); checkRelationshipType(leftTypeString, rightTypeString, leftwardType, rightwardType);
} }
@Test @Test
public void findPersonOrgUnitRelationshipType() throws SQLException { public void findPersonOrgUnitRelationshipType() throws SQLException {
String leftTypeString = "Person"; String leftTypeString = "Person";
String rightTypeString = "OrgUnit"; String rightTypeString = "OrgUnit";
String leftLabel = "isOrgUnitOfPerson"; String leftwardType = "isOrgUnitOfPerson";
String rightLabel = "isPersonOfOrgUnit"; String rightwardType = "isPersonOfOrgUnit";
checkRelationshipType(leftTypeString, rightTypeString, leftLabel, rightLabel); checkRelationshipType(leftTypeString, rightTypeString, leftwardType, rightwardType);
} }
@Test @Test
public void findProjectOrgUnitRelationshipType() throws SQLException { public void findProjectOrgUnitRelationshipType() throws SQLException {
String leftTypeString = "Project"; String leftTypeString = "Project";
String rightTypeString = "OrgUnit"; String rightTypeString = "OrgUnit";
String leftLabel = "isOrgUnitOfProject"; String leftwardType = "isOrgUnitOfProject";
String rightLabel = "isProjectOfOrgUnit"; String rightwardType = "isProjectOfOrgUnit";
checkRelationshipType(leftTypeString, rightTypeString, leftLabel, rightLabel); checkRelationshipType(leftTypeString, rightTypeString, leftwardType, rightwardType);
} }
@Test @Test
public void findJournalJournalVolumeRelationshipType() throws SQLException { public void findJournalJournalVolumeRelationshipType() throws SQLException {
String leftTypeString = "Journal"; String leftTypeString = "Journal";
String rightTypeString = "JournalVolume"; String rightTypeString = "JournalVolume";
String leftLabel = "isVolumeOfJournal"; String leftwardType = "isVolumeOfJournal";
String rightLabel = "isJournalOfVolume"; String rightwardType = "isJournalOfVolume";
checkRelationshipType(leftTypeString, rightTypeString, leftLabel, rightLabel); checkRelationshipType(leftTypeString, rightTypeString, leftwardType, rightwardType);
} }
@Test @Test
public void findJournalVolumeJournalIssueRelationshipType() throws SQLException { public void findJournalVolumeJournalIssueRelationshipType() throws SQLException {
String leftTypeString = "JournalVolume"; String leftTypeString = "JournalVolume";
String rightTypeString = "JournalIssue"; String rightTypeString = "JournalIssue";
String leftLabel = "isIssueOfJournalVolume"; String leftwardType = "isIssueOfJournalVolume";
String rightLabel = "isJournalVolumeOfIssue"; String rightwardType = "isJournalVolumeOfIssue";
checkRelationshipType(leftTypeString, rightTypeString, leftLabel, rightLabel); checkRelationshipType(leftTypeString, rightTypeString, leftwardType, rightwardType);
} }
private void checkRelationshipType(String leftType, String rightType, String leftLabel, String rightLabel) private void checkRelationshipType(String leftType, String rightType, String leftwardType, String rightwardType)
throws SQLException { throws SQLException {
RelationshipType relationshipType = relationshipTypeService RelationshipType relationshipType = relationshipTypeService
.findbyTypesAndLabels(context, entityTypeService.findByEntityType(context, leftType), .findbyTypesAndLabels(context, entityTypeService.findByEntityType(context, leftType),
entityTypeService.findByEntityType(context, rightType), entityTypeService.findByEntityType(context, rightType),
leftLabel, rightLabel); leftwardType, rightwardType);
assertNotNull(relationshipType); assertNotNull(relationshipType);
assertEquals(entityTypeService.findByEntityType(context, leftType), assertEquals(entityTypeService.findByEntityType(context, leftType),
relationshipType.getLeftType()); relationshipType.getLeftType());
assertEquals(entityTypeService.findByEntityType(context, rightType), assertEquals(entityTypeService.findByEntityType(context, rightType),
relationshipType.getRightType()); relationshipType.getRightType());
assertEquals(leftLabel, relationshipType.getLeftLabel()); assertEquals(leftwardType, relationshipType.getLeftwardType());
assertEquals(rightLabel, relationshipType.getRightLabel()); assertEquals(rightwardType, relationshipType.getRightwardType());
} }
@Test @Test
@@ -167,8 +167,8 @@ public class RelationshipTypeRestRepositoryIT extends AbstractEntityIntegrationT
RelationshipType foundRelationshipType = null; RelationshipType foundRelationshipType = null;
for (RelationshipType relationshipType : relationshipTypes) { for (RelationshipType relationshipType : relationshipTypes) {
if (StringUtils.equals(relationshipType.getLeftLabel(), "isAuthorOfPublication") && StringUtils if (StringUtils.equals(relationshipType.getLeftwardType(), "isAuthorOfPublication") && StringUtils
.equals(relationshipType.getRightLabel(), "isPublicationOfAuthor")) { .equals(relationshipType.getRightwardType(), "isPublicationOfAuthor")) {
foundRelationshipType = relationshipType; foundRelationshipType = relationshipType;
break; break;
} }
@@ -212,8 +212,8 @@ public class RelationshipTypeRestRepositoryIT extends AbstractEntityIntegrationT
RelationshipType foundRelationshipType = null; RelationshipType foundRelationshipType = null;
for (RelationshipType relationshipType : relationshipTypes) { for (RelationshipType relationshipType : relationshipTypes) {
if (StringUtils.equals(relationshipType.getLeftLabel(), "isIssueOfJournalVolume") && StringUtils if (StringUtils.equals(relationshipType.getLeftwardType(), "isIssueOfJournalVolume") && StringUtils
.equals(relationshipType.getRightLabel(), "isJournalVolumeOfIssue")) { .equals(relationshipType.getRightwardType(), "isJournalVolumeOfIssue")) {
foundRelationshipType = relationshipType; foundRelationshipType = relationshipType;
break; break;
} }

View File

@@ -63,6 +63,10 @@ public class CommunityBuilder extends AbstractDSpaceObjectBuilder<Community> {
return setMetadataSingleValue(community, MetadataSchemaEnum.DC.getName(), "title", null, communityName); return setMetadataSingleValue(community, MetadataSchemaEnum.DC.getName(), "title", null, communityName);
} }
public CommunityBuilder withTitle(final String communityTitle) {
return addMetadataValue(community, MetadataSchemaEnum.DC.getName(), "title", null, communityTitle);
}
public CommunityBuilder withLogo(String content) throws AuthorizeException, IOException, SQLException { public CommunityBuilder withLogo(String content) throws AuthorizeException, IOException, SQLException {
try (InputStream is = IOUtils.toInputStream(content, CharEncoding.UTF_8)) { try (InputStream is = IOUtils.toInputStream(content, CharEncoding.UTF_8)) {
communityService.setLogo(context, community, is); communityService.setLogo(context, community, is);

View File

@@ -84,4 +84,19 @@ public class RelationshipBuilder extends AbstractBuilder<Relationship, Relations
return this; return this;
} }
public RelationshipBuilder withLeftwardValue(String leftwardValue) throws SQLException {
relationship.setLeftwardValue(leftwardValue);
return this;
}
public RelationshipBuilder withRightwardValue(String rightwardValue) throws SQLException {
relationship.setRightwardValue(rightwardValue);
return this;
}
public RelationshipBuilder withLeftPlace(int leftPlace) {
relationship.setLeftPlace(leftPlace);
return this;
}
} }

View File

@@ -74,28 +74,30 @@ public class RelationshipTypeBuilder extends AbstractBuilder<RelationshipType, R
} }
public static RelationshipTypeBuilder createRelationshipTypeBuilder(Context context, EntityType leftType, public static RelationshipTypeBuilder createRelationshipTypeBuilder(Context context, EntityType leftType,
EntityType rightType, String leftLabel, EntityType rightType,
String rightLabel, Integer leftCardinalityMin, String leftwardType,
String rightwardType,
Integer leftCardinalityMin,
Integer leftCardinalityMax, Integer leftCardinalityMax,
Integer rightCardinalityMin, Integer rightCardinalityMin,
Integer rightCardinalityMax) { Integer rightCardinalityMax) {
RelationshipTypeBuilder relationshipBuilder = new RelationshipTypeBuilder(context); RelationshipTypeBuilder relationshipBuilder = new RelationshipTypeBuilder(context);
return relationshipBuilder.create(context, leftType, return relationshipBuilder.create(context, leftType,
rightType, leftLabel, rightType, leftwardType,
rightLabel, leftCardinalityMin, rightwardType, leftCardinalityMin,
leftCardinalityMax, rightCardinalityMin, leftCardinalityMax, rightCardinalityMin,
rightCardinalityMax); rightCardinalityMax);
} }
private RelationshipTypeBuilder create(Context context, EntityType leftEntityType, EntityType rightEntityType, private RelationshipTypeBuilder create(Context context, EntityType leftEntityType, EntityType rightEntityType,
String leftLabel, String rightLabel, Integer leftCardinalityMin, String leftwardType, String rightwardType, Integer leftCardinalityMin,
Integer leftCardinalityMax, Integer rightCardinalityMin, Integer leftCardinalityMax, Integer rightCardinalityMin,
Integer rightCardinalityMax) { Integer rightCardinalityMax) {
try { try {
this.context = context; this.context = context;
this.relationshipType = relationshipTypeService this.relationshipType = relationshipTypeService
.create(context, leftEntityType, rightEntityType, leftLabel, rightLabel, leftCardinalityMin, .create(context, leftEntityType, rightEntityType, leftwardType, rightwardType, leftCardinalityMin,
leftCardinalityMax, rightCardinalityMin, rightCardinalityMax); leftCardinalityMax, rightCardinalityMin, rightCardinalityMax);
} catch (SQLException | AuthorizeException e) { } catch (SQLException | AuthorizeException e) {

View File

@@ -11,6 +11,7 @@ import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.dspace.content.Collection; import org.dspace.content.Collection;
@@ -21,6 +22,34 @@ public class CommunityMatcher {
private CommunityMatcher() { } private CommunityMatcher() { }
// Matcher for communities with no titles / no name
// Since a name is simply the first title (see Community.java), we cannot use the matchers below
public static Matcher<? super Object> matchCommunityEntry(UUID uuid, String handle) {
return allOf(
hasJsonPath("$.uuid", is(uuid.toString())),
hasJsonPath("$.handle", is(handle)),
hasJsonPath("$.type", is("community")),
hasJsonPath("$._embedded.collections", Matchers.not(Matchers.empty())),
hasJsonPath("$._embedded.logo", Matchers.not(Matchers.empty())),
matchLinks(uuid)
);
}
// Matcher for communities with multiple titles
// The title metadata for communities with multiple titles contains a list, so the matchers below can't be used
public static Matcher<? super Object> matchCommunityEntryMultipleTitles(List<String> titles, UUID uuid,
String handle) {
return allOf(
hasJsonPath("$.uuid", is(uuid.toString())),
hasJsonPath("$.name", is(titles.get(0))),
hasJsonPath("$.handle", is(handle)),
hasJsonPath("$.type", is("community")),
hasJsonPath("$._embedded.collections", Matchers.not(Matchers.empty())),
hasJsonPath("$._embedded.logo", Matchers.not(Matchers.empty())),
matchLinks(uuid)
);
}
public static Matcher<? super Object> matchCommunityEntry(String name, UUID uuid, String handle) { public static Matcher<? super Object> matchCommunityEntry(String name, UUID uuid, String handle) {
return allOf( return allOf(
matchProperties(name, uuid, handle), matchProperties(name, uuid, handle),

View File

@@ -0,0 +1,48 @@
/**
* 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.endsWith;
import static org.hamcrest.Matchers.is;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.hamcrest.Matcher;
/**
* Class used for matching metadata configurations in JSON
*/
public class MetadataConfigsMatcher {
private MetadataConfigsMatcher() { }
public static Matcher<? super Object> matchMetadataConfigs(List<Map<String,String>> configs) {
/**
* This function returns a list of values, containing the values matching the key for each
* configuration (Map<String,String>) in the list. For example, getAllValues("id") returns
* a list with the ids of every configuration in the list.
*/
Function<String,List<String>> getAllValues = key -> configs.stream()
.map(x -> x.get(key))
.collect(Collectors.toList());
return allOf(
hasJsonPath("$.configs[*].id", is(getAllValues.apply("id"))),
hasJsonPath("$.configs[*].label", is(getAllValues.apply("label"))),
hasJsonPath("$.configs[*].namespace", is(getAllValues.apply("namespace"))),
hasJsonPath("$._links.self.href", endsWith("/api/config/harvestermetadata"))
);
}
}

View File

@@ -8,7 +8,7 @@
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.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import org.hamcrest.Matcher; import org.hamcrest.Matcher;
@@ -18,7 +18,8 @@ import org.hamcrest.Matcher;
*/ */
public class MetadataMatcher { public class MetadataMatcher {
private MetadataMatcher() { } private MetadataMatcher() {
}
/** /**
* Gets a matcher to ensure a given value is present among all values for a given metadata key. * Gets a matcher to ensure a given value is present among all values for a given metadata key.
@@ -28,7 +29,7 @@ public class MetadataMatcher {
* @return the matcher. * @return the matcher.
*/ */
public static Matcher<? super Object> matchMetadata(String key, String value) { public static Matcher<? super Object> matchMetadata(String key, String value) {
return hasJsonPath("$.['" + key + "'][*].value", contains(value)); return hasJsonPath("$.['" + key + "'][*].value", hasItem(value));
} }
/** /**

View File

@@ -38,8 +38,8 @@ public class RelationshipTypeMatcher {
String rightEntityTypeLabel) { String rightEntityTypeLabel) {
return matchExplicitRelationshipTypeValuesAndExplicitEntityTypeValues(relationshipType.getID(), return matchExplicitRelationshipTypeValuesAndExplicitEntityTypeValues(relationshipType.getID(),
relationshipType.getLeftLabel(), relationshipType.getLeftwardType(),
relationshipType.getRightLabel(), relationshipType.getRightwardType(),
relationshipType.getLeftMinCardinality(), relationshipType.getLeftMinCardinality(),
relationshipType.getLeftMaxCardinality(), relationshipType.getLeftMaxCardinality(),
relationshipType.getRightMinCardinality(), relationshipType.getRightMinCardinality(),
@@ -49,10 +49,10 @@ public class RelationshipTypeMatcher {
} }
private static Matcher<? super Object> matchExplicitRelationshipTypeValuesAndExplicitEntityType(int id, private static Matcher<? super Object> matchExplicitRelationshipTypeValuesAndExplicitEntityType(int id,
String leftLabel, String rightLabel, Integer leftMinCardinality, Integer leftMaxCardinality, String leftwardType, String rightwardType, Integer leftMinCardinality, Integer leftMaxCardinality,
Integer rightMinCardinality, Integer rightMaxCardinality, Integer rightMinCardinality, Integer rightMaxCardinality,
EntityType leftEntityType, EntityType rightEntityType) { EntityType leftEntityType, EntityType rightEntityType) {
return matchExplicitRelationshipTypeValuesAndExplicitEntityTypeValues(id, leftLabel, rightLabel, return matchExplicitRelationshipTypeValuesAndExplicitEntityTypeValues(id, leftwardType, rightwardType,
leftMinCardinality, leftMaxCardinality, leftMinCardinality, leftMaxCardinality,
rightMinCardinality, rightMinCardinality,
rightMaxCardinality, rightMaxCardinality,
@@ -63,13 +63,13 @@ public class RelationshipTypeMatcher {
} }
private static Matcher<? super Object> matchExplicitRelationshipTypeValuesAndExplicitEntityTypeValues(int id, private static Matcher<? super Object> matchExplicitRelationshipTypeValuesAndExplicitEntityTypeValues(int id,
String leftLabel, String rightLabel, Integer leftMinCardinality, Integer leftMaxCardinality, String leftwardType, String rightwardType, Integer leftMinCardinality, Integer leftMaxCardinality,
Integer rightMinCardinality, Integer rightMaxCardinality, int leftEntityTypeId, String leftEntityTypeLabel, Integer rightMinCardinality, Integer rightMaxCardinality, int leftEntityTypeId, String leftEntityTypeLabel,
int rightEntityTypeId, String rightEntityTypeLabel) { int rightEntityTypeId, String rightEntityTypeLabel) {
return allOf( return allOf(
hasJsonPath("$.id", is(id)), hasJsonPath("$.id", is(id)),
hasJsonPath("$.leftLabel", is(leftLabel)), hasJsonPath("$.leftwardType", is(leftwardType)),
hasJsonPath("$.rightLabel", is(rightLabel)), hasJsonPath("$.rightwardType", is(rightwardType)),
hasJsonPath("$.leftMinCardinality", is(leftMinCardinality)), hasJsonPath("$.leftMinCardinality", is(leftMinCardinality)),
hasJsonPath("$.leftMaxCardinality", is(leftMaxCardinality)), hasJsonPath("$.leftMaxCardinality", is(leftMaxCardinality)),
hasJsonPath("$.rightMinCardinality", is(rightMinCardinality)), hasJsonPath("$.rightMinCardinality", is(rightMinCardinality)),

View File

@@ -0,0 +1,30 @@
/**
* 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.harvest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* A mock for the HarvestedCollectionService
* @author Jelle Pelgrims (jelle.pelgrims at atmire.com)
*/
public class MockHarvestedCollectionServiceImpl extends HarvestedCollectionServiceImpl {
@Override
public List<String> verifyOAIharvester(String oaiSource,
String oaiSetId, String metaPrefix, boolean testORE) {
if (metaPrefix.equals("dc")) {
return new ArrayList<>();
} else {
return Arrays.asList("(Mock error) Incorrect metadataConfigID");
}
}
}

View File

@@ -54,6 +54,7 @@
<bean class="org.dspace.content.EntityTypeServiceImpl"/> <bean class="org.dspace.content.EntityTypeServiceImpl"/>
<bean class="org.dspace.content.EntityServiceImpl"/> <bean class="org.dspace.content.EntityServiceImpl"/>
<bean class="org.dspace.content.RelationshipTypeServiceImpl"/> <bean class="org.dspace.content.RelationshipTypeServiceImpl"/>
<bean class="org.dspace.content.RelationshipMetadataServiceImpl"/>
<bean class="org.dspace.content.authority.ChoiceAuthorityServiceImpl"/> <bean class="org.dspace.content.authority.ChoiceAuthorityServiceImpl"/>
<bean class="org.dspace.content.authority.MetadataAuthorityServiceImpl" lazy-init="true"/> <bean class="org.dspace.content.authority.MetadataAuthorityServiceImpl" lazy-init="true"/>

View File

@@ -59,6 +59,7 @@
<value>, </value> <value>, </value>
</property> </property>
<property name="useForPlace" value="true"/> <property name="useForPlace" value="true"/>
<property name="populateWithNameVariant" value="true"/>
</bean> </bean>
<!-- Config like this will tell our VirtualMetadataPopulator to include the virtual metadata field <!-- Config like this will tell our VirtualMetadataPopulator to include the virtual metadata field

View File

@@ -0,0 +1,86 @@
# Docker Compose Resources
## root directory Resources
- docker-compose.yml
- Docker compose file to orchestrate DSpace 7 REST components
- docker-compose-cli
- Docker compose file to run DSpace CLI tasks within a running DSpace instance in Docker
## dspace/src/main/docker-compose resources
- cli.assetstore.yml
- Docker compose file that will download and install a default assetstore.
- cli.ingest.yml
- Docker compose file that will run an AIP ingest into DSpace 7.
- db.entities.yml
- Docker compose file that pre-populate a database instance using a SQL dump. The default dataset is the configurable entities test dataset.
- local.cfg
- Sets the environment used across containers run with docker-compose
- docker-compose-angular.yml
- Docker compose file that will start a published DSpace angular container that interacts with the branch.
- environment.dev.js
- Default angular environment when testing DSpace-angular from this repo
## To refresh / pull DSpace images from Dockerhub
```
docker-compose -f docker-compose.yml -f docker-compose-cli.yml pull
```
## To build DSpace images using code in your branch
```
docker-compose -f docker-compose.yml -f docker-compose-cli.yml build
```
## Run DSpace 7 REST from your current branch
```
docker-compose -p d7 up -d
```
## Run DSpace 7 REST and Angular from your branch
```
docker-compose -p d7 -f docker-compose.yml -f dspace/src/main/docker-compose/docker-compose-angular.yml up -d
```
## Run DSpace 7 REST and Angular from local branches
_The system will be started in 2 steps. Each step shares the same docker network._
From DSpace/DSpace
```
docker-compose -p d7 up -d
```
From DSpace/DSpace-angular (build as needed)
```
docker-compose -p d7 -f docker/docker-compose.yml up -d
```
## Ingest Option 1: Ingesting test content from AIP files into a running DSpace 7 instance
Prerequisites
- Start DSpace 7 using one of the options listed above
- Build the DSpace CLI image if needed. See the instructions above.
Create an admin account. By default, the dspace-cli container runs the dspace command.
```
docker-compose -p d7 -f docker-compose-cli.yml run --rm dspace-cli create-administrator -e test@test.edu -f admin -l user -p admin -c en
```
Download a Zip file of AIP content and ingest test data
```
docker-compose -p d7 -f docker-compose-cli.yml -f dspace/src/main/docker-compose/cli.ingest.yml run --rm dspace-cli
```
## Ingest Option 2: Ingest Entities Test Data
_Remove your d7 volumes if you already ingested content into your docker volumes_
Start DSpace REST with a postgres database dump downloaded from the internet.
```
docker-compose -p d7 -f docker-compose.yml -f dspace/src/main/docker-compose/db.entities.yml up -d
```
Download an assetstore from a tar file on the internet.
```
docker-compose -p d7 -f docker-compose-cli.yml -f dspace/src/main/docker-compose/cli.assetstore.yml run dspace-cli
```

View File

@@ -0,0 +1,28 @@
#
# The contents of this file are subject to the license and copyright
# detailed in the LICENSE and NOTICE files at the root of the source
# tree and available online at
#
# http://www.dspace.org/license/
#
version: "3.7"
services:
dspace-cli:
environment:
- LOADASSETS=https://www.dropbox.com/s/zv7lj8j2lp3egjs/assetstore.tar.gz?dl=1
entrypoint:
- /bin/bash
- '-c'
- |
if [ ! -z $${LOADASSETS} ]
then
curl $${LOADASSETS} -L -s --output /tmp/assetstore.tar.gz
cd /dspace
tar xvfz /tmp/assetstore.tar.gz
fi
/dspace/bin/dspace index-discovery
/dspace/bin/dspace oai import
/dspace/bin/dspace oai clean-cache

View File

@@ -0,0 +1,34 @@
#
# 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/
#
version: "3.7"
services:
dspace-cli:
environment:
- AIPZIP=https://github.com/DSpace-Labs/AIP-Files/raw/master/dogAndReport.zip
- ADMIN_EMAIL=test@test.edu
- AIPDIR=/tmp/aip-dir
entrypoint:
- /bin/bash
- '-c'
- |
rm -rf $${AIPDIR}
mkdir $${AIPDIR} /dspace/upload
cd $${AIPDIR}
pwd
curl $${AIPZIP} -L -s --output aip.zip
unzip aip.zip
cd $${AIPDIR}
/dspace/bin/dspace packager -r -a -t AIP -e $${ADMIN_EMAIL} -f -u SITE*.zip
/dspace/bin/dspace database update-sequences
touch /dspace/solr/search/conf/reindex.flag
/dspace/bin/dspace oai import
/dspace/bin/dspace oai clean-cache

View File

@@ -0,0 +1,16 @@
#
# 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/
#
version: "3.7"
services:
dspacedb:
image: dspace/dspace-postgres-pgcrypto:loadsql
environment:
# Double underbars in env names will be replaced with periods for apache commons
- LOADSQL=https://www.dropbox.com/s/xh3ack0vg0922p2/configurable-entities-2019-05-08.sql?dl=1

View File

@@ -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/
#
version: '3.7'
networks:
dspacenet:
services:
dspace-angular:
container_name: dspace-angular
depends_on:
- dspace
environment:
DSPACE_HOST: dspace-angular
DSPACE_NAMESPACE: /
DSPACE_PORT: '3000'
DSPACE_SSL: "false"
image: dspace/dspace-angular:latest
networks:
dspacenet: {}
ports:
- published: 3000
target: 3000
- published: 9876
target: 9876
stdin_open: true
tty: true
volumes:
- ./dspace/src/main/docker-compose/environment.dev.js:/app/config/environment.dev.js

View File

@@ -0,0 +1,16 @@
/*
* 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/
*/
module.exports = {
rest: {
ssl: false,
host: 'localhost',
port: 8080,
// NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
nameSpace: '/server/api'
}
};

View File

@@ -0,0 +1,6 @@
dspace.dir=/dspace
db.url=jdbc:postgresql://dspacedb:5432/dspace
dspace.hostname=dspace
dspace.baseUrl=http://localhost:8080
dspace.name=DSpace Started with Docker Compose
solr.server=http://dspacesolr:8983/solr

View File

@@ -0,0 +1,122 @@
# Docker images supporting DSpace
## Dockerfile.dependencies
This dockerfile is used to pre-cache maven downloads that will be used in subsequent DSpace docker builds.
```
docker build -t dspace/dspace-dependencies:dspace-7_x -f Dockerfile.dependencies .
```
This image is built manually. It should be rebuilt each year or after each major release in order to refresh the cache of jars.
A corresponding image exists for DSpace 4-6.
Admins to our DockerHub repo can publish with the following command.
```
docker push dspace/dspace-dependencies:dspace-7_x
```
## Dockerfile.jdk8-test
This dockefile builds a DSpace 7 tomcat image. The legacy REST api will be deployed without requiring https access.
```
docker build -t dspace/dspace:dspace-7_x-jdk8-test -f Dockerfile.jdk8-test .
```
This image is built automatically after each commit is made to the master branch.
A corresponding image exists for DSpace 4-6.
Admins to our DockerHub repo can publish with the following command.
```
docker push dspace/dspace:dspace-7_x-jdk8-test
```
## Dockerfile.jdk8
This dockefile builds a DSpace 7 tomcat image.
```
docker build -t dspace/dspace:dspace-7_x-jdk8 -f Dockerfile.jdk8 .
```
This image is built automatically after each commit is made to the master branch.
A corresponding image exists for DSpace 4-6.
Admins to our DockerHub repo can publish with the following command.
```
docker push dspace/dspace:dspace-7_x-jdk8
```
## Dockefile.cli.jdk8
This dockerfile builds a DSpace 7 CLI image.
```
docker build -t dspace/dspace-cli:dspace-7_x -f Dockerfile.cli.jdk8 .
```
This image is built automatically after each commit is made to the master branch.
A corresponding image exists for DSpace 6.
Admins to our DockerHub repo can publish with the following command.
```
docker push dspace/dspace-cli:dspace-7_x
```
## dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile
This is a postgres docker image containing the pgcrypto extension used in DSpace 6 and DSpace 7.
```
cd dspace/src/main/docker/dspace-postgres-pgcrypto
docker build -t dspace/dspace-postgres-pgcrypto .
```
This image is built manually. It should be rebuilt as needed.
A copy of this file exists in the DSpace 6 branch. A specialized version of this file exists for DSpace 4 in DSpace-Docker-Images.
Admins to our DockerHub repo can publish with the following command.
```
docker push dspace/dspace-postgres-pgcrypto
```
## dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile
This is a postgres docker image containing the pgcrypto extension used in DSpace 6 and DSpace 7.
This image also contains curl. The image is pre-configured to load a postgres database dump on initialization.
```
cd dspace/src/main/docker/dspace-postgres-pgcrypto-curl
docker build -t dspace/dspace-postgres-pgcrypto:loadsql .
```
This image is built manually. It should be rebuilt as needed.
A copy of this file exists in the DSpace 6 branch.
Admins to our DockerHub repo can publish with the following command.
```
docker push dspace/dspace-postgres-pgcrypto:loadsql
```
## dspace/src/main/docker/solr/Dockerfile
This is a standalone solr image containing DSpace solr schemas used in DSpace 7.
```
cd dspace/src/main/docker/solr
docker build -t dspace/dspace-solr .
```
This image is built manually. It should be rebuilt as solr schemas change or as new releases of solr are incorporated.
This file was introduced for DSpace 7.
Admins to our DockerHub repo can publish with the following command.
```
docker push dspace/dspace-solr
```
## local.cfg and test/ folder
These resources are bundled into the _dspace/dspace_ image at build time.

View File

@@ -0,0 +1,19 @@
#
# 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/
#
# This will be deployed as dspace/dspace-postgres-pgcrpyto:loadsql
FROM postgres
ENV POSTGRES_DB dspace
ENV POSTGRES_USER dspace
ENV POSTGRES_PASSWORD dspace
# Load a SQL dump. Set LOADSQL to a URL for the sql dump file.
RUN apt-get update && apt-get install -y curl
COPY install-pgcrypto.sh /docker-entrypoint-initdb.d/

View File

@@ -0,0 +1,33 @@
#!/bin/bash
#
# 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/
#
set -e
CHECKFILE=/pgdata/ingest.hasrun.flag
if [ ! -f $CHECKFILE -a ! -z ${LOADSQL} ]
then
curl ${LOADSQL} -L -s --output /tmp/dspace.sql
psql -U $POSTGRES_USER < /tmp/dspace.sql
touch $CHECKFILE
exit
fi
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL
-- Create a new schema in this database named "extensions" (or whatever you want to name it)
CREATE SCHEMA extensions;
-- Enable this extension in this new schema
CREATE EXTENSION pgcrypto SCHEMA extensions;
-- Update your database's "search_path" to also search the new "extensions" schema.
-- You are just appending it on the end of the existing comma-separated list.
ALTER DATABASE dspace SET search_path TO "\$user",public,extensions;
-- Grant rights to call functions in the extensions schema to your dspace user
GRANT USAGE ON SCHEMA extensions TO $POSTGRES_USER;
EOSQL

View 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/
#
# This will be deployed as dspace/dspace-postgres-pgcrpyto:latest
FROM postgres
ENV POSTGRES_DB dspace
ENV POSTGRES_USER dspace
ENV POSTGRES_PASSWORD dspace
RUN apt-get update
COPY install-pgcrypto.sh /docker-entrypoint-initdb.d/

View File

@@ -0,0 +1,22 @@
#!/bin/bash
#
# 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/
#
set -e
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL
-- Create a new schema in this database named "extensions" (or whatever you want to name it)
CREATE SCHEMA extensions;
-- Enable this extension in this new schema
CREATE EXTENSION pgcrypto SCHEMA extensions;
-- Update your database's "search_path" to also search the new "extensions" schema.
-- You are just appending it on the end of the existing comma-separated list.
ALTER DATABASE dspace SET search_path TO "\$user",public,extensions;
-- Grant rights to call functions in the extensions schema to your dspace user
GRANT USAGE ON SCHEMA extensions TO $POSTGRES_USER;
EOSQL