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

@@ -4,3 +4,8 @@
*/target/
dspace/modules/*/target/
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

@@ -40,8 +40,8 @@ Please be aware that, as a Java web application, DSpace requires a database (Pos
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.
## Dockerfile Usage
See the [DSpace Docker Tutorial](https://dspace-labs.github.io/DSpace-Docker-Images/).
## Running DSpace 7 in Docker
See [Running DSpace 7 with Docker Compose](dspace/src/main/docker-compose/README.md)
## Contributing

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())) {
List<RelationshipType> relationshipTypeList = relationshipTypeService
.findByLeftOrRightLabel(c, element);
.findByLeftwardOrRightwardTypeName(c, element);
for (RelationshipType relationshipType : relationshipTypeList) {
for (Relationship relationship : relationshipService
.findByItemAndRelationshipType(c, item, relationshipType)) {
@@ -762,14 +762,19 @@ public class MetadataImport {
List<RelationshipType> leftRelationshipTypesForEntity = entityService.getLeftRelationshipTypes(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)) {
if (StringUtils.equalsIgnoreCase(relationshipType.getLeftLabel(), element)) {
left = handleLeftLabelEqualityRelationshipTypeElement(c, entity, relationEntity, left,
if (StringUtils.equalsIgnoreCase(relationshipType.getLeftwardType(), element)) {
left = verifyValidLeftwardRelationshipType(c, entity, relationEntity, left,
acceptableRelationshipTypes,
leftRelationshipTypesForEntity,
relationshipType);
} else if (StringUtils.equalsIgnoreCase(relationshipType.getRightLabel(), element)) {
left = handleRightLabelEqualityRelationshipTypeElement(c, entity, relationEntity, left,
} else if (StringUtils.equalsIgnoreCase(relationshipType.getRightwardType(), element)) {
left = verifyValidRightwardRelationshipType(c, entity, relationEntity, left,
acceptableRelationshipTypes,
rightRelationshipTypesForEntity,
relationshipType);
@@ -832,13 +837,17 @@ public class MetadataImport {
* for the right entity
* @param relationshipType The RelationshipType object that we want to check whether it's
* valid to be added or not
* @return A boolean indicating whether the relationship is left or right, will
* be false in this case
* @return A boolean indicating whether the relationship is left or right.
* 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
*/
private boolean handleRightLabelEqualityRelationshipTypeElement(Context c, Entity entity, Entity relationEntity,
private boolean verifyValidRightwardRelationshipType(Context c, Entity entity,
Entity relationEntity,
boolean left,
List<RelationshipType> acceptableRelationshipTypes,
List<RelationshipType>
acceptableRelationshipTypes,
List<RelationshipType>
rightRelationshipTypesForEntity,
RelationshipType relationshipType)
@@ -874,13 +883,17 @@ public class MetadataImport {
* for the left entity
* @param relationshipType The RelationshipType object that we want to check whether it's
* valid to be added or not
* @return A boolean indicating whether the relationship is left or right, will
* be true in this case
* @return A boolean indicating whether the relationship is left or right.
* 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
*/
private boolean handleLeftLabelEqualityRelationshipTypeElement(Context c, Entity entity, Entity relationEntity,
private boolean verifyValidLeftwardRelationshipType(Context c, Entity entity,
Entity relationEntity,
boolean left,
List<RelationshipType> acceptableRelationshipTypes,
List<RelationshipType>
acceptableRelationshipTypes,
List<RelationshipType>
leftRelationshipTypesForEntity,
RelationshipType relationshipType)

View File

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

View File

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

View File

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

View File

@@ -13,11 +13,9 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
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.RelationshipService;
import org.dspace.content.service.WorkspaceItemService;
import org.dspace.content.virtual.VirtualMetadataConfiguration;
import org.dspace.content.virtual.VirtualMetadataPopulator;
import org.dspace.core.Constants;
import org.dspace.core.Context;
@@ -117,6 +114,9 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
@Autowired(required = true)
protected VirtualMetadataPopulator virtualMetadataPopulator;
@Autowired(required = true)
private RelationshipMetadataService relationshipMetadataService;
protected ItemServiceImpl() {
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);
}
@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
public List<MetadataValue> getMetadata(Item item, String schema, String element, String qualifier, String lang,
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
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<>();
for (MetadataValue metadataValue : relationMetadata) {
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<>();
if (enableVirtualMetadata) {
fullMetadataValueList.addAll(getRelationshipMetadata(item, true));
fullMetadataValueList.addAll(relationshipMetadataService.getRelationshipMetadata(item, true));
}
fullMetadataValueList.addAll(dbMetadataValues);
@@ -1427,137 +1407,5 @@ prevent the generation of resource policy entry values with null dspace_object a
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")
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:
* {@link org.dspace.content.service.RelationshipService#create(Context)} }
@@ -170,6 +182,38 @@ public class Relationship implements ReloadableEntity<Integer> {
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
* @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
public Relationship create(Context c, Item leftItem, Item rightItem, RelationshipType relationshipType,
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.setLeftItem(leftItem);
relationship.setRightItem(rightItem);
relationship.setRelationshipType(relationshipType);
relationship.setLeftPlace(leftPlace);
relationship.setRightPlace(rightPlace);
relationship.setLeftwardValue(leftwardValue);
relationship.setRightwardValue(rightwardValue);
return create(c, relationship);
}
@@ -113,7 +122,7 @@ public class RelationshipServiceImpl implements RelationshipService {
rightRelationships.remove(relationship);
}
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.
if (!virtualMetadataPopulator.isUseForPlaceTrueForRelationshipType(relationship.getRelationshipType(), true)) {
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.
if (!virtualMetadataPopulator.isUseForPlaceTrueForRelationshipType(relationship.getRelationshipType(), false)) {
if (!rightRelationships.isEmpty()) {
@@ -202,8 +211,8 @@ public class RelationshipServiceImpl implements RelationshipService {
private void logRelationshipTypeDetailsForError(RelationshipType relationshipType) {
log.warn("The relationshipType's ID is: " + relationshipType.getID());
log.warn("The relationshipType's left label is: " + relationshipType.getLeftLabel());
log.warn("The relationshipType's right label is: " + relationshipType.getRightLabel());
log.warn("The relationshipType's leftward type is: " + relationshipType.getLeftwardType());
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 right entityType label is: " + relationshipType.getRightType().getLabel());
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.sort((o1, o2) -> {
int relationshipType = o1.getRelationshipType().getLeftLabel()
.compareTo(o2.getRelationshipType().getLeftLabel());
int relationshipType = o1.getRelationshipType().getLeftwardType()
.compareTo(o2.getRelationshipType().getLeftwardType());
if (relationshipType != 0) {
return relationshipType;
} else {
@@ -357,12 +366,16 @@ public class RelationshipServiceImpl implements RelationshipService {
for (Relationship relationship : list) {
if (isLeft) {
if (StringUtils
.equals(relationship.getRelationshipType().getLeftLabel(), relationshipType.getLeftLabel())) {
.equals(
relationship.getRelationshipType().getLeftwardType(), relationshipType.getLeftwardType())
) {
listToReturn.add(relationship);
}
} else {
if (StringUtils
.equals(relationship.getRelationshipType().getRightLabel(), relationshipType.getRightLabel())) {
.equals(
relationship.getRelationshipType().getRightwardType(), relationshipType.getRightwardType())
) {
listToReturn.add(relationship);
}
}

View File

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

View File

@@ -48,8 +48,8 @@ public class RelationshipTypeServiceImpl implements RelationshipTypeService {
@Override
public RelationshipType findbyTypesAndLabels(Context context,EntityType leftType,EntityType rightType,
String leftLabel,String rightLabel) throws SQLException {
return relationshipTypeDAO.findByTypesAndLabels(context, leftType, rightType, leftLabel, rightLabel);
String leftwardType,String rightwardType) throws SQLException {
return relationshipTypeDAO.findByTypesAndLabels(context, leftType, rightType, leftwardType, rightwardType);
}
@Override
@@ -58,8 +58,8 @@ public class RelationshipTypeServiceImpl implements RelationshipTypeService {
}
@Override
public List<RelationshipType> findByLeftOrRightLabel(Context context, String label) throws SQLException {
return relationshipTypeDAO.findByLeftOrRightLabel(context, label);
public List<RelationshipType> findByLeftwardOrRightwardTypeName(Context context, String label) throws SQLException {
return relationshipTypeDAO.findByLeftwardOrRightwardTypeName(context, label);
}
@Override
@@ -69,15 +69,15 @@ public class RelationshipTypeServiceImpl implements RelationshipTypeService {
@Override
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 rightCardinalityMaxInteger)
throws SQLException, AuthorizeException {
RelationshipType relationshipType = new RelationshipType();
relationshipType.setLeftType(leftEntityType);
relationshipType.setRightType(rightEntityType);
relationshipType.setLeftLabel(leftLabel);
relationshipType.setRightLabel(rightLabel);
relationshipType.setLeftwardType(leftwardType);
relationshipType.setRightwardType(rightwardType);
relationshipType.setLeftMinCardinality(leftCardinalityMinInteger);
relationshipType.setLeftMaxCardinality(leftCardinalityMaxInteger);
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
* 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 leftType The leftType 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 rightLabel The rightLabel String to be matched in the query
* @param leftwardType The leftwardType 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
* @throws SQLException If something goes wrong
*/
RelationshipType findByTypesAndLabels(Context context,
EntityType leftType,EntityType rightType,String leftLabel,String rightLabel)
RelationshipType findByTypesAndLabels(Context context, EntityType leftType,EntityType rightType,
String leftwardType,
String rightwardType)
throws SQLException;
/**
* 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 label 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
* @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 leftwardType
* or rightwardType
* @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

View File

@@ -8,8 +8,6 @@
package org.dspace.content.dao.impl;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
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)
throws SQLException {
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());
if (offset != null) {
query.setFirstResult(offset);
@@ -74,7 +81,8 @@ public class CommunityDAOImpl extends AbstractHibernateDSODAO<Community> impleme
if (limit != null) {
query.setMaxResults(limit);
}
query.setParameter(sortField.toString(), sortField.getID());
query.setParameter("sortField", sortField);
return list(query);
}
@@ -91,16 +99,26 @@ public class CommunityDAOImpl extends AbstractHibernateDSODAO<Community> impleme
@Override
public List<Community> findAllNoParent(Context context, MetadataField sortField) throws SQLException {
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.setParameter(sortField.toString(), sortField.getID());
query.setParameter("sortField", sortField);
query.setHint("org.hibernate.cacheable", Boolean.TRUE);
return findMany(context, query);
}

View File

@@ -24,31 +24,31 @@ public class RelationshipTypeDAOImpl extends AbstractHibernateDAO<RelationshipTy
@Override
public RelationshipType findByTypesAndLabels(Context context, EntityType leftType, EntityType rightType,
String leftLabel, String rightLabel)
String leftwardType, String rightwardType)
throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, RelationshipType.class);
Root<RelationshipType> relationshipTypeRoot = criteriaQuery.from(RelationshipType.class);
criteriaQuery.select(relationshipTypeRoot);
criteriaQuery.where(
criteriaBuilder.and(criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.leftType), leftType),
criteriaBuilder.and(
criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.leftType), leftType),
criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.rightType), rightType),
criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.leftLabel), leftLabel),
criteriaBuilder
.equal(relationshipTypeRoot.get(RelationshipType_.rightLabel), rightLabel)));
criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.leftwardType), leftwardType),
criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.rightwardType), rightwardType)));
return uniqueResult(context, criteriaQuery, false, RelationshipType.class, -1, -1);
}
@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);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, RelationshipType.class);
Root<RelationshipType> relationshipTypeRoot = criteriaQuery.from(RelationshipType.class);
criteriaQuery.select(relationshipTypeRoot);
criteriaQuery.where(
criteriaBuilder.or(
criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.leftLabel), label),
criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.rightLabel), label)
criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.leftwardType), type),
criteriaBuilder.equal(relationshipTypeRoot.get(RelationshipType_.rightwardType), type)
)
);
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.InProgressSubmission;
import org.dspace.content.RelationshipMetadataService;
import org.dspace.content.WorkspaceItem;
import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.BitstreamService;
@@ -112,6 +113,8 @@ public abstract class ContentServiceFactory {
*/
public abstract EntityService getEntityService();
public abstract RelationshipMetadataService getRelationshipMetadataService();
public InProgressSubmissionService getInProgressSubmissionService(InProgressSubmission inProgressSubmission) {
if (inProgressSubmission instanceof WorkspaceItem) {
return getWorkspaceItemService();

View File

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

View File

@@ -122,6 +122,6 @@ public interface EntityService {
* to the given label parameter
* @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.MetadataField;
import org.dspace.content.MetadataValue;
import org.dspace.content.RelationshipMetadataValue;
import org.dspace.content.Thumbnail;
import org.dspace.content.WorkspaceItem;
import org.dspace.content.virtual.VirtualMetadataPopulator;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
@@ -677,19 +675,6 @@ public interface ItemService
*/
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.
* 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
* 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
* by the leftItem, rightItem and relationshipType of the given Relationship.
* @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
* @param context The relevant DSpace context
* @param relationshipType The RelationshipType object that will be used to check the Relationship on
@@ -133,6 +133,26 @@ public interface RelationshipService extends DSpaceCRUDService<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
* @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
* @throws AuthorizeException If something goes wrong
* @throws SQLException If something goes wrong

View File

@@ -36,13 +36,13 @@ public interface RelationshipTypeService extends DSpaceCRUDService<RelationshipT
* @param context The relevant DSpace context
* @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 leftLabel The leftLabel String that needs to match for the returned RelationshipType
* @param rightLabel The rightLabel String that needs to match for the returned RelationshipType
* @param leftwardType The leftwardType String that needs to match for the returned RelationshipType
* @param rightwardType The rightwardType String that needs to match for the returned RelationshipType
* @return
* @throws SQLException If something goes wrong
*/
RelationshipType findbyTypesAndLabels(Context context,EntityType leftType,EntityType rightType,
String leftLabel,String rightLabel)
String leftwardType,String rightwardType)
throws SQLException;
/**
@@ -54,7 +54,7 @@ public interface RelationshipTypeService extends DSpaceCRUDService<RelationshipT
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
* @param context The relevant DSpace context
* @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
* @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
@@ -80,8 +80,8 @@ public interface RelationshipTypeService extends DSpaceCRUDService<RelationshipT
* @param context The relevant DSpace context
* @param leftEntityType The leftEntityType EntityType object for this relationshipType
* @param rightEntityType The rightEntityType EntityType object for this relationshipType
* @param leftLabel The leftLabel String object for this relationshipType
* @param rightLabel The rightLabel String object for this relationshipType
* @param leftwardType The leftwardType String object for this relationshipType
* @param rightwardType The rightwardType String object for this relationshipType
* @param leftCardinalityMinInteger The leftCardinalityMinInteger Integer object for this relationshipType
* @param leftCardinalityMaxInteger The leftCardinalityMaxInteger 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 AuthorizeException If something goes wrong
*/
RelationshipType create(Context context, EntityType leftEntityType, EntityType rightEntityType, String leftLabel,
String rightLabel, Integer leftCardinalityMinInteger, Integer leftCardinalityMaxInteger,
Integer rightCardinalityMinInteger, Integer rightCardinalityMaxInteger)
RelationshipType create(Context context, EntityType leftEntityType, EntityType rightEntityType,
String leftwardType, String rightwardType, Integer leftCardinalityMinInteger,
Integer leftCardinalityMaxInteger, Integer rightCardinalityMinInteger,
Integer rightCardinalityMaxInteger)
throws SQLException, AuthorizeException;
}

View File

@@ -65,6 +65,7 @@ public class Collected implements VirtualMetadataConfiguration {
* Generic setter for the useForPlace property
* @param useForPlace The boolean value that the useForPlace property will be set to
*/
@Override
public void setUseForPlace(boolean useForPlace) {
this.useForPlace = useForPlace;
}
@@ -73,10 +74,19 @@ public class Collected implements VirtualMetadataConfiguration {
* Generic getter for the useForPlace property
* @return The useForPlace to be used by this bean
*/
@Override
public boolean getUseForPlace() {
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
* 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 populateWithNameVariant = false;
/**
* Generic getter for the fields property
* @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
* @param useForPlace The boolean value that the useForPlace property will be set to
*/
@Override
public void setUseForPlace(boolean useForPlace) {
this.useForPlace = useForPlace;
}
@@ -88,10 +90,21 @@ public class Concatenate implements VirtualMetadataConfiguration {
* Generic getter for the useForPlace property
* @return The useForPlace to be used by this bean
*/
@Override
public boolean getUseForPlace() {
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
* 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
* @return The String value for all of the retrieved metadatavalues combined with the separator
*/
@Override
public List<String> getValues(Context context, Item item) {
List<String> resultValues = new LinkedList<>();

View File

@@ -120,6 +120,7 @@ public class Related implements VirtualMetadataConfiguration {
* Generic setter for the useForPlace property
* @param useForPlace The boolean value that the useForPlace property will be set to
*/
@Override
public void setUseForPlace(boolean useForPlace) {
this.useForPlace = useForPlace;
}
@@ -128,10 +129,19 @@ public class Related implements VirtualMetadataConfiguration {
* Generic getter for the useForPlace property
* @return The useForPlace to be used by this bean
*/
@Override
public boolean getUseForPlace() {
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
* 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
* @throws SQLException If something goes wrong
*/
@Override
public List<String> getValues(Context context, Item item) throws SQLException {
Entity entity = entityService.findByItemId(context, item.getID());
EntityType entityType = entityService.getType(context, entity);
@@ -149,8 +160,8 @@ public class Related implements VirtualMetadataConfiguration {
List<RelationshipType> relationshipTypes = entityService.getAllRelationshipTypes(context, entity);
List<RelationshipType> possibleRelationshipTypes = new LinkedList<>();
for (RelationshipType relationshipType : relationshipTypes) {
if (StringUtils.equals(relationshipType.getLeftLabel(), relationshipTypeString) || StringUtils
.equals(relationshipType.getRightLabel(), relationshipTypeString)) {
if (StringUtils.equals(relationshipType.getLeftwardType(), relationshipTypeString) || StringUtils
.equals(relationshipType.getRightwardType(), relationshipTypeString)) {
possibleRelationshipTypes.add(relationshipType);
}
}

View File

@@ -38,4 +38,12 @@ public class UUIDValue implements VirtualMetadataConfiguration {
public boolean getUseForPlace() {
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
*/
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
* 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 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
*/
public boolean isUseForPlaceTrueForRelationshipType(RelationshipType relationshipType, boolean isLeft) {
HashMap<String, VirtualMetadataConfiguration> hashMaps;
if (isLeft) {
hashMaps = this.getMap().get(relationshipType.getLeftLabel());
hashMaps = this.getMap().get(relationshipType.getLeftwardType());
} else {
hashMaps = this.getMap().get(relationshipType.getRightLabel());
hashMaps = this.getMap().get(relationshipType.getRightwardType());
}
if (hashMaps != null) {
for (Map.Entry<String, VirtualMetadataConfiguration> entry : hashMaps.entrySet()) {

View File

@@ -7,16 +7,28 @@
*/
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.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
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.core.ConfigurationManager;
import org.dspace.core.Context;
import org.dspace.harvest.dao.HarvestedCollectionDAO;
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;
/**
@@ -27,6 +39,10 @@ import org.springframework.beans.factory.annotation.Autowired;
* @author kevinvandevelde at atmire.com
*/
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)
protected HarvestedCollectionDAO harvestedCollectionDAO;
@@ -156,5 +172,94 @@ public class HarvestedCollectionServiceImpl implements HarvestedCollectionServic
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.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
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.Identify;
import ORG.oclc.oai.harvester2.verb.ListIdentifiers;
import ORG.oclc.oai.harvester2.verb.ListMetadataFormats;
import ORG.oclc.oai.harvester2.verb.ListRecords;
import org.apache.commons.lang3.StringUtils;
@@ -102,7 +105,7 @@ public class OAIHarvester {
protected BitstreamFormatService bitstreamFormatService;
protected BundleService bundleService;
protected CollectionService collectionService;
protected HarvestedCollectionService harvestedCollection;
protected HarvestedCollectionService harvestedCollectionService;
protected InstallItemService installItemService;
protected ItemService itemService;
protected HandleService handleService;
@@ -140,7 +143,7 @@ public class OAIHarvester {
bundleService = ContentServiceFactory.getInstance().getBundleService();
collectionService = ContentServiceFactory.getInstance().getCollectionService();
handleService = HandleServiceFactory.getInstance().getHandleService();
harvestedCollection = HarvestServiceFactory.getInstance().getHarvestedCollectionService();
harvestedCollectionService = HarvestServiceFactory.getInstance().getHarvestedCollectionService();
harvestedItemService = HarvestServiceFactory.getInstance().getHarvestedItemService();
itemService = ContentServiceFactory.getInstance().getItemService();
installItemService = ContentServiceFactory.getInstance().getInstallItemService();
@@ -150,7 +153,6 @@ public class OAIHarvester {
configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
if (dso.getType() != Constants.COLLECTION) {
throw new HarvestingException("OAIHarvester can only harvest collections");
}
@@ -159,7 +161,7 @@ public class OAIHarvester {
targetCollection = (Collection) dso;
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");
}
@@ -188,7 +190,7 @@ public class OAIHarvester {
*
* @return Namespace of the supported ORE format. Returns null if not found.
*/
private static Namespace getORENamespace() {
public static Namespace getORENamespace() {
String ORESerializationString = null;
String ORESeialKey = null;
String oreString = "oai.harvester.oreSerializationFormat";
@@ -213,7 +215,7 @@ public class OAIHarvester {
* @param metadataKey
* @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 metaString = "oai.harvester.metadataformats";
@@ -313,7 +315,7 @@ public class OAIHarvester {
harvestRow.setHarvestStatus(HarvestedCollection.STATUS_BUSY);
harvestRow.setHarvestMessage("Collection harvesting is initializing...");
harvestRow.setHarvestStartTime(startTime);
harvestedCollection.update(ourContext, harvestRow);
harvestedCollectionService.update(ourContext, harvestRow);
intermediateCommit();
// expiration timer starts
@@ -354,7 +356,7 @@ public class OAIHarvester {
harvestRow.setHarvestStartTime(new Date());
harvestRow.setHarvestMessage("OAI server did not contain any updates");
harvestRow.setHarvestStatus(HarvestedCollection.STATUS_READY);
harvestedCollection.update(ourContext, harvestRow);
harvestedCollectionService.update(ourContext, harvestRow);
return;
} else {
throw new HarvestingException(errorSet.toString());
@@ -411,7 +413,7 @@ public class OAIHarvester {
harvestRow.setHarvestMessage(String
.format("Collection is currently being harvested (item %d of %d)",
currentRecord, totalListSize));
harvestedCollection.update(ourContext, harvestRow);
harvestedCollectionService.update(ourContext, harvestRow);
} finally {
//In case of an exception, make sure to restore our authentication state to the previous state
ourContext.restoreAuthSystemState();
@@ -429,19 +431,19 @@ public class OAIHarvester {
alertAdmin(HarvestedCollection.STATUS_OAI_ERROR, hex);
}
harvestRow.setHarvestStatus(HarvestedCollection.STATUS_OAI_ERROR);
harvestedCollection.update(ourContext, harvestRow);
harvestedCollectionService.update(ourContext, harvestRow);
ourContext.complete();
return;
} catch (Exception ex) {
harvestRow.setHarvestMessage("Unknown error occurred while generating an OAI response");
harvestRow.setHarvestStatus(HarvestedCollection.STATUS_UNKNOWN_ERROR);
harvestedCollection.update(ourContext, harvestRow);
harvestedCollectionService.update(ourContext, harvestRow);
alertAdmin(HarvestedCollection.STATUS_UNKNOWN_ERROR, ex);
log.error("Error occurred while generating an OAI response: " + ex.getMessage() + " " + ex.getCause(), ex);
ourContext.complete();
return;
} finally {
harvestedCollection.update(ourContext, harvestRow);
harvestedCollectionService.update(ourContext, harvestRow);
ourContext.turnOffAuthorisationSystem();
collectionService.update(ourContext, targetCollection);
ourContext.restoreAuthSystemState();
@@ -456,7 +458,7 @@ public class OAIHarvester {
log.info(
"Harvest from " + oaiSource + " successful. The process took " + timeTaken + " milliseconds. Harvested "
+ currentRecord + " items.");
harvestedCollection.update(ourContext, harvestRow);
harvestedCollectionService.update(ourContext, harvestRow);
ourContext.setMode(originalMode);
}
@@ -900,94 +902,44 @@ public class OAIHarvester {
String oaiSetId = harvestRow.getOaiSetId();
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
* supporting the provided metadata formats.
* Return all available 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.
* @return a list containing a map for each supported metadata format
*/
public static List<String> verifyOAIharvester(String oaiSource,
String oaiSetId, String metaPrefix, boolean testORE) {
List<String> errorSet = new ArrayList<String>();
public static List<Map<String,String>> getAvailableMetadataFormats() {
List<Map<String,String>> configs = new ArrayList<>();
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.
try {
new Identify(oaiSource);
} catch (Exception ex) {
errorSet.add(OAI_ADDRESS_ERROR + ": OAI server could not be reached.");
return errorSet;
}
String id = key.substring(metaString.length());
String label;
String namespace = "";
// 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, 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 + ")");
}
if (metadataString.indexOf(',') != -1) {
label = metadataString.substring(metadataString.indexOf(',') + 2);
namespace = metadataString.substring(0, metadataString.indexOf(','));
} 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;
} catch (Exception e) {
errorSet.add(OAI_ADDRESS_ERROR + ": OAI server could not be reached");
return errorSet;
label = id + "(" + metadataString + ")";
}
return errorSet;
Map<String,String> config = new HashMap<>();
config.put("id", id);
config.put("label", label);
config.put("namespace", namespace);
configs.add(config);
}
return configs;
}
}

View File

@@ -137,4 +137,15 @@ public interface HarvestedCollectionService {
public void update(Context context, HarvestedCollection harvestedCollection) 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>
<leftType>Publication</leftType>
<rightType>Person</rightType>
<leftLabel>isAuthorOfPublication</leftLabel>
<rightLabel>isPublicationOfAuthor</rightLabel>
<leftwardType>isAuthorOfPublication</leftwardType>
<rightwardType>isPublicationOfAuthor</rightwardType>
<leftCardinality>
<min>10</min>
</leftCardinality>
@@ -17,8 +17,8 @@
<type>
<leftType>Publication</leftType>
<rightType>Project</rightType>
<leftLabel>isProjectOfPublication</leftLabel>
<rightLabel>isPublicationOfProject</rightLabel>
<leftwardType>isProjectOfPublication</leftwardType>
<rightwardType>isPublicationOfProject</rightwardType>
<leftCardinality>
<min>0</min>
</leftCardinality>
@@ -29,8 +29,8 @@
<type>
<leftType>Publication</leftType>
<rightType>OrgUnit</rightType>
<leftLabel>isOrgUnitOfPublication</leftLabel>
<rightLabel>isPublicationOfOrgUnit</rightLabel>
<leftwardType>isOrgUnitOfPublication</leftwardType>
<rightwardType>isPublicationOfOrgUnit</rightwardType>
<leftCardinality>
<min>0</min>
</leftCardinality>
@@ -41,8 +41,8 @@
<type>
<leftType>Person</leftType>
<rightType>Project</rightType>
<leftLabel>isProjectOfPerson</leftLabel>
<rightLabel>isPersonOfProject</rightLabel>
<leftwardType>isProjectOfPerson</leftwardType>
<rightwardType>isPersonOfProject</rightwardType>
<leftCardinality>
<min>0</min>
</leftCardinality>
@@ -53,8 +53,8 @@
<type>
<leftType>Person</leftType>
<rightType>OrgUnit</rightType>
<leftLabel>isOrgUnitOfPerson</leftLabel>
<rightLabel>isPersonOfOrgUnit</rightLabel>
<leftwardType>isOrgUnitOfPerson</leftwardType>
<rightwardType>isPersonOfOrgUnit</rightwardType>
<leftCardinality>
<min>0</min>
</leftCardinality>
@@ -65,8 +65,8 @@
<type>
<leftType>Project</leftType>
<rightType>OrgUnit</rightType>
<leftLabel>isOrgUnitOfProject</leftLabel>
<rightLabel>isProjectOfOrgUnit</rightLabel>
<leftwardType>isOrgUnitOfProject</leftwardType>
<rightwardType>isProjectOfOrgUnit</rightwardType>
<leftCardinality>
<min>0</min>
</leftCardinality>
@@ -77,8 +77,8 @@
<type>
<leftType>Journal</leftType>
<rightType>JournalVolume</rightType>
<leftLabel>isVolumeOfJournal</leftLabel>
<rightLabel>isJournalOfVolume</rightLabel>
<leftwardType>isVolumeOfJournal</leftwardType>
<rightwardType>isJournalOfVolume</rightwardType>
<leftCardinality>
<min>0</min>
</leftCardinality>
@@ -89,8 +89,8 @@
<type>
<leftType>JournalVolume</leftType>
<rightType>JournalIssue</rightType>
<leftLabel>isIssueOfJournalVolume</leftLabel>
<rightLabel>isJournalVolumeOfIssue</rightLabel>
<leftwardType>isIssueOfJournalVolume</leftwardType>
<rightwardType>isJournalVolumeOfIssue</rightwardType>
<leftCardinality>
<min>0</min>
</leftCardinality>
@@ -102,8 +102,8 @@
<type>
<leftType>Publication</leftType>
<rightType>OrgUnit</rightType>
<leftLabel>isAuthorOfPublication</leftLabel>
<rightLabel>isPublicationOfAuthor</rightLabel>
<leftwardType>isAuthorOfPublication</leftwardType>
<rightwardType>isPublicationOfAuthor</rightwardType>
<leftCardinality>
<min>0</min>
</leftCardinality>
@@ -114,8 +114,8 @@
<type>
<leftType>JournalIssue</leftType>
<rightType>Publication</rightType>
<leftLabel>isPublicationOfJournalIssue</leftLabel>
<rightLabel>isJournalIssueOfPublication</rightLabel>
<leftwardType>isPublicationOfJournalIssue</leftwardType>
<rightwardType>isJournalIssueOfPublication</rightwardType>
<leftCardinality>
<min>0</min>
</leftCardinality>

View File

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

View File

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

View File

@@ -145,15 +145,15 @@ public class EntityServiceImplTest {
List<Relationship> relationshipList = new ArrayList<>();
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(relationship.getRelationshipType()).thenReturn(relationshipType);
when(relationshipType.getLeftLabel()).thenReturn("leftLabel");
when(relationshipType.getRightLabel()).thenReturn("rightLabel");
when(relationshipType.getLeftwardType()).thenReturn("leftwardType");
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,
entityService.getRelationsByLabel(context, "leftLabel"));
entityService.getRelationsByLabel(context, "leftwardType"));
}
@Test
@@ -260,15 +260,15 @@ public class EntityServiceImplTest {
RelationshipType relationshipType = mock(RelationshipType.class);
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
when(relationshipTypeService.findAll(context)).thenReturn(list);
when(relationshipType.getLeftLabel()).thenReturn("leftLabel");
when(relationshipType.getRightLabel()).thenReturn("rightLabel");
when(relationshipType.getLeftwardType()).thenReturn("leftwardType");
when(relationshipType.getRightwardType()).thenReturn("rightwardType");
// The RelationshipType(s) reported from our mocked Entity should match our 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 hasFather = new RelationshipType();
RelationshipType hasMother = new RelationshipType();
hasDog.setLeftLabel("hasDog");
hasDog.setRightLabel("isDogOf");
hasFather.setLeftLabel("hasFather");
hasFather.setRightLabel("isFatherOf");
hasMother.setLeftLabel("hasMother");
hasMother.setRightLabel("isMotherOf");
hasDog.setLeftwardType("hasDog");
hasDog.setRightwardType("isDogOf");
hasFather.setLeftwardType("hasFather");
hasFather.setRightwardType("isFatherOf");
hasMother.setLeftwardType("hasMother");
hasMother.setRightwardType("isMotherOf");
relationshipTest.add(getRelationship(cindy, spot, hasDog,0,0));
relationshipTest.add(getRelationship(cindy, jasper, hasDog,0,1));
@@ -194,8 +194,8 @@ public class RelationshipServiceImplTest {
EntityType rightEntityType = mock(EntityType.class);
testRel.setLeftType(leftEntityType);
testRel.setRightType(rightEntityType);
testRel.setLeftLabel("Entitylabel");
testRel.setRightLabel("Entitylabel");
testRel.setLeftwardType("Entitylabel");
testRel.setRightwardType("Entitylabel");
metsList.add(metVal);
relationship = getRelationship(leftItem, rightItem, testRel, 0,0);
leftTypelist.add(relationship);
@@ -247,8 +247,8 @@ public class RelationshipServiceImplTest {
EntityType rightEntityType = mock(EntityType.class);
testRel.setLeftType(leftEntityType);
testRel.setRightType(rightEntityType);
testRel.setLeftLabel("Entitylabel");
testRel.setRightLabel("Entitylabel");
testRel.setLeftwardType("Entitylabel");
testRel.setRightwardType("Entitylabel");
testRel.setLeftMinCardinality(0);
testRel.setRightMinCardinality(0);
metsList.add(metVal);
@@ -299,8 +299,8 @@ public class RelationshipServiceImplTest {
EntityType rightEntityType = mock(EntityType.class);
testRel.setLeftType(leftEntityType);
testRel.setRightType(rightEntityType);
testRel.setLeftLabel("Entitylabel");
testRel.setRightLabel("Entitylabel");
testRel.setLeftwardType("Entitylabel");
testRel.setRightwardType("Entitylabel");
testRel.setLeftMinCardinality(0);
testRel.setRightMinCardinality(0);
metsList.add(metVal);

View File

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

View File

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

View File

@@ -62,15 +62,15 @@ public class VirtualMetadataPopulatorTest {
HashMap<String, VirtualMetadataConfiguration> mapExt = new HashMap<>();
VirtualMetadataConfiguration virtualMetadataConfiguration = mock(VirtualMetadataConfiguration.class);
mapExt.put("hashKey", virtualMetadataConfiguration);
map.put("LeftLabel", mapExt);
map.put("NotRightLabel", mapExt);
map.put("LeftwardType", mapExt);
map.put("NotRightwardType", mapExt);
virtualMetadataPopulator.setMap(map);
// Mock the state of objects utilized in isUseForPlaceTrueForRelationshipType()
// to meet the success criteria of an invocation
when(virtualMetadataConfiguration.getUseForPlace()).thenReturn(true);
when(relationshipType.getLeftLabel()).thenReturn("LeftLabel");
when(relationshipType.getRightLabel()).thenReturn("RightLabel");
when(relationshipType.getLeftwardType()).thenReturn("LeftwardType");
when(relationshipType.getRightwardType()).thenReturn("RightwardType");
// Assert that the useForPlace for our mocked relationshipType is 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.Utils;
import org.dspace.authorize.AuthorizeException;
import org.dspace.util.UUIDUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
@@ -390,14 +391,17 @@ public class RestResourceController implements InitializingBean {
* @param request The relevant request
* @param apiCategory The apiCategory to be used
* @param model The model to be used
* @param parent Optional parent identifier
* @return The relevant ResponseEntity for this request
* @throws HttpRequestMethodNotSupportedException If something goes wrong
*/
@RequestMapping(method = RequestMethod.POST, consumes = {"application/json", "application/hal+json"})
public ResponseEntity<ResourceSupport> post(HttpServletRequest request, @PathVariable String apiCategory,
@PathVariable String model)
public ResponseEntity<ResourceSupport> post(HttpServletRequest request,
@PathVariable String apiCategory,
@PathVariable String model,
@RequestParam(required = false) String parent)
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 apiCategory The apiCategory to be used
* @param model The model to be used
* @param parent The parent object id (optional)
* @return The relevant ResponseEntity for this request
* @throws HttpRequestMethodNotSupportedException If something goes wrong
*/
public <ID extends Serializable> ResponseEntity<ResourceSupport> postJsonInternal(HttpServletRequest request,
String apiCategory,
String model)
String model, String parent)
throws HttpRequestMethodNotSupportedException {
checkModelPluralForm(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) {
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.setLeftPlace(obj.getLeftPlace());
relationshipRest.setRightPlace(obj.getRightPlace());
relationshipRest.setLeftwardValue(obj.getLeftwardValue());
relationshipRest.setRightwardValue(obj.getRightwardValue());
return relationshipRest;
}

View File

@@ -32,8 +32,8 @@ public class RelationshipTypeConverter implements DSpaceConverter<RelationshipTy
RelationshipTypeRest relationshipTypeRest = new RelationshipTypeRest();
relationshipTypeRest.setId(obj.getID());
relationshipTypeRest.setLeftLabel(obj.getLeftLabel());
relationshipTypeRest.setRightLabel(obj.getRightLabel());
relationshipTypeRest.setLeftwardType(obj.getLeftwardType());
relationshipTypeRest.setRightwardType(obj.getRightwardType());
relationshipTypeRest.setLeftMinCardinality(obj.getLeftMinCardinality());
relationshipTypeRest.setLeftMaxCardinality(obj.getLeftMaxCardinality());
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("viewevents", getMethodOn().getViewEvents()));
list.add(buildLink("searchevents", getMethodOn().getViewEvents()));
list.add(buildLink("searchevents", getMethodOn().getSearchEvents()));
}
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 CATEGORY = RestAddressableModel.CORE;
public static final String LICENSE = "license";
public static final String HARVEST = "harvester";
public static final String DEFAULT_ACCESS_CONDITIONS = "defaultAccessConditions";
@JsonIgnore
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 int leftPlace;
private int rightPlace;
private String leftwardValue;
private String rightwardValue;
public String getType() {
return NAME;
@@ -92,4 +94,20 @@ public class RelationshipRest extends BaseObjectRest<Integer> {
public void setRelationshipTypeId(int 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 CATEGORY = "core";
private String leftLabel;
private String rightLabel;
private String leftwardType;
private String rightwardType;
private Integer leftMinCardinality;
private Integer leftMaxCardinality;
private Integer rightMinCardinality;
@@ -41,20 +41,20 @@ public class RelationshipTypeRest extends BaseObjectRest<Integer> {
return RestResourceController.class;
}
public String getLeftLabel() {
return leftLabel;
public String getLeftwardType() {
return leftwardType;
}
public void setLeftLabel(String leftLabel) {
this.leftLabel = leftLabel;
public void setLeftwardType(String leftwardType) {
this.leftwardType = leftwardType;
}
public String getRightLabel() {
return rightLabel;
public String getRightwardType() {
return rightwardType;
}
public void setRightLabel(String rightLabel) {
this.rightLabel = rightLabel;
public void setRightwardType(String rightwardType) {
this.rightwardType = rightwardType;
}
public Integer getLeftMinCardinality() {

View File

@@ -22,6 +22,7 @@ public class CollectionResource extends DSpaceResource<CollectionRest> {
public CollectionResource(CollectionRest collection, Utils utils, String... rels) {
super(collection, utils, rels);
add(utils.linkToSubResource(collection, CollectionRest.LICENSE));
add(utils.linkToSubResource(collection, CollectionRest.HARVEST));
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.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.dspace.app.rest.Parameter;
import org.dspace.app.rest.SearchRestMethod;
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.core.Constants;
import org.dspace.core.Context;
import org.dspace.util.UUIDUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
@@ -169,8 +167,19 @@ public class CollectionRestRepository extends DSpaceObjectRestRepository<Collect
}
@Override
@PreAuthorize("hasAuthority('ADMIN')")
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();
ObjectMapper mapper = new ObjectMapper();
CollectionRest collectionRest;
@@ -178,38 +187,21 @@ public class CollectionRestRepository extends DSpaceObjectRestRepository<Collect
ServletInputStream input = req.getInputStream();
collectionRest = mapper.readValue(input, CollectionRest.class);
} catch (IOException e1) {
throw new UnprocessableEntityException("Error parsing request body: " + e1.toString());
throw new UnprocessableEntityException("Error parsing request body.", e1);
}
Collection collection;
String parentCommunityString = req.getParameter("parent");
try {
Community parent = null;
if (StringUtils.isNotBlank(parentCommunityString)) {
UUID parentCommunityUuid = UUIDUtils.fromString(parentCommunityString);
if (parentCommunityUuid == null) {
throw new DSpaceBadRequestException("The given parent was invalid: "
+ parentCommunityString);
}
parent = communityService.find(context, parentCommunityUuid);
Community parent = communityService.find(context, id);
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.");
+ id + " not found");
}
collection = cs.create(context, parent);
cs.update(context, collection);
metadataConverter.setMetadata(context, collection, collectionRest.getMetadata());
} catch (SQLException e) {
throw new RuntimeException("Unable to create new Collection under parent Community " +
parentCommunityString, e);
throw new RuntimeException("Unable to create new Collection under parent Community " + id, e);
}
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.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.dspace.app.rest.Parameter;
import org.dspace.app.rest.SearchRestMethod;
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.service.CommunityService;
import org.dspace.core.Context;
import org.dspace.util.UUIDUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
@@ -83,25 +81,45 @@ public class CommunityRestRepository extends DSpaceObjectRestRepository<Communit
}
Community community;
try {
Community parent = null;
String parentCommunityString = req.getParameter("parent");
if (StringUtils.isNotBlank(parentCommunityString)) {
UUID parentCommunityUuid = UUIDUtils.fromString(parentCommunityString);
if (parentCommunityUuid == null) {
throw new DSpaceBadRequestException("The given parent parameter was invalid: "
+ parentCommunityString);
// top-level community
community = cs.create(null, context);
cs.update(context, community);
metadataConverter.setMetadata(context, community, communityRest.getMetadata());
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
parent = cs.find(context, parentCommunityUuid);
return dsoConverter.convert(community);
}
@Override
@PreAuthorize("hasPermission(#id, 'COMMUNITY', 'ADD')")
protected CommunityRest createAndReturn(Context context, UUID id) throws AuthorizeException {
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: "
+ parentCommunityUuid + " not found");
}
+ id + " not found");
}
// sub-community
community = cs.create(parent, context);
cs.update(context, community);
metadataConverter.setMetadata(context, community, communityRest.getMetadata());

View File

@@ -12,6 +12,7 @@ import java.io.IOException;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
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
* 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);
/**
* 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
* 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
* @throws AuthorizeException
* @throws SQLException
* @throws RepositoryMethodNotImplementedException
* returned by the default implementation when the operation is not supported for the entity
*/
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);
}
protected T createAndReturn(Context context, UUID uuid)
throws AuthorizeException, SQLException, RepositoryMethodNotImplementedException {
throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", "");
}
/**

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;
import java.io.IOException;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.log4j.Logger;
import org.dspace.app.rest.Parameter;
import org.dspace.app.rest.SearchRestMethod;
@@ -109,11 +112,14 @@ public class RelationshipRestRepository extends DSpaceRestRepository<Relationshi
RelationshipType relationshipType = relationshipTypeService
.find(context, Integer.parseInt(req.getParameter("relationshipType")));
String leftwardValue = req.getParameter("leftwardValue");
String rightwardValue = req.getParameter("rightwardValue");
EPerson ePerson = context.getCurrentUser();
if (authorizeService.authorizeActionBoolean(context, leftItem, Constants.WRITE) ||
authorizeService.authorizeActionBoolean(context, rightItem, Constants.WRITE)) {
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
// 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.
@@ -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
* new items for the relationship.
@@ -253,7 +319,8 @@ public class RelationshipRestRepository extends DSpaceRestRepository<Relationshi
Context context = obtainContext();
List<RelationshipType> relationshipTypeList = relationshipTypeService.findByLeftOrRightLabel(context, label);
List<RelationshipType> relationshipTypeList =
relationshipTypeService.findByLeftwardOrRightwardTypeName(context, label);
List<Relationship> relationships = new LinkedList<>();
if (dsoId != null) {

View File

@@ -30,8 +30,8 @@ import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
/**
* An authenicated user is allowed to view, update or delete his or her own data. This {@link RestPermissionEvaluatorPlugin}
* implemenents that requirement.
* An authenticated user is allowed to view, update or delete his or her own data. This {@link RestPermissionEvaluatorPlugin}
* implements that requirement.
*/
@Component
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 col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections")
.param("size", "1"))
@@ -147,6 +148,7 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections/" + col1.getID()))
.andExpect(status().isOk())
@@ -180,6 +182,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
.withLogo("TestingContentForLogo").build();
Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections/" + col1.getID()))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
@@ -225,6 +229,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections/search/findAuthorized"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
@@ -254,6 +260,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections/search/findAuthorizedByCommunity")
.param("uuid", parentCommunity.getID().toString()))
.andExpect(status().isOk())
@@ -294,6 +302,7 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections/" + UUID.randomUUID()))
.andExpect(status().isNotFound());
@@ -318,6 +327,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child2).withName("Collection 2").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections/" + col1.getID()))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
@@ -363,6 +374,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
collectionRest.setMetadata(new MetadataRest()
.put("dc.title", new MetadataValueRest("Electronic theses and dissertations")));
context.restoreAuthSystemState();
getClient(token).perform(put("/api/core/collections/" + col1.getID().toString())
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsBytes(collectionRest)))
@@ -408,6 +421,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
String token = getAuthToken(admin.getEmail(), password);
context.restoreAuthSystemState();
getClient(token).perform(get("/api/core/collections/" + col1.getID().toString()))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
@@ -449,6 +464,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
.withName("Collection 1")
.build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections/" + col1.getID().toString()))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
@@ -473,6 +490,7 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
.withName("Parent Community")
.withLogo("ThisIsSomeDummyText")
.build();
context.restoreAuthSystemState();
ObjectMapper mapper = new ObjectMapper();
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
public void deleteCollectionEpersonWithDeleteRightsTest() throws Exception {
@@ -552,6 +675,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
String token = getAuthToken(eperson.getEmail(), password);
context.restoreAuthSystemState();
getClient(token).perform(get("/api/core/collections/" + col1.getID().toString()))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
@@ -600,6 +725,8 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
context.setCurrentUser(eperson);
authorizeService.addPolicy(context, col1, Constants.WRITE, eperson);
context.restoreAuthSystemState();
String token = getAuthToken(eperson.getEmail(), password);
ObjectMapper mapper = new ObjectMapper();
@@ -677,6 +804,9 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
new MetadataValueRest("Title Text")));
String authToken = getAuthToken(admin.getEmail(), password);
context.restoreAuthSystemState();
getClient(authToken).perform(post("/api/core/collections")
.content(mapper.writeValueAsBytes(collectionRest))
.param("parent", "123")
@@ -716,6 +846,9 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
new MetadataValueRest("Title Text")));
String authToken = getAuthToken(admin.getEmail(), password);
context.restoreAuthSystemState();
getClient(authToken).perform(post("/api/core/collections")
.content(mapper.writeValueAsBytes(collectionRest))
.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.matchers.JsonPathMatchers.hasJsonPath;
import static junit.framework.TestCase.assertEquals;
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadata;
import static org.hamcrest.Matchers.empty;
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.status;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
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.matcher.CommunityMatcher;
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.MetadataRest;
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.eperson.EPerson;
import org.hamcrest.Matchers;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
/**
* Integration Tests against the /api/core/communities endpoint (including any subpaths)
@@ -132,7 +141,7 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
}
@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
context.turnOffAuthorisationSystem();
@@ -140,6 +149,7 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
context.restoreAuthSystemState();
ObjectMapper mapper = new ObjectMapper();
@@ -147,6 +157,46 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
// We send a name but the created community should set this to the title
comm.setName("Test Sub-Level Community");
// Anonymous user tries to create a community.
// Should fail because user is not authenticated. Error 401.
getClient().perform(post("/api/core/communities")
.content(mapper.writeValueAsBytes(comm))
.param("parent", parentCommunity.getID().toString())
.contentType(contentType))
.andExpect(status().isUnauthorized());
// Non-admin Eperson tries to create a community.
// 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>"))
@@ -159,7 +209,6 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
.put("dc.title",
new MetadataValueRest("Title Text")));
String authToken = getAuthToken(admin.getEmail(), password);
// Capture the UUID of the created Community (see andDo() below)
AtomicReference<UUID> idRef = new AtomicReference<UUID>();
try {
@@ -218,6 +267,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
comm.setMetadata(metadataRest);
context.restoreAuthSystemState();
// Anonymous user tries to create a community.
// Should fail because user is not authenticated. Error 401.
getClient().perform(post("/api/core/communities")
@@ -253,6 +304,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
.withLogo("Test Logo")
.build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities"))
.andExpect(status().isOk())
.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
public void findAllPaginationTest() throws Exception {
//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();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities")
.param("size", "1"))
.andExpect(status().isOk())
@@ -335,6 +509,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
@@ -367,6 +543,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
@@ -444,6 +622,7 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities/search/top"))
.andExpect(status().isOk())
@@ -504,6 +683,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
.withName("Collection 1")
.build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities/search/subCommunities")
.param("parent", parentCommunity.getID().toString()))
.andExpect(status().isOk())
@@ -611,6 +792,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities/" + UUID.randomUUID())).andExpect(status().isNotFound());
}
@@ -653,6 +836,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
communityRest.setMetadata(new MetadataRest()
.put("dc.title", new MetadataValueRest("Electronic theses and dissertations")));
context.restoreAuthSystemState();
getClient(token).perform(put("/api/core/communities/" + parentCommunity.getID().toString())
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsBytes(communityRest)))
@@ -711,6 +896,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
String token = getAuthToken(admin.getEmail(), password);
context.restoreAuthSystemState();
getClient(token).perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
@@ -770,6 +957,7 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
.withName("Collection 1")
.build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
.andExpect(status().isOk())
@@ -801,6 +989,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
String token = getAuthToken(eperson.getEmail(), password);
context.restoreAuthSystemState();
getClient(token).perform(get("/api/core/communities/" + parentCommunity.getID().toString()))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
@@ -859,6 +1049,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
context.setCurrentUser(eperson);
authorizeService.addPolicy(context, parentCommunity, Constants.WRITE, eperson);
context.restoreAuthSystemState();
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(put("/api/core/communities/" + parentCommunity.getID().toString())
@@ -935,6 +1127,8 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
comm.setMetadata(metadataRest);
context.restoreAuthSystemState();
String authToken = getAuthToken(admin.getEmail(), password);
getClient(authToken).perform(post("/api/core/communities")
.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 {
String leftTypeString = "Publication";
String rightTypeString = "Person";
String leftLabel = "isAuthorOfPublication";
String rightLabel = "isPublicationOfAuthor";
checkRelationshipType(leftTypeString, rightTypeString, leftLabel, rightLabel);
String leftwardType = "isAuthorOfPublication";
String rightwardType = "isPublicationOfAuthor";
checkRelationshipType(leftTypeString, rightTypeString, leftwardType, rightwardType);
}
@Test
public void findPublicationProjectRelationshipType() throws SQLException {
String leftTypeString = "Publication";
String rightTypeString = "Project";
String leftLabel = "isProjectOfPublication";
String rightLabel = "isPublicationOfProject";
checkRelationshipType(leftTypeString, rightTypeString, leftLabel, rightLabel);
String leftwardType = "isProjectOfPublication";
String rightwardType = "isPublicationOfProject";
checkRelationshipType(leftTypeString, rightTypeString, leftwardType, rightwardType);
}
@Test
public void findPublicationOrgUnitRelationshipType() throws SQLException {
String leftTypeString = "Publication";
String rightTypeString = "OrgUnit";
String leftLabel = "isOrgUnitOfPublication";
String rightLabel = "isPublicationOfOrgUnit";
checkRelationshipType(leftTypeString, rightTypeString, leftLabel, rightLabel);
String leftwardType = "isOrgUnitOfPublication";
String rightwardType = "isPublicationOfOrgUnit";
checkRelationshipType(leftTypeString, rightTypeString, leftwardType, rightwardType);
}
@Test
public void findPersonProjectRelationshipType() throws SQLException {
String leftTypeString = "Person";
String rightTypeString = "Project";
String leftLabel = "isProjectOfPerson";
String rightLabel = "isPersonOfProject";
checkRelationshipType(leftTypeString, rightTypeString, leftLabel, rightLabel);
String leftwardType = "isProjectOfPerson";
String rightwardType = "isPersonOfProject";
checkRelationshipType(leftTypeString, rightTypeString, leftwardType, rightwardType);
}
@Test
public void findPersonOrgUnitRelationshipType() throws SQLException {
String leftTypeString = "Person";
String rightTypeString = "OrgUnit";
String leftLabel = "isOrgUnitOfPerson";
String rightLabel = "isPersonOfOrgUnit";
checkRelationshipType(leftTypeString, rightTypeString, leftLabel, rightLabel);
String leftwardType = "isOrgUnitOfPerson";
String rightwardType = "isPersonOfOrgUnit";
checkRelationshipType(leftTypeString, rightTypeString, leftwardType, rightwardType);
}
@Test
public void findProjectOrgUnitRelationshipType() throws SQLException {
String leftTypeString = "Project";
String rightTypeString = "OrgUnit";
String leftLabel = "isOrgUnitOfProject";
String rightLabel = "isProjectOfOrgUnit";
checkRelationshipType(leftTypeString, rightTypeString, leftLabel, rightLabel);
String leftwardType = "isOrgUnitOfProject";
String rightwardType = "isProjectOfOrgUnit";
checkRelationshipType(leftTypeString, rightTypeString, leftwardType, rightwardType);
}
@Test
public void findJournalJournalVolumeRelationshipType() throws SQLException {
String leftTypeString = "Journal";
String rightTypeString = "JournalVolume";
String leftLabel = "isVolumeOfJournal";
String rightLabel = "isJournalOfVolume";
checkRelationshipType(leftTypeString, rightTypeString, leftLabel, rightLabel);
String leftwardType = "isVolumeOfJournal";
String rightwardType = "isJournalOfVolume";
checkRelationshipType(leftTypeString, rightTypeString, leftwardType, rightwardType);
}
@Test
public void findJournalVolumeJournalIssueRelationshipType() throws SQLException {
String leftTypeString = "JournalVolume";
String rightTypeString = "JournalIssue";
String leftLabel = "isIssueOfJournalVolume";
String rightLabel = "isJournalVolumeOfIssue";
checkRelationshipType(leftTypeString, rightTypeString, leftLabel, rightLabel);
String leftwardType = "isIssueOfJournalVolume";
String rightwardType = "isJournalVolumeOfIssue";
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 {
RelationshipType relationshipType = relationshipTypeService
.findbyTypesAndLabels(context, entityTypeService.findByEntityType(context, leftType),
entityTypeService.findByEntityType(context, rightType),
leftLabel, rightLabel);
leftwardType, rightwardType);
assertNotNull(relationshipType);
assertEquals(entityTypeService.findByEntityType(context, leftType),
relationshipType.getLeftType());
assertEquals(entityTypeService.findByEntityType(context, rightType),
relationshipType.getRightType());
assertEquals(leftLabel, relationshipType.getLeftLabel());
assertEquals(rightLabel, relationshipType.getRightLabel());
assertEquals(leftwardType, relationshipType.getLeftwardType());
assertEquals(rightwardType, relationshipType.getRightwardType());
}
@Test
@@ -167,8 +167,8 @@ public class RelationshipTypeRestRepositoryIT extends AbstractEntityIntegrationT
RelationshipType foundRelationshipType = null;
for (RelationshipType relationshipType : relationshipTypes) {
if (StringUtils.equals(relationshipType.getLeftLabel(), "isAuthorOfPublication") && StringUtils
.equals(relationshipType.getRightLabel(), "isPublicationOfAuthor")) {
if (StringUtils.equals(relationshipType.getLeftwardType(), "isAuthorOfPublication") && StringUtils
.equals(relationshipType.getRightwardType(), "isPublicationOfAuthor")) {
foundRelationshipType = relationshipType;
break;
}
@@ -212,8 +212,8 @@ public class RelationshipTypeRestRepositoryIT extends AbstractEntityIntegrationT
RelationshipType foundRelationshipType = null;
for (RelationshipType relationshipType : relationshipTypes) {
if (StringUtils.equals(relationshipType.getLeftLabel(), "isIssueOfJournalVolume") && StringUtils
.equals(relationshipType.getRightLabel(), "isJournalVolumeOfIssue")) {
if (StringUtils.equals(relationshipType.getLeftwardType(), "isIssueOfJournalVolume") && StringUtils
.equals(relationshipType.getRightwardType(), "isJournalVolumeOfIssue")) {
foundRelationshipType = relationshipType;
break;
}

View File

@@ -63,6 +63,10 @@ public class CommunityBuilder extends AbstractDSpaceObjectBuilder<Community> {
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 {
try (InputStream is = IOUtils.toInputStream(content, CharEncoding.UTF_8)) {
communityService.setLogo(context, community, is);

View File

@@ -84,4 +84,19 @@ public class RelationshipBuilder extends AbstractBuilder<Relationship, Relations
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,
EntityType rightType, String leftLabel,
String rightLabel, Integer leftCardinalityMin,
EntityType rightType,
String leftwardType,
String rightwardType,
Integer leftCardinalityMin,
Integer leftCardinalityMax,
Integer rightCardinalityMin,
Integer rightCardinalityMax) {
RelationshipTypeBuilder relationshipBuilder = new RelationshipTypeBuilder(context);
return relationshipBuilder.create(context, leftType,
rightType, leftLabel,
rightLabel, leftCardinalityMin,
rightType, leftwardType,
rightwardType, leftCardinalityMin,
leftCardinalityMax, rightCardinalityMin,
rightCardinalityMax);
}
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 rightCardinalityMax) {
try {
this.context = context;
this.relationshipType = relationshipTypeService
.create(context, leftEntityType, rightEntityType, leftLabel, rightLabel, leftCardinalityMin,
.create(context, leftEntityType, rightEntityType, leftwardType, rightwardType, leftCardinalityMin,
leftCardinalityMax, rightCardinalityMin, rightCardinalityMax);
} 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.is;
import java.util.List;
import java.util.UUID;
import org.dspace.content.Collection;
@@ -21,6 +22,34 @@ public class 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) {
return allOf(
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;
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 org.hamcrest.Matcher;
@@ -18,7 +18,8 @@ import org.hamcrest.Matcher;
*/
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.
@@ -28,7 +29,7 @@ public class MetadataMatcher {
* @return the matcher.
*/
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) {
return matchExplicitRelationshipTypeValuesAndExplicitEntityTypeValues(relationshipType.getID(),
relationshipType.getLeftLabel(),
relationshipType.getRightLabel(),
relationshipType.getLeftwardType(),
relationshipType.getRightwardType(),
relationshipType.getLeftMinCardinality(),
relationshipType.getLeftMaxCardinality(),
relationshipType.getRightMinCardinality(),
@@ -49,10 +49,10 @@ public class RelationshipTypeMatcher {
}
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,
EntityType leftEntityType, EntityType rightEntityType) {
return matchExplicitRelationshipTypeValuesAndExplicitEntityTypeValues(id, leftLabel, rightLabel,
return matchExplicitRelationshipTypeValuesAndExplicitEntityTypeValues(id, leftwardType, rightwardType,
leftMinCardinality, leftMaxCardinality,
rightMinCardinality,
rightMaxCardinality,
@@ -63,13 +63,13 @@ public class RelationshipTypeMatcher {
}
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,
int rightEntityTypeId, String rightEntityTypeLabel) {
return allOf(
hasJsonPath("$.id", is(id)),
hasJsonPath("$.leftLabel", is(leftLabel)),
hasJsonPath("$.rightLabel", is(rightLabel)),
hasJsonPath("$.leftwardType", is(leftwardType)),
hasJsonPath("$.rightwardType", is(rightwardType)),
hasJsonPath("$.leftMinCardinality", is(leftMinCardinality)),
hasJsonPath("$.leftMaxCardinality", is(leftMaxCardinality)),
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.EntityServiceImpl"/>
<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.MetadataAuthorityServiceImpl" lazy-init="true"/>

View File

@@ -59,6 +59,7 @@
<value>, </value>
</property>
<property name="useForPlace" value="true"/>
<property name="populateWithNameVariant" value="true"/>
</bean>
<!-- 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