mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-16 06:23:10 +00:00
Merge pull request #8184 from 4Science/CST-5249
Add a correction service to dspace to enhance the data quality (was Enrich local data via the OpenAIRE Graph)
This commit is contained in:
@@ -121,6 +121,8 @@ services:
|
|||||||
cp -r /opt/solr/server/solr/configsets/search/* search
|
cp -r /opt/solr/server/solr/configsets/search/* search
|
||||||
precreate-core statistics /opt/solr/server/solr/configsets/statistics
|
precreate-core statistics /opt/solr/server/solr/configsets/statistics
|
||||||
cp -r /opt/solr/server/solr/configsets/statistics/* statistics
|
cp -r /opt/solr/server/solr/configsets/statistics/* statistics
|
||||||
|
precreate-core qaevent /opt/solr/server/solr/configsets/qaevent
|
||||||
|
cp -r /opt/solr/server/solr/configsets/qaevent/* qaevent
|
||||||
exec solr -f
|
exec solr -f
|
||||||
volumes:
|
volumes:
|
||||||
assetstore:
|
assetstore:
|
||||||
|
@@ -819,6 +819,12 @@
|
|||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>eu.openaire</groupId>
|
||||||
|
<artifactId>broker-client</artifactId>
|
||||||
|
<version>1.1.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mock-server</groupId>
|
<groupId>org.mock-server</groupId>
|
||||||
|
@@ -1021,6 +1021,61 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl<Collection> i
|
|||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection retrieveCollectionWithSubmitByEntityType(Context context, Item item,
|
||||||
|
String entityType) throws SQLException {
|
||||||
|
Collection ownCollection = item.getOwningCollection();
|
||||||
|
return retrieveWithSubmitCollectionByEntityType(context, ownCollection.getCommunities(), entityType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection retrieveWithSubmitCollectionByEntityType(Context context, List<Community> communities,
|
||||||
|
String entityType) {
|
||||||
|
|
||||||
|
for (Community community : communities) {
|
||||||
|
Collection collection = retrieveCollectionWithSubmitByCommunityAndEntityType(context, community,
|
||||||
|
entityType);
|
||||||
|
if (collection != null) {
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Community community : communities) {
|
||||||
|
List<Community> parentCommunities = community.getParentCommunities();
|
||||||
|
Collection collection = retrieveWithSubmitCollectionByEntityType(context, parentCommunities, entityType);
|
||||||
|
if (collection != null) {
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retrieveCollectionWithSubmitByCommunityAndEntityType(context, null, entityType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection retrieveCollectionWithSubmitByCommunityAndEntityType(Context context, Community community,
|
||||||
|
String entityType) {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
List<Collection> collections;
|
||||||
|
try {
|
||||||
|
collections = findCollectionsWithSubmit(null, context, community, entityType, 0, 1);
|
||||||
|
} catch (SQLException | SearchServiceException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
if (collections != null && collections.size() > 0) {
|
||||||
|
return collections.get(0);
|
||||||
|
}
|
||||||
|
if (community != null) {
|
||||||
|
for (Community subCommunity : community.getSubcommunities()) {
|
||||||
|
Collection collection = retrieveCollectionWithSubmitByCommunityAndEntityType(context,
|
||||||
|
subCommunity, entityType);
|
||||||
|
if (collection != null) {
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Collection> findCollectionsWithSubmit(String q, Context context, Community community, String entityType,
|
public List<Collection> findCollectionsWithSubmit(String q, Context context, Community community, String entityType,
|
||||||
int offset, int limit) throws SQLException, SearchServiceException {
|
int offset, int limit) throws SQLException, SearchServiceException {
|
||||||
|
@@ -77,6 +77,7 @@ import org.dspace.orcid.service.OrcidQueueService;
|
|||||||
import org.dspace.orcid.service.OrcidSynchronizationService;
|
import org.dspace.orcid.service.OrcidSynchronizationService;
|
||||||
import org.dspace.orcid.service.OrcidTokenService;
|
import org.dspace.orcid.service.OrcidTokenService;
|
||||||
import org.dspace.profile.service.ResearcherProfileService;
|
import org.dspace.profile.service.ResearcherProfileService;
|
||||||
|
import org.dspace.qaevent.dao.QAEventsDAO;
|
||||||
import org.dspace.services.ConfigurationService;
|
import org.dspace.services.ConfigurationService;
|
||||||
import org.dspace.versioning.service.VersioningService;
|
import org.dspace.versioning.service.VersioningService;
|
||||||
import org.dspace.workflow.WorkflowItemService;
|
import org.dspace.workflow.WorkflowItemService;
|
||||||
@@ -170,6 +171,9 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
|
|||||||
@Autowired(required = true)
|
@Autowired(required = true)
|
||||||
protected SubscribeService subscribeService;
|
protected SubscribeService subscribeService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private QAEventsDAO qaEventsDao;
|
||||||
|
|
||||||
protected ItemServiceImpl() {
|
protected ItemServiceImpl() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@@ -819,6 +823,11 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
|
|||||||
orcidToken.setProfileItem(null);
|
orcidToken.setProfileItem(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<QAEventProcessed> qaEvents = qaEventsDao.findByItem(context, item);
|
||||||
|
for (QAEventProcessed qaEvent : qaEvents) {
|
||||||
|
qaEventsDao.delete(context, qaEvent);
|
||||||
|
}
|
||||||
|
|
||||||
//Only clear collections after we have removed everything else from the item
|
//Only clear collections after we have removed everything else from the item
|
||||||
item.clearCollections();
|
item.clearCollections();
|
||||||
item.setOwningCollection(null);
|
item.setOwningCollection(null);
|
||||||
|
213
dspace-api/src/main/java/org/dspace/content/QAEvent.java
Normal file
213
dspace-api/src/main/java/org/dspace/content/QAEvent.java
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
/**
|
||||||
|
* 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.io.UnsupportedEncodingException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import org.dspace.qaevent.service.dto.OpenaireMessageDTO;
|
||||||
|
import org.dspace.qaevent.service.dto.QAMessageDTO;
|
||||||
|
import org.dspace.util.RawJsonDeserializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represent the Quality Assurance broker data as loaded in our solr
|
||||||
|
* qaevent core
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QAEvent {
|
||||||
|
public static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e',
|
||||||
|
'f' };
|
||||||
|
public static final String ACCEPTED = "accepted";
|
||||||
|
public static final String REJECTED = "rejected";
|
||||||
|
public static final String DISCARDED = "discarded";
|
||||||
|
|
||||||
|
public static final String OPENAIRE_SOURCE = "openaire";
|
||||||
|
|
||||||
|
private String source;
|
||||||
|
|
||||||
|
private String eventId;
|
||||||
|
/**
|
||||||
|
* contains the targeted dspace object,
|
||||||
|
* ie: oai:www.openstarts.units.it:123456789/1120 contains the handle
|
||||||
|
* of the DSpace pbject in its final part 123456789/1120
|
||||||
|
* */
|
||||||
|
private String originalId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* evaluated with the targeted dspace object id
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
private String target;
|
||||||
|
|
||||||
|
private String related;
|
||||||
|
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
private String topic;
|
||||||
|
|
||||||
|
private double trust;
|
||||||
|
|
||||||
|
@JsonDeserialize(using = RawJsonDeserializer.class)
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
private Date lastUpdate;
|
||||||
|
|
||||||
|
private String status = "PENDING";
|
||||||
|
|
||||||
|
public QAEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public QAEvent(String source, String originalId, String target, String title,
|
||||||
|
String topic, double trust, String message, Date lastUpdate) {
|
||||||
|
super();
|
||||||
|
this.source = source;
|
||||||
|
this.originalId = originalId;
|
||||||
|
this.target = target;
|
||||||
|
this.title = title;
|
||||||
|
this.topic = topic;
|
||||||
|
this.trust = trust;
|
||||||
|
this.message = message;
|
||||||
|
this.lastUpdate = lastUpdate;
|
||||||
|
try {
|
||||||
|
computedEventId();
|
||||||
|
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOriginalId() {
|
||||||
|
return originalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOriginalId(String originalId) {
|
||||||
|
this.originalId = originalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTopic() {
|
||||||
|
return topic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTopic(String topic) {
|
||||||
|
this.topic = topic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getTrust() {
|
||||||
|
return trust;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTrust(double trust) {
|
||||||
|
this.trust = trust;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEventId() {
|
||||||
|
if (eventId == null) {
|
||||||
|
try {
|
||||||
|
computedEventId();
|
||||||
|
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return eventId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEventId(String eventId) {
|
||||||
|
this.eventId = eventId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTarget() {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTarget(String target) {
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLastUpdate() {
|
||||||
|
return lastUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastUpdate(Date lastUpdate) {
|
||||||
|
this.lastUpdate = lastUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRelated(String related) {
|
||||||
|
this.related = related;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRelated() {
|
||||||
|
return related;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(String status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSource() {
|
||||||
|
return source != null ? source : OPENAIRE_SOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSource(String source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DTO constructed via Jackson use empty constructor. In this case, the eventId
|
||||||
|
* must be compute on the get method. This method create a signature based on
|
||||||
|
* the event fields and store it in the eventid attribute.
|
||||||
|
*/
|
||||||
|
private void computedEventId() throws NoSuchAlgorithmException, UnsupportedEncodingException {
|
||||||
|
MessageDigest digester = MessageDigest.getInstance("MD5");
|
||||||
|
String dataToString = "source=" + source + ",originalId=" + originalId + ", title=" + title + ", topic="
|
||||||
|
+ topic + ", trust=" + trust + ", message=" + message;
|
||||||
|
digester.update(dataToString.getBytes("UTF-8"));
|
||||||
|
byte[] signature = digester.digest();
|
||||||
|
char[] arr = new char[signature.length << 1];
|
||||||
|
for (int i = 0; i < signature.length; i++) {
|
||||||
|
int b = signature[i];
|
||||||
|
int idx = i << 1;
|
||||||
|
arr[idx] = HEX_DIGITS[(b >> 4) & 0xf];
|
||||||
|
arr[idx + 1] = HEX_DIGITS[b & 0xf];
|
||||||
|
}
|
||||||
|
eventId = new String(arr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<? extends QAMessageDTO> getMessageDtoClass() {
|
||||||
|
switch (getSource()) {
|
||||||
|
case OPENAIRE_SOURCE:
|
||||||
|
return OpenaireMessageDTO.class;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unknown event's source: " + getSource());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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.content;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.Temporal;
|
||||||
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represent the stored information about processed notification
|
||||||
|
* broker events
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "qaevent_processed")
|
||||||
|
public class QAEventProcessed implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 3427340199132007814L;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "qaevent_id")
|
||||||
|
private String eventId;
|
||||||
|
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
@Column(name = "qaevent_timestamp")
|
||||||
|
private Date eventTimestamp;
|
||||||
|
|
||||||
|
@JoinColumn(name = "eperson_uuid")
|
||||||
|
@ManyToOne
|
||||||
|
private EPerson eperson;
|
||||||
|
|
||||||
|
@JoinColumn(name = "item_uuid")
|
||||||
|
@ManyToOne
|
||||||
|
private Item item;
|
||||||
|
|
||||||
|
public String getEventId() {
|
||||||
|
return eventId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEventId(String eventId) {
|
||||||
|
this.eventId = eventId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getEventTimestamp() {
|
||||||
|
return eventTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEventTimestamp(Date eventTimestamp) {
|
||||||
|
this.eventTimestamp = eventTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EPerson getEperson() {
|
||||||
|
return eperson;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEperson(EPerson eperson) {
|
||||||
|
this.eperson = eperson;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Item getItem() {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItem(Item item) {
|
||||||
|
this.item = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -417,6 +417,34 @@ public interface CollectionService
|
|||||||
public List<Collection> findCollectionsWithSubmit(String q, Context context, Community community,
|
public List<Collection> findCollectionsWithSubmit(String q, Context context, Community community,
|
||||||
int offset, int limit) throws SQLException, SearchServiceException;
|
int offset, int limit) throws SQLException, SearchServiceException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the first collection in the community or its descending that support
|
||||||
|
* the provided entityType
|
||||||
|
*
|
||||||
|
* @param context the DSpace context
|
||||||
|
* @param community the root from where the search start
|
||||||
|
* @param entityType the requested entity type
|
||||||
|
* @return the first collection in the community or its descending
|
||||||
|
* that support the provided entityType
|
||||||
|
*/
|
||||||
|
public Collection retrieveCollectionWithSubmitByCommunityAndEntityType(Context context, Community community,
|
||||||
|
String entityType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the close collection to the item for which the current user has
|
||||||
|
* 'submit' privileges that support the provided entityType. Close mean the
|
||||||
|
* collection that can be reach with the minimum steps starting from the item
|
||||||
|
* (owningCollection, brothers collections, etc)
|
||||||
|
*
|
||||||
|
* @param context the DSpace context
|
||||||
|
* @param item the item from where the search start
|
||||||
|
* @param entityType the requested entity type
|
||||||
|
* @return the first collection in the community or its descending
|
||||||
|
* that support the provided entityType
|
||||||
|
*/
|
||||||
|
public Collection retrieveCollectionWithSubmitByEntityType(Context context, Item item, String entityType)
|
||||||
|
throws SQLException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counts the number of Collection for which the current user has 'submit' privileges.
|
* Counts the number of Collection for which the current user has 'submit' privileges.
|
||||||
* NOTE: for better performance, this method retrieves its results from an index (cache)
|
* NOTE: for better performance, this method retrieves its results from an index (cache)
|
||||||
|
@@ -33,6 +33,7 @@ import org.dspace.content.DSpaceObjectServiceImpl;
|
|||||||
import org.dspace.content.Item;
|
import org.dspace.content.Item;
|
||||||
import org.dspace.content.MetadataField;
|
import org.dspace.content.MetadataField;
|
||||||
import org.dspace.content.MetadataValue;
|
import org.dspace.content.MetadataValue;
|
||||||
|
import org.dspace.content.QAEventProcessed;
|
||||||
import org.dspace.content.WorkspaceItem;
|
import org.dspace.content.WorkspaceItem;
|
||||||
import org.dspace.content.factory.ContentServiceFactory;
|
import org.dspace.content.factory.ContentServiceFactory;
|
||||||
import org.dspace.content.service.ItemService;
|
import org.dspace.content.service.ItemService;
|
||||||
@@ -47,6 +48,7 @@ import org.dspace.eperson.service.GroupService;
|
|||||||
import org.dspace.eperson.service.SubscribeService;
|
import org.dspace.eperson.service.SubscribeService;
|
||||||
import org.dspace.event.Event;
|
import org.dspace.event.Event;
|
||||||
import org.dspace.orcid.service.OrcidTokenService;
|
import org.dspace.orcid.service.OrcidTokenService;
|
||||||
|
import org.dspace.qaevent.dao.QAEventsDAO;
|
||||||
import org.dspace.services.ConfigurationService;
|
import org.dspace.services.ConfigurationService;
|
||||||
import org.dspace.util.UUIDUtils;
|
import org.dspace.util.UUIDUtils;
|
||||||
import org.dspace.versioning.Version;
|
import org.dspace.versioning.Version;
|
||||||
@@ -106,6 +108,8 @@ public class EPersonServiceImpl extends DSpaceObjectServiceImpl<EPerson> impleme
|
|||||||
protected ConfigurationService configurationService;
|
protected ConfigurationService configurationService;
|
||||||
@Autowired
|
@Autowired
|
||||||
protected OrcidTokenService orcidTokenService;
|
protected OrcidTokenService orcidTokenService;
|
||||||
|
@Autowired
|
||||||
|
protected QAEventsDAO qaEventsDao;
|
||||||
|
|
||||||
protected EPersonServiceImpl() {
|
protected EPersonServiceImpl() {
|
||||||
super();
|
super();
|
||||||
@@ -487,6 +491,11 @@ public class EPersonServiceImpl extends DSpaceObjectServiceImpl<EPerson> impleme
|
|||||||
// Remove any subscriptions
|
// Remove any subscriptions
|
||||||
subscribeService.deleteByEPerson(context, ePerson);
|
subscribeService.deleteByEPerson(context, ePerson);
|
||||||
|
|
||||||
|
List<QAEventProcessed> qaEvents = qaEventsDao.findByEPerson(context, ePerson);
|
||||||
|
for (QAEventProcessed qaEvent : qaEvents) {
|
||||||
|
qaEventsDao.delete(context, qaEvent);
|
||||||
|
}
|
||||||
|
|
||||||
// Remove ourself
|
// Remove ourself
|
||||||
ePersonDAO.delete(context, ePerson);
|
ePersonDAO.delete(context, ePerson);
|
||||||
|
|
||||||
|
@@ -40,20 +40,20 @@ import org.json.JSONObject;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* based on OrcidRestConnector it's a rest connector for OpenAIRE API providing
|
* based on OrcidRestConnector it's a rest connector for Openaire API providing
|
||||||
* ways to perform searches and token grabbing
|
* ways to perform searches and token grabbing
|
||||||
*
|
*
|
||||||
* @author paulo-graca
|
* @author paulo-graca
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class OpenAIRERestConnector {
|
public class OpenaireRestConnector {
|
||||||
/**
|
/**
|
||||||
* log4j logger
|
* log4j logger
|
||||||
*/
|
*/
|
||||||
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(OpenAIRERestConnector.class);
|
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(OpenaireRestConnector.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OpenAIRE API Url
|
* Openaire API Url
|
||||||
* and can be configured with: openaire.api.url
|
* and can be configured with: openaire.api.url
|
||||||
*/
|
*/
|
||||||
private String url = "https://api.openaire.eu";
|
private String url = "https://api.openaire.eu";
|
||||||
@@ -65,30 +65,30 @@ public class OpenAIRERestConnector {
|
|||||||
boolean tokenEnabled = false;
|
boolean tokenEnabled = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OpenAIRE Authorization and Authentication Token Service URL
|
* Openaire Authorization and Authentication Token Service URL
|
||||||
* and can be configured with: openaire.token.url
|
* and can be configured with: openaire.token.url
|
||||||
*/
|
*/
|
||||||
private String tokenServiceUrl;
|
private String tokenServiceUrl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OpenAIRE clientId
|
* Openaire clientId
|
||||||
* and can be configured with: openaire.token.clientId
|
* and can be configured with: openaire.token.clientId
|
||||||
*/
|
*/
|
||||||
private String clientId;
|
private String clientId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OpenAIRERest access token
|
* OpenaireRest access token
|
||||||
*/
|
*/
|
||||||
private OpenAIRERestToken accessToken;
|
private OpenaireRestToken accessToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OpenAIRE clientSecret
|
* Openaire clientSecret
|
||||||
* and can be configured with: openaire.token.clientSecret
|
* and can be configured with: openaire.token.clientSecret
|
||||||
*/
|
*/
|
||||||
private String clientSecret;
|
private String clientSecret;
|
||||||
|
|
||||||
|
|
||||||
public OpenAIRERestConnector(String url) {
|
public OpenaireRestConnector(String url) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ public class OpenAIRERestConnector {
|
|||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public OpenAIRERestToken grabNewAccessToken() throws IOException {
|
public OpenaireRestToken grabNewAccessToken() throws IOException {
|
||||||
|
|
||||||
if (StringUtils.isBlank(tokenServiceUrl) || StringUtils.isBlank(clientId)
|
if (StringUtils.isBlank(tokenServiceUrl) || StringUtils.isBlank(clientId)
|
||||||
|| StringUtils.isBlank(clientSecret)) {
|
|| StringUtils.isBlank(clientSecret)) {
|
||||||
@@ -145,13 +145,13 @@ public class OpenAIRERestConnector {
|
|||||||
throw new IOException("Unable to grab the access token using provided service url, client id and secret");
|
throw new IOException("Unable to grab the access token using provided service url, client id and secret");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OpenAIRERestToken(responseObject.get("access_token").toString(),
|
return new OpenaireRestToken(responseObject.get("access_token").toString(),
|
||||||
Long.valueOf(responseObject.get("expires_in").toString()));
|
Long.valueOf(responseObject.get("expires_in").toString()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a GET request to the OpenAIRE API
|
* Perform a GET request to the Openaire API
|
||||||
*
|
*
|
||||||
* @param file
|
* @param file
|
||||||
* @param accessToken
|
* @param accessToken
|
||||||
@@ -218,12 +218,12 @@ public class OpenAIRERestConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform an OpenAIRE Project Search By Keywords
|
* Perform an Openaire Project Search By Keywords
|
||||||
*
|
*
|
||||||
* @param page
|
* @param page
|
||||||
* @param size
|
* @param size
|
||||||
* @param keywords
|
* @param keywords
|
||||||
* @return OpenAIRE Response
|
* @return Openaire Response
|
||||||
*/
|
*/
|
||||||
public Response searchProjectByKeywords(int page, int size, String... keywords) {
|
public Response searchProjectByKeywords(int page, int size, String... keywords) {
|
||||||
String path = "search/projects?keywords=" + String.join("+", keywords);
|
String path = "search/projects?keywords=" + String.join("+", keywords);
|
||||||
@@ -231,13 +231,13 @@ public class OpenAIRERestConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform an OpenAIRE Project Search By ID and by Funder
|
* Perform an Openaire Project Search By ID and by Funder
|
||||||
*
|
*
|
||||||
* @param projectID
|
* @param projectID
|
||||||
* @param projectFunder
|
* @param projectFunder
|
||||||
* @param page
|
* @param page
|
||||||
* @param size
|
* @param size
|
||||||
* @return OpenAIRE Response
|
* @return Openaire Response
|
||||||
*/
|
*/
|
||||||
public Response searchProjectByIDAndFunder(String projectID, String projectFunder, int page, int size) {
|
public Response searchProjectByIDAndFunder(String projectID, String projectFunder, int page, int size) {
|
||||||
String path = "search/projects?grantID=" + projectID + "&funder=" + projectFunder;
|
String path = "search/projects?grantID=" + projectID + "&funder=" + projectFunder;
|
||||||
@@ -245,12 +245,12 @@ public class OpenAIRERestConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform an OpenAIRE Search request
|
* Perform an Openaire Search request
|
||||||
*
|
*
|
||||||
* @param path
|
* @param path
|
||||||
* @param page
|
* @param page
|
||||||
* @param size
|
* @param size
|
||||||
* @return OpenAIRE Response
|
* @return Openaire Response
|
||||||
*/
|
*/
|
||||||
public Response search(String path, int page, int size) {
|
public Response search(String path, int page, int size) {
|
||||||
String[] queryStringPagination = { "page=" + page, "size=" + size };
|
String[] queryStringPagination = { "page=" + page, "size=" + size };
|
@@ -8,13 +8,13 @@
|
|||||||
package org.dspace.external;
|
package org.dspace.external;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OpenAIRE rest API token to be used when grabbing an accessToken.<br/>
|
* Openaire rest API token to be used when grabbing an accessToken.<br/>
|
||||||
* Based on https://develop.openaire.eu/basic.html
|
* Based on https://develop.openaire.eu/basic.html
|
||||||
*
|
*
|
||||||
* @author paulo-graca
|
* @author paulo-graca
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class OpenAIRERestToken {
|
public class OpenaireRestToken {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stored access token
|
* Stored access token
|
||||||
@@ -32,7 +32,7 @@ public class OpenAIRERestToken {
|
|||||||
* @param accessToken
|
* @param accessToken
|
||||||
* @param expiresIn
|
* @param expiresIn
|
||||||
*/
|
*/
|
||||||
public OpenAIRERestToken(String accessToken, Long expiresIn) {
|
public OpenaireRestToken(String accessToken, Long expiresIn) {
|
||||||
this.accessToken = accessToken;
|
this.accessToken = accessToken;
|
||||||
this.setExpirationDate(expiresIn);
|
this.setExpirationDate(expiresIn);
|
||||||
}
|
}
|
@@ -31,7 +31,7 @@ import eu.openaire.oaf.model.base.Project;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.dspace.content.dto.MetadataValueDTO;
|
import org.dspace.content.dto.MetadataValueDTO;
|
||||||
import org.dspace.external.OpenAIRERestConnector;
|
import org.dspace.external.OpenaireRestConnector;
|
||||||
import org.dspace.external.model.ExternalDataObject;
|
import org.dspace.external.model.ExternalDataObject;
|
||||||
import org.dspace.external.provider.AbstractExternalDataProvider;
|
import org.dspace.external.provider.AbstractExternalDataProvider;
|
||||||
import org.dspace.importer.external.metadatamapping.MetadataFieldConfig;
|
import org.dspace.importer.external.metadatamapping.MetadataFieldConfig;
|
||||||
@@ -39,13 +39,13 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is the implementation of the ExternalDataProvider interface that
|
* This class is the implementation of the ExternalDataProvider interface that
|
||||||
* will deal with the OpenAIRE External Data lookup
|
* will deal with the Openaire External Data lookup
|
||||||
*
|
*
|
||||||
* @author paulo-graca
|
* @author paulo-graca
|
||||||
*/
|
*/
|
||||||
public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
|
public class OpenaireFundingDataProvider extends AbstractExternalDataProvider {
|
||||||
|
|
||||||
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(OpenAIREFundingDataProvider.class);
|
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(OpenaireFundingDataProvider.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GrantAgreement prefix
|
* GrantAgreement prefix
|
||||||
@@ -75,7 +75,7 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
|
|||||||
/**
|
/**
|
||||||
* Connector to handle token and requests
|
* Connector to handle token and requests
|
||||||
*/
|
*/
|
||||||
protected OpenAIRERestConnector connector;
|
protected OpenaireRestConnector connector;
|
||||||
|
|
||||||
protected Map<String, MetadataFieldConfig> metadataFields;
|
protected Map<String, MetadataFieldConfig> metadataFields;
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
|
|||||||
// characters that must be escaped for the <:entry-id>
|
// characters that must be escaped for the <:entry-id>
|
||||||
String decodedId = new String(Base64.getDecoder().decode(id));
|
String decodedId = new String(Base64.getDecoder().decode(id));
|
||||||
if (!isValidProjectURI(decodedId)) {
|
if (!isValidProjectURI(decodedId)) {
|
||||||
log.error("Invalid ID for OpenAIREFunding - " + id);
|
log.error("Invalid ID for OpenaireFunding - " + id);
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
Response response = searchByProjectURI(decodedId);
|
Response response = searchByProjectURI(decodedId);
|
||||||
@@ -101,7 +101,7 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
|
|||||||
try {
|
try {
|
||||||
if (response.getHeader() != null && Integer.parseInt(response.getHeader().getTotal()) > 0) {
|
if (response.getHeader() != null && Integer.parseInt(response.getHeader().getTotal()) > 0) {
|
||||||
Project project = response.getResults().getResult().get(0).getMetadata().getEntity().getProject();
|
Project project = response.getResults().getResult().get(0).getMetadata().getEntity().getProject();
|
||||||
ExternalDataObject externalDataObject = new OpenAIREFundingDataProvider
|
ExternalDataObject externalDataObject = new OpenaireFundingDataProvider
|
||||||
.ExternalDataObjectBuilder(project)
|
.ExternalDataObjectBuilder(project)
|
||||||
.setId(generateProjectURI(project))
|
.setId(generateProjectURI(project))
|
||||||
.setSource(sourceIdentifier)
|
.setSource(sourceIdentifier)
|
||||||
@@ -123,7 +123,7 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
|
|||||||
limit = LIMIT_DEFAULT;
|
limit = LIMIT_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenAIRE uses pages and first page starts with 1
|
// Openaire uses pages and first page starts with 1
|
||||||
int page = (start / limit) + 1;
|
int page = (start / limit) + 1;
|
||||||
|
|
||||||
// escaping query
|
// escaping query
|
||||||
@@ -148,7 +148,7 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
|
|||||||
|
|
||||||
if (projects.size() > 0) {
|
if (projects.size() > 0) {
|
||||||
return projects.stream()
|
return projects.stream()
|
||||||
.map(project -> new OpenAIREFundingDataProvider
|
.map(project -> new OpenaireFundingDataProvider
|
||||||
.ExternalDataObjectBuilder(project)
|
.ExternalDataObjectBuilder(project)
|
||||||
.setId(generateProjectURI(project))
|
.setId(generateProjectURI(project))
|
||||||
.setSource(sourceIdentifier)
|
.setSource(sourceIdentifier)
|
||||||
@@ -176,24 +176,24 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
|
|||||||
* Generic setter for the sourceIdentifier
|
* Generic setter for the sourceIdentifier
|
||||||
*
|
*
|
||||||
* @param sourceIdentifier The sourceIdentifier to be set on this
|
* @param sourceIdentifier The sourceIdentifier to be set on this
|
||||||
* OpenAIREFunderDataProvider
|
* OpenaireFunderDataProvider
|
||||||
*/
|
*/
|
||||||
@Autowired(required = true)
|
@Autowired(required = true)
|
||||||
public void setSourceIdentifier(String sourceIdentifier) {
|
public void setSourceIdentifier(String sourceIdentifier) {
|
||||||
this.sourceIdentifier = sourceIdentifier;
|
this.sourceIdentifier = sourceIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenAIRERestConnector getConnector() {
|
public OpenaireRestConnector getConnector() {
|
||||||
return connector;
|
return connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic setter for OpenAIRERestConnector
|
* Generic setter for OpenaireRestConnector
|
||||||
*
|
*
|
||||||
* @param connector
|
* @param connector
|
||||||
*/
|
*/
|
||||||
@Autowired(required = true)
|
@Autowired(required = true)
|
||||||
public void setConnector(OpenAIRERestConnector connector) {
|
public void setConnector(OpenaireRestConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +219,7 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method returns an URI based on OpenAIRE 3.0 guidelines
|
* This method returns an URI based on Openaire 3.0 guidelines
|
||||||
* https://guidelines.openaire.eu/en/latest/literature/field_projectid.html that
|
* https://guidelines.openaire.eu/en/latest/literature/field_projectid.html that
|
||||||
* can be used as an ID if is there any missing part, that part it will be
|
* can be used as an ID if is there any missing part, that part it will be
|
||||||
* replaced by the character '+'
|
* replaced by the character '+'
|
||||||
@@ -281,7 +281,7 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OpenAIRE Funding External Data Builder Class
|
* Openaire Funding External Data Builder Class
|
||||||
*
|
*
|
||||||
* @author pgraca
|
* @author pgraca
|
||||||
*/
|
*/
|
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent;
|
||||||
|
|
||||||
|
import org.dspace.core.Constants;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.event.Consumer;
|
||||||
|
import org.dspace.event.Event;
|
||||||
|
import org.dspace.qaevent.service.QAEventService;
|
||||||
|
import org.dspace.utils.DSpace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consumer to delete qaevents from solr due to the target item deletion
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QAEventsDeleteCascadeConsumer implements Consumer {
|
||||||
|
|
||||||
|
private QAEventService qaEventService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() throws Exception {
|
||||||
|
qaEventService = new DSpace().getSingletonService(QAEventService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish(Context context) throws Exception {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void consume(Context context, Event event) throws Exception {
|
||||||
|
if (event.getEventType() == Event.DELETE) {
|
||||||
|
if (event.getSubjectType() == Constants.ITEM && event.getSubjectID() != null) {
|
||||||
|
qaEventService.deleteEventsByTargetId(event.getSubjectID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void end(Context context) throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
46
dspace-api/src/main/java/org/dspace/qaevent/QASource.java
Normal file
46
dspace-api/src/main/java/org/dspace/qaevent/QASource.java
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This model class represent the source/provider of the QA events (as Openaire).
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4Science)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QASource {
|
||||||
|
private String name;
|
||||||
|
private long totalEvents;
|
||||||
|
private Date lastEvent;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTotalEvents() {
|
||||||
|
return totalEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalEvents(long totalEvents) {
|
||||||
|
this.totalEvents = totalEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLastEvent() {
|
||||||
|
return lastEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastEvent(Date lastEvent) {
|
||||||
|
this.lastEvent = lastEvent;
|
||||||
|
}
|
||||||
|
}
|
47
dspace-api/src/main/java/org/dspace/qaevent/QATopic.java
Normal file
47
dspace-api/src/main/java/org/dspace/qaevent/QATopic.java
Normal 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.qaevent;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This model class represent the quality assurance broker topic concept. A
|
||||||
|
* topic represents a type of event and is therefore used to group events.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QATopic {
|
||||||
|
private String key;
|
||||||
|
private long totalEvents;
|
||||||
|
private Date lastEvent;
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKey(String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTotalEvents() {
|
||||||
|
return totalEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalEvents(long totalEvents) {
|
||||||
|
this.totalEvents = totalEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLastEvent() {
|
||||||
|
return lastEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastEvent(Date lastEvent) {
|
||||||
|
this.lastEvent = lastEvent;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent;
|
||||||
|
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.qaevent.service.dto.QAMessageDTO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for classes that perform a correction on the given item.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface QualityAssuranceAction {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a correction on the given item.
|
||||||
|
*
|
||||||
|
* @param context the DSpace context
|
||||||
|
* @param item the item to correct
|
||||||
|
* @param relatedItem the related item, if any
|
||||||
|
* @param message the message with the correction details
|
||||||
|
*/
|
||||||
|
public void applyCorrection(Context context, Item item, Item relatedItem, QAMessageDTO message);
|
||||||
|
}
|
@@ -0,0 +1,180 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent.action;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.content.Collection;
|
||||||
|
import org.dspace.content.EntityType;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.RelationshipType;
|
||||||
|
import org.dspace.content.WorkspaceItem;
|
||||||
|
import org.dspace.content.service.CollectionService;
|
||||||
|
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.dspace.core.Context;
|
||||||
|
import org.dspace.qaevent.QualityAssuranceAction;
|
||||||
|
import org.dspace.qaevent.service.dto.OpenaireMessageDTO;
|
||||||
|
import org.dspace.qaevent.service.dto.QAMessageDTO;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link QualityAssuranceAction} that handle the relationship between the
|
||||||
|
* item to correct and a related item.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QAEntityOpenaireMetadataAction implements QualityAssuranceAction {
|
||||||
|
private String relation;
|
||||||
|
private String entityType;
|
||||||
|
private Map<String, String> entityMetadata;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private InstallItemService installItemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ItemService itemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EntityTypeService entityTypeService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RelationshipService relationshipService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RelationshipTypeService relationshipTypeService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WorkspaceItemService workspaceItemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CollectionService collectionService;
|
||||||
|
|
||||||
|
public void setItemService(ItemService itemService) {
|
||||||
|
this.itemService = itemService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRelation() {
|
||||||
|
return relation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRelation(String relation) {
|
||||||
|
this.relation = relation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] splitMetadata(String metadata) {
|
||||||
|
String[] result = new String[3];
|
||||||
|
String[] split = metadata.split("\\.");
|
||||||
|
result[0] = split[0];
|
||||||
|
result[1] = split[1];
|
||||||
|
if (split.length == 3) {
|
||||||
|
result[2] = split[2];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEntityType() {
|
||||||
|
return entityType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEntityType(String entityType) {
|
||||||
|
this.entityType = entityType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getEntityMetadata() {
|
||||||
|
return entityMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEntityMetadata(Map<String, String> entityMetadata) {
|
||||||
|
this.entityMetadata = entityMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyCorrection(Context context, Item item, Item relatedItem, QAMessageDTO message) {
|
||||||
|
try {
|
||||||
|
if (relatedItem != null) {
|
||||||
|
link(context, item, relatedItem);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
Collection collection = collectionService.retrieveCollectionWithSubmitByEntityType(context,
|
||||||
|
item, entityType);
|
||||||
|
if (collection == null) {
|
||||||
|
throw new IllegalStateException("No collection found by entity type: " + collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkspaceItem workspaceItem = workspaceItemService.create(context, collection, true);
|
||||||
|
relatedItem = workspaceItem.getItem();
|
||||||
|
|
||||||
|
for (String key : entityMetadata.keySet()) {
|
||||||
|
String value = getValue(message, key);
|
||||||
|
if (StringUtils.isNotBlank(value)) {
|
||||||
|
String[] targetMetadata = splitMetadata(entityMetadata.get(key));
|
||||||
|
itemService.addMetadata(context, relatedItem, targetMetadata[0], targetMetadata[1],
|
||||||
|
targetMetadata[2], null, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
installItemService.installItem(context, workspaceItem);
|
||||||
|
itemService.update(context, relatedItem);
|
||||||
|
link(context, item, relatedItem);
|
||||||
|
}
|
||||||
|
} catch (SQLException | AuthorizeException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new relationship between the two given item, based on the configured
|
||||||
|
* relation.
|
||||||
|
*/
|
||||||
|
private void link(Context context, Item item, Item relatedItem) throws SQLException, AuthorizeException {
|
||||||
|
EntityType project = entityTypeService.findByEntityType(context, entityType);
|
||||||
|
RelationshipType relType = relationshipTypeService.findByEntityType(context, project).stream()
|
||||||
|
.filter(r -> StringUtils.equals(r.getRightwardType(), relation)).findFirst()
|
||||||
|
.orElseThrow(() -> new IllegalStateException("No relationshipType named " + relation
|
||||||
|
+ " was found for the entity type " + entityType
|
||||||
|
+ ". A proper configuration is required to use the QAEntitiyMetadataAction."
|
||||||
|
+ " If you don't manage funding in your repository please skip this topic in"
|
||||||
|
+ " the qaevents.cfg"));
|
||||||
|
// Create the relationship
|
||||||
|
relationshipService.create(context, item, relatedItem, relType, -1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getValue(QAMessageDTO message, String key) {
|
||||||
|
if (!(message instanceof OpenaireMessageDTO)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenaireMessageDTO openaireMessage = (OpenaireMessageDTO) message;
|
||||||
|
|
||||||
|
if (StringUtils.equals(key, "acronym")) {
|
||||||
|
return openaireMessage.getAcronym();
|
||||||
|
} else if (StringUtils.equals(key, "code")) {
|
||||||
|
return openaireMessage.getCode();
|
||||||
|
} else if (StringUtils.equals(key, "funder")) {
|
||||||
|
return openaireMessage.getFunder();
|
||||||
|
} else if (StringUtils.equals(key, "fundingProgram")) {
|
||||||
|
return openaireMessage.getFundingProgram();
|
||||||
|
} else if (StringUtils.equals(key, "jurisdiction")) {
|
||||||
|
return openaireMessage.getJurisdiction();
|
||||||
|
} else if (StringUtils.equals(key, "openaireId")) {
|
||||||
|
return openaireMessage.getOpenaireId();
|
||||||
|
} else if (StringUtils.equals(key, "title")) {
|
||||||
|
return openaireMessage.getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.qaevent.action;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.service.ItemService;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.qaevent.QualityAssuranceAction;
|
||||||
|
import org.dspace.qaevent.service.dto.OpenaireMessageDTO;
|
||||||
|
import org.dspace.qaevent.service.dto.QAMessageDTO;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link QualityAssuranceAction} that add a specific metadata on the given
|
||||||
|
* item based on the OPENAIRE message type.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QAOpenaireMetadataMapAction implements QualityAssuranceAction {
|
||||||
|
public static final String DEFAULT = "default";
|
||||||
|
|
||||||
|
private Map<String, String> types;
|
||||||
|
@Autowired
|
||||||
|
private ItemService itemService;
|
||||||
|
|
||||||
|
public void setItemService(ItemService itemService) {
|
||||||
|
this.itemService = itemService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getTypes() {
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTypes(Map<String, String> types) {
|
||||||
|
this.types = types;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the correction on one metadata field of the given item based on the
|
||||||
|
* openaire message type.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void applyCorrection(Context context, Item item, Item relatedItem, QAMessageDTO message) {
|
||||||
|
|
||||||
|
if (!(message instanceof OpenaireMessageDTO)) {
|
||||||
|
throw new IllegalArgumentException("Unsupported message type: " + message.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenaireMessageDTO openaireMessage = (OpenaireMessageDTO) message;
|
||||||
|
|
||||||
|
try {
|
||||||
|
String targetMetadata = types.get(openaireMessage.getType());
|
||||||
|
if (targetMetadata == null) {
|
||||||
|
targetMetadata = types.get(DEFAULT);
|
||||||
|
}
|
||||||
|
String[] metadata = splitMetadata(targetMetadata);
|
||||||
|
itemService.addMetadata(context, item, metadata[0], metadata[1], metadata[2], null,
|
||||||
|
openaireMessage.getValue());
|
||||||
|
itemService.update(context, item);
|
||||||
|
} catch (SQLException | AuthorizeException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] splitMetadata(String metadata) {
|
||||||
|
String[] result = new String[3];
|
||||||
|
String[] split = metadata.split("\\.");
|
||||||
|
result[0] = split[0];
|
||||||
|
result[1] = split[1];
|
||||||
|
if (split.length == 3) {
|
||||||
|
result[2] = split[2];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@@ -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.qaevent.action;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.service.ItemService;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.qaevent.QualityAssuranceAction;
|
||||||
|
import org.dspace.qaevent.service.dto.OpenaireMessageDTO;
|
||||||
|
import org.dspace.qaevent.service.dto.QAMessageDTO;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link QualityAssuranceAction} that add a simple metadata to the given
|
||||||
|
* item.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QAOpenaireSimpleMetadataAction implements QualityAssuranceAction {
|
||||||
|
private String metadata;
|
||||||
|
private String metadataSchema;
|
||||||
|
private String metadataElement;
|
||||||
|
private String metadataQualifier;
|
||||||
|
@Autowired
|
||||||
|
private ItemService itemService;
|
||||||
|
|
||||||
|
public void setItemService(ItemService itemService) {
|
||||||
|
this.itemService = itemService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMetadata() {
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMetadata(String metadata) {
|
||||||
|
this.metadata = metadata;
|
||||||
|
String[] split = metadata.split("\\.");
|
||||||
|
this.metadataSchema = split[0];
|
||||||
|
this.metadataElement = split[1];
|
||||||
|
if (split.length == 3) {
|
||||||
|
this.metadataQualifier = split[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyCorrection(Context context, Item item, Item relatedItem, QAMessageDTO message) {
|
||||||
|
try {
|
||||||
|
itemService.addMetadata(context, item, metadataSchema, metadataElement, metadataQualifier, null,
|
||||||
|
((OpenaireMessageDTO) message).getAbstracts());
|
||||||
|
itemService.update(context, item);
|
||||||
|
} catch (SQLException | AuthorizeException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent.dao;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.QAEventProcessed;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.core.GenericDAO;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DAO that handle processed QA Events.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface QAEventsDAO extends GenericDAO<QAEventProcessed> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all the stored QAEventProcessed entities.
|
||||||
|
*
|
||||||
|
* @param context the DSpace context
|
||||||
|
* @return the found entities
|
||||||
|
* @throws SQLException if an SQL error occurs
|
||||||
|
*/
|
||||||
|
public List<QAEventProcessed> findAll(Context context) throws SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the stored QAEventProcessed entities by item.
|
||||||
|
*
|
||||||
|
* @param context the DSpace context
|
||||||
|
* @param item the item to search for
|
||||||
|
* @return the found entities
|
||||||
|
* @throws SQLException if an SQL error occurs
|
||||||
|
*/
|
||||||
|
public List<QAEventProcessed> findByItem(Context context, Item item) throws SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the stored QAEventProcessed entities by eperson.
|
||||||
|
*
|
||||||
|
* @param context the DSpace context
|
||||||
|
* @param ePerson the ePerson to search for
|
||||||
|
* @return the found entities
|
||||||
|
* @throws SQLException if an SQL error occurs
|
||||||
|
*/
|
||||||
|
public List<QAEventProcessed> findByEPerson(Context context, EPerson ePerson) throws SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search a page of quality assurance broker events by notification ID.
|
||||||
|
*
|
||||||
|
* @param context the DSpace context
|
||||||
|
* @param eventId the event id
|
||||||
|
* @param start the start index
|
||||||
|
* @param size the size to be applied
|
||||||
|
* @return the processed events
|
||||||
|
* @throws SQLException if an SQL error occurs
|
||||||
|
*/
|
||||||
|
public List<QAEventProcessed> searchByEventId(Context context, String eventId, Integer start, Integer size)
|
||||||
|
throws SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an event with the given checksum is already stored.
|
||||||
|
*
|
||||||
|
* @param context the DSpace context
|
||||||
|
* @param checksum the checksum to search for
|
||||||
|
* @return true if the given checksum is related to an already
|
||||||
|
* stored event, false otherwise
|
||||||
|
* @throws SQLException if an SQL error occurs
|
||||||
|
*/
|
||||||
|
public boolean isEventStored(Context context, String checksum) throws SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store an event related to the given checksum.
|
||||||
|
*
|
||||||
|
* @param context the DSpace context
|
||||||
|
* @param checksum the checksum of the event to be store
|
||||||
|
* @param eperson the eperson who handle the event
|
||||||
|
* @param item the item related to the event
|
||||||
|
* @return true if the creation is completed with success, false
|
||||||
|
* otherwise
|
||||||
|
*/
|
||||||
|
boolean storeEvent(Context context, String checksum, EPerson eperson, Item item);
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.qaevent.dao.impl;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.persistence.Query;
|
||||||
|
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.QAEventProcessed;
|
||||||
|
import org.dspace.core.AbstractHibernateDAO;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
|
import org.dspace.qaevent.dao.QAEventsDAO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link QAEventsDAO} that store processed events using an
|
||||||
|
* SQL DBMS.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QAEventsDAOImpl extends AbstractHibernateDAO<QAEventProcessed> implements QAEventsDAO {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<QAEventProcessed> findAll(Context context) throws SQLException {
|
||||||
|
return findAll(context, QAEventProcessed.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean storeEvent(Context context, String checksum, EPerson eperson, Item item) {
|
||||||
|
QAEventProcessed qaEvent = new QAEventProcessed();
|
||||||
|
qaEvent.setEperson(eperson);
|
||||||
|
qaEvent.setEventId(checksum);
|
||||||
|
qaEvent.setItem(item);
|
||||||
|
qaEvent.setEventTimestamp(new Date());
|
||||||
|
try {
|
||||||
|
create(context, qaEvent);
|
||||||
|
return true;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEventStored(Context context, String checksum) throws SQLException {
|
||||||
|
Query query = createQuery(context,
|
||||||
|
"SELECT count(eventId) FROM QAEventProcessed qaevent WHERE qaevent.eventId = :event_id ");
|
||||||
|
query.setParameter("event_id", checksum);
|
||||||
|
return count(query) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<QAEventProcessed> searchByEventId(Context context, String eventId, Integer start, Integer size)
|
||||||
|
throws SQLException {
|
||||||
|
Query query = createQuery(context,
|
||||||
|
"SELECT * FROM QAEventProcessed qaevent WHERE qaevent.qaevent_id = :event_id ");
|
||||||
|
query.setFirstResult(start);
|
||||||
|
query.setMaxResults(size);
|
||||||
|
query.setParameter("event_id", eventId);
|
||||||
|
return findMany(context, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<QAEventProcessed> findByItem(Context context, Item item) throws SQLException {
|
||||||
|
Query query = createQuery(context, ""
|
||||||
|
+ " SELECT qaevent "
|
||||||
|
+ " FROM QAEventProcessed qaevent "
|
||||||
|
+ " WHERE qaevent.item = :item ");
|
||||||
|
query.setParameter("item", item);
|
||||||
|
return findMany(context, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<QAEventProcessed> findByEPerson(Context context, EPerson ePerson) throws SQLException {
|
||||||
|
Query query = createQuery(context, ""
|
||||||
|
+ " SELECT qaevent "
|
||||||
|
+ " FROM QAEventProcessed qaevent "
|
||||||
|
+ " WHERE qaevent.eperson = :eperson ");
|
||||||
|
query.setParameter("eperson", ePerson);
|
||||||
|
return findMany(context, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,314 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent.script;
|
||||||
|
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.substringAfter;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||||
|
import eu.dnetlib.broker.BrokerClient;
|
||||||
|
import org.apache.commons.cli.ParseException;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
|
import org.dspace.eperson.factory.EPersonServiceFactory;
|
||||||
|
import org.dspace.qaevent.service.OpenaireClientFactory;
|
||||||
|
import org.dspace.qaevent.service.QAEventService;
|
||||||
|
import org.dspace.scripts.DSpaceRunnable;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||||
|
import org.dspace.utils.DSpace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link DSpaceRunnable} to perfom a QAEvents import from a
|
||||||
|
* json file. The JSON file contains an array of JSON Events, where each event
|
||||||
|
* has the following structure. The message attribute follows the structure
|
||||||
|
* documented at
|
||||||
|
* @see <a href="https://graph.openaire.eu/docs/category/entities" target="_blank"> see </a>
|
||||||
|
*
|
||||||
|
* <code> <br/>
|
||||||
|
* { <br/>
|
||||||
|
* "originalId": "oai:www.openstarts.units.it:10077/21838",<br/>
|
||||||
|
* "title": "Egypt, crossroad of translations and literary interweavings", <br/>
|
||||||
|
* "topic": "ENRICH/MORE/PROJECT", <br/>
|
||||||
|
* "trust": 1.0, <br/>
|
||||||
|
* "message": { <br/>
|
||||||
|
* "projects[0].acronym": "PAThs", <br/>
|
||||||
|
* "projects[0].code": "687567", <br/>
|
||||||
|
* "projects[0].funder": "EC",<br/>
|
||||||
|
* "projects[0].fundingProgram": "H2020", <br/>
|
||||||
|
* "projects[0].jurisdiction": "EU",<br/>
|
||||||
|
* "projects[0].openaireId": "40|corda__h2020::6e32f5eb912688f2424c68b851483ea4", <br/>
|
||||||
|
* "projects[0].title": "Tracking Papyrus and Parchment Paths" <br/>
|
||||||
|
* } <br/>
|
||||||
|
* }
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @author Alessandro Martelli (alessandro.martelli at 4science.it)
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4Science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class OpenaireEventsImport
|
||||||
|
extends DSpaceRunnable<OpenaireEventsImportScriptConfiguration<OpenaireEventsImport>> {
|
||||||
|
|
||||||
|
private QAEventService qaEventService;
|
||||||
|
|
||||||
|
private String[] topicsToImport;
|
||||||
|
|
||||||
|
private ConfigurationService configurationService;
|
||||||
|
|
||||||
|
private BrokerClient brokerClient;
|
||||||
|
|
||||||
|
private ObjectMapper jsonMapper;
|
||||||
|
|
||||||
|
private URL openaireBrokerURL;
|
||||||
|
|
||||||
|
private String fileLocation;
|
||||||
|
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings({ "rawtypes" })
|
||||||
|
public OpenaireEventsImportScriptConfiguration getScriptConfiguration() {
|
||||||
|
OpenaireEventsImportScriptConfiguration configuration = new DSpace().getServiceManager()
|
||||||
|
.getServiceByName("import-openaire-events", OpenaireEventsImportScriptConfiguration.class);
|
||||||
|
return configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup() throws ParseException {
|
||||||
|
|
||||||
|
jsonMapper = new JsonMapper();
|
||||||
|
jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
|
||||||
|
qaEventService = new DSpace().getSingletonService(QAEventService.class);
|
||||||
|
configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
|
||||||
|
brokerClient = OpenaireClientFactory.getInstance().getBrokerClient();
|
||||||
|
|
||||||
|
topicsToImport = configurationService.getArrayProperty("qaevents.openaire.import.topic");
|
||||||
|
openaireBrokerURL = getOpenaireBrokerUri();
|
||||||
|
|
||||||
|
fileLocation = commandLine.getOptionValue("f");
|
||||||
|
email = commandLine.getOptionValue("e");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void internalRun() throws Exception {
|
||||||
|
|
||||||
|
if (StringUtils.isAllBlank(fileLocation, email)) {
|
||||||
|
throw new IllegalArgumentException("One parameter between the location of the file and the email "
|
||||||
|
+ "must be entered to proceed with the import.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNoneBlank(fileLocation, email)) {
|
||||||
|
throw new IllegalArgumentException("Only one parameter between the location of the file and the email "
|
||||||
|
+ "must be entered to proceed with the import.");
|
||||||
|
}
|
||||||
|
|
||||||
|
context = new Context();
|
||||||
|
assignCurrentUserInContext();
|
||||||
|
|
||||||
|
try {
|
||||||
|
importOpenaireEvents();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
handler.logError("A not recoverable error occurs during OPENAIRE events import: " + getMessage(ex), ex);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the OPENAIRE events from the given JSON file or directly from the
|
||||||
|
* OPENAIRE broker and try to store them.
|
||||||
|
*/
|
||||||
|
private void importOpenaireEvents() throws Exception {
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(fileLocation)) {
|
||||||
|
handler.logInfo("Trying to read the QA events from the provided file");
|
||||||
|
importOpenaireEventsFromFile();
|
||||||
|
} else {
|
||||||
|
handler.logInfo("Trying to read the QA events from the OPENAIRE broker");
|
||||||
|
importOpenaireEventsFromBroker();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the OPENAIRE events from the given file location and try to store them.
|
||||||
|
*/
|
||||||
|
private void importOpenaireEventsFromFile() throws Exception {
|
||||||
|
|
||||||
|
InputStream eventsFileInputStream = getQAEventsFileInputStream();
|
||||||
|
List<QAEvent> qaEvents = readOpenaireQAEventsFromJson(eventsFileInputStream);
|
||||||
|
|
||||||
|
handler.logInfo("Found " + qaEvents.size() + " events in the given file");
|
||||||
|
|
||||||
|
storeOpenaireQAEvents(qaEvents);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import the OPENAIRE events from the Broker using the subscription related to
|
||||||
|
* the given email and try to store them.
|
||||||
|
*/
|
||||||
|
private void importOpenaireEventsFromBroker() {
|
||||||
|
|
||||||
|
List<String> subscriptionIds = listEmailSubscriptions();
|
||||||
|
|
||||||
|
handler.logInfo("Found " + subscriptionIds.size() + " subscriptions related to the given email");
|
||||||
|
|
||||||
|
for (String subscriptionId : subscriptionIds) {
|
||||||
|
|
||||||
|
List<QAEvent> events = readOpenaireQAEventsFromBroker(subscriptionId);
|
||||||
|
|
||||||
|
handler.logInfo("Found " + events.size() + " events from the subscription " + subscriptionId);
|
||||||
|
|
||||||
|
storeOpenaireQAEvents(events);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain an InputStream from the runnable instance.
|
||||||
|
*/
|
||||||
|
private InputStream getQAEventsFileInputStream() throws Exception {
|
||||||
|
return handler.getFileStream(context, fileLocation)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Error reading file, the file couldn't be "
|
||||||
|
+ "found for filename: " + fileLocation));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read all the QAEvent from the OPENAIRE Broker related to the subscription
|
||||||
|
* with the given id.
|
||||||
|
*/
|
||||||
|
private List<QAEvent> readOpenaireQAEventsFromBroker(String subscriptionId) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
InputStream eventsInputStream = getEventsBySubscriptions(subscriptionId);
|
||||||
|
return readOpenaireQAEventsFromJson(eventsInputStream);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
handler.logError("An error occurs downloading the events related to the subscription "
|
||||||
|
+ subscriptionId + ": " + getMessage(ex), ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return List.of();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read all the QAEvent present in the given input stream.
|
||||||
|
*
|
||||||
|
* @return the QA events to be imported
|
||||||
|
*/
|
||||||
|
private List<QAEvent> readOpenaireQAEventsFromJson(InputStream inputStream) throws Exception {
|
||||||
|
return jsonMapper.readValue(inputStream, new TypeReference<List<QAEvent>>() {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the given QAEvents.
|
||||||
|
*
|
||||||
|
* @param events the event to be stored
|
||||||
|
*/
|
||||||
|
private void storeOpenaireQAEvents(List<QAEvent> events) {
|
||||||
|
for (QAEvent event : events) {
|
||||||
|
try {
|
||||||
|
storeOpenaireQAEvent(event);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
handler.logWarning("An error occurs storing the event with id "
|
||||||
|
+ event.getEventId() + ": " + getMessage(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the given QAEvent, skipping it if it is not supported.
|
||||||
|
*
|
||||||
|
* @param event the event to be stored
|
||||||
|
*/
|
||||||
|
private void storeOpenaireQAEvent(QAEvent event) {
|
||||||
|
|
||||||
|
if (!StringUtils.equalsAny(event.getTopic(), topicsToImport)) {
|
||||||
|
handler.logWarning("Event for topic " + event.getTopic() + " is not allowed in the qaevents.cfg");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.setSource(QAEvent.OPENAIRE_SOURCE);
|
||||||
|
|
||||||
|
qaEventService.store(context, event);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download the events related to the given subscription from the OPENAIRE broker.
|
||||||
|
*
|
||||||
|
* @param subscriptionId the subscription id
|
||||||
|
* @return an input stream from which to read the events in json format
|
||||||
|
*/
|
||||||
|
private InputStream getEventsBySubscriptions(String subscriptionId) throws Exception {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
brokerClient.downloadEvents(openaireBrokerURL, subscriptionId, outputStream);
|
||||||
|
return new ByteArrayInputStream(outputStream.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes all the subscription related to the given email from the OPENAIRE
|
||||||
|
* broker.
|
||||||
|
*/
|
||||||
|
private List<String> listEmailSubscriptions() {
|
||||||
|
try {
|
||||||
|
return brokerClient.listSubscriptions(openaireBrokerURL, email);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new IllegalArgumentException("An error occurs retriving the subscriptions "
|
||||||
|
+ "from the OPENAIRE broker: " + getMessage(ex), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private URL getOpenaireBrokerUri() {
|
||||||
|
try {
|
||||||
|
return new URL(configurationService.getProperty("qaevents.openaire.broker-url", "http://api.openaire.eu/broker"));
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new IllegalStateException("The configured OPENAIRE broker URL is not valid.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the root exception message from the given exception.
|
||||||
|
*/
|
||||||
|
private String getMessage(Exception ex) {
|
||||||
|
String message = ExceptionUtils.getRootCauseMessage(ex);
|
||||||
|
// Remove the Exception name from the message
|
||||||
|
return isNotBlank(message) ? substringAfter(message, ":").trim() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assignCurrentUserInContext() throws SQLException {
|
||||||
|
UUID uuid = getEpersonIdentifier();
|
||||||
|
if (uuid != null) {
|
||||||
|
EPerson ePerson = EPersonServiceFactory.getInstance().getEPersonService().find(context, uuid);
|
||||||
|
context.setCurrentUser(ePerson);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent.script;
|
||||||
|
|
||||||
|
import org.apache.commons.cli.HelpFormatter;
|
||||||
|
import org.apache.commons.cli.ParseException;
|
||||||
|
import org.dspace.utils.DSpace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extensions of {@link OpenaireEventsImport} to run the script on console.
|
||||||
|
*
|
||||||
|
* @author Alessandro Martelli (alessandro.martelli at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class OpenaireEventsImportCli extends OpenaireEventsImport {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings({ "rawtypes" })
|
||||||
|
public OpenaireEventsImportCliScriptConfiguration getScriptConfiguration() {
|
||||||
|
return new DSpace().getServiceManager()
|
||||||
|
.getServiceByName("import-openaire-events", OpenaireEventsImportCliScriptConfiguration.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup() throws ParseException {
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
// in case of CLI we show the help prompt
|
||||||
|
if (commandLine.hasOption('h')) {
|
||||||
|
HelpFormatter formatter = new HelpFormatter();
|
||||||
|
formatter.printHelp("Import Notification event json file", getScriptConfiguration().getOptions());
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent.script;
|
||||||
|
|
||||||
|
import org.apache.commons.cli.Options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension of {@link OpenaireEventsImportScriptConfiguration} to run the script on
|
||||||
|
* console.
|
||||||
|
*
|
||||||
|
* @author Alessandro Martelli (alessandro.martelli at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class OpenaireEventsImportCliScriptConfiguration<T extends OpenaireEventsImport>
|
||||||
|
extends OpenaireEventsImportScriptConfiguration<T> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Options getOptions() {
|
||||||
|
Options options = super.getOptions();
|
||||||
|
options.addOption("h", "help", false, "help");
|
||||||
|
options.getOption("h").setType(boolean.class);
|
||||||
|
super.options = options;
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent.script;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.apache.commons.cli.Options;
|
||||||
|
import org.dspace.scripts.configuration.ScriptConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension of {@link ScriptConfiguration} to perfom a QAEvents import from
|
||||||
|
* file.
|
||||||
|
*
|
||||||
|
* @author Alessandro Martelli (alessandro.martelli at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class OpenaireEventsImportScriptConfiguration<T extends OpenaireEventsImport> extends ScriptConfiguration<T> {
|
||||||
|
|
||||||
|
/*
|
||||||
|
private AuthorizeService authorizeService;
|
||||||
|
*/
|
||||||
|
private Class<T> dspaceRunnableClass;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<T> getDspaceRunnableClass() {
|
||||||
|
return dspaceRunnableClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic setter for the dspaceRunnableClass
|
||||||
|
* @param dspaceRunnableClass The dspaceRunnableClass to be set on this OpenaireEventsImportScriptConfiguration
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setDspaceRunnableClass(Class<T> dspaceRunnableClass) {
|
||||||
|
this.dspaceRunnableClass = dspaceRunnableClass;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
@Override
|
||||||
|
public boolean isAllowedToExecute(Context context) {
|
||||||
|
try {
|
||||||
|
return authorizeService.isAdmin(context);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException("SQLException occurred when checking if the current user is an admin", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Options getOptions() {
|
||||||
|
if (options == null) {
|
||||||
|
Options options = new Options();
|
||||||
|
|
||||||
|
options.addOption("f", "file", true, "Import data from Openaire quality assurance broker JSON file."
|
||||||
|
+ " This parameter is mutually exclusive to the email parameter.");
|
||||||
|
options.getOption("f").setType(InputStream.class);
|
||||||
|
options.getOption("f").setRequired(false);
|
||||||
|
|
||||||
|
options.addOption("e", "email", true, "Email related to the subscriptions to import data from Openaire "
|
||||||
|
+ "broker. This parameter is mutually exclusive to the file parameter.");
|
||||||
|
options.getOption("e").setType(String.class);
|
||||||
|
options.getOption("e").setRequired(false);
|
||||||
|
|
||||||
|
super.options = options;
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent.service;
|
||||||
|
|
||||||
|
import eu.dnetlib.broker.BrokerClient;
|
||||||
|
import org.dspace.utils.DSpace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for the {@link BrokerClient}.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface OpenaireClientFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an instance of the {@link BrokerClient}.
|
||||||
|
*
|
||||||
|
* @return the client instance
|
||||||
|
*/
|
||||||
|
public BrokerClient getBrokerClient();
|
||||||
|
|
||||||
|
public static OpenaireClientFactory getInstance() {
|
||||||
|
return new DSpace().getServiceManager().getServiceByName("openaireClientFactory", OpenaireClientFactory.class);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.qaevent.service;
|
||||||
|
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that handle the actions that can be done related to an
|
||||||
|
* {@link QAEvent}.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface QAEventActionService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept the given event.
|
||||||
|
*
|
||||||
|
* @param context the DSpace context
|
||||||
|
* @param qaevent the event to be accepted
|
||||||
|
*/
|
||||||
|
public void accept(Context context, QAEvent qaevent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discard the given event.
|
||||||
|
*
|
||||||
|
* @param context the DSpace context
|
||||||
|
* @param qaevent the event to be discarded
|
||||||
|
*/
|
||||||
|
public void discard(Context context, QAEvent qaevent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reject the given event.
|
||||||
|
*
|
||||||
|
* @param context the DSpace context
|
||||||
|
* @param qaevent the event to be rejected
|
||||||
|
*/
|
||||||
|
public void reject(Context context, QAEvent qaevent);
|
||||||
|
}
|
@@ -0,0 +1,158 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.qaevent.QASource;
|
||||||
|
import org.dspace.qaevent.QATopic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that handles {@link QAEvent}.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface QAEventService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all the event's topics.
|
||||||
|
*
|
||||||
|
* @param offset the offset to apply
|
||||||
|
* @return the topics list
|
||||||
|
*/
|
||||||
|
public List<QATopic> findAllTopics(long offset, long count, String orderField, boolean ascending);
|
||||||
|
/**
|
||||||
|
* Find all the event's topics related to the given source.
|
||||||
|
*
|
||||||
|
* @param source the source to search for
|
||||||
|
* @param offset the offset to apply
|
||||||
|
* @param count the page size
|
||||||
|
* @return the topics list
|
||||||
|
*/
|
||||||
|
public List<QATopic> findAllTopicsBySource(String source, long offset, long count,
|
||||||
|
String orderField, boolean ascending);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count all the event's topics.
|
||||||
|
*
|
||||||
|
* @return the count result
|
||||||
|
*/
|
||||||
|
public long countTopics();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count all the event's topics related to the given source.
|
||||||
|
*
|
||||||
|
* @param source the source to search for
|
||||||
|
* @return the count result
|
||||||
|
*/
|
||||||
|
public long countTopicsBySource(String source);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all the events by topic.
|
||||||
|
*
|
||||||
|
* @param topic the topic to search for
|
||||||
|
* @param offset the offset to apply
|
||||||
|
* @param pageSize the page size
|
||||||
|
* @param orderField the field to order for
|
||||||
|
* @param ascending true if the order should be ascending, false otherwise
|
||||||
|
* @return the events
|
||||||
|
*/
|
||||||
|
public List<QAEvent> findEventsByTopicAndPage(String topic, long offset, int pageSize,
|
||||||
|
String orderField, boolean ascending);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all the events by topic.
|
||||||
|
*
|
||||||
|
* @param topic the topic to search for
|
||||||
|
* @return the events
|
||||||
|
*/
|
||||||
|
public List<QAEvent> findEventsByTopic(String topic);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all the events by topic.
|
||||||
|
*
|
||||||
|
* @param topic the topic to search for
|
||||||
|
* @return the events count
|
||||||
|
*/
|
||||||
|
public long countEventsByTopic(String topic);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find an event by the given id.
|
||||||
|
*
|
||||||
|
* @param id the id of the event to search for
|
||||||
|
* @return the event
|
||||||
|
*/
|
||||||
|
public QAEvent findEventByEventId(String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the given event.
|
||||||
|
*
|
||||||
|
* @param context the DSpace context
|
||||||
|
* @param event the event to store
|
||||||
|
*/
|
||||||
|
public void store(Context context, QAEvent event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an event by the given id.
|
||||||
|
*
|
||||||
|
* @param id the id of the event to delete
|
||||||
|
*/
|
||||||
|
public void deleteEventByEventId(String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete events by the given target id.
|
||||||
|
*
|
||||||
|
* @param targetId the id of the target id
|
||||||
|
*/
|
||||||
|
public void deleteEventsByTargetId(UUID targetId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a specific topid by the given id.
|
||||||
|
*
|
||||||
|
* @param topicId the topic id to search for
|
||||||
|
* @return the topic
|
||||||
|
*/
|
||||||
|
public QATopic findTopicByTopicId(String topicId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a specific source by the given name.
|
||||||
|
*
|
||||||
|
* @param source the source name
|
||||||
|
* @return the source
|
||||||
|
*/
|
||||||
|
public QASource findSource(String source);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all the event's sources.
|
||||||
|
*
|
||||||
|
* @param offset the offset to apply
|
||||||
|
* @param pageSize the page size
|
||||||
|
* @return the sources list
|
||||||
|
*/
|
||||||
|
public List<QASource> findAllSources(long offset, int pageSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count all the event's sources.
|
||||||
|
*
|
||||||
|
* @return the count result
|
||||||
|
*/
|
||||||
|
public long countSources();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given QA event supports a related item.
|
||||||
|
*
|
||||||
|
* @param qaevent the event to be verified
|
||||||
|
* @return true if the event supports a related item, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isRelatedItemSupported(QAEvent qaevent);
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,173 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent.service.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link QAMessageDTO} that model message coming from OPENAIRE.
|
||||||
|
* @see <a href="https://graph.openaire.eu/docs/category/entities" target="_blank"> see </a>
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class OpenaireMessageDTO implements QAMessageDTO {
|
||||||
|
|
||||||
|
@JsonProperty("pids[0].value")
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
@JsonProperty("pids[0].type")
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
@JsonProperty("instances[0].hostedby")
|
||||||
|
private String instanceHostedBy;
|
||||||
|
|
||||||
|
@JsonProperty("instances[0].instancetype")
|
||||||
|
private String instanceInstanceType;
|
||||||
|
|
||||||
|
@JsonProperty("instances[0].license")
|
||||||
|
private String instanceLicense;
|
||||||
|
|
||||||
|
@JsonProperty("instances[0].url")
|
||||||
|
private String instanceUrl;
|
||||||
|
|
||||||
|
@JsonProperty("abstracts[0]")
|
||||||
|
private String abstracts;
|
||||||
|
|
||||||
|
@JsonProperty("projects[0].acronym")
|
||||||
|
private String acronym;
|
||||||
|
|
||||||
|
@JsonProperty("projects[0].code")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@JsonProperty("projects[0].funder")
|
||||||
|
private String funder;
|
||||||
|
|
||||||
|
@JsonProperty("projects[0].fundingProgram")
|
||||||
|
private String fundingProgram;
|
||||||
|
|
||||||
|
@JsonProperty("projects[0].jurisdiction")
|
||||||
|
private String jurisdiction;
|
||||||
|
|
||||||
|
@JsonProperty("projects[0].openaireId")
|
||||||
|
private String openaireId;
|
||||||
|
|
||||||
|
@JsonProperty("projects[0].title")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInstanceHostedBy() {
|
||||||
|
return instanceHostedBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInstanceHostedBy(String instanceHostedBy) {
|
||||||
|
this.instanceHostedBy = instanceHostedBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInstanceInstanceType() {
|
||||||
|
return instanceInstanceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInstanceInstanceType(String instanceInstanceType) {
|
||||||
|
this.instanceInstanceType = instanceInstanceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInstanceLicense() {
|
||||||
|
return instanceLicense;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInstanceLicense(String instanceLicense) {
|
||||||
|
this.instanceLicense = instanceLicense;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInstanceUrl() {
|
||||||
|
return instanceUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInstanceUrl(String instanceUrl) {
|
||||||
|
this.instanceUrl = instanceUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAbstracts() {
|
||||||
|
return abstracts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAbstracts(String abstracts) {
|
||||||
|
this.abstracts = abstracts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAcronym() {
|
||||||
|
return acronym;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAcronym(String acronym) {
|
||||||
|
this.acronym = acronym;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(String code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFunder() {
|
||||||
|
return funder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFunder(String funder) {
|
||||||
|
this.funder = funder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFundingProgram() {
|
||||||
|
return fundingProgram;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFundingProgram(String fundingProgram) {
|
||||||
|
this.fundingProgram = fundingProgram;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJurisdiction() {
|
||||||
|
return jurisdiction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setJurisdiction(String jurisdiction) {
|
||||||
|
this.jurisdiction = jurisdiction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOpenaireId() {
|
||||||
|
return openaireId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOpenaireId(String openaireId) {
|
||||||
|
this.openaireId = openaireId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent.service.dto;
|
||||||
|
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for classes that contains the details related to a {@link QAEvent}.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface QAMessageDTO {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent.service.impl;
|
||||||
|
|
||||||
|
import eu.dnetlib.broker.BrokerClient;
|
||||||
|
import org.dspace.qaevent.service.OpenaireClientFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link OpenaireClientFactory} that returns the instance of
|
||||||
|
* {@link BrokerClient} managed by the Spring context.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class OpenaireClientFactoryImpl implements OpenaireClientFactory {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BrokerClient brokerClient;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BrokerClient getBrokerClient() {
|
||||||
|
return brokerClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBrokerClient(BrokerClient brokerClient) {
|
||||||
|
this.brokerClient = brokerClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,127 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent.service.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.dspace.content.service.ItemService;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.qaevent.QualityAssuranceAction;
|
||||||
|
import org.dspace.qaevent.service.QAEventActionService;
|
||||||
|
import org.dspace.qaevent.service.QAEventService;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link QAEventActionService}.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QAEventActionServiceImpl implements QAEventActionService {
|
||||||
|
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(QAEventActionServiceImpl.class);
|
||||||
|
|
||||||
|
private ObjectMapper jsonMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private QAEventService qaEventService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ItemService itemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ConfigurationService configurationService;
|
||||||
|
|
||||||
|
private Map<String, QualityAssuranceAction> topicsToActions;
|
||||||
|
|
||||||
|
public void setTopicsToActions(Map<String, QualityAssuranceAction> topicsToActions) {
|
||||||
|
this.topicsToActions = topicsToActions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, QualityAssuranceAction> getTopicsToActions() {
|
||||||
|
return topicsToActions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QAEventActionServiceImpl() {
|
||||||
|
jsonMapper = new JsonMapper();
|
||||||
|
jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(Context context, QAEvent qaevent) {
|
||||||
|
Item item = null;
|
||||||
|
Item related = null;
|
||||||
|
try {
|
||||||
|
item = itemService.find(context, UUID.fromString(qaevent.getTarget()));
|
||||||
|
if (qaevent.getRelated() != null) {
|
||||||
|
related = itemService.find(context, UUID.fromString(qaevent.getRelated()));
|
||||||
|
}
|
||||||
|
topicsToActions.get(qaevent.getTopic()).applyCorrection(context, item, related,
|
||||||
|
jsonMapper.readValue(qaevent.getMessage(), qaevent.getMessageDtoClass()));
|
||||||
|
qaEventService.deleteEventByEventId(qaevent.getEventId());
|
||||||
|
makeAcknowledgement(qaevent.getEventId(), qaevent.getSource(), QAEvent.ACCEPTED);
|
||||||
|
} catch (SQLException | JsonProcessingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void discard(Context context, QAEvent qaevent) {
|
||||||
|
qaEventService.deleteEventByEventId(qaevent.getEventId());
|
||||||
|
makeAcknowledgement(qaevent.getEventId(), qaevent.getSource(), QAEvent.DISCARDED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reject(Context context, QAEvent qaevent) {
|
||||||
|
qaEventService.deleteEventByEventId(qaevent.getEventId());
|
||||||
|
makeAcknowledgement(qaevent.getEventId(), qaevent.getSource(), QAEvent.REJECTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make acknowledgement to the configured urls for the event status.
|
||||||
|
*/
|
||||||
|
private void makeAcknowledgement(String eventId, String source, String status) {
|
||||||
|
String[] ackwnoledgeCallbacks = configurationService
|
||||||
|
.getArrayProperty("qaevents." + source + ".acknowledge-url");
|
||||||
|
if (ackwnoledgeCallbacks != null) {
|
||||||
|
for (String ackwnoledgeCallback : ackwnoledgeCallbacks) {
|
||||||
|
if (StringUtils.isNotBlank(ackwnoledgeCallback)) {
|
||||||
|
ObjectNode node = jsonMapper.createObjectNode();
|
||||||
|
node.put("eventId", eventId);
|
||||||
|
node.put("status", status);
|
||||||
|
StringEntity requestEntity = new StringEntity(node.toString(), ContentType.APPLICATION_JSON);
|
||||||
|
CloseableHttpClient httpclient = HttpClients.createDefault();
|
||||||
|
HttpPost postMethod = new HttpPost(ackwnoledgeCallback);
|
||||||
|
postMethod.setEntity(requestEntity);
|
||||||
|
try {
|
||||||
|
httpclient.execute(postMethod);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,467 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent.service.impl;
|
||||||
|
|
||||||
|
import static java.util.Comparator.comparing;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.endsWith;
|
||||||
|
import static org.dspace.content.QAEvent.OPENAIRE_SOURCE;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.solr.client.solrj.SolrClient;
|
||||||
|
import org.apache.solr.client.solrj.SolrQuery;
|
||||||
|
import org.apache.solr.client.solrj.SolrQuery.ORDER;
|
||||||
|
import org.apache.solr.client.solrj.SolrServerException;
|
||||||
|
import org.apache.solr.client.solrj.impl.HttpSolrClient;
|
||||||
|
import org.apache.solr.client.solrj.request.UpdateRequest;
|
||||||
|
import org.apache.solr.client.solrj.response.FacetField;
|
||||||
|
import org.apache.solr.client.solrj.response.FacetField.Count;
|
||||||
|
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||||
|
import org.apache.solr.common.SolrDocument;
|
||||||
|
import org.apache.solr.common.SolrDocumentList;
|
||||||
|
import org.apache.solr.common.SolrInputDocument;
|
||||||
|
import org.apache.solr.common.params.FacetParams;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.dspace.content.service.ItemService;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.handle.service.HandleService;
|
||||||
|
import org.dspace.qaevent.QASource;
|
||||||
|
import org.dspace.qaevent.QATopic;
|
||||||
|
import org.dspace.qaevent.dao.QAEventsDAO;
|
||||||
|
import org.dspace.qaevent.dao.impl.QAEventsDAOImpl;
|
||||||
|
import org.dspace.qaevent.service.QAEventService;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link QAEventService} that use Solr to store events. When
|
||||||
|
* the user performs an action on the event (such as accepting the suggestion or
|
||||||
|
* rejecting it) then the event is removed from solr and saved in the database
|
||||||
|
* (see {@link QAEventsDAO}) so that it is no longer proposed.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QAEventServiceImpl implements QAEventService {
|
||||||
|
|
||||||
|
@Autowired(required = true)
|
||||||
|
protected ConfigurationService configurationService;
|
||||||
|
|
||||||
|
@Autowired(required = true)
|
||||||
|
protected ItemService itemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private HandleService handleService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private QAEventsDAOImpl qaEventsDao;
|
||||||
|
|
||||||
|
private ObjectMapper jsonMapper;
|
||||||
|
|
||||||
|
public QAEventServiceImpl() {
|
||||||
|
jsonMapper = new JsonMapper();
|
||||||
|
jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Non-Static CommonsHttpSolrServer for processing indexing events.
|
||||||
|
*/
|
||||||
|
protected SolrClient solr = null;
|
||||||
|
|
||||||
|
public static final String SOURCE = "source";
|
||||||
|
public static final String ORIGINAL_ID = "original_id";
|
||||||
|
public static final String TITLE = "title";
|
||||||
|
public static final String TOPIC = "topic";
|
||||||
|
public static final String TRUST = "trust";
|
||||||
|
public static final String MESSAGE = "message";
|
||||||
|
public static final String EVENT_ID = "event_id";
|
||||||
|
public static final String RESOURCE_UUID = "resource_uuid";
|
||||||
|
public static final String LAST_UPDATE = "last_update";
|
||||||
|
public static final String RELATED_UUID = "related_uuid";
|
||||||
|
|
||||||
|
protected SolrClient getSolr() {
|
||||||
|
if (solr == null) {
|
||||||
|
String solrService = DSpaceServicesFactory.getInstance().getConfigurationService()
|
||||||
|
.getProperty("qaevents.solr.server", "http://localhost:8983/solr/qaevent");
|
||||||
|
return new HttpSolrClient.Builder(solrService).build();
|
||||||
|
}
|
||||||
|
return solr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long countTopics() {
|
||||||
|
SolrQuery solrQuery = new SolrQuery();
|
||||||
|
solrQuery.setRows(0);
|
||||||
|
solrQuery.setQuery("*:*");
|
||||||
|
solrQuery.setFacet(true);
|
||||||
|
solrQuery.setFacetMinCount(1);
|
||||||
|
solrQuery.addFacetField(TOPIC);
|
||||||
|
QueryResponse response;
|
||||||
|
try {
|
||||||
|
response = getSolr().query(solrQuery);
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return response.getFacetField(TOPIC).getValueCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long countTopicsBySource(String source) {
|
||||||
|
SolrQuery solrQuery = new SolrQuery();
|
||||||
|
solrQuery.setRows(0);
|
||||||
|
solrQuery.setQuery("*:*");
|
||||||
|
solrQuery.setFacet(true);
|
||||||
|
solrQuery.setFacetMinCount(1);
|
||||||
|
solrQuery.addFacetField(TOPIC);
|
||||||
|
solrQuery.addFilterQuery("source:" + source);
|
||||||
|
QueryResponse response;
|
||||||
|
try {
|
||||||
|
response = getSolr().query(solrQuery);
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return response.getFacetField(TOPIC).getValueCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteEventByEventId(String id) {
|
||||||
|
try {
|
||||||
|
getSolr().deleteById(id);
|
||||||
|
getSolr().commit();
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteEventsByTargetId(UUID targetId) {
|
||||||
|
try {
|
||||||
|
getSolr().deleteByQuery(RESOURCE_UUID + ":" + targetId.toString());
|
||||||
|
getSolr().commit();
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QATopic findTopicByTopicId(String topicId) {
|
||||||
|
SolrQuery solrQuery = new SolrQuery();
|
||||||
|
solrQuery.setRows(0);
|
||||||
|
solrQuery.setQuery(TOPIC + ":" + topicId.replaceAll("!", "/"));
|
||||||
|
solrQuery.setFacet(true);
|
||||||
|
solrQuery.setFacetMinCount(1);
|
||||||
|
solrQuery.addFacetField(TOPIC);
|
||||||
|
QueryResponse response;
|
||||||
|
try {
|
||||||
|
response = getSolr().query(solrQuery);
|
||||||
|
FacetField facetField = response.getFacetField(TOPIC);
|
||||||
|
for (Count c : facetField.getValues()) {
|
||||||
|
if (c.getName().equals(topicId.replace("!", "/"))) {
|
||||||
|
QATopic topic = new QATopic();
|
||||||
|
topic.setKey(c.getName());
|
||||||
|
topic.setTotalEvents(c.getCount());
|
||||||
|
topic.setLastEvent(new Date());
|
||||||
|
return topic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<QATopic> findAllTopics(long offset, long count, String orderField, boolean ascending) {
|
||||||
|
return findAllTopicsBySource(null, offset, count, orderField, ascending);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<QATopic> findAllTopicsBySource(String source, long offset, long count,
|
||||||
|
String orderField, boolean ascending) {
|
||||||
|
|
||||||
|
if (source != null && isNotSupportedSource(source)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SolrQuery solrQuery = new SolrQuery();
|
||||||
|
solrQuery.setRows(0);
|
||||||
|
solrQuery.setSort(orderField, ascending ? ORDER.asc : ORDER.desc);
|
||||||
|
solrQuery.setFacetSort(FacetParams.FACET_SORT_INDEX);
|
||||||
|
solrQuery.setQuery("*:*");
|
||||||
|
solrQuery.setFacet(true);
|
||||||
|
solrQuery.setFacetMinCount(1);
|
||||||
|
solrQuery.setFacetLimit((int) (offset + count));
|
||||||
|
solrQuery.addFacetField(TOPIC);
|
||||||
|
if (source != null) {
|
||||||
|
solrQuery.addFilterQuery(SOURCE + ":" + source);
|
||||||
|
}
|
||||||
|
QueryResponse response;
|
||||||
|
List<QATopic> topics = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
response = getSolr().query(solrQuery);
|
||||||
|
FacetField facetField = response.getFacetField(TOPIC);
|
||||||
|
topics = new ArrayList<>();
|
||||||
|
int idx = 0;
|
||||||
|
for (Count c : facetField.getValues()) {
|
||||||
|
if (idx < offset) {
|
||||||
|
idx++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QATopic topic = new QATopic();
|
||||||
|
topic.setKey(c.getName());
|
||||||
|
topic.setTotalEvents(c.getCount());
|
||||||
|
topic.setLastEvent(new Date());
|
||||||
|
topics.add(topic);
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return topics;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void store(Context context, QAEvent dto) {
|
||||||
|
|
||||||
|
if (isNotSupportedSource(dto.getSource())) {
|
||||||
|
throw new IllegalArgumentException("The source of the given event is not supported: " + dto.getSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(dto.getTopic())) {
|
||||||
|
throw new IllegalArgumentException("A topic is mandatory for an event");
|
||||||
|
}
|
||||||
|
|
||||||
|
String checksum = dto.getEventId();
|
||||||
|
try {
|
||||||
|
if (!qaEventsDao.isEventStored(context, checksum)) {
|
||||||
|
|
||||||
|
SolrInputDocument doc = createSolrDocument(context, dto, checksum);
|
||||||
|
|
||||||
|
UpdateRequest updateRequest = new UpdateRequest();
|
||||||
|
|
||||||
|
updateRequest.add(doc);
|
||||||
|
updateRequest.process(getSolr());
|
||||||
|
|
||||||
|
getSolr().commit();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QAEvent findEventByEventId(String eventId) {
|
||||||
|
SolrQuery param = new SolrQuery(EVENT_ID + ":" + eventId);
|
||||||
|
QueryResponse response;
|
||||||
|
try {
|
||||||
|
response = getSolr().query(param);
|
||||||
|
if (response != null) {
|
||||||
|
SolrDocumentList list = response.getResults();
|
||||||
|
if (list != null && list.size() == 1) {
|
||||||
|
SolrDocument doc = list.get(0);
|
||||||
|
return getQAEventFromSOLR(doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new RuntimeException("Exception querying Solr", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<QAEvent> findEventsByTopicAndPage(String topic, long offset,
|
||||||
|
int pageSize, String orderField, boolean ascending) {
|
||||||
|
|
||||||
|
SolrQuery solrQuery = new SolrQuery();
|
||||||
|
solrQuery.setStart(((Long) offset).intValue());
|
||||||
|
if (pageSize != -1) {
|
||||||
|
solrQuery.setRows(pageSize);
|
||||||
|
}
|
||||||
|
solrQuery.setSort(orderField, ascending ? ORDER.asc : ORDER.desc);
|
||||||
|
solrQuery.setQuery(TOPIC + ":" + topic.replaceAll("!", "/"));
|
||||||
|
|
||||||
|
QueryResponse response;
|
||||||
|
try {
|
||||||
|
response = getSolr().query(solrQuery);
|
||||||
|
if (response != null) {
|
||||||
|
SolrDocumentList list = response.getResults();
|
||||||
|
List<QAEvent> responseItem = new ArrayList<>();
|
||||||
|
for (SolrDocument doc : list) {
|
||||||
|
QAEvent item = getQAEventFromSOLR(doc);
|
||||||
|
responseItem.add(item);
|
||||||
|
}
|
||||||
|
return responseItem;
|
||||||
|
}
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<QAEvent> findEventsByTopic(String topic) {
|
||||||
|
return findEventsByTopicAndPage(topic, 0, -1, TRUST, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long countEventsByTopic(String topic) {
|
||||||
|
SolrQuery solrQuery = new SolrQuery();
|
||||||
|
solrQuery.setRows(0);
|
||||||
|
solrQuery.setQuery(TOPIC + ":" + topic.replace("!", "/"));
|
||||||
|
QueryResponse response = null;
|
||||||
|
try {
|
||||||
|
response = getSolr().query(solrQuery);
|
||||||
|
return response.getResults().getNumFound();
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QASource findSource(String sourceName) {
|
||||||
|
|
||||||
|
if (isNotSupportedSource(sourceName)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SolrQuery solrQuery = new SolrQuery("*:*");
|
||||||
|
solrQuery.setRows(0);
|
||||||
|
solrQuery.addFilterQuery(SOURCE + ":" + sourceName);
|
||||||
|
solrQuery.setFacet(true);
|
||||||
|
solrQuery.setFacetMinCount(1);
|
||||||
|
solrQuery.addFacetField(SOURCE);
|
||||||
|
|
||||||
|
QueryResponse response;
|
||||||
|
try {
|
||||||
|
response = getSolr().query(solrQuery);
|
||||||
|
FacetField facetField = response.getFacetField(SOURCE);
|
||||||
|
for (Count c : facetField.getValues()) {
|
||||||
|
if (c.getName().equalsIgnoreCase(sourceName)) {
|
||||||
|
QASource source = new QASource();
|
||||||
|
source.setName(c.getName());
|
||||||
|
source.setTotalEvents(c.getCount());
|
||||||
|
source.setLastEvent(new Date());
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
QASource source = new QASource();
|
||||||
|
source.setName(sourceName);
|
||||||
|
source.setTotalEvents(0L);
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<QASource> findAllSources(long offset, int pageSize) {
|
||||||
|
return Arrays.stream(getSupportedSources())
|
||||||
|
.map((sourceName) -> findSource(sourceName))
|
||||||
|
.sorted(comparing(QASource::getTotalEvents).reversed())
|
||||||
|
.skip(offset)
|
||||||
|
.limit(pageSize)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long countSources() {
|
||||||
|
return getSupportedSources().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRelatedItemSupported(QAEvent qaevent) {
|
||||||
|
// Currently only PROJECT topics related to OPENAIRE supports related items
|
||||||
|
return qaevent.getSource().equals(OPENAIRE_SOURCE) && endsWith(qaevent.getTopic(), "/PROJECT");
|
||||||
|
}
|
||||||
|
|
||||||
|
private SolrInputDocument createSolrDocument(Context context, QAEvent dto, String checksum) throws Exception {
|
||||||
|
SolrInputDocument doc = new SolrInputDocument();
|
||||||
|
doc.addField(SOURCE, dto.getSource());
|
||||||
|
doc.addField(EVENT_ID, checksum);
|
||||||
|
doc.addField(ORIGINAL_ID, dto.getOriginalId());
|
||||||
|
doc.addField(TITLE, dto.getTitle());
|
||||||
|
doc.addField(TOPIC, dto.getTopic());
|
||||||
|
doc.addField(TRUST, dto.getTrust());
|
||||||
|
doc.addField(MESSAGE, dto.getMessage());
|
||||||
|
doc.addField(LAST_UPDATE, new Date());
|
||||||
|
final String resourceUUID = getResourceUUID(context, dto.getOriginalId());
|
||||||
|
if (resourceUUID == null) {
|
||||||
|
throw new IllegalArgumentException("Skipped event " + checksum +
|
||||||
|
" related to the oai record " + dto.getOriginalId() + " as the record was not found");
|
||||||
|
}
|
||||||
|
doc.addField(RESOURCE_UUID, resourceUUID);
|
||||||
|
doc.addField(RELATED_UUID, dto.getRelated());
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getResourceUUID(Context context, String originalId) throws Exception {
|
||||||
|
String id = getHandleFromOriginalId(originalId);
|
||||||
|
if (id != null) {
|
||||||
|
Item item = (Item) handleService.resolveToObject(context, id);
|
||||||
|
if (item != null) {
|
||||||
|
final String itemUuid = item.getID().toString();
|
||||||
|
context.uncacheEntity(item);
|
||||||
|
return itemUuid;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Malformed originalId " + originalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// oai:www.openstarts.units.it:10077/21486
|
||||||
|
private String getHandleFromOriginalId(String originalId) {
|
||||||
|
int startPosition = originalId.lastIndexOf(':');
|
||||||
|
if (startPosition != -1) {
|
||||||
|
return originalId.substring(startPosition + 1, originalId.length());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private QAEvent getQAEventFromSOLR(SolrDocument doc) {
|
||||||
|
QAEvent item = new QAEvent();
|
||||||
|
item.setSource((String) doc.get(SOURCE));
|
||||||
|
item.setEventId((String) doc.get(EVENT_ID));
|
||||||
|
item.setLastUpdate((Date) doc.get(LAST_UPDATE));
|
||||||
|
item.setMessage((String) doc.get(MESSAGE));
|
||||||
|
item.setOriginalId((String) doc.get(ORIGINAL_ID));
|
||||||
|
item.setTarget((String) doc.get(RESOURCE_UUID));
|
||||||
|
item.setTitle((String) doc.get(TITLE));
|
||||||
|
item.setTopic((String) doc.get(TOPIC));
|
||||||
|
item.setTrust((double) doc.get(TRUST));
|
||||||
|
item.setRelated((String) doc.get(RELATED_UUID));
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isNotSupportedSource(String source) {
|
||||||
|
return !ArrayUtils.contains(getSupportedSources(), source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] getSupportedSources() {
|
||||||
|
return configurationService.getArrayProperty("qaevent.sources", new String[] { QAEvent.OPENAIRE_SOURCE });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension of {@link JsonDeserializer} that convert a json to a String.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class RawJsonDeserializer extends JsonDeserializer<String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String deserialize(JsonParser jp, DeserializationContext ctxt)
|
||||||
|
throws IOException, JsonProcessingException {
|
||||||
|
|
||||||
|
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
|
||||||
|
JsonNode node = mapper.readTree(jp);
|
||||||
|
return mapper.writeValueAsString(node);
|
||||||
|
}
|
||||||
|
}
|
@@ -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/
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE qaevent_processed (
|
||||||
|
qaevent_id VARCHAR(255) NOT NULL,
|
||||||
|
qaevent_timestamp TIMESTAMP NULL,
|
||||||
|
eperson_uuid UUID NULL REFERENCES eperson(uuid),
|
||||||
|
item_uuid uuid NOT NULL REFERENCES item(uuid)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX item_uuid_idx ON qaevent_processed(item_uuid);
|
@@ -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/
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE qaevent_processed (
|
||||||
|
qaevent_id VARCHAR(255) NOT NULL,
|
||||||
|
qaevent_timestamp TIMESTAMP NULL,
|
||||||
|
eperson_uuid UUID NULL,
|
||||||
|
item_uuid UUID NULL,
|
||||||
|
CONSTRAINT qaevent_pk PRIMARY KEY (qaevent_id),
|
||||||
|
CONSTRAINT eperson_uuid_fkey FOREIGN KEY (eperson_uuid) REFERENCES eperson (uuid),
|
||||||
|
CONSTRAINT item_uuid_fkey FOREIGN KEY (item_uuid) REFERENCES item (uuid)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX item_uuid_idx ON qaevent_processed(item_uuid);
|
@@ -95,14 +95,14 @@ loglevel.dspace = INFO
|
|||||||
# IIIF TEST SETTINGS #
|
# IIIF TEST SETTINGS #
|
||||||
########################
|
########################
|
||||||
iiif.enabled = true
|
iiif.enabled = true
|
||||||
event.dispatcher.default.consumers = versioning, discovery, eperson, orcidqueue, iiif
|
event.dispatcher.default.consumers = versioning, discovery, eperson, orcidqueue, iiif, qaeventsdelete
|
||||||
|
|
||||||
###########################################
|
###########################################
|
||||||
# CUSTOM UNIT / INTEGRATION TEST SETTINGS #
|
# CUSTOM UNIT / INTEGRATION TEST SETTINGS #
|
||||||
###########################################
|
###########################################
|
||||||
# custom dispatcher to be used by dspace-api IT that doesn't need SOLR
|
# custom dispatcher to be used by dspace-api IT that doesn't need SOLR
|
||||||
event.dispatcher.exclude-discovery.class = org.dspace.event.BasicDispatcher
|
event.dispatcher.exclude-discovery.class = org.dspace.event.BasicDispatcher
|
||||||
event.dispatcher.exclude-discovery.consumers = versioning, eperson
|
event.dispatcher.exclude-discovery.consumers = versioning, eperson, qaeventsdelete
|
||||||
|
|
||||||
# Configure authority control for Unit Testing (in DSpaceControlledVocabularyTest)
|
# Configure authority control for Unit Testing (in DSpaceControlledVocabularyTest)
|
||||||
# (This overrides default, commented out settings in dspace.cfg)
|
# (This overrides default, commented out settings in dspace.cfg)
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
http://www.springframework.org/schema/util/spring-util.xsd"
|
http://www.springframework.org/schema/util/spring-util.xsd"
|
||||||
default-lazy-init="true">
|
default-lazy-init="true">
|
||||||
|
|
||||||
<bean id="mockOpenAIRERestConnector" class="org.dspace.external.MockOpenAIRERestConnector">
|
<bean id="mockOpenaireRestConnector" class="org.dspace.external.MockOpenaireRestConnector">
|
||||||
<constructor-arg value="${openaire.api.url:https://api.openaire.eu}"/>
|
<constructor-arg value="${openaire.api.url:https://api.openaire.eu}"/>
|
||||||
<property name="tokenEnabled" value="${openaire.token.enabled:false}"/>
|
<property name="tokenEnabled" value="${openaire.token.enabled:false}"/>
|
||||||
<property name="tokenServiceUrl" value="${openaire.token.url:https://aai.openaire.eu/oidc/token}"/>
|
<property name="tokenServiceUrl" value="${openaire.token.url:https://aai.openaire.eu/oidc/token}"/>
|
||||||
@@ -15,10 +15,10 @@
|
|||||||
<property name="clientSecret" value="${openaire.token.clientSecret}"/>
|
<property name="clientSecret" value="${openaire.token.clientSecret}"/>
|
||||||
</bean>
|
</bean>
|
||||||
<bean
|
<bean
|
||||||
class="org.dspace.external.provider.impl.OpenAIREFundingDataProvider"
|
class="org.dspace.external.provider.impl.OpenaireFundingDataProvider"
|
||||||
init-method="init">
|
init-method="init">
|
||||||
<property name="sourceIdentifier" value="openAIREFunding" />
|
<property name="sourceIdentifier" value="openaireFunding" />
|
||||||
<property name="connector" ref="mockOpenAIRERestConnector" />
|
<property name="connector" ref="mockOpenaireRestConnector" />
|
||||||
<property name="metadataFields" ref="mapOfmetadata"/>
|
<property name="metadataFields" ref="mapOfmetadata"/>
|
||||||
<property name="supportedEntityTypes">
|
<property name="supportedEntityTypes">
|
||||||
<list>
|
<list>
|
||||||
|
@@ -286,7 +286,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<!-- An example of an OpenAIRE compliance filter based on the same rules in xoai.xml
|
<!-- An example of an Openaire compliance filter based on the same rules in xoai.xml
|
||||||
some sub-statements are defined within this bean, and some are referenced from earlier definitions
|
some sub-statements are defined within this bean, and some are referenced from earlier definitions
|
||||||
-->
|
-->
|
||||||
<bean id="openaire_filter" class="org.dspace.content.logic.DefaultFilter">
|
<bean id="openaire_filter" class="org.dspace.content.logic.DefaultFilter">
|
||||||
@@ -329,7 +329,7 @@
|
|||||||
</list>
|
</list>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
<!-- AND the dc.relation is a valid OpenAIRE identifier
|
<!-- AND the dc.relation is a valid Openaire identifier
|
||||||
(starts with "info:eu-repo/grantAgreement/") -->
|
(starts with "info:eu-repo/grantAgreement/") -->
|
||||||
<bean id="has-openaire-relation_condition"
|
<bean id="has-openaire-relation_condition"
|
||||||
class="org.dspace.content.logic.condition.MetadataValueMatchCondition">
|
class="org.dspace.content.logic.condition.MetadataValueMatchCondition">
|
||||||
|
@@ -64,6 +64,11 @@
|
|||||||
<property name="description" value="Perform the bulk synchronization of all the BATCH configured ORCID entities placed in the ORCID queue"/>
|
<property name="description" value="Perform the bulk synchronization of all the BATCH configured ORCID entities placed in the ORCID queue"/>
|
||||||
<property name="dspaceRunnableClass" value="org.dspace.orcid.script.OrcidBulkPush"/>
|
<property name="dspaceRunnableClass" value="org.dspace.orcid.script.OrcidBulkPush"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="import-openaire-events" class="org.dspace.qaevent.script.OpenaireEventsImportCliScriptConfiguration" primary="true">
|
||||||
|
<property name="description" value="Import new openaire quality assurance broker events"/>
|
||||||
|
<property name="dspaceRunnableClass" value="org.dspace.qaevent.script.OpenaireEventsImportCli"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<bean id="process-cleaner" class="org.dspace.administer.ProcessCleanerCliConfiguration">
|
<bean id="process-cleaner" class="org.dspace.administer.ProcessCleanerCliConfiguration">
|
||||||
<property name="description" value="Cleanup all the old processes in the specified state"/>
|
<property name="description" value="Cleanup all the old processes in the specified state"/>
|
||||||
|
@@ -48,6 +48,11 @@
|
|||||||
class="org.dspace.statistics.MockSolrStatisticsCore"
|
class="org.dspace.statistics.MockSolrStatisticsCore"
|
||||||
autowire-candidate="true"/>
|
autowire-candidate="true"/>
|
||||||
|
|
||||||
|
<!-- qa events -->
|
||||||
|
<bean class="org.dspace.qaevent.MockQAEventService"
|
||||||
|
id="org.dspace.qaevent.service.QAEventService" />
|
||||||
|
|
||||||
<bean class="org.dspace.statistics.GeoIpService" autowire-candidate="true"/>
|
<bean class="org.dspace.statistics.GeoIpService" autowire-candidate="true"/>
|
||||||
|
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
@@ -29,6 +29,8 @@ import org.dspace.eperson.factory.EPersonServiceFactory;
|
|||||||
import org.dspace.eperson.service.EPersonService;
|
import org.dspace.eperson.service.EPersonService;
|
||||||
import org.dspace.eperson.service.GroupService;
|
import org.dspace.eperson.service.GroupService;
|
||||||
import org.dspace.kernel.ServiceManager;
|
import org.dspace.kernel.ServiceManager;
|
||||||
|
import org.dspace.qaevent.MockQAEventService;
|
||||||
|
import org.dspace.qaevent.service.QAEventService;
|
||||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||||
import org.dspace.statistics.MockSolrLoggerServiceImpl;
|
import org.dspace.statistics.MockSolrLoggerServiceImpl;
|
||||||
import org.dspace.statistics.MockSolrStatisticsCore;
|
import org.dspace.statistics.MockSolrStatisticsCore;
|
||||||
@@ -196,6 +198,10 @@ public class AbstractIntegrationTestWithDatabase extends AbstractDSpaceIntegrati
|
|||||||
.getServiceByName(AuthoritySearchService.class.getName(), MockAuthoritySolrServiceImpl.class);
|
.getServiceByName(AuthoritySearchService.class.getName(), MockAuthoritySolrServiceImpl.class);
|
||||||
authorityService.reset();
|
authorityService.reset();
|
||||||
|
|
||||||
|
MockQAEventService qaEventService = serviceManager
|
||||||
|
.getServiceByName(QAEventService.class.getName(), MockQAEventService.class);
|
||||||
|
qaEventService.reset();
|
||||||
|
|
||||||
// Reload our ConfigurationService (to reset configs to defaults again)
|
// Reload our ConfigurationService (to reset configs to defaults again)
|
||||||
DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig();
|
DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig();
|
||||||
|
|
||||||
|
@@ -61,6 +61,12 @@ public class TestDSpaceRunnableHandler extends CommandLineDSpaceRunnableHandler
|
|||||||
errorMessages.add(message);
|
errorMessages.add(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logError(String message, Throwable throwable) {
|
||||||
|
super.logError(message, throwable);
|
||||||
|
errorMessages.add(message);
|
||||||
|
}
|
||||||
|
|
||||||
public List<String> getInfoMessages() {
|
public List<String> getInfoMessages() {
|
||||||
return infoMessages;
|
return infoMessages;
|
||||||
}
|
}
|
||||||
|
@@ -49,6 +49,7 @@ import org.dspace.orcid.factory.OrcidServiceFactory;
|
|||||||
import org.dspace.orcid.service.OrcidHistoryService;
|
import org.dspace.orcid.service.OrcidHistoryService;
|
||||||
import org.dspace.orcid.service.OrcidQueueService;
|
import org.dspace.orcid.service.OrcidQueueService;
|
||||||
import org.dspace.orcid.service.OrcidTokenService;
|
import org.dspace.orcid.service.OrcidTokenService;
|
||||||
|
import org.dspace.qaevent.service.QAEventService;
|
||||||
import org.dspace.scripts.factory.ScriptServiceFactory;
|
import org.dspace.scripts.factory.ScriptServiceFactory;
|
||||||
import org.dspace.scripts.service.ProcessService;
|
import org.dspace.scripts.service.ProcessService;
|
||||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||||
@@ -56,6 +57,7 @@ import org.dspace.submit.factory.SubmissionServiceFactory;
|
|||||||
import org.dspace.submit.service.SubmissionConfigService;
|
import org.dspace.submit.service.SubmissionConfigService;
|
||||||
import org.dspace.supervision.factory.SupervisionOrderServiceFactory;
|
import org.dspace.supervision.factory.SupervisionOrderServiceFactory;
|
||||||
import org.dspace.supervision.service.SupervisionOrderService;
|
import org.dspace.supervision.service.SupervisionOrderService;
|
||||||
|
import org.dspace.utils.DSpace;
|
||||||
import org.dspace.versioning.factory.VersionServiceFactory;
|
import org.dspace.versioning.factory.VersionServiceFactory;
|
||||||
import org.dspace.versioning.service.VersionHistoryService;
|
import org.dspace.versioning.service.VersionHistoryService;
|
||||||
import org.dspace.versioning.service.VersioningService;
|
import org.dspace.versioning.service.VersioningService;
|
||||||
@@ -113,7 +115,7 @@ public abstract class AbstractBuilder<T, S> {
|
|||||||
static SubmissionConfigService submissionConfigService;
|
static SubmissionConfigService submissionConfigService;
|
||||||
static SubscribeService subscribeService;
|
static SubscribeService subscribeService;
|
||||||
static SupervisionOrderService supervisionOrderService;
|
static SupervisionOrderService supervisionOrderService;
|
||||||
|
static QAEventService qaEventService;
|
||||||
|
|
||||||
protected Context context;
|
protected Context context;
|
||||||
|
|
||||||
@@ -182,6 +184,7 @@ public abstract class AbstractBuilder<T, S> {
|
|||||||
}
|
}
|
||||||
subscribeService = ContentServiceFactory.getInstance().getSubscribeService();
|
subscribeService = ContentServiceFactory.getInstance().getSubscribeService();
|
||||||
supervisionOrderService = SupervisionOrderServiceFactory.getInstance().getSupervisionOrderService();
|
supervisionOrderService = SupervisionOrderServiceFactory.getInstance().getSupervisionOrderService();
|
||||||
|
qaEventService = new DSpace().getSingletonService(QAEventService.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -219,6 +222,7 @@ public abstract class AbstractBuilder<T, S> {
|
|||||||
submissionConfigService = null;
|
submissionConfigService = null;
|
||||||
subscribeService = null;
|
subscribeService = null;
|
||||||
supervisionOrderService = null;
|
supervisionOrderService = null;
|
||||||
|
qaEventService = null;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
141
dspace-api/src/test/java/org/dspace/builder/QAEventBuilder.java
Normal file
141
dspace-api/src/test/java/org/dspace/builder/QAEventBuilder.java
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
/**
|
||||||
|
* 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.builder;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.dspace.content.Collection;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.qaevent.service.QAEventService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder to construct Quality Assurance Broker Event objects
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*/
|
||||||
|
public class QAEventBuilder extends AbstractBuilder<QAEvent, QAEventService> {
|
||||||
|
|
||||||
|
private Item item;
|
||||||
|
private QAEvent target;
|
||||||
|
private String source = QAEvent.OPENAIRE_SOURCE;
|
||||||
|
/**
|
||||||
|
* the title of the DSpace object
|
||||||
|
* */
|
||||||
|
private String title;
|
||||||
|
/**
|
||||||
|
* the name of the Quality Assurance Event Topic
|
||||||
|
* */
|
||||||
|
private String topic;
|
||||||
|
/**
|
||||||
|
* thr original QA Event imported
|
||||||
|
* */
|
||||||
|
private String message;
|
||||||
|
/**
|
||||||
|
* uuid of the targeted DSpace object
|
||||||
|
* */
|
||||||
|
private String relatedItem;
|
||||||
|
private double trust = 0.5;
|
||||||
|
private Date lastUpdate = new Date();
|
||||||
|
|
||||||
|
protected QAEventBuilder(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static QAEventBuilder createTarget(final Context context, final Collection col, final String name) {
|
||||||
|
QAEventBuilder builder = new QAEventBuilder(context);
|
||||||
|
return builder.create(context, col, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static QAEventBuilder createTarget(final Context context, final Item item) {
|
||||||
|
QAEventBuilder builder = new QAEventBuilder(context);
|
||||||
|
return builder.create(context, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private QAEventBuilder create(final Context context, final Collection col, final String name) {
|
||||||
|
this.context = context;
|
||||||
|
|
||||||
|
try {
|
||||||
|
ItemBuilder itemBuilder = ItemBuilder.createItem(context, col).withTitle(name);
|
||||||
|
item = itemBuilder.build();
|
||||||
|
this.title = name;
|
||||||
|
context.dispatchEvents();
|
||||||
|
indexingService.commit();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return handleException(e);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private QAEventBuilder create(final Context context, final Item item) {
|
||||||
|
this.context = context;
|
||||||
|
this.item = item;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QAEventBuilder withTopic(final String topic) {
|
||||||
|
this.topic = topic;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public QAEventBuilder withSource(final String source) {
|
||||||
|
this.source = source;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public QAEventBuilder withTitle(final String title) {
|
||||||
|
this.title = title;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public QAEventBuilder withMessage(final String message) {
|
||||||
|
this.message = message;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public QAEventBuilder withTrust(final double trust) {
|
||||||
|
this.trust = trust;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public QAEventBuilder withLastUpdate(final Date lastUpdate) {
|
||||||
|
this.lastUpdate = lastUpdate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QAEventBuilder withRelatedItem(String relatedItem) {
|
||||||
|
this.relatedItem = relatedItem;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QAEvent build() {
|
||||||
|
target = new QAEvent(source, "oai:www.dspace.org:" + item.getHandle(), item.getID().toString(), title, topic,
|
||||||
|
trust, message, lastUpdate);
|
||||||
|
target.setRelated(relatedItem);
|
||||||
|
try {
|
||||||
|
qaEventService.store(context, target);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanup() throws Exception {
|
||||||
|
qaEventService.deleteEventByEventId(target.getEventId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected QAEventService getService() {
|
||||||
|
return qaEventService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(Context c, QAEvent dso) throws Exception {
|
||||||
|
qaEventService.deleteEventByEventId(target.getEventId());
|
||||||
|
|
||||||
|
// qaEventService.deleteTarget(dso);
|
||||||
|
}
|
||||||
|
}
|
@@ -1200,4 +1200,71 @@ public class CollectionTest extends AbstractDSpaceObjectTest {
|
|||||||
equalTo(owningCommunity));
|
equalTo(owningCommunity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test of retrieveCollectionWithSubmitByEntityType method getting the closest
|
||||||
|
* collection of non-item type starting from an item
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testRetrieveCollectionWithSubmitByEntityType() throws SQLException, AuthorizeException {
|
||||||
|
context.setDispatcher("default");
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
Community com = communityService.create(null, context);
|
||||||
|
Group submitters = groupService.create(context);
|
||||||
|
Collection collection = collectionService.create(context, com);
|
||||||
|
collectionService.addMetadata(context, collection, "dspace", "entity", "type",
|
||||||
|
null, "Publication");
|
||||||
|
com.addCollection(collection);
|
||||||
|
WorkspaceItem workspaceItem = workspaceItemService.create(context, collection, false);
|
||||||
|
Item item = installItemService.installItem(context, workspaceItem);
|
||||||
|
EPerson epersonA = ePersonService.create(context);
|
||||||
|
Collection collectionPerson = collectionService.create(context, com);
|
||||||
|
collectionService.addMetadata(context, collectionPerson, "dspace", "entity", "type",
|
||||||
|
null, "Person");
|
||||||
|
collectionPerson.setSubmitters(submitters);
|
||||||
|
groupService.addMember(context, submitters, epersonA);
|
||||||
|
context.setCurrentUser(epersonA);
|
||||||
|
context.commit();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
Collection resultCollection = collectionService.retrieveCollectionWithSubmitByEntityType
|
||||||
|
(context, item, "Person");
|
||||||
|
|
||||||
|
assertThat("testRetrieveCollectionWithSubmitByEntityType 0", resultCollection, notNullValue());
|
||||||
|
assertThat("testRetrieveCollectionWithSubmitByEntityType 1", resultCollection, equalTo(collectionPerson));
|
||||||
|
|
||||||
|
context.setDispatcher("exclude-discovery");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test of rretrieveCollectionWithSubmitByCommunityAndEntityType method getting the closest
|
||||||
|
* collection of non-community type starting from an community
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testRetrieveCollectionWithSubmitByCommunityAndEntityType() throws SQLException, AuthorizeException {
|
||||||
|
context.setDispatcher("default");
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
Community com = communityService.create(null, context);
|
||||||
|
Group submitters = groupService.create(context);
|
||||||
|
Collection collection = collectionService.create(context, com);
|
||||||
|
collectionService.addMetadata(context, collection, "dspace", "entity", "type",
|
||||||
|
null, "Publication");
|
||||||
|
com.addCollection(collection);
|
||||||
|
WorkspaceItem workspaceItem = workspaceItemService.create(context, collection, false);
|
||||||
|
Item item = installItemService.installItem(context, workspaceItem);
|
||||||
|
EPerson epersonA = ePersonService.create(context);
|
||||||
|
Collection collectionPerson = collectionService.create(context, com);
|
||||||
|
collectionService.addMetadata(context, collectionPerson, "dspace", "entity", "type",
|
||||||
|
null, "Person");
|
||||||
|
collectionPerson.setSubmitters(submitters);
|
||||||
|
groupService.addMember(context, submitters, epersonA);
|
||||||
|
context.setCurrentUser(epersonA);
|
||||||
|
context.commit();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
Collection resultCollection = collectionService.retrieveCollectionWithSubmitByCommunityAndEntityType
|
||||||
|
(context, com, "Person");
|
||||||
|
|
||||||
|
assertThat("testRetrieveCollectionWithSubmitByEntityType 0", resultCollection, notNullValue());
|
||||||
|
assertThat("testRetrieveCollectionWithSubmitByEntityType 1", resultCollection, equalTo(collectionPerson));
|
||||||
|
|
||||||
|
context.setDispatcher("exclude-discovery");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,15 +14,15 @@ import eu.openaire.jaxb.helper.OpenAIREHandler;
|
|||||||
import eu.openaire.jaxb.model.Response;
|
import eu.openaire.jaxb.model.Response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mock the OpenAIRE rest connector for unit testing<br>
|
* Mock the Openaire rest connector for unit testing<br>
|
||||||
* will be resolved against static test xml files
|
* will be resolved against static test xml files
|
||||||
*
|
*
|
||||||
* @author pgraca
|
* @author pgraca
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class MockOpenAIRERestConnector extends OpenAIRERestConnector {
|
public class MockOpenaireRestConnector extends OpenaireRestConnector {
|
||||||
|
|
||||||
public MockOpenAIRERestConnector(String url) {
|
public MockOpenaireRestConnector(String url) {
|
||||||
super(url);
|
super(url);
|
||||||
}
|
}
|
||||||
|
|
@@ -23,15 +23,15 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for OpenAIREFundingDataProvider
|
* Unit tests for OpenaireFundingDataProvider
|
||||||
*
|
*
|
||||||
* @author pgraca
|
* @author pgraca
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class OpenAIREFundingDataProviderTest extends AbstractDSpaceTest {
|
public class OpenaireFundingDataProviderTest extends AbstractDSpaceTest {
|
||||||
|
|
||||||
ExternalDataService externalDataService;
|
ExternalDataService externalDataService;
|
||||||
ExternalDataProvider openAIREFundingDataProvider;
|
ExternalDataProvider openaireFundingDataProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method will be run before every test as per @Before. It will initialize
|
* This method will be run before every test as per @Before. It will initialize
|
||||||
@@ -44,38 +44,38 @@ public class OpenAIREFundingDataProviderTest extends AbstractDSpaceTest {
|
|||||||
public void init() {
|
public void init() {
|
||||||
// Set up External Service Factory and set data providers
|
// Set up External Service Factory and set data providers
|
||||||
externalDataService = ExternalServiceFactory.getInstance().getExternalDataService();
|
externalDataService = ExternalServiceFactory.getInstance().getExternalDataService();
|
||||||
openAIREFundingDataProvider = externalDataService.getExternalDataProvider("openAIREFunding");
|
openaireFundingDataProvider = externalDataService.getExternalDataProvider("openaireFunding");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNumberOfResultsWSingleKeyword() {
|
public void testNumberOfResultsWSingleKeyword() {
|
||||||
assertNotNull("openAIREFundingDataProvider is not null", openAIREFundingDataProvider);
|
assertNotNull("openaireFundingDataProvider is not null", openaireFundingDataProvider);
|
||||||
assertEquals("openAIREFunding.numberOfResults.query:mock", 77,
|
assertEquals("openaireFunding.numberOfResults.query:mock", 77,
|
||||||
openAIREFundingDataProvider.getNumberOfResults("mock"));
|
openaireFundingDataProvider.getNumberOfResults("mock"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNumberOfResultsWKeywords() {
|
public void testNumberOfResultsWKeywords() {
|
||||||
assertNotNull("openAIREFundingDataProvider is not null", openAIREFundingDataProvider);
|
assertNotNull("openaireFundingDataProvider is not null", openaireFundingDataProvider);
|
||||||
assertEquals("openAIREFunding.numberOfResults.query:mock+test", 77,
|
assertEquals("openaireFunding.numberOfResults.query:mock+test", 77,
|
||||||
openAIREFundingDataProvider.getNumberOfResults("mock+test"));
|
openaireFundingDataProvider.getNumberOfResults("mock+test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQueryResultsWSingleKeyword() {
|
public void testQueryResultsWSingleKeyword() {
|
||||||
assertNotNull("openAIREFundingDataProvider is not null", openAIREFundingDataProvider);
|
assertNotNull("openaireFundingDataProvider is not null", openaireFundingDataProvider);
|
||||||
List<ExternalDataObject> results = openAIREFundingDataProvider.searchExternalDataObjects("mock", 0, 10);
|
List<ExternalDataObject> results = openaireFundingDataProvider.searchExternalDataObjects("mock", 0, 10);
|
||||||
assertEquals("openAIREFunding.searchExternalDataObjects.size", 10, results.size());
|
assertEquals("openaireFunding.searchExternalDataObjects.size", 10, results.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQueryResultsWKeywords() {
|
public void testQueryResultsWKeywords() {
|
||||||
String value = "Mushroom Robo-Pic - Development of an autonomous robotic mushroom picking system";
|
String value = "Mushroom Robo-Pic - Development of an autonomous robotic mushroom picking system";
|
||||||
|
|
||||||
assertNotNull("openAIREFundingDataProvider is not null", openAIREFundingDataProvider);
|
assertNotNull("openaireFundingDataProvider is not null", openaireFundingDataProvider);
|
||||||
List<ExternalDataObject> results = openAIREFundingDataProvider.searchExternalDataObjects("mock+test", 0, 10);
|
List<ExternalDataObject> results = openaireFundingDataProvider.searchExternalDataObjects("mock+test", 0, 10);
|
||||||
assertEquals("openAIREFunding.searchExternalDataObjects.size", 10, results.size());
|
assertEquals("openaireFunding.searchExternalDataObjects.size", 10, results.size());
|
||||||
assertTrue("openAIREFunding.searchExternalDataObjects.first.value", value.equals(results.get(0).getValue()));
|
assertTrue("openaireFunding.searchExternalDataObjects.first.value", value.equals(results.get(0).getValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -84,22 +84,22 @@ public class OpenAIREFundingDataProviderTest extends AbstractDSpaceTest {
|
|||||||
String value = "Portuguese Wild Mushrooms: Chemical characterization and functional study"
|
String value = "Portuguese Wild Mushrooms: Chemical characterization and functional study"
|
||||||
+ " of antiproliferative and proapoptotic properties in cancer cell lines";
|
+ " of antiproliferative and proapoptotic properties in cancer cell lines";
|
||||||
|
|
||||||
assertNotNull("openAIREFundingDataProvider is not null", openAIREFundingDataProvider);
|
assertNotNull("openaireFundingDataProvider is not null", openaireFundingDataProvider);
|
||||||
|
|
||||||
Optional<ExternalDataObject> result = openAIREFundingDataProvider.getExternalDataObject(id);
|
Optional<ExternalDataObject> result = openaireFundingDataProvider.getExternalDataObject(id);
|
||||||
|
|
||||||
assertTrue("openAIREFunding.getExternalDataObject.exists", result.isPresent());
|
assertTrue("openaireFunding.getExternalDataObject.exists", result.isPresent());
|
||||||
assertTrue("openAIREFunding.getExternalDataObject.value", value.equals(result.get().getValue()));
|
assertTrue("openaireFunding.getExternalDataObject.value", value.equals(result.get().getValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetDataObjectWInvalidId() {
|
public void testGetDataObjectWInvalidId() {
|
||||||
String id = "WRONGID";
|
String id = "WRONGID";
|
||||||
|
|
||||||
assertNotNull("openAIREFundingDataProvider is not null", openAIREFundingDataProvider);
|
assertNotNull("openaireFundingDataProvider is not null", openaireFundingDataProvider);
|
||||||
|
|
||||||
Optional<ExternalDataObject> result = openAIREFundingDataProvider.getExternalDataObject(id);
|
Optional<ExternalDataObject> result = openaireFundingDataProvider.getExternalDataObject(id);
|
||||||
|
|
||||||
assertTrue("openAIREFunding.getExternalDataObject.notExists:WRONGID", result.isEmpty());
|
assertTrue("openaireFunding.getExternalDataObject.notExists:WRONGID", result.isEmpty());
|
||||||
}
|
}
|
||||||
}
|
}
|
117
dspace-api/src/test/java/org/dspace/matcher/QAEventMatcher.java
Normal file
117
dspace-api/src/test/java/org/dspace/matcher/QAEventMatcher.java
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* 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.matcher;
|
||||||
|
|
||||||
|
import static org.dspace.content.QAEvent.OPENAIRE_SOURCE;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
|
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.hamcrest.Description;
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
|
import org.hamcrest.TypeSafeMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link org.hamcrest.Matcher} to match a QAEvent by all its
|
||||||
|
* attributes.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QAEventMatcher extends TypeSafeMatcher<QAEvent> {
|
||||||
|
|
||||||
|
private Matcher<String> eventIdMatcher;
|
||||||
|
|
||||||
|
private Matcher<String> originalIdMatcher;
|
||||||
|
|
||||||
|
private Matcher<String> relatedMatcher;
|
||||||
|
|
||||||
|
private Matcher<String> sourceMatcher;
|
||||||
|
|
||||||
|
private Matcher<String> statusMatcher;
|
||||||
|
|
||||||
|
private Matcher<String> targetMatcher;
|
||||||
|
|
||||||
|
private Matcher<String> titleMatcher;
|
||||||
|
|
||||||
|
private Matcher<String> messageMatcher;
|
||||||
|
|
||||||
|
private Matcher<String> topicMatcher;
|
||||||
|
|
||||||
|
private Matcher<Double> trustMatcher;
|
||||||
|
|
||||||
|
private QAEventMatcher(Matcher<String> eventIdMatcher, Matcher<String> originalIdMatcher,
|
||||||
|
Matcher<String> relatedMatcher, Matcher<String> sourceMatcher, Matcher<String> statusMatcher,
|
||||||
|
Matcher<String> targetMatcher, Matcher<String> titleMatcher, Matcher<String> messageMatcher,
|
||||||
|
Matcher<String> topicMatcher, Matcher<Double> trustMatcher) {
|
||||||
|
this.eventIdMatcher = eventIdMatcher;
|
||||||
|
this.originalIdMatcher = originalIdMatcher;
|
||||||
|
this.relatedMatcher = relatedMatcher;
|
||||||
|
this.sourceMatcher = sourceMatcher;
|
||||||
|
this.statusMatcher = statusMatcher;
|
||||||
|
this.targetMatcher = targetMatcher;
|
||||||
|
this.titleMatcher = titleMatcher;
|
||||||
|
this.messageMatcher = messageMatcher;
|
||||||
|
this.topicMatcher = topicMatcher;
|
||||||
|
this.trustMatcher = trustMatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link QAEventMatcher} that matches an OPENAIRE
|
||||||
|
* QAEvent with PENDING status, with an event id, without a related item and
|
||||||
|
* with the given attributes.
|
||||||
|
*
|
||||||
|
* @param originalId the original id to match
|
||||||
|
* @param target the target to match
|
||||||
|
* @param title the title to match
|
||||||
|
* @param message the message to match
|
||||||
|
* @param topic the topic to match
|
||||||
|
* @param trust the trust to match
|
||||||
|
* @return the matcher istance
|
||||||
|
*/
|
||||||
|
public static QAEventMatcher pendingOpenaireEventWith(String originalId, Item target,
|
||||||
|
String title, String message, String topic, Double trust) {
|
||||||
|
|
||||||
|
return new QAEventMatcher(notNullValue(String.class), is(originalId), nullValue(String.class),
|
||||||
|
is(OPENAIRE_SOURCE), is("PENDING"), is(target.getID().toString()), is(title), is(message), is(topic),
|
||||||
|
is(trust));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matchesSafely(QAEvent event) {
|
||||||
|
return eventIdMatcher.matches(event.getEventId())
|
||||||
|
&& originalIdMatcher.matches(event.getOriginalId())
|
||||||
|
&& relatedMatcher.matches(event.getRelated())
|
||||||
|
&& sourceMatcher.matches(event.getSource())
|
||||||
|
&& statusMatcher.matches(event.getStatus())
|
||||||
|
&& targetMatcher.matches(event.getTarget())
|
||||||
|
&& titleMatcher.matches(event.getTitle())
|
||||||
|
&& messageMatcher.matches(event.getMessage())
|
||||||
|
&& topicMatcher.matches(event.getTopic())
|
||||||
|
&& trustMatcher.matches(event.getTrust());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void describeTo(Description description) {
|
||||||
|
description.appendText("a QA event with the following attributes:")
|
||||||
|
.appendText(" event id ").appendDescriptionOf(eventIdMatcher)
|
||||||
|
.appendText(", original id ").appendDescriptionOf(originalIdMatcher)
|
||||||
|
.appendText(", related ").appendDescriptionOf(relatedMatcher)
|
||||||
|
.appendText(", source ").appendDescriptionOf(sourceMatcher)
|
||||||
|
.appendText(", status ").appendDescriptionOf(statusMatcher)
|
||||||
|
.appendText(", target ").appendDescriptionOf(targetMatcher)
|
||||||
|
.appendText(", title ").appendDescriptionOf(titleMatcher)
|
||||||
|
.appendText(", message ").appendDescriptionOf(messageMatcher)
|
||||||
|
.appendText(", topic ").appendDescriptionOf(topicMatcher)
|
||||||
|
.appendText(" and trust ").appendDescriptionOf(trustMatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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.matcher;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
|
import org.dspace.qaevent.QASource;
|
||||||
|
import org.hamcrest.Description;
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
|
import org.hamcrest.TypeSafeMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link org.hamcrest.Matcher} to match a QASource by all its
|
||||||
|
* attributes.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QASourceMatcher extends TypeSafeMatcher<QASource> {
|
||||||
|
|
||||||
|
private Matcher<String> nameMatcher;
|
||||||
|
|
||||||
|
private Matcher<Long> totalEventsMatcher;
|
||||||
|
|
||||||
|
private QASourceMatcher(Matcher<String> nameMatcher, Matcher<Long> totalEventsMatcher) {
|
||||||
|
this.nameMatcher = nameMatcher;
|
||||||
|
this.totalEventsMatcher = totalEventsMatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link QASourceMatcher} that matches a QATopic with
|
||||||
|
* the given name and total events count.
|
||||||
|
* @param name the name to match
|
||||||
|
* @param totalEvents the total events count to match
|
||||||
|
* @return the matcher instance
|
||||||
|
*/
|
||||||
|
public static QASourceMatcher with(String name, long totalEvents) {
|
||||||
|
return new QASourceMatcher(is(name), is(totalEvents));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matchesSafely(QASource event) {
|
||||||
|
return nameMatcher.matches(event.getName()) && totalEventsMatcher.matches(event.getTotalEvents());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void describeTo(Description description) {
|
||||||
|
description.appendText("a QA source with the following attributes:")
|
||||||
|
.appendText(" name ").appendDescriptionOf(nameMatcher)
|
||||||
|
.appendText(" and total events ").appendDescriptionOf(totalEventsMatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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.matcher;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
|
import org.dspace.qaevent.QATopic;
|
||||||
|
import org.hamcrest.Description;
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
|
import org.hamcrest.TypeSafeMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link org.hamcrest.Matcher} to match a QATopic by all its
|
||||||
|
* attributes.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QATopicMatcher extends TypeSafeMatcher<QATopic> {
|
||||||
|
|
||||||
|
private Matcher<String> keyMatcher;
|
||||||
|
|
||||||
|
private Matcher<Long> totalEventsMatcher;
|
||||||
|
|
||||||
|
private QATopicMatcher(Matcher<String> keyMatcher, Matcher<Long> totalEventsMatcher) {
|
||||||
|
this.keyMatcher = keyMatcher;
|
||||||
|
this.totalEventsMatcher = totalEventsMatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link QATopicMatcher} that matches a QATopic with the
|
||||||
|
* given key and total events count.
|
||||||
|
* @param key the key to match
|
||||||
|
* @param totalEvents the total events count to match
|
||||||
|
* @return the matcher instance
|
||||||
|
*/
|
||||||
|
public static QATopicMatcher with(String key, long totalEvents) {
|
||||||
|
return new QATopicMatcher(is(key), is(totalEvents));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matchesSafely(QATopic event) {
|
||||||
|
return keyMatcher.matches(event.getKey()) && totalEventsMatcher.matches(event.getTotalEvents());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void describeTo(Description description) {
|
||||||
|
description.appendText("a QA topic with the following attributes:")
|
||||||
|
.appendText(" key ").appendDescriptionOf(keyMatcher)
|
||||||
|
.appendText(" and total events ").appendDescriptionOf(totalEventsMatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.solr.client.solrj.SolrServerException;
|
||||||
|
import org.dspace.qaevent.service.impl.QAEventServiceImpl;
|
||||||
|
import org.dspace.solr.MockSolrServer;
|
||||||
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock SOLR service for the qaevents Core.
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class MockQAEventService extends QAEventServiceImpl implements InitializingBean, DisposableBean {
|
||||||
|
private MockSolrServer mockSolrServer;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
mockSolrServer = new MockSolrServer("qaevent");
|
||||||
|
solr = mockSolrServer.getSolrServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clear all records from the search core. */
|
||||||
|
public void reset() {
|
||||||
|
mockSolrServer.reset();
|
||||||
|
try {
|
||||||
|
mockSolrServer.getSolrServer().commit();
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() throws Exception {
|
||||||
|
mockSolrServer.destroy();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,488 @@
|
|||||||
|
/**
|
||||||
|
* 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.qaevent.script;
|
||||||
|
|
||||||
|
import static java.util.List.of;
|
||||||
|
import static org.dspace.content.QAEvent.OPENAIRE_SOURCE;
|
||||||
|
import static org.dspace.matcher.QAEventMatcher.pendingOpenaireEventWith;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.contains;
|
||||||
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.empty;
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import eu.dnetlib.broker.BrokerClient;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.dspace.AbstractIntegrationTestWithDatabase;
|
||||||
|
import org.dspace.app.launcher.ScriptLauncher;
|
||||||
|
import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler;
|
||||||
|
import org.dspace.builder.CollectionBuilder;
|
||||||
|
import org.dspace.builder.CommunityBuilder;
|
||||||
|
import org.dspace.builder.ItemBuilder;
|
||||||
|
import org.dspace.content.Collection;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.matcher.QASourceMatcher;
|
||||||
|
import org.dspace.matcher.QATopicMatcher;
|
||||||
|
import org.dspace.qaevent.service.OpenaireClientFactory;
|
||||||
|
import org.dspace.qaevent.service.QAEventService;
|
||||||
|
import org.dspace.qaevent.service.impl.OpenaireClientFactoryImpl;
|
||||||
|
import org.dspace.utils.DSpace;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for {@link OpenaireEventsImport}.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase {
|
||||||
|
|
||||||
|
private static final String BASE_JSON_DIR_PATH = "org/dspace/app/openaire-events/";
|
||||||
|
|
||||||
|
private static final String ORDER_FIELD = "topic";
|
||||||
|
|
||||||
|
private QAEventService qaEventService = new DSpace().getSingletonService(QAEventService.class);
|
||||||
|
|
||||||
|
private Collection collection;
|
||||||
|
|
||||||
|
private BrokerClient brokerClient = OpenaireClientFactory.getInstance().getBrokerClient();
|
||||||
|
|
||||||
|
private BrokerClient mockBrokerClient = mock(BrokerClient.class);
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
collection = CollectionBuilder.createCollection(context, parentCommunity)
|
||||||
|
.withName("Collection")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
((OpenaireClientFactoryImpl) OpenaireClientFactory.getInstance()).setBrokerClient(mockBrokerClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() {
|
||||||
|
((OpenaireClientFactoryImpl) OpenaireClientFactory.getInstance()).setBrokerClient(brokerClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithoutParameters() throws Exception {
|
||||||
|
TestDSpaceRunnableHandler handler = new TestDSpaceRunnableHandler();
|
||||||
|
|
||||||
|
String[] args = new String[] { "import-openaire-events" };
|
||||||
|
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), handler, kernelImpl);
|
||||||
|
|
||||||
|
assertThat(handler.getErrorMessages(), empty());
|
||||||
|
assertThat(handler.getWarningMessages(), empty());
|
||||||
|
assertThat(handler.getInfoMessages(), empty());
|
||||||
|
|
||||||
|
Exception exception = handler.getException();
|
||||||
|
assertThat(exception, instanceOf(IllegalArgumentException.class));
|
||||||
|
assertThat(exception.getMessage(), is("One parameter between the location of the file and the email "
|
||||||
|
+ "must be entered to proceed with the import."));
|
||||||
|
|
||||||
|
verifyNoInteractions(mockBrokerClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithBothFileAndEmailParameters() throws Exception {
|
||||||
|
TestDSpaceRunnableHandler handler = new TestDSpaceRunnableHandler();
|
||||||
|
|
||||||
|
String[] args = new String[] { "import-openaire-events", "-f", getFileLocation("events.json"),
|
||||||
|
"-e", "test@user.com" };
|
||||||
|
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), handler, kernelImpl);
|
||||||
|
|
||||||
|
assertThat(handler.getErrorMessages(), empty());
|
||||||
|
assertThat(handler.getWarningMessages(), empty());
|
||||||
|
assertThat(handler.getInfoMessages(), empty());
|
||||||
|
|
||||||
|
Exception exception = handler.getException();
|
||||||
|
assertThat(exception, instanceOf(IllegalArgumentException.class));
|
||||||
|
assertThat(exception.getMessage(), is("Only one parameter between the location of the file and the email "
|
||||||
|
+ "must be entered to proceed with the import."));
|
||||||
|
|
||||||
|
verifyNoInteractions(mockBrokerClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void testManyEventsImportFromFile() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
Item firstItem = createItem("Test item", "123456789/99998");
|
||||||
|
Item secondItem = createItem("Test item 2", "123456789/99999");
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
TestDSpaceRunnableHandler handler = new TestDSpaceRunnableHandler();
|
||||||
|
|
||||||
|
String[] args = new String[] { "import-openaire-events", "-f", getFileLocation("events.json") };
|
||||||
|
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), handler, kernelImpl);
|
||||||
|
|
||||||
|
assertThat(handler.getErrorMessages(), empty());
|
||||||
|
assertThat(handler.getWarningMessages(), empty());
|
||||||
|
assertThat(handler.getInfoMessages(), contains(
|
||||||
|
"Trying to read the QA events from the provided file",
|
||||||
|
"Found 5 events in the given file"));
|
||||||
|
|
||||||
|
assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 5L)));
|
||||||
|
|
||||||
|
assertThat(qaEventService.findAllTopics(0, 20, ORDER_FIELD, false), containsInAnyOrder(
|
||||||
|
QATopicMatcher.with("ENRICH/MORE/PROJECT", 1L),
|
||||||
|
QATopicMatcher.with("ENRICH/MORE/PID", 1L),
|
||||||
|
QATopicMatcher.with("ENRICH/MISSING/PID", 1L),
|
||||||
|
QATopicMatcher.with("ENRICH/MISSING/PROJECT", 1L),
|
||||||
|
QATopicMatcher.with("ENRICH/MISSING/ABSTRACT", 1L)));
|
||||||
|
|
||||||
|
String projectMessage = "{\"projects[0].acronym\":\"PAThs\",\"projects[0].code\":\"687567\","
|
||||||
|
+ "\"projects[0].funder\":\"EC\",\"projects[0].fundingProgram\":\"H2020\","
|
||||||
|
+ "\"projects[0].jurisdiction\":\"EU\","
|
||||||
|
+ "\"projects[0].openaireId\":\"40|corda__h2020::6e32f5eb912688f2424c68b851483ea4\","
|
||||||
|
+ "\"projects[0].title\":\"Tracking Papyrus and Parchment Paths\"}";
|
||||||
|
|
||||||
|
assertThat(qaEventService.findEventsByTopic("ENRICH/MORE/PROJECT"), contains(
|
||||||
|
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/99998", firstItem,
|
||||||
|
"Egypt, crossroad of translations and literary interweavings", projectMessage,
|
||||||
|
"ENRICH/MORE/PROJECT", 1.00d)));
|
||||||
|
|
||||||
|
String abstractMessage = "{\"abstracts[0]\":\"Missing Abstract\"}";
|
||||||
|
|
||||||
|
assertThat(qaEventService.findEventsByTopic("ENRICH/MISSING/ABSTRACT"), contains(
|
||||||
|
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/99999", secondItem, "Test Publication",
|
||||||
|
abstractMessage, "ENRICH/MISSING/ABSTRACT", 1.00d)));
|
||||||
|
|
||||||
|
verifyNoInteractions(mockBrokerClient);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testManyEventsImportFromFileWithUnknownHandle() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
Item item = createItem("Test item", "123456789/99999");
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
TestDSpaceRunnableHandler handler = new TestDSpaceRunnableHandler();
|
||||||
|
|
||||||
|
String[] args = new String[] { "import-openaire-events", "-f", getFileLocation("events.json") };
|
||||||
|
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), handler, kernelImpl);
|
||||||
|
|
||||||
|
assertThat(handler.getErrorMessages(), empty());
|
||||||
|
assertThat(handler.getWarningMessages(),
|
||||||
|
contains("An error occurs storing the event with id b4e09c71312cd7c397969f56c900823f: "
|
||||||
|
+ "Skipped event b4e09c71312cd7c397969f56c900823f related to the oai record "
|
||||||
|
+ "oai:www.openstarts.units.it:123456789/99998 as the record was not found",
|
||||||
|
"An error occurs storing the event with id d050d2c4399c6c6ccf27d52d479d26e4: "
|
||||||
|
+ "Skipped event d050d2c4399c6c6ccf27d52d479d26e4 related to the oai record "
|
||||||
|
+ "oai:www.openstarts.units.it:123456789/99998 as the record was not found"));
|
||||||
|
assertThat(handler.getInfoMessages(), contains(
|
||||||
|
"Trying to read the QA events from the provided file",
|
||||||
|
"Found 5 events in the given file"));
|
||||||
|
|
||||||
|
assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 3L)));
|
||||||
|
|
||||||
|
assertThat(qaEventService.findAllTopics(0, 20, ORDER_FIELD, false), containsInAnyOrder(
|
||||||
|
QATopicMatcher.with("ENRICH/MISSING/ABSTRACT", 1L),
|
||||||
|
QATopicMatcher.with("ENRICH/MISSING/PROJECT", 1L),
|
||||||
|
QATopicMatcher.with("ENRICH/MORE/PID", 1L)
|
||||||
|
));
|
||||||
|
|
||||||
|
String abstractMessage = "{\"abstracts[0]\":\"Missing Abstract\"}";
|
||||||
|
|
||||||
|
assertThat(qaEventService.findEventsByTopic("ENRICH/MISSING/ABSTRACT"), contains(
|
||||||
|
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/99999", item, "Test Publication",
|
||||||
|
abstractMessage, "ENRICH/MISSING/ABSTRACT", 1.00d)));
|
||||||
|
|
||||||
|
verifyNoInteractions(mockBrokerClient);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testManyEventsImportFromFileWithUnknownTopic() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
createItem("Test item", "123456789/99999");
|
||||||
|
Item secondItem = createItem("Test item 2", "123456789/999991");
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
TestDSpaceRunnableHandler handler = new TestDSpaceRunnableHandler();
|
||||||
|
|
||||||
|
String[] args = new String[] { "import-openaire-events", "-f", getFileLocation("unknown-topic-events.json") };
|
||||||
|
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), handler, kernelImpl);
|
||||||
|
|
||||||
|
assertThat(handler.getErrorMessages(), empty());
|
||||||
|
assertThat(handler.getWarningMessages(),
|
||||||
|
contains("Event for topic ENRICH/MORE/UNKNOWN is not allowed in the qaevents.cfg"));
|
||||||
|
assertThat(handler.getInfoMessages(), contains(
|
||||||
|
"Trying to read the QA events from the provided file",
|
||||||
|
"Found 2 events in the given file"));
|
||||||
|
|
||||||
|
assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 1L)));
|
||||||
|
|
||||||
|
assertThat(qaEventService.findAllTopics(0, 20, ORDER_FIELD, false),
|
||||||
|
contains(QATopicMatcher.with("ENRICH/MISSING/ABSTRACT", 1L)));
|
||||||
|
|
||||||
|
String abstractMessage = "{\"abstracts[0]\":\"Missing Abstract\"}";
|
||||||
|
|
||||||
|
assertThat(qaEventService.findEventsByTopic("ENRICH/MISSING/ABSTRACT"), contains(
|
||||||
|
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/999991", secondItem, "Test Publication 2",
|
||||||
|
abstractMessage, "ENRICH/MISSING/ABSTRACT", 1.00d)));
|
||||||
|
|
||||||
|
verifyNoInteractions(mockBrokerClient);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImportFromFileWithoutEvents() throws Exception {
|
||||||
|
|
||||||
|
TestDSpaceRunnableHandler handler = new TestDSpaceRunnableHandler();
|
||||||
|
|
||||||
|
String[] args = new String[] { "import-openaire-events", "-f", getFileLocation("empty-file.json") };
|
||||||
|
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), handler, kernelImpl);
|
||||||
|
|
||||||
|
assertThat(handler.getErrorMessages(),
|
||||||
|
contains(containsString("A not recoverable error occurs during OPENAIRE events import")));
|
||||||
|
assertThat(handler.getWarningMessages(),empty());
|
||||||
|
assertThat(handler.getInfoMessages(), contains("Trying to read the QA events from the provided file"));
|
||||||
|
|
||||||
|
assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 0L)));
|
||||||
|
|
||||||
|
assertThat(qaEventService.findAllTopics(0, 20, ORDER_FIELD, false), empty());
|
||||||
|
|
||||||
|
verifyNoInteractions(mockBrokerClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void testImportFromOpenaireBroker() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
Item firstItem = createItem("Test item", "123456789/99998");
|
||||||
|
Item secondItem = createItem("Test item 2", "123456789/99999");
|
||||||
|
Item thirdItem = createItem("Test item 3", "123456789/999991");
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
URL openaireURL = new URL("http://api.openaire.eu/broker");
|
||||||
|
|
||||||
|
when(mockBrokerClient.listSubscriptions(openaireURL, "user@test.com")).thenReturn(of("sub1", "sub2", "sub3"));
|
||||||
|
|
||||||
|
doAnswer(i -> writeToOutputStream(i.getArgument(2, OutputStream.class), "events.json"))
|
||||||
|
.when(mockBrokerClient).downloadEvents(eq(openaireURL), eq("sub1"), any());
|
||||||
|
|
||||||
|
doAnswer(i -> writeToOutputStream(i.getArgument(2, OutputStream.class), "empty-events-list.json"))
|
||||||
|
.when(mockBrokerClient).downloadEvents(eq(openaireURL), eq("sub2"), any());
|
||||||
|
|
||||||
|
doAnswer(i -> writeToOutputStream(i.getArgument(2, OutputStream.class), "unknown-topic-events.json"))
|
||||||
|
.when(mockBrokerClient).downloadEvents(eq(openaireURL), eq("sub3"), any());
|
||||||
|
|
||||||
|
TestDSpaceRunnableHandler handler = new TestDSpaceRunnableHandler();
|
||||||
|
|
||||||
|
String[] args = new String[] { "import-openaire-events", "-e", "user@test.com" };
|
||||||
|
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), handler, kernelImpl);
|
||||||
|
|
||||||
|
assertThat(handler.getErrorMessages(), empty());
|
||||||
|
assertThat(handler.getWarningMessages(),
|
||||||
|
contains("Event for topic ENRICH/MORE/UNKNOWN is not allowed in the qaevents.cfg"));
|
||||||
|
assertThat(handler.getInfoMessages(), contains(
|
||||||
|
"Trying to read the QA events from the OPENAIRE broker",
|
||||||
|
"Found 3 subscriptions related to the given email",
|
||||||
|
"Found 5 events from the subscription sub1",
|
||||||
|
"Found 0 events from the subscription sub2",
|
||||||
|
"Found 2 events from the subscription sub3"));
|
||||||
|
|
||||||
|
assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 6L)));
|
||||||
|
|
||||||
|
assertThat(qaEventService.findAllTopics(0, 20, ORDER_FIELD, false), containsInAnyOrder(
|
||||||
|
QATopicMatcher.with("ENRICH/MORE/PROJECT", 1L),
|
||||||
|
QATopicMatcher.with("ENRICH/MORE/PID", 1L),
|
||||||
|
QATopicMatcher.with("ENRICH/MISSING/PID", 1L),
|
||||||
|
QATopicMatcher.with("ENRICH/MISSING/PROJECT", 1L),
|
||||||
|
QATopicMatcher.with("ENRICH/MISSING/ABSTRACT", 2L)));
|
||||||
|
|
||||||
|
String projectMessage = "{\"projects[0].acronym\":\"PAThs\",\"projects[0].code\":\"687567\","
|
||||||
|
+ "\"projects[0].funder\":\"EC\",\"projects[0].fundingProgram\":\"H2020\","
|
||||||
|
+ "\"projects[0].jurisdiction\":\"EU\","
|
||||||
|
+ "\"projects[0].openaireId\":\"40|corda__h2020::6e32f5eb912688f2424c68b851483ea4\","
|
||||||
|
+ "\"projects[0].title\":\"Tracking Papyrus and Parchment Paths\"}";
|
||||||
|
|
||||||
|
assertThat(qaEventService.findEventsByTopic("ENRICH/MORE/PROJECT"), contains(
|
||||||
|
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/99998", firstItem,
|
||||||
|
"Egypt, crossroad of translations and literary interweavings", projectMessage,
|
||||||
|
"ENRICH/MORE/PROJECT", 1.00d)));
|
||||||
|
|
||||||
|
String abstractMessage = "{\"abstracts[0]\":\"Missing Abstract\"}";
|
||||||
|
|
||||||
|
assertThat(qaEventService.findEventsByTopic("ENRICH/MISSING/ABSTRACT"), containsInAnyOrder(
|
||||||
|
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/99999", secondItem, "Test Publication",
|
||||||
|
abstractMessage, "ENRICH/MISSING/ABSTRACT", 1.00d),
|
||||||
|
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/999991", thirdItem, "Test Publication 2",
|
||||||
|
abstractMessage, "ENRICH/MISSING/ABSTRACT", 1.00d)));
|
||||||
|
|
||||||
|
verify(mockBrokerClient).listSubscriptions(openaireURL, "user@test.com");
|
||||||
|
verify(mockBrokerClient).downloadEvents(eq(openaireURL), eq("sub1"), any());
|
||||||
|
verify(mockBrokerClient).downloadEvents(eq(openaireURL), eq("sub2"), any());
|
||||||
|
verify(mockBrokerClient).downloadEvents(eq(openaireURL), eq("sub3"), any());
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(mockBrokerClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImportFromOpenaireBrokerWithErrorDuringListSubscription() throws Exception {
|
||||||
|
|
||||||
|
URL openaireURL = new URL("http://api.openaire.eu/broker");
|
||||||
|
|
||||||
|
when(mockBrokerClient.listSubscriptions(openaireURL, "user@test.com"))
|
||||||
|
.thenThrow(new RuntimeException("Connection refused"));
|
||||||
|
|
||||||
|
TestDSpaceRunnableHandler handler = new TestDSpaceRunnableHandler();
|
||||||
|
|
||||||
|
String[] args = new String[] { "import-openaire-events", "-e", "user@test.com" };
|
||||||
|
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), handler, kernelImpl);
|
||||||
|
|
||||||
|
assertThat(handler.getErrorMessages(),
|
||||||
|
contains("A not recoverable error occurs during OPENAIRE events import: Connection refused"));
|
||||||
|
assertThat(handler.getWarningMessages(), empty());
|
||||||
|
assertThat(handler.getInfoMessages(), contains("Trying to read the QA events from the OPENAIRE broker"));
|
||||||
|
|
||||||
|
assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 0L)));
|
||||||
|
|
||||||
|
assertThat(qaEventService.findAllTopics(0, 20, ORDER_FIELD, false), empty());
|
||||||
|
|
||||||
|
verify(mockBrokerClient).listSubscriptions(openaireURL, "user@test.com");
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(mockBrokerClient);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void testImportFromOpenaireBrokerWithErrorDuringEventsDownload() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
createItem("Test item", "123456789/99998");
|
||||||
|
createItem("Test item 2", "123456789/99999");
|
||||||
|
createItem("Test item 3", "123456789/999991");
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
URL openaireURL = new URL("http://api.openaire.eu/broker");
|
||||||
|
|
||||||
|
when(mockBrokerClient.listSubscriptions(openaireURL, "user@test.com")).thenReturn(of("sub1", "sub2", "sub3"));
|
||||||
|
|
||||||
|
doAnswer(i -> writeToOutputStream(i.getArgument(2, OutputStream.class), "events.json"))
|
||||||
|
.when(mockBrokerClient).downloadEvents(eq(openaireURL), eq("sub1"), any());
|
||||||
|
|
||||||
|
doThrow(new RuntimeException("Invalid subscription id"))
|
||||||
|
.when(mockBrokerClient).downloadEvents(eq(openaireURL), eq("sub2"), any());
|
||||||
|
|
||||||
|
doAnswer(i -> writeToOutputStream(i.getArgument(2, OutputStream.class), "unknown-topic-events.json"))
|
||||||
|
.when(mockBrokerClient).downloadEvents(eq(openaireURL), eq("sub3"), any());
|
||||||
|
|
||||||
|
TestDSpaceRunnableHandler handler = new TestDSpaceRunnableHandler();
|
||||||
|
|
||||||
|
String[] args = new String[] { "import-openaire-events", "-e", "user@test.com" };
|
||||||
|
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), handler, kernelImpl);
|
||||||
|
|
||||||
|
assertThat(handler.getErrorMessages(), contains("An error occurs downloading the events "
|
||||||
|
+ "related to the subscription sub2: Invalid subscription id"));
|
||||||
|
assertThat(handler.getWarningMessages(),
|
||||||
|
contains("Event for topic ENRICH/MORE/UNKNOWN is not allowed in the qaevents.cfg"));
|
||||||
|
assertThat(handler.getInfoMessages(), contains(
|
||||||
|
"Trying to read the QA events from the OPENAIRE broker",
|
||||||
|
"Found 3 subscriptions related to the given email",
|
||||||
|
"Found 5 events from the subscription sub1",
|
||||||
|
"Found 0 events from the subscription sub2",
|
||||||
|
"Found 2 events from the subscription sub3"));
|
||||||
|
|
||||||
|
assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 6L)));
|
||||||
|
|
||||||
|
assertThat(qaEventService.findAllTopics(0, 20, ORDER_FIELD, false), containsInAnyOrder(
|
||||||
|
QATopicMatcher.with("ENRICH/MORE/PROJECT", 1L),
|
||||||
|
QATopicMatcher.with("ENRICH/MISSING/PID", 1L),
|
||||||
|
QATopicMatcher.with("ENRICH/MORE/PID", 1L),
|
||||||
|
QATopicMatcher.with("ENRICH/MISSING/PROJECT", 1L),
|
||||||
|
QATopicMatcher.with("ENRICH/MISSING/ABSTRACT", 2L)));
|
||||||
|
|
||||||
|
assertThat(qaEventService.findEventsByTopic("ENRICH/MORE/PROJECT"), hasSize(1));
|
||||||
|
assertThat(qaEventService.findEventsByTopic("ENRICH/MISSING/ABSTRACT"), hasSize(2));
|
||||||
|
|
||||||
|
verify(mockBrokerClient).listSubscriptions(openaireURL, "user@test.com");
|
||||||
|
verify(mockBrokerClient).downloadEvents(eq(openaireURL), eq("sub1"), any());
|
||||||
|
verify(mockBrokerClient).downloadEvents(eq(openaireURL), eq("sub2"), any());
|
||||||
|
verify(mockBrokerClient).downloadEvents(eq(openaireURL), eq("sub3"), any());
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(mockBrokerClient);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Item createItem(String title, String handle) {
|
||||||
|
return ItemBuilder.createItem(context, collection)
|
||||||
|
.withTitle(title)
|
||||||
|
.withHandle(handle)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Void writeToOutputStream(OutputStream outputStream, String fileName) {
|
||||||
|
try {
|
||||||
|
byte[] fileContent = getFileContent(fileName);
|
||||||
|
IOUtils.write(fileContent, outputStream);
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getFileContent(String fileName) throws Exception {
|
||||||
|
String fileLocation = getFileLocation(fileName);
|
||||||
|
try (FileInputStream fis = new FileInputStream(new File(fileLocation))) {
|
||||||
|
return IOUtils.toByteArray(fis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFileLocation(String fileName) throws Exception {
|
||||||
|
URL resource = getClass().getClassLoader().getResource(BASE_JSON_DIR_PATH + fileName);
|
||||||
|
if (resource == null) {
|
||||||
|
throw new IllegalStateException("No resource found named " + BASE_JSON_DIR_PATH + fileName);
|
||||||
|
}
|
||||||
|
return new File(resource.getFile()).getAbsolutePath();
|
||||||
|
}
|
||||||
|
}
|
@@ -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.util;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link RawJsonDeserializer}.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class RawJsonDeserializerTest {
|
||||||
|
|
||||||
|
private String json = ""
|
||||||
|
+ "{"
|
||||||
|
+ " \"attribute\": {"
|
||||||
|
+ " \"firstField\":\"value\","
|
||||||
|
+ " \"secondField\": 1"
|
||||||
|
+ " }"
|
||||||
|
+ "}";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeserialization() throws JsonMappingException, JsonProcessingException {
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
DeserializationTestClass object = mapper.readValue(json, DeserializationTestClass.class);
|
||||||
|
assertThat(object, notNullValue());
|
||||||
|
assertThat(object.getAttribute(), is("{\"firstField\":\"value\",\"secondField\":1}"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DeserializationTestClass {
|
||||||
|
|
||||||
|
@JsonDeserialize(using = RawJsonDeserializer.class)
|
||||||
|
private String attribute;
|
||||||
|
|
||||||
|
public String getAttribute() {
|
||||||
|
return attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1 @@
|
|||||||
|
[]
|
@@ -0,0 +1,62 @@
|
|||||||
|
[
|
||||||
|
|
||||||
|
{
|
||||||
|
"originalId": "oai:www.openstarts.units.it:123456789/99998",
|
||||||
|
"title": "Egypt, crossroad of translations and literary interweavings",
|
||||||
|
"topic": "ENRICH/MORE/PROJECT",
|
||||||
|
"trust": 1.0,
|
||||||
|
"message": {
|
||||||
|
"projects[0].acronym": "PAThs",
|
||||||
|
"projects[0].code": "687567",
|
||||||
|
"projects[0].funder": "EC",
|
||||||
|
"projects[0].fundingProgram": "H2020",
|
||||||
|
"projects[0].jurisdiction": "EU",
|
||||||
|
"projects[0].openaireId": "40|corda__h2020::6e32f5eb912688f2424c68b851483ea4",
|
||||||
|
"projects[0].title": "Tracking Papyrus and Parchment Paths"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"originalId": "oai:www.openstarts.units.it:123456789/99999",
|
||||||
|
"title": "Test Publication",
|
||||||
|
"topic": "ENRICH/MISSING/ABSTRACT",
|
||||||
|
"trust": 1.0,
|
||||||
|
"message": {
|
||||||
|
"abstracts[0]": "Missing Abstract"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"originalId": "oai:www.openstarts.units.it:123456789/99998",
|
||||||
|
"title": "Egypt, crossroad of translations and literary interweavings",
|
||||||
|
"topic": "ENRICH/MISSING/PID",
|
||||||
|
"trust": 1.0,
|
||||||
|
"message": {
|
||||||
|
"pids[0].type": "doi",
|
||||||
|
"pids[0].value": "10.13137/2282-572x/987"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"originalId": "oai:www.openstarts.units.it:123456789/99999",
|
||||||
|
"title": "Test Publication",
|
||||||
|
"topic": "ENRICH/MORE/PID",
|
||||||
|
"trust": 0.375,
|
||||||
|
"message": {
|
||||||
|
"pids[0].type": "doi",
|
||||||
|
"pids[0].value": "987654"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"originalId": "oai:www.openstarts.units.it:123456789/99999",
|
||||||
|
"title": "Test Publication",
|
||||||
|
"topic": "ENRICH/MISSING/PROJECT",
|
||||||
|
"trust": 1.0,
|
||||||
|
"message": {
|
||||||
|
"projects[0].acronym": "02.SNES missing project acronym",
|
||||||
|
"projects[0].code": "prjcode_snes",
|
||||||
|
"projects[0].funder": "02.SNES missing project funder",
|
||||||
|
"projects[0].fundingProgram": "02.SNES missing project fundingProgram",
|
||||||
|
"projects[0].jurisdiction": "02.SNES missing project jurisdiction",
|
||||||
|
"projects[0].title": "Project01"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
@@ -0,0 +1,20 @@
|
|||||||
|
[
|
||||||
|
|
||||||
|
{
|
||||||
|
"originalId": "oai:www.openstarts.units.it:123456789/99998",
|
||||||
|
"title": "Egypt, crossroad of translations and literary interweavings (3rd-6th centuries). A reconsideration of earlier Coptic literature",
|
||||||
|
"topic": "ENRICH/MORE/UNKNOWN",
|
||||||
|
"trust": 1.0
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"originalId": "oai:www.openstarts.units.it:123456789/999991",
|
||||||
|
"title": "Test Publication 2",
|
||||||
|
"topic": "ENRICH/MISSING/ABSTRACT",
|
||||||
|
"trust": 1.0,
|
||||||
|
"message": {
|
||||||
|
"abstracts[0]": "Missing Abstract"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
@@ -0,0 +1,143 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest;
|
||||||
|
|
||||||
|
import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFIER_AS_STRING_VERSION_STRONG;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.converter.ConverterService;
|
||||||
|
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||||
|
import org.dspace.app.rest.model.ItemRest;
|
||||||
|
import org.dspace.app.rest.model.QAEventRest;
|
||||||
|
import org.dspace.app.rest.model.hateoas.ItemResource;
|
||||||
|
import org.dspace.app.rest.utils.ContextUtil;
|
||||||
|
import org.dspace.app.rest.utils.Utils;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.dspace.content.service.ItemService;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.qaevent.service.QAEventService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.rest.webmvc.ControllerUtils;
|
||||||
|
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
|
||||||
|
import org.springframework.hateoas.RepresentationModel;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This RestController will take care to manipulate the related item eventually
|
||||||
|
* associated with a qa event
|
||||||
|
* "/api/integration/qualityassuranceevents/{qaeventid}/related"
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/" + QAEventRest.CATEGORY + "/qualityassuranceevents"
|
||||||
|
+ REGEX_REQUESTMAPPING_IDENTIFIER_AS_STRING_VERSION_STRONG + "/related")
|
||||||
|
public class QAEventRelatedRestController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected Utils utils;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ConverterService converterService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ItemService itemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private QAEventService qaEventService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method associate an item to a qa event
|
||||||
|
*
|
||||||
|
* @param qaeventId The qa event id
|
||||||
|
* @param relatedItemUUID The uuid of the related item to associate with the qa event
|
||||||
|
* @return The related item
|
||||||
|
* @throws SQLException If something goes wrong
|
||||||
|
* @throws AuthorizeException If something goes wrong
|
||||||
|
*/
|
||||||
|
@PostMapping
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
public ResponseEntity<RepresentationModel<?>> addRelatedItem(@PathVariable(name = "id") String qaeventId,
|
||||||
|
@RequestParam(name = "item") UUID relatedItemUUID) throws SQLException, AuthorizeException {
|
||||||
|
|
||||||
|
Context context = ContextUtil.obtainCurrentRequestContext();
|
||||||
|
|
||||||
|
QAEvent qaevent = qaEventService.findEventByEventId(qaeventId);
|
||||||
|
if (qaevent == null) {
|
||||||
|
throw new ResourceNotFoundException("No such qa event: " + qaeventId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qaEventService.isRelatedItemSupported(qaevent)) {
|
||||||
|
throw new UnprocessableEntityException("The given event does not supports a related item");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qaevent.getRelated() != null) {
|
||||||
|
throw new UnprocessableEntityException("The given event already has a related item");
|
||||||
|
}
|
||||||
|
|
||||||
|
Item relatedItem = itemService.find(context, relatedItemUUID);
|
||||||
|
if (relatedItem == null) {
|
||||||
|
throw new UnprocessableEntityException("The proposed related item was not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
qaevent.setRelated(relatedItemUUID.toString());
|
||||||
|
qaEventService.store(context, qaevent);
|
||||||
|
|
||||||
|
ItemRest relatedItemRest = converterService.toRest(relatedItem, utils.obtainProjection());
|
||||||
|
ItemResource itemResource = converterService.toResource(relatedItemRest);
|
||||||
|
|
||||||
|
context.complete();
|
||||||
|
|
||||||
|
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, new HttpHeaders(), itemResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method remove the association to a related item from a qa event
|
||||||
|
*
|
||||||
|
* @param qaeventId The qa event id
|
||||||
|
* @return The related item
|
||||||
|
* @throws SQLException If something goes wrong
|
||||||
|
* @throws AuthorizeException If something goes wrong
|
||||||
|
*/
|
||||||
|
@DeleteMapping
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
public ResponseEntity<RepresentationModel<?>> removeRelatedItem(@PathVariable(name = "id") String qaeventId)
|
||||||
|
throws SQLException, AuthorizeException, IOException {
|
||||||
|
|
||||||
|
Context context = ContextUtil.obtainCurrentRequestContext();
|
||||||
|
QAEvent qaevent = qaEventService.findEventByEventId(qaeventId);
|
||||||
|
|
||||||
|
if (qaevent == null) {
|
||||||
|
throw new ResourceNotFoundException("No such qa event: " + qaeventId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qaEventService.isRelatedItemSupported(qaevent)) {
|
||||||
|
throw new UnprocessableEntityException("The given event does not supports a related item");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qaevent.getRelated() != null) {
|
||||||
|
qaevent.setRelated(null);
|
||||||
|
qaEventService.store(context, qaevent);
|
||||||
|
context.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT);
|
||||||
|
}
|
||||||
|
}
|
@@ -572,6 +572,37 @@ public class RestResourceController implements InitializingBean {
|
|||||||
return uploadInternal(request, apiCategory, model, uuid, uploadfile);
|
return uploadInternal(request, apiCategory, model, uuid, uploadfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called in POST, multipart, upload to a specific rest resource the file passed as "file" request parameter
|
||||||
|
*
|
||||||
|
* Note that the regular expression in the request mapping accept a String as identifier;
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* the http request
|
||||||
|
* @param apiCategory
|
||||||
|
* the api category
|
||||||
|
* @param model
|
||||||
|
* the rest model that identify the REST resource collection
|
||||||
|
* @param id
|
||||||
|
* the id of the specific rest resource
|
||||||
|
* @param uploadfile
|
||||||
|
* the file to upload
|
||||||
|
* @return the created resource
|
||||||
|
* @throws HttpRequestMethodNotSupportedException
|
||||||
|
*/
|
||||||
|
@RequestMapping(method = RequestMethod.POST,
|
||||||
|
value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_STRING_VERSION_STRONG,
|
||||||
|
headers = "content-type=multipart/form-data")
|
||||||
|
public <ID extends Serializable> ResponseEntity<RepresentationModel<?>> upload(HttpServletRequest request,
|
||||||
|
@PathVariable String apiCategory,
|
||||||
|
@PathVariable String model,
|
||||||
|
@PathVariable String id,
|
||||||
|
@RequestParam("file") MultipartFile
|
||||||
|
uploadfile)
|
||||||
|
throws HttpRequestMethodNotSupportedException {
|
||||||
|
return uploadInternal(request, apiCategory, model, id, uploadfile);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal upload method.
|
* Internal upload method.
|
||||||
*
|
*
|
||||||
@@ -684,6 +715,28 @@ public class RestResourceController implements InitializingBean {
|
|||||||
return patchInternal(request, apiCategory, model, id, jsonNode);
|
return patchInternal(request, apiCategory, model, id, jsonNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PATCH method, using operation on the resources following (JSON) Patch notation (https://tools.ietf
|
||||||
|
* .org/html/rfc6902)
|
||||||
|
*
|
||||||
|
* Note that the regular expression in the request mapping accept a UUID as identifier;
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @param apiCategory
|
||||||
|
* @param model
|
||||||
|
* @param id
|
||||||
|
* @param jsonNode
|
||||||
|
* @return
|
||||||
|
* @throws HttpRequestMethodNotSupportedException
|
||||||
|
*/
|
||||||
|
@RequestMapping(method = RequestMethod.PATCH, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_STRING_VERSION_STRONG)
|
||||||
|
public ResponseEntity<RepresentationModel<?>> patch(HttpServletRequest request, @PathVariable String apiCategory,
|
||||||
|
@PathVariable String model,
|
||||||
|
@PathVariable String id,
|
||||||
|
@RequestBody(required = true) JsonNode jsonNode) {
|
||||||
|
return patchInternal(request, apiCategory, model, id, jsonNode);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal patch method
|
* Internal patch method
|
||||||
*
|
*
|
||||||
@@ -711,9 +764,13 @@ public class RestResourceController implements InitializingBean {
|
|||||||
log.error(e.getMessage(), e);
|
log.error(e.getMessage(), e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
DSpaceResource result = converter.toResource(modelObject);
|
if (modelObject != null) {
|
||||||
//TODO manage HTTPHeader
|
DSpaceResource result = converter.toResource(modelObject);
|
||||||
return ControllerUtils.toResponseEntity(HttpStatus.OK, new HttpHeaders(), result);
|
//TODO manage HTTPHeader
|
||||||
|
return ControllerUtils.toResponseEntity(HttpStatus.OK, new HttpHeaders(), result);
|
||||||
|
} else {
|
||||||
|
return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1036,6 +1093,17 @@ public class RestResourceController implements InitializingBean {
|
|||||||
return uriComponentsBuilder.encode().build().toString();
|
return uriComponentsBuilder.encode().build().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to delete an entity by ID
|
||||||
|
* Note that the regular expression in the request mapping accept a number as identifier;
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @param apiCategory
|
||||||
|
* @param model
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
* @throws HttpRequestMethodNotSupportedException
|
||||||
|
*/
|
||||||
@RequestMapping(method = RequestMethod.DELETE, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_DIGIT)
|
@RequestMapping(method = RequestMethod.DELETE, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_DIGIT)
|
||||||
public ResponseEntity<RepresentationModel<?>> delete(HttpServletRequest request, @PathVariable String apiCategory,
|
public ResponseEntity<RepresentationModel<?>> delete(HttpServletRequest request, @PathVariable String apiCategory,
|
||||||
@PathVariable String model, @PathVariable Integer id)
|
@PathVariable String model, @PathVariable Integer id)
|
||||||
@@ -1050,6 +1118,13 @@ public class RestResourceController implements InitializingBean {
|
|||||||
return deleteInternal(apiCategory, model, uuid);
|
return deleteInternal(apiCategory, model, uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(method = RequestMethod.DELETE, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_STRING_VERSION_STRONG)
|
||||||
|
public ResponseEntity<RepresentationModel<?>> delete(HttpServletRequest request, @PathVariable String apiCategory,
|
||||||
|
@PathVariable String model, @PathVariable String id)
|
||||||
|
throws HttpRequestMethodNotSupportedException {
|
||||||
|
return deleteInternal(apiCategory, model, id);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal method to delete resource.
|
* Internal method to delete resource.
|
||||||
*
|
*
|
||||||
|
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.authorization.impl;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.authorization.AuthorizationFeature;
|
||||||
|
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
|
||||||
|
import org.dspace.app.rest.model.BaseObjectRest;
|
||||||
|
import org.dspace.app.rest.model.SiteRest;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The QA Event feature. It can be used to verify if Quality Assurance can be seen.
|
||||||
|
*
|
||||||
|
* Authorization is granted if the current user has READ permissions on the given bitstream.
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@AuthorizationFeatureDocumentation(name = QAAuthorizationFeature.NAME,
|
||||||
|
description = "It can be used to verify if the user can manage Quality Assurance events")
|
||||||
|
public class QAAuthorizationFeature implements AuthorizationFeature {
|
||||||
|
public final static String NAME = "canSeeQA";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ConfigurationService configurationService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
|
||||||
|
return configurationService.getBooleanProperty("qaevents.enabled", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getSupportedTypes() {
|
||||||
|
return new String[]{
|
||||||
|
SiteRest.CATEGORY + "." + SiteRest.NAME
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,115 @@
|
|||||||
|
/**
|
||||||
|
* 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.text.DecimalFormat;
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||||
|
import org.dspace.app.rest.model.OpenaireQAEventMessageRest;
|
||||||
|
import org.dspace.app.rest.model.QAEventMessageRest;
|
||||||
|
import org.dspace.app.rest.model.QAEventRest;
|
||||||
|
import org.dspace.app.rest.projection.Projection;
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.dspace.qaevent.service.dto.OpenaireMessageDTO;
|
||||||
|
import org.dspace.qaevent.service.dto.QAMessageDTO;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link DSpaceConverter} that converts {@link QAEvent} to
|
||||||
|
* {@link QAEventRest}.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class QAEventConverter implements DSpaceConverter<QAEvent, QAEventRest> {
|
||||||
|
|
||||||
|
private static final String OPENAIRE_PID_HREF_PREFIX_PROPERTY = "qaevents.openaire.pid-href-prefix.";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ConfigurationService configurationService;
|
||||||
|
|
||||||
|
private ObjectMapper jsonMapper;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void setup() {
|
||||||
|
jsonMapper = new JsonMapper();
|
||||||
|
jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QAEventRest convert(QAEvent modelObject, Projection projection) {
|
||||||
|
QAEventRest rest = new QAEventRest();
|
||||||
|
rest.setId(modelObject.getEventId());
|
||||||
|
try {
|
||||||
|
rest.setMessage(convertMessage(jsonMapper.readValue(modelObject.getMessage(),
|
||||||
|
modelObject.getMessageDtoClass())));
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
rest.setOriginalId(modelObject.getOriginalId());
|
||||||
|
rest.setProjection(projection);
|
||||||
|
rest.setTitle(modelObject.getTitle());
|
||||||
|
rest.setTopic(modelObject.getTopic());
|
||||||
|
rest.setEventDate(modelObject.getLastUpdate());
|
||||||
|
rest.setTrust(new DecimalFormat("0.000").format(modelObject.getTrust()));
|
||||||
|
// right now only the pending status can be found in persisted qa events
|
||||||
|
rest.setStatus(modelObject.getStatus());
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private QAEventMessageRest convertMessage(QAMessageDTO dto) {
|
||||||
|
if (dto instanceof OpenaireMessageDTO) {
|
||||||
|
return convertOpenaireMessage(dto);
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unknown message type: " + dto.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
private QAEventMessageRest convertOpenaireMessage(QAMessageDTO dto) {
|
||||||
|
OpenaireMessageDTO openaireDto = (OpenaireMessageDTO) dto;
|
||||||
|
OpenaireQAEventMessageRest message = new OpenaireQAEventMessageRest();
|
||||||
|
message.setAbstractValue(openaireDto.getAbstracts());
|
||||||
|
message.setOpenaireId(openaireDto.getOpenaireId());
|
||||||
|
message.setAcronym(openaireDto.getAcronym());
|
||||||
|
message.setCode(openaireDto.getCode());
|
||||||
|
message.setFunder(openaireDto.getFunder());
|
||||||
|
message.setFundingProgram(openaireDto.getFundingProgram());
|
||||||
|
message.setJurisdiction(openaireDto.getJurisdiction());
|
||||||
|
message.setTitle(openaireDto.getTitle());
|
||||||
|
message.setType(openaireDto.getType());
|
||||||
|
message.setValue(openaireDto.getValue());
|
||||||
|
message.setPidHref(calculateOpenairePidHref(openaireDto.getType(), openaireDto.getValue()));
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String calculateOpenairePidHref(String type, String value) {
|
||||||
|
if (type == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String hrefType = type.toLowerCase();
|
||||||
|
if (!configurationService.hasProperty(OPENAIRE_PID_HREF_PREFIX_PROPERTY + hrefType)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String hrefPrefix = configurationService.getProperty(OPENAIRE_PID_HREF_PREFIX_PROPERTY + hrefType, "");
|
||||||
|
return hrefPrefix + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<QAEvent> getModelClass() {
|
||||||
|
return QAEvent.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.converter;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.model.QASourceRest;
|
||||||
|
import org.dspace.app.rest.projection.Projection;
|
||||||
|
import org.dspace.qaevent.QASource;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link DSpaceConverter} that converts {@link QASource} to
|
||||||
|
* {@link QASourceRest}.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class QASourceConverter implements DSpaceConverter<QASource, QASourceRest> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<QASource> getModelClass() {
|
||||||
|
return QASource.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QASourceRest convert(QASource modelObject, Projection projection) {
|
||||||
|
QASourceRest rest = new QASourceRest();
|
||||||
|
rest.setProjection(projection);
|
||||||
|
rest.setId(modelObject.getName());
|
||||||
|
rest.setLastEvent(modelObject.getLastEvent());
|
||||||
|
rest.setTotalEvents(modelObject.getTotalEvents());
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.converter;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.model.QATopicRest;
|
||||||
|
import org.dspace.app.rest.projection.Projection;
|
||||||
|
import org.dspace.qaevent.QATopic;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link DSpaceConverter} that converts {@link QATopic} to
|
||||||
|
* {@link QATopicRest}.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class QATopicConverter implements DSpaceConverter<QATopic, QATopicRest> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<QATopic> getModelClass() {
|
||||||
|
return QATopic.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QATopicRest convert(QATopic modelObject, Projection projection) {
|
||||||
|
QATopicRest rest = new QATopicRest();
|
||||||
|
rest.setProjection(projection);
|
||||||
|
rest.setId(modelObject.getKey().replace("/", "!"));
|
||||||
|
rest.setName(modelObject.getKey());
|
||||||
|
rest.setLastEvent(modelObject.getLastEvent());
|
||||||
|
rest.setTotalEvents(modelObject.getTotalEvents());
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,115 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.model;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link QAEventMessageRest} related to OPENAIRE events.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class OpenaireQAEventMessageRest implements QAEventMessageRest {
|
||||||
|
|
||||||
|
// pids
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
private String pidHref;
|
||||||
|
|
||||||
|
// abstract
|
||||||
|
@JsonProperty(value = "abstract")
|
||||||
|
private String abstractValue;
|
||||||
|
|
||||||
|
// project
|
||||||
|
private String openaireId;
|
||||||
|
|
||||||
|
private String acronym;
|
||||||
|
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
private String funder;
|
||||||
|
|
||||||
|
private String fundingProgram;
|
||||||
|
|
||||||
|
private String jurisdiction;
|
||||||
|
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
public String getAbstractValue() {
|
||||||
|
return abstractValue;
|
||||||
|
}
|
||||||
|
public void setAbstractValue(String abstractValue) {
|
||||||
|
this.abstractValue = abstractValue;
|
||||||
|
}
|
||||||
|
public String getOpenaireId() {
|
||||||
|
return openaireId;
|
||||||
|
}
|
||||||
|
public void setOpenaireId(String openaireId) {
|
||||||
|
this.openaireId = openaireId;
|
||||||
|
}
|
||||||
|
public String getAcronym() {
|
||||||
|
return acronym;
|
||||||
|
}
|
||||||
|
public void setAcronym(String acronym) {
|
||||||
|
this.acronym = acronym;
|
||||||
|
}
|
||||||
|
public String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
public void setCode(String code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
public String getFunder() {
|
||||||
|
return funder;
|
||||||
|
}
|
||||||
|
public void setFunder(String funder) {
|
||||||
|
this.funder = funder;
|
||||||
|
}
|
||||||
|
public String getFundingProgram() {
|
||||||
|
return fundingProgram;
|
||||||
|
}
|
||||||
|
public void setFundingProgram(String fundingProgram) {
|
||||||
|
this.fundingProgram = fundingProgram;
|
||||||
|
}
|
||||||
|
public String getJurisdiction() {
|
||||||
|
return jurisdiction;
|
||||||
|
}
|
||||||
|
public void setJurisdiction(String jurisdiction) {
|
||||||
|
this.jurisdiction = jurisdiction;
|
||||||
|
}
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPidHref() {
|
||||||
|
return pidHref;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPidHref(String pidHref) {
|
||||||
|
this.pidHref = pidHref;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for classes that model a message with the details of a QA event.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface QAEventMessageRest {
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,136 @@
|
|||||||
|
/**
|
||||||
|
* 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 org.dspace.app.rest.RestResourceController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* QA event Rest object.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@LinksRest(
|
||||||
|
links = {
|
||||||
|
@LinkRest(name = "topic", method = "getTopic"),
|
||||||
|
@LinkRest(name = "target", method = "getTarget"),
|
||||||
|
@LinkRest(name = "related", method = "getRelated")
|
||||||
|
})
|
||||||
|
public class QAEventRest extends BaseObjectRest<String> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -5001130073350654793L;
|
||||||
|
public static final String NAME = "qualityassuranceevent";
|
||||||
|
public static final String CATEGORY = RestAddressableModel.INTEGRATION;
|
||||||
|
|
||||||
|
public static final String TOPIC = "topic";
|
||||||
|
public static final String TARGET = "target";
|
||||||
|
public static final String RELATED = "related";
|
||||||
|
private String source;
|
||||||
|
private String originalId;
|
||||||
|
private String title;
|
||||||
|
private String topic;
|
||||||
|
private String trust;
|
||||||
|
private Date eventDate;
|
||||||
|
private QAEventMessageRest message;
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCategory() {
|
||||||
|
return CATEGORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class getController() {
|
||||||
|
return RestResourceController.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOriginalId() {
|
||||||
|
return originalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOriginalId(String originalId) {
|
||||||
|
this.originalId = originalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTopic() {
|
||||||
|
return topic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTopic(String topic) {
|
||||||
|
this.topic = topic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTrust() {
|
||||||
|
return trust;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTrust(String trust) {
|
||||||
|
this.trust = trust;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getEventDate() {
|
||||||
|
return eventDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEventDate(Date eventDate) {
|
||||||
|
this.eventDate = eventDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QAEventMessageRest getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(QAEventMessageRest message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(String status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the source
|
||||||
|
*/
|
||||||
|
public String getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param source the source to set
|
||||||
|
*/
|
||||||
|
public void setSource(String source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.model;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.RestResourceController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST Representation of a quality assurance broker source
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4Science)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QASourceRest extends BaseObjectRest<String> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -7455358581579629244L;
|
||||||
|
|
||||||
|
public static final String NAME = "qualityassurancesource";
|
||||||
|
public static final String CATEGORY = RestAddressableModel.INTEGRATION;
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
private Date lastEvent;
|
||||||
|
private long totalEvents;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCategory() {
|
||||||
|
return CATEGORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class getController() {
|
||||||
|
return RestResourceController.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLastEvent() {
|
||||||
|
return lastEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastEvent(Date lastEvent) {
|
||||||
|
this.lastEvent = lastEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTotalEvents() {
|
||||||
|
return totalEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalEvents(long totalEvents) {
|
||||||
|
this.totalEvents = totalEvents;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* 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 org.dspace.app.rest.RestResourceController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST Representation of a quality assurance broker topic
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QATopicRest extends BaseObjectRest<String> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -7455358581579629244L;
|
||||||
|
|
||||||
|
public static final String NAME = "qualityassurancetopic";
|
||||||
|
public static final String CATEGORY = RestAddressableModel.INTEGRATION;
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
private String name;
|
||||||
|
private Date lastEvent;
|
||||||
|
private long totalEvents;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCategory() {
|
||||||
|
return CATEGORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class getController() {
|
||||||
|
return RestResourceController.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLastEvent() {
|
||||||
|
return lastEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastEvent(Date lastEvent) {
|
||||||
|
this.lastEvent = lastEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTotalEvents() {
|
||||||
|
return totalEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalEvents(long totalEvents) {
|
||||||
|
this.totalEvents = totalEvents;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 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.QAEventRest;
|
||||||
|
import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource;
|
||||||
|
import org.dspace.app.rest.utils.Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* QA event Rest resource.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@RelNameDSpaceResource(QAEventRest.NAME)
|
||||||
|
public class QAEventResource extends DSpaceResource<QAEventRest> {
|
||||||
|
|
||||||
|
public QAEventResource(QAEventRest data, Utils utils) {
|
||||||
|
super(data, utils);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 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.QASourceRest;
|
||||||
|
import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource;
|
||||||
|
import org.dspace.app.rest.utils.Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* QA source Rest resource.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4Science)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@RelNameDSpaceResource(QASourceRest.NAME)
|
||||||
|
public class QASourceResource extends DSpaceResource<QASourceRest> {
|
||||||
|
|
||||||
|
public QASourceResource(QASourceRest data, Utils utils) {
|
||||||
|
super(data, utils);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 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.QATopicRest;
|
||||||
|
import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource;
|
||||||
|
import org.dspace.app.rest.utils.Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* QA topic Rest resource.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@RelNameDSpaceResource(QATopicRest.NAME)
|
||||||
|
public class QATopicResource extends DSpaceResource<QATopicRest> {
|
||||||
|
|
||||||
|
public QATopicResource(QATopicRest data, Utils utils) {
|
||||||
|
super(data, utils);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.repository;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.model.ItemRest;
|
||||||
|
import org.dspace.app.rest.model.QAEventRest;
|
||||||
|
import org.dspace.app.rest.projection.Projection;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.dspace.content.service.ItemService;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.qaevent.service.QAEventService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link repository for "related" subresource of a qa event.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component(QAEventRest.CATEGORY + "." + QAEventRest.NAME + "." + QAEventRest.RELATED)
|
||||||
|
public class QAEventRelatedLinkRepository extends AbstractDSpaceRestRepository implements LinkRestRepository {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private QAEventService qaEventService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ItemService itemService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the item related to the qa event with the given id. This is another
|
||||||
|
* item that should be linked to the target item as part of the correction
|
||||||
|
*
|
||||||
|
* @param request the http servlet request
|
||||||
|
* @param id the qa event id
|
||||||
|
* @param pageable the optional pageable
|
||||||
|
* @param projection the projection object
|
||||||
|
* @return the item rest representation of the secondary item related to qa event
|
||||||
|
*/
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
public ItemRest getRelated(@Nullable HttpServletRequest request, String id, @Nullable Pageable pageable,
|
||||||
|
Projection projection) {
|
||||||
|
Context context = obtainContext();
|
||||||
|
QAEvent qaEvent = qaEventService.findEventByEventId(id);
|
||||||
|
if (qaEvent == null) {
|
||||||
|
throw new ResourceNotFoundException("No qa event with ID: " + id);
|
||||||
|
}
|
||||||
|
if (qaEvent.getRelated() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
UUID itemUuid = UUID.fromString(qaEvent.getRelated());
|
||||||
|
Item item;
|
||||||
|
try {
|
||||||
|
item = itemService.find(context, itemUuid);
|
||||||
|
if (item == null) {
|
||||||
|
throw new ResourceNotFoundException("No related item found with id : " + id);
|
||||||
|
}
|
||||||
|
return converter.toRest(item, projection);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,134 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.repository;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.Parameter;
|
||||||
|
import org.dspace.app.rest.SearchRestMethod;
|
||||||
|
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
|
||||||
|
import org.dspace.app.rest.model.QAEventRest;
|
||||||
|
import org.dspace.app.rest.model.patch.Patch;
|
||||||
|
import org.dspace.app.rest.repository.patch.ResourcePatch;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.dspace.content.service.ItemService;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
|
import org.dspace.qaevent.dao.QAEventsDAO;
|
||||||
|
import org.dspace.qaevent.service.QAEventService;
|
||||||
|
import org.dspace.util.UUIDUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort.Direction;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rest repository that handle QA events.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component(QAEventRest.CATEGORY + "." + QAEventRest.NAME)
|
||||||
|
public class QAEventRestRepository extends DSpaceRestRepository<QAEventRest, String> {
|
||||||
|
|
||||||
|
final static String ORDER_FIELD = "trust";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private QAEventService qaEventService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private QAEventsDAO qaEventDao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ItemService itemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ResourcePatch<QAEvent> resourcePatch;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
public QAEventRest findOne(Context context, String id) {
|
||||||
|
QAEvent qaEvent = qaEventService.findEventByEventId(id);
|
||||||
|
if (qaEvent == null) {
|
||||||
|
// check if this request is part of a patch flow
|
||||||
|
qaEvent = (QAEvent) requestService.getCurrentRequest().getAttribute("patchedNotificationEvent");
|
||||||
|
if (qaEvent != null && qaEvent.getEventId().contentEquals(id)) {
|
||||||
|
return converter.toRest(qaEvent, utils.obtainProjection());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return converter.toRest(qaEvent, utils.obtainProjection());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SearchRestMethod(name = "findByTopic")
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
public Page<QAEventRest> findByTopic(Context context, @Parameter(value = "topic", required = true) String topic,
|
||||||
|
Pageable pageable) {
|
||||||
|
List<QAEvent> qaEvents = null;
|
||||||
|
long count = 0L;
|
||||||
|
boolean ascending = false;
|
||||||
|
if (pageable.getSort() != null && pageable.getSort().getOrderFor(ORDER_FIELD) != null) {
|
||||||
|
ascending = pageable.getSort().getOrderFor(ORDER_FIELD).getDirection() == Direction.ASC;
|
||||||
|
}
|
||||||
|
qaEvents = qaEventService.findEventsByTopicAndPage(topic,
|
||||||
|
pageable.getOffset(), pageable.getPageSize(), ORDER_FIELD, ascending);
|
||||||
|
count = qaEventService.countEventsByTopic(topic);
|
||||||
|
if (qaEvents == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return converter.toRestPage(qaEvents, pageable, count, utils.obtainProjection());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
protected void delete(Context context, String eventId) throws AuthorizeException {
|
||||||
|
Item item = findTargetItem(context, eventId);
|
||||||
|
EPerson eperson = context.getCurrentUser();
|
||||||
|
qaEventService.deleteEventByEventId(eventId);
|
||||||
|
qaEventDao.storeEvent(context, eventId, eperson, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<QAEventRest> findAll(Context context, Pageable pageable) {
|
||||||
|
throw new RepositoryMethodNotImplementedException(QAEventRest.NAME, "findAll");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
protected void patch(Context context, HttpServletRequest request, String apiCategory, String model,
|
||||||
|
String id, Patch patch) throws SQLException, AuthorizeException {
|
||||||
|
QAEvent qaEvent = qaEventService.findEventByEventId(id);
|
||||||
|
resourcePatch.patch(context, qaEvent, patch.getOperations());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Item findTargetItem(Context context, String eventId) {
|
||||||
|
QAEvent qaEvent = qaEventService.findEventByEventId(eventId);
|
||||||
|
if (qaEvent == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return itemService.find(context, UUIDUtils.fromString(qaEvent.getTarget()));
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<QAEventRest> getDomainClass() {
|
||||||
|
return QAEventRest.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,73 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.repository;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.model.ItemRest;
|
||||||
|
import org.dspace.app.rest.model.QAEventRest;
|
||||||
|
import org.dspace.app.rest.projection.Projection;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.dspace.content.service.ItemService;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.qaevent.service.QAEventService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link repository for "target" subresource of a qa event.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component(QAEventRest.CATEGORY + "." + QAEventRest.NAME + "." + QAEventRest.TARGET)
|
||||||
|
public class QAEventTargetLinkRepository extends AbstractDSpaceRestRepository implements LinkRestRepository {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private QAEventService qaEventService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ItemService itemService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the item target of the qa event with the given id.
|
||||||
|
*
|
||||||
|
* @param request the http servlet request
|
||||||
|
* @param id the qa event id
|
||||||
|
* @param pageable the optional pageable
|
||||||
|
* @param projection the projection object
|
||||||
|
* @return the item rest representation of the qa event target
|
||||||
|
*/
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
public ItemRest getTarget(@Nullable HttpServletRequest request, String id, @Nullable Pageable pageable,
|
||||||
|
Projection projection) {
|
||||||
|
Context context = obtainContext();
|
||||||
|
QAEvent qaEvent = qaEventService.findEventByEventId(id);
|
||||||
|
if (qaEvent == null) {
|
||||||
|
throw new ResourceNotFoundException("No qa event with ID: " + id);
|
||||||
|
}
|
||||||
|
UUID itemUuid = UUID.fromString(qaEvent.getTarget());
|
||||||
|
Item item;
|
||||||
|
try {
|
||||||
|
item = itemService.find(context, itemUuid);
|
||||||
|
if (item == null) {
|
||||||
|
throw new ResourceNotFoundException("No target item found with id : " + id);
|
||||||
|
}
|
||||||
|
return converter.toRest(item, projection);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* 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 javax.annotation.Nullable;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.model.QAEventRest;
|
||||||
|
import org.dspace.app.rest.model.QATopicRest;
|
||||||
|
import org.dspace.app.rest.projection.Projection;
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.qaevent.QATopic;
|
||||||
|
import org.dspace.qaevent.service.QAEventService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link repository for "topic" subresource of a qa event.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component(QAEventRest.CATEGORY + "." + QAEventRest.NAME + "." + QAEventRest.TOPIC)
|
||||||
|
public class QAEventTopicLinkRepository extends AbstractDSpaceRestRepository implements LinkRestRepository {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private QAEventService qaEventService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the topic of the qa event with the given id.
|
||||||
|
*
|
||||||
|
* @param request the http servlet request
|
||||||
|
* @param id the qa event id
|
||||||
|
* @param pageable the optional pageable
|
||||||
|
* @param projection the projection object
|
||||||
|
* @return the qa topic rest representation
|
||||||
|
*/
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
public QATopicRest getTopic(@Nullable HttpServletRequest request, String id, @Nullable Pageable pageable,
|
||||||
|
Projection projection) {
|
||||||
|
Context context = obtainContext();
|
||||||
|
QAEvent qaEvent = qaEventService.findEventByEventId(id);
|
||||||
|
if (qaEvent == null) {
|
||||||
|
throw new ResourceNotFoundException("No qa event with ID: " + id);
|
||||||
|
}
|
||||||
|
QATopic topic = qaEventService.findTopicByTopicId(qaEvent.getTopic().replace("/", "!"));
|
||||||
|
if (topic == null) {
|
||||||
|
throw new ResourceNotFoundException("No topic found with id : " + id);
|
||||||
|
}
|
||||||
|
return converter.toRest(topic, projection);
|
||||||
|
}
|
||||||
|
}
|
@@ -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.repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.model.QASourceRest;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.qaevent.QASource;
|
||||||
|
import org.dspace.qaevent.service.QAEventService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rest repository that handle QA sources.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component(QASourceRest.CATEGORY + "." + QASourceRest.NAME)
|
||||||
|
public class QASourceRestRepository extends DSpaceRestRepository<QASourceRest, String> {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private QAEventService qaEventService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
public QASourceRest findOne(Context context, String id) {
|
||||||
|
QASource qaSource = qaEventService.findSource(id);
|
||||||
|
if (qaSource == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return converter.toRest(qaSource, utils.obtainProjection());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
public Page<QASourceRest> findAll(Context context, Pageable pageable) {
|
||||||
|
List<QASource> qaSources = qaEventService.findAllSources(pageable.getOffset(), pageable.getPageSize());
|
||||||
|
long count = qaEventService.countSources();
|
||||||
|
return converter.toRestPage(qaSources, pageable, count, utils.obtainProjection());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<QASourceRest> getDomainClass() {
|
||||||
|
return QASourceRest.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.Parameter;
|
||||||
|
import org.dspace.app.rest.SearchRestMethod;
|
||||||
|
import org.dspace.app.rest.model.QATopicRest;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.qaevent.QATopic;
|
||||||
|
import org.dspace.qaevent.service.QAEventService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort.Direction;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rest repository that handle QA topics.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component(QATopicRest.CATEGORY + "." + QATopicRest.NAME)
|
||||||
|
public class QATopicRestRepository extends DSpaceRestRepository<QATopicRest, String> {
|
||||||
|
|
||||||
|
final static String ORDER_FIELD = "topic";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private QAEventService qaEventService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
public QATopicRest findOne(Context context, String id) {
|
||||||
|
QATopic topic = qaEventService.findTopicByTopicId(id);
|
||||||
|
if (topic == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return converter.toRest(topic, utils.obtainProjection());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
public Page<QATopicRest> findAll(Context context, Pageable pageable) {
|
||||||
|
boolean ascending = false;
|
||||||
|
if (pageable.getSort() != null && pageable.getSort().getOrderFor(ORDER_FIELD) != null) {
|
||||||
|
ascending = pageable.getSort()
|
||||||
|
.getOrderFor(ORDER_FIELD).getDirection() == Direction.ASC;
|
||||||
|
}
|
||||||
|
List<QATopic> topics = qaEventService.findAllTopics(pageable.getOffset(), pageable.getPageSize(),
|
||||||
|
ORDER_FIELD, ascending);
|
||||||
|
long count = qaEventService.countTopics();
|
||||||
|
if (topics == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return converter.toRestPage(topics, pageable, count, utils.obtainProjection());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SearchRestMethod(name = "bySource")
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
public Page<QATopicRest> findBySource(Context context,
|
||||||
|
@Parameter(value = "source", required = true) String source, Pageable pageable) {
|
||||||
|
boolean ascending = false;
|
||||||
|
if (pageable.getSort() != null && pageable.getSort().getOrderFor(ORDER_FIELD) != null) {
|
||||||
|
ascending = pageable.getSort().getOrderFor(ORDER_FIELD).getDirection() == Direction.ASC;
|
||||||
|
}
|
||||||
|
List<QATopic> topics = qaEventService.findAllTopicsBySource(source,
|
||||||
|
pageable.getOffset(), pageable.getPageSize(), ORDER_FIELD, ascending);
|
||||||
|
long count = qaEventService.countTopicsBySource(source);
|
||||||
|
if (topics == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return converter.toRestPage(topics, pageable, count, utils.obtainProjection());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<QATopicRest> getDomainClass() {
|
||||||
|
return QATopicRest.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.repository.patch.operation;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dspace.app.rest.model.patch.Operation;
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.qaevent.service.QAEventActionService;
|
||||||
|
import org.dspace.services.RequestService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace operation related to the {@link QAEvent} status.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class QAEventStatusReplaceOperation extends PatchOperation<QAEvent> {
|
||||||
|
@Autowired
|
||||||
|
private RequestService requestService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private QAEventActionService qaEventActionService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QAEvent perform(Context context, QAEvent qaevent, Operation operation) throws SQLException {
|
||||||
|
String value = (String) operation.getValue();
|
||||||
|
if (StringUtils.equalsIgnoreCase(value, QAEvent.ACCEPTED)) {
|
||||||
|
qaEventActionService.accept(context, qaevent);
|
||||||
|
} else if (StringUtils.equalsIgnoreCase(value, QAEvent.REJECTED)) {
|
||||||
|
qaEventActionService.reject(context, qaevent);
|
||||||
|
} else if (StringUtils.equalsIgnoreCase(value, QAEvent.DISCARDED)) {
|
||||||
|
qaEventActionService.discard(context, qaevent);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The received operation is not valid: " + operation.getPath() + " - " + value);
|
||||||
|
}
|
||||||
|
qaevent.setStatus(value.toUpperCase());
|
||||||
|
// HACK, we need to store the temporary object in the request so that a subsequent find would get it
|
||||||
|
requestService.getCurrentRequest().setAttribute("patchedNotificationEvent", qaevent);
|
||||||
|
return qaevent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(Object objectToMatch, Operation operation) {
|
||||||
|
return StringUtils.equals(operation.getOp(), "replace") && objectToMatch instanceof QAEvent && StringUtils
|
||||||
|
.containsAny(operation.getValue().toString().toLowerCase(), QAEvent.ACCEPTED, QAEvent.DISCARDED,
|
||||||
|
QAEvent.REJECTED);
|
||||||
|
}
|
||||||
|
}
|
@@ -33,7 +33,7 @@ public class RegexUtils {
|
|||||||
*/
|
*/
|
||||||
public static final String REGEX_REQUESTMAPPING_IDENTIFIER_AS_STRING_VERSION_STRONG = "/{id:^(?!^\\d+$)" +
|
public static final String REGEX_REQUESTMAPPING_IDENTIFIER_AS_STRING_VERSION_STRONG = "/{id:^(?!^\\d+$)" +
|
||||||
"(?!^[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}$)"
|
"(?!^[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}$)"
|
||||||
+ "[\\w+\\-\\.:]+$+}";
|
+ "[\\w+\\-\\.:!]+$+}";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Regular expression in the request mapping to accept number as identifier
|
* Regular expression in the request mapping to accept number as identifier
|
||||||
|
@@ -135,28 +135,28 @@
|
|||||||
<heading>fake.workflow.readonly</heading> <processing-class>org.dspace.submit.step.SampleStep</processing-class>
|
<heading>fake.workflow.readonly</heading> <processing-class>org.dspace.submit.step.SampleStep</processing-class>
|
||||||
<type>sample</type> <scope visibility="read-only">workflow</scope> </step-definition> -->
|
<type>sample</type> <scope visibility="read-only">workflow</scope> </step-definition> -->
|
||||||
|
|
||||||
<!-- OpenAIRE submission steps/forms -->
|
<!-- Openaire submission steps/forms -->
|
||||||
<step-definition id="openAIREProjectForm" mandatory="true">
|
<step-definition id="openaireProjectForm" mandatory="true">
|
||||||
<heading>submit.progressbar.describe.stepone</heading>
|
<heading>submit.progressbar.describe.stepone</heading>
|
||||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||||
<type>submission-form</type>
|
<type>submission-form</type>
|
||||||
</step-definition>
|
</step-definition>
|
||||||
<step-definition id="openAIREPersonForm" mandatory="true">
|
<step-definition id="openairePersonForm" mandatory="true">
|
||||||
<heading>submit.progressbar.describe.stepone</heading>
|
<heading>submit.progressbar.describe.stepone</heading>
|
||||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||||
<type>submission-form</type>
|
<type>submission-form</type>
|
||||||
</step-definition>
|
</step-definition>
|
||||||
<step-definition id="openAIREOrganizationForm" mandatory="true">
|
<step-definition id="openaireOrganizationForm" mandatory="true">
|
||||||
<heading>submit.progressbar.describe.stepone</heading>
|
<heading>submit.progressbar.describe.stepone</heading>
|
||||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||||
<type>submission-form</type>
|
<type>submission-form</type>
|
||||||
</step-definition>
|
</step-definition>
|
||||||
<step-definition id="openAIREPublicationPageoneForm" mandatory="true">
|
<step-definition id="openairePublicationPageoneForm" mandatory="true">
|
||||||
<heading>submit.progressbar.describe.stepone</heading>
|
<heading>submit.progressbar.describe.stepone</heading>
|
||||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||||
<type>submission-form</type>
|
<type>submission-form</type>
|
||||||
</step-definition>
|
</step-definition>
|
||||||
<step-definition id="openAIREPublicationPagetwoForm" mandatory="true">
|
<step-definition id="openairePublicationPagetwoForm" mandatory="true">
|
||||||
<heading>submit.progressbar.describe.stepone</heading>
|
<heading>submit.progressbar.describe.stepone</heading>
|
||||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||||
<type>submission-form</type>
|
<type>submission-form</type>
|
||||||
@@ -259,13 +259,13 @@
|
|||||||
<step id="license"/>
|
<step id="license"/>
|
||||||
</submission-process>
|
</submission-process>
|
||||||
|
|
||||||
<!-- OpenAIRE submission processes -->
|
<!-- Openaire submission processes -->
|
||||||
<submission-process name="openAIREPublicationSubmission">
|
<submission-process name="openairePublicationSubmission">
|
||||||
<step id="collection"/>
|
<step id="collection"/>
|
||||||
|
|
||||||
<!--Step will be to Describe the item. -->
|
<!--Step will be to Describe the item. -->
|
||||||
<step id="openAIREPublicationPageoneForm"/>
|
<step id="openairePublicationPageoneForm"/>
|
||||||
<step id="openAIREPublicationPagetwoForm"/>
|
<step id="openairePublicationPagetwoForm"/>
|
||||||
|
|
||||||
<!--Step will be to Upload the item -->
|
<!--Step will be to Upload the item -->
|
||||||
<!-- step id="upload-with-embargo"/-->
|
<!-- step id="upload-with-embargo"/-->
|
||||||
@@ -274,17 +274,17 @@
|
|||||||
<!--Step will be to Sign off on the License -->
|
<!--Step will be to Sign off on the License -->
|
||||||
<step id="license"/>
|
<step id="license"/>
|
||||||
</submission-process>
|
</submission-process>
|
||||||
<submission-process name="openAIREPersonSubmission">
|
<submission-process name="openairePersonSubmission">
|
||||||
<step id="collection"/>
|
<step id="collection"/>
|
||||||
<step id="openAIREPersonForm"/>
|
<step id="openairePersonForm"/>
|
||||||
</submission-process>
|
</submission-process>
|
||||||
<submission-process name="openAIREProjectSubmission">
|
<submission-process name="openaireProjectSubmission">
|
||||||
<step id="collection"/>
|
<step id="collection"/>
|
||||||
<step id="openAIREProjectForm"/>
|
<step id="openaireProjectForm"/>
|
||||||
</submission-process>
|
</submission-process>
|
||||||
<submission-process name="openAIREOrganizationSubmission">
|
<submission-process name="openaireOrganizationSubmission">
|
||||||
<step id="collection"/>
|
<step id="collection"/>
|
||||||
<step id="openAIREOrganizationForm"/>
|
<step id="openaireOrganizationForm"/>
|
||||||
</submission-process>
|
</submission-process>
|
||||||
</submission-definitions>
|
</submission-definitions>
|
||||||
|
|
||||||
|
@@ -4,8 +4,8 @@
|
|||||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
|
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
|
||||||
default-lazy-init="true">
|
default-lazy-init="true">
|
||||||
|
|
||||||
<bean id="openAIRERestConnector"
|
<bean id="openaireRestConnector"
|
||||||
class="org.dspace.external.OpenAIRERestConnector">
|
class="org.dspace.external.OpenaireRestConnector">
|
||||||
<constructor-arg
|
<constructor-arg
|
||||||
value="${openaire.api.url:https://api.openaire.eu}" />
|
value="${openaire.api.url:https://api.openaire.eu}" />
|
||||||
<property name="tokenEnabled"
|
<property name="tokenEnabled"
|
||||||
@@ -18,10 +18,10 @@
|
|||||||
value="${openaire.token.clientSecret}" />
|
value="${openaire.token.clientSecret}" />
|
||||||
</bean>
|
</bean>
|
||||||
<bean
|
<bean
|
||||||
class="org.dspace.external.provider.impl.MockOpenAIREFundingDataProvider"
|
class="org.dspace.external.provider.impl.MockOpenaireFundingDataProvider"
|
||||||
init-method="init">
|
init-method="init">
|
||||||
<property name="sourceIdentifier" value="openAIREFunding" />
|
<property name="sourceIdentifier" value="openaireFunding" />
|
||||||
<property name="connector" ref="openAIRERestConnector" />
|
<property name="connector" ref="openaireRestConnector" />
|
||||||
<property name="supportedEntityTypes">
|
<property name="supportedEntityTypes">
|
||||||
<list>
|
<list>
|
||||||
<value>Project</value>
|
<value>Project</value>
|
||||||
|
@@ -86,8 +86,8 @@
|
|||||||
|
|
||||||
<!-- search for an entity that can be a Person or an OrgUnit -->
|
<!-- search for an entity that can be a Person or an OrgUnit -->
|
||||||
<entry key="personOrOrgunit" value-ref="personOrOrgunit"/>
|
<entry key="personOrOrgunit" value-ref="personOrOrgunit"/>
|
||||||
<!-- OpenAIRE4 guidelines - search for an OrgUnit that have a specific dc.type=FundingOrganization -->
|
<!-- Openaire4 guidelines - search for an OrgUnit that have a specific dc.type=FundingOrganization -->
|
||||||
<entry key="openAIREFundingAgency" value-ref="openAIREFundingAgency"/>
|
<entry key="openaireFundingAgency" value-ref="openaireFundingAgency"/>
|
||||||
<entry key="eperson_claims" value-ref="eperson_claims"/>
|
<entry key="eperson_claims" value-ref="eperson_claims"/>
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1057,8 +1057,8 @@ public class DiscoveryVersioningIT extends AbstractControllerIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_discoveryXml_openAIREFundingAgency_expectLatestVersionsOnly() throws Exception {
|
public void test_discoveryXml_openaireFundingAgency_expectLatestVersionsOnly() throws Exception {
|
||||||
final String configuration = "openAIREFundingAgency";
|
final String configuration = "openaireFundingAgency";
|
||||||
|
|
||||||
|
|
||||||
Collection collection = createCollection("OrgUnit");
|
Collection collection = createCollection("OrgUnit");
|
||||||
|
@@ -51,7 +51,7 @@ public class ExternalSourcesRestControllerIT extends AbstractControllerIntegrati
|
|||||||
ExternalSourceMatcher.matchExternalSource(
|
ExternalSourceMatcher.matchExternalSource(
|
||||||
"pubmed", "pubmed", false),
|
"pubmed", "pubmed", false),
|
||||||
ExternalSourceMatcher.matchExternalSource(
|
ExternalSourceMatcher.matchExternalSource(
|
||||||
"openAIREFunding", "openAIREFunding", false)
|
"openaireFunding", "openaireFunding", false)
|
||||||
)))
|
)))
|
||||||
.andExpect(jsonPath("$.page.totalElements", Matchers.is(10)));
|
.andExpect(jsonPath("$.page.totalElements", Matchers.is(10)));
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,7 @@ import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
|||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class OpenAIREFundingExternalSourcesIT extends AbstractControllerIntegrationTest {
|
public class OpenaireFundingExternalSourcesIT extends AbstractControllerIntegrationTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test openaire funding external source
|
* Test openaire funding external source
|
||||||
@@ -27,10 +27,10 @@ public class OpenAIREFundingExternalSourcesIT extends AbstractControllerIntegrat
|
|||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void findOneOpenAIREFundingExternalSourceTest() throws Exception {
|
public void findOneOpenaireFundingExternalSourceTest() throws Exception {
|
||||||
getClient().perform(get("/api/integration/externalsources")).andExpect(status().isOk())
|
getClient().perform(get("/api/integration/externalsources")).andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$._embedded.externalsources", Matchers.hasItem(
|
.andExpect(jsonPath("$._embedded.externalsources", Matchers.hasItem(
|
||||||
ExternalSourceMatcher.matchExternalSource("openAIREFunding", "openAIREFunding", false))));
|
ExternalSourceMatcher.matchExternalSource("openaireFunding", "openaireFunding", false))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,9 +39,9 @@ public class OpenAIREFundingExternalSourcesIT extends AbstractControllerIntegrat
|
|||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void findOneOpenAIREFundingExternalSourceEntriesEmptyWithQueryTest() throws Exception {
|
public void findOneOpenaireFundingExternalSourceEntriesEmptyWithQueryTest() throws Exception {
|
||||||
|
|
||||||
getClient().perform(get("/api/integration/externalsources/openAIREFunding/entries").param("query", "empty"))
|
getClient().perform(get("/api/integration/externalsources/openaireFunding/entries").param("query", "empty"))
|
||||||
.andExpect(status().isOk()).andExpect(jsonPath("$.page.number", is(0)));
|
.andExpect(status().isOk()).andExpect(jsonPath("$.page.number", is(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,11 +52,11 @@ public class OpenAIREFundingExternalSourcesIT extends AbstractControllerIntegrat
|
|||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void findOneOpenAIREFundingExternalSourceEntriesWithQueryMultipleKeywordsTest() throws Exception {
|
public void findOneOpenaireFundingExternalSourceEntriesWithQueryMultipleKeywordsTest() throws Exception {
|
||||||
|
|
||||||
getClient()
|
getClient()
|
||||||
.perform(
|
.perform(
|
||||||
get("/api/integration/externalsources/openAIREFunding/entries").param("query", "empty+results"))
|
get("/api/integration/externalsources/openaireFunding/entries").param("query", "empty+results"))
|
||||||
.andExpect(status().isOk()).andExpect(jsonPath("$.page.number", is(0)));
|
.andExpect(status().isOk()).andExpect(jsonPath("$.page.number", is(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,14 +66,14 @@ public class OpenAIREFundingExternalSourcesIT extends AbstractControllerIntegrat
|
|||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void findOneOpenAIREFundingExternalSourceEntriesWithQueryTest() throws Exception {
|
public void findOneOpenaireFundingExternalSourceEntriesWithQueryTest() throws Exception {
|
||||||
getClient().perform(get("/api/integration/externalsources/openAIREFunding/entries").param("query", "mushroom"))
|
getClient().perform(get("/api/integration/externalsources/openaireFunding/entries").param("query", "mushroom"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$._embedded.externalSourceEntries",
|
.andExpect(jsonPath("$._embedded.externalSourceEntries",
|
||||||
Matchers.hasItem(ExternalSourceEntryMatcher.matchExternalSourceEntry(
|
Matchers.hasItem(ExternalSourceEntryMatcher.matchExternalSourceEntry(
|
||||||
"aW5mbzpldS1yZXBvL2dyYW50QWdyZWVtZW50L05XTy8rLzIzMDAxNDc3MjgvTkw=",
|
"aW5mbzpldS1yZXBvL2dyYW50QWdyZWVtZW50L05XTy8rLzIzMDAxNDc3MjgvTkw=",
|
||||||
"Master switches of initiation of mushroom formation",
|
"Master switches of initiation of mushroom formation",
|
||||||
"Master switches of initiation of mushroom formation", "openAIREFunding"))));
|
"Master switches of initiation of mushroom formation", "openaireFunding"))));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,19 +83,19 @@ public class OpenAIREFundingExternalSourcesIT extends AbstractControllerIntegrat
|
|||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void findOneOpenAIREFundingExternalSourceEntryValueTest() throws Exception {
|
public void findOneOpenaireFundingExternalSourceEntryValueTest() throws Exception {
|
||||||
|
|
||||||
// "info:eu-repo/grantAgreement/mock/mock/mock/mock" base64 encoded
|
// "info:eu-repo/grantAgreement/mock/mock/mock/mock" base64 encoded
|
||||||
String projectID = "aW5mbzpldS1yZXBvL2dyYW50QWdyZWVtZW50L0ZDVC81ODc2LVBQQ0RUSS8xMTAwNjIvUFQ=";
|
String projectID = "aW5mbzpldS1yZXBvL2dyYW50QWdyZWVtZW50L0ZDVC81ODc2LVBQQ0RUSS8xMTAwNjIvUFQ=";
|
||||||
String projectName = "Portuguese Wild Mushrooms: Chemical characterization and functional study"
|
String projectName = "Portuguese Wild Mushrooms: Chemical characterization and functional study"
|
||||||
+ " of antiproliferative and proapoptotic properties in cancer cell lines";
|
+ " of antiproliferative and proapoptotic properties in cancer cell lines";
|
||||||
|
|
||||||
getClient().perform(get("/api/integration/externalsources/openAIREFunding/entryValues/" + projectID))
|
getClient().perform(get("/api/integration/externalsources/openaireFunding/entryValues/" + projectID))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$",
|
.andExpect(jsonPath("$",
|
||||||
Matchers.allOf(hasJsonPath("$.id", is(projectID)), hasJsonPath("$.display", is(projectName)),
|
Matchers.allOf(hasJsonPath("$.id", is(projectID)), hasJsonPath("$.display", is(projectName)),
|
||||||
hasJsonPath("$.value", is(projectName)),
|
hasJsonPath("$.value", is(projectName)),
|
||||||
hasJsonPath("$.externalSource", is("openAIREFunding")),
|
hasJsonPath("$.externalSource", is("openaireFunding")),
|
||||||
hasJsonPath("$.type", is("externalSourceEntry")))));
|
hasJsonPath("$.type", is("externalSourceEntry")))));
|
||||||
|
|
||||||
}
|
}
|
@@ -0,0 +1,801 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest;
|
||||||
|
|
||||||
|
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
||||||
|
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasNoJsonPath;
|
||||||
|
import static org.dspace.app.rest.matcher.QAEventMatcher.matchQAEventEntry;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.empty;
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.Matchers.not;
|
||||||
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.matcher.ItemMatcher;
|
||||||
|
import org.dspace.app.rest.matcher.QAEventMatcher;
|
||||||
|
import org.dspace.app.rest.model.patch.Operation;
|
||||||
|
import org.dspace.app.rest.model.patch.ReplaceOperation;
|
||||||
|
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||||
|
import org.dspace.builder.CollectionBuilder;
|
||||||
|
import org.dspace.builder.CommunityBuilder;
|
||||||
|
import org.dspace.builder.EntityTypeBuilder;
|
||||||
|
import org.dspace.builder.ItemBuilder;
|
||||||
|
import org.dspace.builder.QAEventBuilder;
|
||||||
|
import org.dspace.builder.RelationshipTypeBuilder;
|
||||||
|
import org.dspace.content.Collection;
|
||||||
|
import org.dspace.content.EntityType;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.dspace.content.QAEventProcessed;
|
||||||
|
import org.dspace.qaevent.dao.QAEventsDAO;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for {@link QAEventRestRepository}.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private QAEventsDAO qaEventsDao;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findAllNotImplementedTest() throws Exception {
|
||||||
|
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(adminToken).perform(get("/api/integration/qualityassuranceevents"))
|
||||||
|
.andExpect(status().isMethodNotAllowed());
|
||||||
|
String epersonToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(epersonToken).perform(get("/api/integration/qualityassuranceevents"))
|
||||||
|
.andExpect(status().isMethodNotAllowed());
|
||||||
|
getClient().perform(get("/api/integration/qualityassuranceevents")).andExpect(status().isMethodNotAllowed());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findOneTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEvent event1 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||||
|
QAEvent event4 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
|
||||||
|
.withTopic("ENRICH/MISSING/ABSTRACT")
|
||||||
|
.withMessage("{\"abstracts[0]\": \"Descrizione delle caratteristiche...\"}").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassuranceevents/" + event1.getEventId()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventEntry(event1)));
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassuranceevents/" + event4.getEventId()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventEntry(event4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findOneWithProjectionTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEvent event1 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||||
|
QAEvent event5 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 5")
|
||||||
|
.withTopic("ENRICH/MISSING/PROJECT")
|
||||||
|
.withMessage(
|
||||||
|
"{\"projects[0].acronym\":\"PAThs\","
|
||||||
|
+ "\"projects[0].code\":\"687567\","
|
||||||
|
+ "\"projects[0].funder\":\"EC\","
|
||||||
|
+ "\"projects[0].fundingProgram\":\"H2020\","
|
||||||
|
+ "\"projects[0].jurisdiction\":\"EU\","
|
||||||
|
+ "\"projects[0].openaireId\":\"40|corda__h2020::6e32f5eb912688f2424c68b851483ea4\","
|
||||||
|
+ "\"projects[0].title\":\"Tracking Papyrus and Parchment Paths: "
|
||||||
|
+ "An Archaeological Atlas of Coptic Literature."
|
||||||
|
+ "\\nLiterary Texts in their Geographical Context: Production, Copying, Usage, "
|
||||||
|
+ "Dissemination and Storage\"}")
|
||||||
|
.build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(get("/api/integration/qualityassuranceevents/" + event1.getEventId()).param("projection", "full"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventFullEntry(event1)));
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(get("/api/integration/qualityassuranceevents/" + event5.getEventId()).param("projection", "full"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventFullEntry(event5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findOneUnauthorizedTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEvent event1 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
getClient().perform(get("/api/integration/qualityassuranceevents/" + event1.getEventId()))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findOneForbiddenTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEvent event1 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String authToken = getAuthToken(eperson.getEmail(), password);
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassuranceevents/" + event1.getEventId()))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findByTopicTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEvent event1 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||||
|
QAEvent event2 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
|
||||||
|
QAEvent event3 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 3")
|
||||||
|
.withTopic("ENRICH/MORE/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"10.2307/2144302\"}").build();
|
||||||
|
QAEvent event4 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
|
||||||
|
.withTopic("ENRICH/MISSING/ABSTRACT")
|
||||||
|
.withMessage("{\"abstracts[0]\": \"Descrizione delle caratteristiche...\"}").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(
|
||||||
|
get("/api/integration/qualityassuranceevents/search/findByTopic").param("topic", "ENRICH!MISSING!PID"))
|
||||||
|
.andExpect(status().isOk()).andExpect(jsonPath("$._embedded.qualityassuranceevents", Matchers.hasSize(2)))
|
||||||
|
.andExpect(jsonPath("$._embedded.qualityassuranceevents",
|
||||||
|
Matchers.containsInAnyOrder(QAEventMatcher.matchQAEventEntry(event1),
|
||||||
|
QAEventMatcher.matchQAEventEntry(event2))))
|
||||||
|
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(2)));
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(get("/api/integration/qualityassuranceevents/search/findByTopic").param("topic",
|
||||||
|
"ENRICH!MISSING!ABSTRACT"))
|
||||||
|
.andExpect(status().isOk()).andExpect(jsonPath("$._embedded.qualityassuranceevents", Matchers.hasSize(1)))
|
||||||
|
.andExpect(jsonPath("$._embedded.qualityassuranceevents",
|
||||||
|
Matchers.containsInAnyOrder(QAEventMatcher.matchQAEventEntry(event4))))
|
||||||
|
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(1)));
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(get("/api/integration/qualityassuranceevents/search/findByTopic").param("topic", "not-existing"))
|
||||||
|
.andExpect(status().isOk()).andExpect(jsonPath("$.page.size", is(20)))
|
||||||
|
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findByTopicPaginatedTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEvent event1 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||||
|
QAEvent event2 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
|
||||||
|
QAEvent event3 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 3")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144302\"}").build();
|
||||||
|
QAEvent event4 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"pmc\",\"pids[0].value\":\"2144303\"}").build();
|
||||||
|
QAEvent event5 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 5")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"2144304\"}").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(
|
||||||
|
get("/api/integration/qualityassuranceevents/search/findByTopic").param("topic", "ENRICH!MISSING!PID")
|
||||||
|
.param("size", "2"))
|
||||||
|
.andExpect(status().isOk()).andExpect(jsonPath("$._embedded.qualityassuranceevents", Matchers.hasSize(2)))
|
||||||
|
.andExpect(jsonPath("$._embedded.qualityassuranceevents",
|
||||||
|
Matchers.containsInAnyOrder(
|
||||||
|
QAEventMatcher.matchQAEventEntry(event1),
|
||||||
|
QAEventMatcher.matchQAEventEntry(event2))))
|
||||||
|
.andExpect(jsonPath("$._links.self.href",
|
||||||
|
Matchers.allOf(
|
||||||
|
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
|
||||||
|
Matchers.containsString("topic=ENRICH!MISSING!PID"),
|
||||||
|
Matchers.containsString("size=2"))))
|
||||||
|
.andExpect(jsonPath("$._links.next.href",
|
||||||
|
Matchers.allOf(
|
||||||
|
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
|
||||||
|
Matchers.containsString("topic=ENRICH!MISSING!PID"), Matchers.containsString("page=1"),
|
||||||
|
Matchers.containsString("size=2"))))
|
||||||
|
.andExpect(jsonPath("$._links.last.href",
|
||||||
|
Matchers.allOf(
|
||||||
|
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
|
||||||
|
Matchers.containsString("topic=ENRICH!MISSING!PID"), Matchers.containsString("page=2"),
|
||||||
|
Matchers.containsString("size=2"))))
|
||||||
|
.andExpect(jsonPath("$._links.first.href",
|
||||||
|
Matchers.allOf(
|
||||||
|
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
|
||||||
|
Matchers.containsString("topic=ENRICH!MISSING!PID"), Matchers.containsString("page=0"),
|
||||||
|
Matchers.containsString("size=2"))))
|
||||||
|
.andExpect(jsonPath("$._links.prev.href").doesNotExist())
|
||||||
|
.andExpect(jsonPath("$.page.size", is(2)))
|
||||||
|
.andExpect(jsonPath("$.page.totalPages", is(3)))
|
||||||
|
.andExpect(jsonPath("$.page.totalElements", is(5)));
|
||||||
|
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(
|
||||||
|
get("/api/integration/qualityassuranceevents/search/findByTopic").param("topic", "ENRICH!MISSING!PID")
|
||||||
|
.param("size", "2").param("page", "1"))
|
||||||
|
.andExpect(status().isOk()).andExpect(jsonPath("$._embedded.qualityassuranceevents", Matchers.hasSize(2)))
|
||||||
|
.andExpect(jsonPath("$._embedded.qualityassuranceevents",
|
||||||
|
Matchers.containsInAnyOrder(
|
||||||
|
QAEventMatcher.matchQAEventEntry(event3),
|
||||||
|
QAEventMatcher.matchQAEventEntry(event4))))
|
||||||
|
.andExpect(jsonPath("$._links.self.href",
|
||||||
|
Matchers.allOf(
|
||||||
|
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
|
||||||
|
Matchers.containsString("topic=ENRICH!MISSING!PID"), Matchers.containsString("page=1"),
|
||||||
|
Matchers.containsString("size=2"))))
|
||||||
|
.andExpect(jsonPath("$._links.next.href",
|
||||||
|
Matchers.allOf(
|
||||||
|
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
|
||||||
|
Matchers.containsString("topic=ENRICH!MISSING!PID"), Matchers.containsString("page=2"),
|
||||||
|
Matchers.containsString("size=2"))))
|
||||||
|
.andExpect(jsonPath("$._links.last.href",
|
||||||
|
Matchers.allOf(
|
||||||
|
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
|
||||||
|
Matchers.containsString("topic=ENRICH!MISSING!PID"), Matchers.containsString("page=2"),
|
||||||
|
Matchers.containsString("size=2"))))
|
||||||
|
.andExpect(jsonPath("$._links.first.href",
|
||||||
|
Matchers.allOf(
|
||||||
|
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
|
||||||
|
Matchers.containsString("topic=ENRICH!MISSING!PID"), Matchers.containsString("page=0"),
|
||||||
|
Matchers.containsString("size=2"))))
|
||||||
|
.andExpect(jsonPath("$._links.prev.href",
|
||||||
|
Matchers.allOf(
|
||||||
|
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
|
||||||
|
Matchers.containsString("topic=ENRICH!MISSING!PID"), Matchers.containsString("page=0"),
|
||||||
|
Matchers.containsString("size=2"))))
|
||||||
|
.andExpect(jsonPath("$.page.size", is(2)))
|
||||||
|
.andExpect(jsonPath("$.page.totalPages", is(3)))
|
||||||
|
.andExpect(jsonPath("$.page.totalElements", is(5)));
|
||||||
|
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(
|
||||||
|
get("/api/integration/qualityassuranceevents/search/findByTopic").param("topic", "ENRICH!MISSING!PID")
|
||||||
|
.param("size", "2").param("page", "2"))
|
||||||
|
.andExpect(status().isOk()).andExpect(jsonPath("$._embedded.qualityassuranceevents", Matchers.hasSize(1)))
|
||||||
|
.andExpect(jsonPath("$._embedded.qualityassuranceevents",
|
||||||
|
Matchers.containsInAnyOrder(
|
||||||
|
QAEventMatcher.matchQAEventEntry(event5))))
|
||||||
|
.andExpect(jsonPath("$._links.self.href",
|
||||||
|
Matchers.allOf(
|
||||||
|
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
|
||||||
|
Matchers.containsString("topic=ENRICH!MISSING!PID"), Matchers.containsString("page=2"),
|
||||||
|
Matchers.containsString("size=2"))))
|
||||||
|
.andExpect(jsonPath("$._links.next.href").doesNotExist())
|
||||||
|
.andExpect(jsonPath("$._links.last.href",
|
||||||
|
Matchers.allOf(
|
||||||
|
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
|
||||||
|
Matchers.containsString("topic=ENRICH!MISSING!PID"), Matchers.containsString("page=2"),
|
||||||
|
Matchers.containsString("size=2"))))
|
||||||
|
.andExpect(jsonPath("$._links.first.href",
|
||||||
|
Matchers.allOf(
|
||||||
|
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
|
||||||
|
Matchers.containsString("topic=ENRICH!MISSING!PID"), Matchers.containsString("page=0"),
|
||||||
|
Matchers.containsString("size=2"))))
|
||||||
|
.andExpect(jsonPath("$._links.prev.href",
|
||||||
|
Matchers.allOf(
|
||||||
|
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
|
||||||
|
Matchers.containsString("topic=ENRICH!MISSING!PID"), Matchers.containsString("page=1"),
|
||||||
|
Matchers.containsString("size=2"))))
|
||||||
|
.andExpect(jsonPath("$.page.size", is(2)))
|
||||||
|
.andExpect(jsonPath("$.page.totalPages", is(3)))
|
||||||
|
.andExpect(jsonPath("$.page.totalElements", is(5)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findByTopicUnauthorizedTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEvent event1 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||||
|
QAEvent event2 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
|
||||||
|
QAEvent event3 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 3")
|
||||||
|
.withTopic("ENRICH/MORE/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"10.2307/2144302\"}").build();
|
||||||
|
QAEvent event4 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
|
||||||
|
.withTopic("ENRICH/MISSING/ABSTRACT")
|
||||||
|
.withMessage("{\"abstracts[0]\": \"Descrizione delle caratteristiche...\"}").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
getClient()
|
||||||
|
.perform(
|
||||||
|
get("/api/integration/qualityassuranceevents/search/findByTopic").param("topic", "ENRICH!MISSING!PID"))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findByTopicForbiddenTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEvent event1 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||||
|
QAEvent event2 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
|
||||||
|
QAEvent event3 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 3")
|
||||||
|
.withTopic("ENRICH/MORE/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"10.2307/2144302\"}").build();
|
||||||
|
QAEvent event4 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
|
||||||
|
.withTopic("ENRICH/MISSING/ABSTRACT")
|
||||||
|
.withMessage("{\"abstracts[0]\": \"Descrizione delle caratteristiche...\"}").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String epersonToken = getAuthToken(eperson.getEmail(), password);
|
||||||
|
getClient(epersonToken)
|
||||||
|
.perform(
|
||||||
|
get("/api/integration/qualityassuranceevents/search/findByTopic").param("topic", "ENRICH!MISSING!PID"))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findByTopicBadRequestTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEvent event1 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||||
|
QAEvent event2 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
|
||||||
|
QAEvent event3 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 3")
|
||||||
|
.withTopic("ENRICH/MORE/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"10.2307/2144302\"}").build();
|
||||||
|
QAEvent event4 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
|
||||||
|
.withTopic("ENRICH/MISSING/ABSTRACT")
|
||||||
|
.withMessage("{\"abstracts[0]\": \"Descrizione delle caratteristiche...\"}").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(adminToken).perform(get("/api/integration/qualityassuranceevents/search/findByTopic"))
|
||||||
|
.andExpect(status().isBadRequest());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void recordDecisionTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
EntityType publication = EntityTypeBuilder.createEntityTypeBuilder(context, "Publication").build();
|
||||||
|
EntityType project = EntityTypeBuilder.createEntityTypeBuilder(context, "Project").build();
|
||||||
|
RelationshipTypeBuilder.createRelationshipTypeBuilder(context, publication, project, "isProjectOfPublication",
|
||||||
|
"isPublicationOfProject", 0, null, 0,
|
||||||
|
null).withCopyToRight(true).build();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
|
||||||
|
.withEntityType("Publication")
|
||||||
|
.withName("Collection 1").build();
|
||||||
|
Collection colFunding = CollectionBuilder.createCollection(context, parentCommunity)
|
||||||
|
.withName("Collection Fundings")
|
||||||
|
.withEntityType("Project").build();
|
||||||
|
Item funding = ItemBuilder.createItem(context, colFunding).withTitle("Tracking Papyrus and Parchment Paths")
|
||||||
|
.build();
|
||||||
|
QAEvent eventProjectBound = QAEventBuilder.createTarget(context, col1, "Science and Freedom with project")
|
||||||
|
.withTopic("ENRICH/MISSING/PROJECT")
|
||||||
|
.withMessage(
|
||||||
|
"{\"projects[0].acronym\":\"PAThs\","
|
||||||
|
+ "\"projects[0].code\":\"687567\","
|
||||||
|
+ "\"projects[0].funder\":\"EC\","
|
||||||
|
+ "\"projects[0].fundingProgram\":\"H2020\","
|
||||||
|
+ "\"projects[0].jurisdiction\":\"EU\","
|
||||||
|
+ "\"projects[0].openaireId\":\"40|corda__h2020::6e32f5eb912688f2424c68b851483ea4\","
|
||||||
|
+ "\"projects[0].title\":\"Tracking Papyrus and Parchment Paths: "
|
||||||
|
+ "An Archaeological Atlas of Coptic Literature."
|
||||||
|
+ "\\nLiterary Texts in their Geographical Context: Production, Copying, Usage, "
|
||||||
|
+ "Dissemination and Storage\"}")
|
||||||
|
.withRelatedItem(funding.getID().toString())
|
||||||
|
.build();
|
||||||
|
QAEvent eventProjectNoBound = QAEventBuilder
|
||||||
|
.createTarget(context, col1, "Science and Freedom with unrelated project")
|
||||||
|
.withTopic("ENRICH/MISSING/PROJECT")
|
||||||
|
.withMessage(
|
||||||
|
"{\"projects[0].acronym\":\"NEW\","
|
||||||
|
+ "\"projects[0].code\":\"123456\","
|
||||||
|
+ "\"projects[0].funder\":\"EC\","
|
||||||
|
+ "\"projects[0].fundingProgram\":\"H2020\","
|
||||||
|
+ "\"projects[0].jurisdiction\":\"EU\","
|
||||||
|
+ "\"projects[0].openaireId\":\"newProjectID\","
|
||||||
|
+ "\"projects[0].title\":\"A new project\"}")
|
||||||
|
.build();
|
||||||
|
QAEvent eventMissingPID1 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||||
|
QAEvent eventMissingPID2 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
|
||||||
|
QAEvent eventMissingUnknownPID = QAEventBuilder.createTarget(context, col1, "Science and Freedom URN PID")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage(
|
||||||
|
"{\"pids[0].type\":\"urn\",\"pids[0].value\":\"http://thesis2.sba.units.it/store/handle/item/12937\"}")
|
||||||
|
.build();
|
||||||
|
QAEvent eventMorePID = QAEventBuilder.createTarget(context, col1, "Science and Freedom 3")
|
||||||
|
.withTopic("ENRICH/MORE/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"2144302\"}").build();
|
||||||
|
QAEvent eventAbstract = QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
|
||||||
|
.withTopic("ENRICH/MISSING/ABSTRACT")
|
||||||
|
.withMessage("{\"abstracts[0]\": \"An abstract to add...\"}").build();
|
||||||
|
QAEvent eventAbstractToDiscard = QAEventBuilder.createTarget(context, col1, "Science and Freedom 7")
|
||||||
|
.withTopic("ENRICH/MISSING/ABSTRACT")
|
||||||
|
.withMessage("{\"abstracts[0]\": \"Abstract to discard...\"}").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
// prepare the different patches for our decisions
|
||||||
|
List<Operation> acceptOp = new ArrayList<Operation>();
|
||||||
|
acceptOp.add(new ReplaceOperation("/status", QAEvent.ACCEPTED));
|
||||||
|
List<Operation> acceptOpUppercase = new ArrayList<Operation>();
|
||||||
|
acceptOpUppercase.add(new ReplaceOperation("/status", QAEvent.ACCEPTED));
|
||||||
|
List<Operation> discardOp = new ArrayList<Operation>();
|
||||||
|
discardOp.add(new ReplaceOperation("/status", QAEvent.DISCARDED));
|
||||||
|
List<Operation> rejectOp = new ArrayList<Operation>();
|
||||||
|
rejectOp.add(new ReplaceOperation("/status", QAEvent.REJECTED));
|
||||||
|
String patchAccept = getPatchContent(acceptOp);
|
||||||
|
String patchAcceptUppercase = getPatchContent(acceptOpUppercase);
|
||||||
|
String patchDiscard = getPatchContent(discardOp);
|
||||||
|
String patchReject = getPatchContent(rejectOp);
|
||||||
|
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
// accept pid1, unknownPID, morePID, the two projects and abstract
|
||||||
|
eventMissingPID1.setStatus(QAEvent.ACCEPTED);
|
||||||
|
eventMorePID.setStatus(QAEvent.ACCEPTED);
|
||||||
|
eventMissingUnknownPID.setStatus(QAEvent.ACCEPTED);
|
||||||
|
eventMissingUnknownPID.setStatus(QAEvent.ACCEPTED);
|
||||||
|
eventProjectBound.setStatus(QAEvent.ACCEPTED);
|
||||||
|
eventProjectNoBound.setStatus(QAEvent.ACCEPTED);
|
||||||
|
eventAbstract.setStatus(QAEvent.ACCEPTED);
|
||||||
|
|
||||||
|
getClient(authToken).perform(patch("/api/integration/qualityassuranceevents/" + eventMissingPID1.getEventId())
|
||||||
|
.content(patchAccept)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventEntry(eventMissingPID1)));
|
||||||
|
getClient(authToken).perform(patch("/api/integration/qualityassuranceevents/" + eventMorePID.getEventId())
|
||||||
|
.content(patchAcceptUppercase)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventEntry(eventMorePID)));
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(patch("/api/integration/qualityassuranceevents/" + eventMissingUnknownPID.getEventId())
|
||||||
|
.content(patchAccept)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventEntry(eventMissingUnknownPID)));
|
||||||
|
getClient(authToken).perform(patch("/api/integration/qualityassuranceevents/" + eventProjectBound.getEventId())
|
||||||
|
.content(patchAccept)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventEntry(eventProjectBound)));
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(patch("/api/integration/qualityassuranceevents/" + eventProjectNoBound.getEventId())
|
||||||
|
.content(patchAccept)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventEntry(eventProjectNoBound)));
|
||||||
|
getClient(authToken).perform(patch("/api/integration/qualityassuranceevents/" + eventAbstract.getEventId())
|
||||||
|
.content(patchAccept)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventEntry(eventAbstract)));
|
||||||
|
// check if the item has been updated
|
||||||
|
getClient(authToken).perform(get("/api/core/items/" + eventMissingPID1.getTarget())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(
|
||||||
|
jsonPath("$",
|
||||||
|
hasJsonPath("$.metadata['dc.identifier.other'][0].value", is("10.2307/2144300"))));
|
||||||
|
getClient(authToken).perform(get("/api/core/items/" + eventMorePID.getTarget())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", hasJsonPath("$.metadata['dc.identifier.other'][0].value", is("2144302"))));
|
||||||
|
getClient(authToken).perform(get("/api/core/items/" + eventMissingUnknownPID.getTarget())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", hasJsonPath("$.metadata['dc.identifier.other'][0].value",
|
||||||
|
is("http://thesis2.sba.units.it/store/handle/item/12937"))));
|
||||||
|
getClient(authToken).perform(get("/api/core/items/" + eventProjectBound.getTarget())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$",
|
||||||
|
hasJsonPath("$.metadata['relation.isProjectOfPublication'][0].value",
|
||||||
|
is(funding.getID().toString()))));
|
||||||
|
getClient(authToken).perform(get("/api/core/items/" + eventProjectNoBound.getTarget())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$",
|
||||||
|
hasJsonPath("$.metadata['relation.isProjectOfPublication'][0].value",
|
||||||
|
is(not(empty())))));
|
||||||
|
getClient(authToken).perform(get("/api/core/items/" + eventAbstract.getTarget())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$",
|
||||||
|
hasJsonPath("$.metadata['dc.description.abstract'][0].value", is("An abstract to add..."))));
|
||||||
|
// reject pid2
|
||||||
|
eventMissingPID2.setStatus(QAEvent.REJECTED);
|
||||||
|
getClient(authToken).perform(patch("/api/integration/qualityassuranceevents/" + eventMissingPID2.getEventId())
|
||||||
|
.content(patchReject)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventEntry(eventMissingPID2)));
|
||||||
|
getClient(authToken).perform(get("/api/core/items/" + eventMissingPID2.getTarget())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$",
|
||||||
|
hasNoJsonPath("$.metadata['dc.identifier.other']")));
|
||||||
|
// discard abstractToDiscard
|
||||||
|
eventAbstractToDiscard.setStatus(QAEvent.DISCARDED);
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(patch("/api/integration/qualityassuranceevents/" + eventAbstractToDiscard.getEventId())
|
||||||
|
.content(patchDiscard)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventEntry(eventAbstractToDiscard)));
|
||||||
|
getClient(authToken).perform(get("/api/core/items/" + eventMissingPID2.getTarget())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$",
|
||||||
|
hasNoJsonPath("$.metadata['dc.description.abstract']")));
|
||||||
|
// no pending qa events should be longer available
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancetopics")).andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||||
|
// we should have stored the decision into the database as well
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setRelatedTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
Collection colFunding = CollectionBuilder.createCollection(context, parentCommunity)
|
||||||
|
.withName("Collection Fundings").build();
|
||||||
|
QAEvent event = QAEventBuilder.createTarget(context, col1, "Science and Freedom 5")
|
||||||
|
.withTopic("ENRICH/MISSING/PROJECT")
|
||||||
|
.withMessage(
|
||||||
|
"{\"projects[0].acronym\":\"PAThs\","
|
||||||
|
+ "\"projects[0].code\":\"687567\","
|
||||||
|
+ "\"projects[0].funder\":\"EC\","
|
||||||
|
+ "\"projects[0].fundingProgram\":\"H2020\","
|
||||||
|
+ "\"projects[0].jurisdiction\":\"EU\","
|
||||||
|
+ "\"projects[0].openaireId\":\"40|corda__h2020::6e32f5eb912688f2424c68b851483ea4\","
|
||||||
|
+ "\"projects[0].title\":\"Tracking Papyrus and Parchment Paths: "
|
||||||
|
+ "An Archaeological Atlas of Coptic Literature."
|
||||||
|
+ "\\nLiterary Texts in their Geographical Context: Production, Copying, Usage, "
|
||||||
|
+ "Dissemination and Storage\"}")
|
||||||
|
.build();
|
||||||
|
Item funding = ItemBuilder.createItem(context, colFunding).withTitle("Tracking Papyrus and Parchment Paths")
|
||||||
|
.build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(get("/api/integration/qualityassuranceevents/" + event.getEventId()).param("projection", "full"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventFullEntry(event)));
|
||||||
|
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(post("/api/integration/qualityassuranceevents/" + event.getEventId() + "/related").param("item",
|
||||||
|
funding.getID().toString()))
|
||||||
|
.andExpect(status().isCreated())
|
||||||
|
.andExpect(jsonPath("$", ItemMatcher.matchItemProperties(funding)));
|
||||||
|
// update our local event copy to reflect the association with the related item
|
||||||
|
event.setRelated(funding.getID().toString());
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(get("/api/integration/qualityassuranceevents/" + event.getEventId()).param("projection", "full"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventFullEntry(event)));
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(get("/api/integration/qualityassuranceevents/" + event.getEventId() + "/related"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", ItemMatcher.matchItemProperties(funding)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void unsetRelatedTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
Collection colFunding = CollectionBuilder.createCollection(context, parentCommunity)
|
||||||
|
.withName("Collection Fundings").build();
|
||||||
|
Item funding = ItemBuilder.createItem(context, colFunding).withTitle("Tracking Papyrus and Parchment Paths")
|
||||||
|
.build();
|
||||||
|
QAEvent event = QAEventBuilder.createTarget(context, col1, "Science and Freedom 5")
|
||||||
|
.withTopic("ENRICH/MISSING/PROJECT")
|
||||||
|
.withMessage(
|
||||||
|
"{\"projects[0].acronym\":\"PAThs\","
|
||||||
|
+ "\"projects[0].code\":\"687567\","
|
||||||
|
+ "\"projects[0].funder\":\"EC\","
|
||||||
|
+ "\"projects[0].fundingProgram\":\"H2020\","
|
||||||
|
+ "\"projects[0].jurisdiction\":\"EU\","
|
||||||
|
+ "\"projects[0].openaireId\":\"40|corda__h2020::6e32f5eb912688f2424c68b851483ea4\","
|
||||||
|
+ "\"projects[0].title\":\"Tracking Papyrus and Parchment Paths: "
|
||||||
|
+ "An Archaeological Atlas of Coptic Literature."
|
||||||
|
+ "\\nLiterary Texts in their Geographical Context: Production, Copying, Usage, "
|
||||||
|
+ "Dissemination and Storage\"}")
|
||||||
|
.withRelatedItem(funding.getID().toString())
|
||||||
|
.build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(get("/api/integration/qualityassuranceevents/" + event.getEventId()).param("projection", "full"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventFullEntry(event)));
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(delete("/api/integration/qualityassuranceevents/" + event.getEventId() + "/related"))
|
||||||
|
.andExpect(status().isNoContent());
|
||||||
|
|
||||||
|
// update our local event copy to reflect the association with the related item
|
||||||
|
event.setRelated(null);
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(get("/api/integration/qualityassuranceevents/" + event.getEventId()).param("projection", "full"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventFullEntry(event)));
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(get("/api/integration/qualityassuranceevents/" + event.getEventId() + "/related"))
|
||||||
|
.andExpect(status().isNoContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setInvalidRelatedTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
Collection colFunding = CollectionBuilder.createCollection(context, parentCommunity)
|
||||||
|
.withName("Collection Fundings").build();
|
||||||
|
QAEvent event = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||||
|
Item funding = ItemBuilder.createItem(context, colFunding).withTitle("Tracking Papyrus and Parchment Paths")
|
||||||
|
.build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(get("/api/integration/qualityassuranceevents/" + event.getEventId()).param("projection", "full"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventFullEntry(event)));
|
||||||
|
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(post("/api/integration/qualityassuranceevents/" + event.getEventId() + "/related").param("item",
|
||||||
|
funding.getID().toString()))
|
||||||
|
.andExpect(status().isUnprocessableEntity());
|
||||||
|
// check that no related item has been added to our event
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(get("/api/integration/qualityassuranceevents/" + event.getEventId()).param("projection", "full"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventFullEntry(event)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteItemWithEventTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEvent event1 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||||
|
QAEvent event2 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(
|
||||||
|
get("/api/integration/qualityassuranceevents/search/findByTopic").param("topic", "ENRICH!MISSING!PID"))
|
||||||
|
.andExpect(status().isOk()).andExpect(jsonPath("$._embedded.qualityassuranceevents", Matchers.hasSize(2)))
|
||||||
|
.andExpect(jsonPath("$._embedded.qualityassuranceevents",
|
||||||
|
Matchers.containsInAnyOrder(QAEventMatcher.matchQAEventEntry(event1),
|
||||||
|
QAEventMatcher.matchQAEventEntry(event2))))
|
||||||
|
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(2)));
|
||||||
|
|
||||||
|
getClient(authToken).perform(delete("/api/core/items/" + event1.getTarget()))
|
||||||
|
.andExpect(status().is(204));
|
||||||
|
|
||||||
|
getClient(authToken).perform(get("/api/core/items/" + event1.getTarget()))
|
||||||
|
.andExpect(status().is(404));
|
||||||
|
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(
|
||||||
|
get("/api/integration/qualityassuranceevents/search/findByTopic").param("topic", "ENRICH!MISSING!PID"))
|
||||||
|
.andExpect(status().isOk()).andExpect(jsonPath("$._embedded.qualityassuranceevents", Matchers.hasSize(1)))
|
||||||
|
.andExpect(jsonPath("$._embedded.qualityassuranceevents",
|
||||||
|
Matchers.containsInAnyOrder(
|
||||||
|
QAEventMatcher.matchQAEventEntry(event2))))
|
||||||
|
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEventDeletion() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
|
||||||
|
.withName("Collection 1")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
QAEvent event = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
QAEvent event2 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassuranceevents/" + event.getEventId()))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$", matchQAEventEntry(event)));
|
||||||
|
|
||||||
|
List<QAEventProcessed> processedEvents = qaEventsDao.findAll(context);
|
||||||
|
assertThat(processedEvents, empty());
|
||||||
|
|
||||||
|
getClient(authToken).perform(delete("/api/integration/qualityassuranceevents/" + event.getEventId()))
|
||||||
|
.andExpect(status().isNoContent());
|
||||||
|
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassuranceevents/" + event.getEventId()))
|
||||||
|
.andExpect(status().isNotFound());
|
||||||
|
|
||||||
|
processedEvents = qaEventsDao.findAll(context);
|
||||||
|
assertThat(processedEvents, hasSize(1));
|
||||||
|
|
||||||
|
QAEventProcessed processedEvent = processedEvents.get(0);
|
||||||
|
assertThat(processedEvent.getEventId(), is(event.getEventId()));
|
||||||
|
assertThat(processedEvent.getItem(), notNullValue());
|
||||||
|
assertThat(processedEvent.getItem().getID().toString(), is(event.getTarget()));
|
||||||
|
assertThat(processedEvent.getEventTimestamp(), notNullValue());
|
||||||
|
assertThat(processedEvent.getEperson().getID(), is(admin.getID()));
|
||||||
|
|
||||||
|
getClient(authToken).perform(delete("/api/integration/qualityassuranceevents/" + event.getEventId()))
|
||||||
|
.andExpect(status().isInternalServerError());
|
||||||
|
|
||||||
|
authToken = getAuthToken(eperson.getEmail(), password);
|
||||||
|
getClient(authToken).perform(delete("/api/integration/qualityassuranceevents/" + event2.getEventId()))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,200 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest;
|
||||||
|
|
||||||
|
import static org.dspace.app.rest.matcher.QASourceMatcher.matchQASourceEntry;
|
||||||
|
import static org.hamcrest.Matchers.contains;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||||
|
import org.dspace.builder.CollectionBuilder;
|
||||||
|
import org.dspace.builder.CommunityBuilder;
|
||||||
|
import org.dspace.builder.ItemBuilder;
|
||||||
|
import org.dspace.builder.QAEventBuilder;
|
||||||
|
import org.dspace.content.Collection;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for {@link QASourceRestRepository}.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QASourceRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ConfigurationService configurationService;
|
||||||
|
|
||||||
|
private Item target;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withTitle("Community")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Collection collection = CollectionBuilder.createCollection(context, parentCommunity)
|
||||||
|
.withName("Collection")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
target = ItemBuilder.createItem(context, collection)
|
||||||
|
.withTitle("Item")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
configurationService.setProperty("qaevent.sources",
|
||||||
|
new String[] { "openaire", "test-source", "test-source-2" });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindAll() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
createEvent("openaire", "TOPIC/OPENAIRE/1", "Title 1");
|
||||||
|
createEvent("openaire", "TOPIC/OPENAIRE/2", "Title 2");
|
||||||
|
createEvent("openaire", "TOPIC/OPENAIRE/2", "Title 3");
|
||||||
|
|
||||||
|
createEvent("test-source", "TOPIC/TEST/1", "Title 4");
|
||||||
|
createEvent("test-source", "TOPIC/TEST/1", "Title 5");
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancesources"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$._embedded.qualityassurancesources", contains(
|
||||||
|
matchQASourceEntry("openaire", 3),
|
||||||
|
matchQASourceEntry("test-source", 2),
|
||||||
|
matchQASourceEntry("test-source-2", 0))))
|
||||||
|
.andExpect(jsonPath("$.page.size", is(20)))
|
||||||
|
.andExpect(jsonPath("$.page.totalElements", is(3)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindAllForbidden() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
createEvent("openaire", "TOPIC/OPENAIRE/1", "Title 1");
|
||||||
|
createEvent("test-source", "TOPIC/TEST/1", "Title 4");
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
String token = getAuthToken(eperson.getEmail(), password);
|
||||||
|
getClient(token).perform(get("/api/integration/qualityassurancesources"))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindAllUnauthorized() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
createEvent("openaire", "TOPIC/OPENAIRE/1", "Title 1");
|
||||||
|
createEvent("test-source", "TOPIC/TEST/1", "Title 4");
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
getClient().perform(get("/api/integration/qualityassurancesources"))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindOne() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
createEvent("openaire", "TOPIC/OPENAIRE/1", "Title 1");
|
||||||
|
createEvent("openaire", "TOPIC/OPENAIRE/2", "Title 2");
|
||||||
|
createEvent("openaire", "TOPIC/OPENAIRE/2", "Title 3");
|
||||||
|
|
||||||
|
createEvent("test-source", "TOPIC/TEST/1", "Title 4");
|
||||||
|
createEvent("test-source", "TOPIC/TEST/1", "Title 5");
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancesources/openaire"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", matchQASourceEntry("openaire", 3)));
|
||||||
|
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancesources/test-source"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", matchQASourceEntry("test-source", 2)));
|
||||||
|
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancesources/test-source-2"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$", matchQASourceEntry("test-source-2", 0)));
|
||||||
|
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancesources/unknown-test-source"))
|
||||||
|
.andExpect(status().isNotFound());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindOneForbidden() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
createEvent("openaire", "TOPIC/OPENAIRE/1", "Title 1");
|
||||||
|
createEvent("test-source", "TOPIC/TEST/1", "Title 4");
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
String token = getAuthToken(eperson.getEmail(), password);
|
||||||
|
getClient(token).perform(get("/api/integration/qualityassurancesources/openaire"))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindOneUnauthorized() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
createEvent("openaire", "TOPIC/OPENAIRE/1", "Title 1");
|
||||||
|
createEvent("test-source", "TOPIC/TEST/1", "Title 4");
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
getClient().perform(get("/api/integration/qualityassurancesources/openaire"))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private QAEvent createEvent(String source, String topic, String title) {
|
||||||
|
return QAEventBuilder.createTarget(context, target)
|
||||||
|
.withSource(source)
|
||||||
|
.withTopic(topic)
|
||||||
|
.withTitle(title)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,277 @@
|
|||||||
|
/**
|
||||||
|
* 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.is;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.matcher.QATopicMatcher;
|
||||||
|
import org.dspace.app.rest.repository.QATopicRestRepository;
|
||||||
|
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||||
|
import org.dspace.builder.CollectionBuilder;
|
||||||
|
import org.dspace.builder.CommunityBuilder;
|
||||||
|
import org.dspace.builder.QAEventBuilder;
|
||||||
|
import org.dspace.content.Collection;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for {@link QATopicRestRepository}.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QATopicRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ConfigurationService configurationService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findAllTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom 3")
|
||||||
|
.withTopic("ENRICH/MORE/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"10.2307/2144302\"}").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
|
||||||
|
.withTopic("ENRICH/MISSING/ABSTRACT")
|
||||||
|
.withMessage(
|
||||||
|
"{\"abstracts[0]\": \"Descrizione delle caratteristiche...\"}")
|
||||||
|
.build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancetopics")).andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$._embedded.qualityassurancetopics",
|
||||||
|
Matchers.containsInAnyOrder(QATopicMatcher.matchQATopicEntry("ENRICH/MISSING/PID", 2),
|
||||||
|
QATopicMatcher.matchQATopicEntry("ENRICH/MISSING/ABSTRACT", 1),
|
||||||
|
QATopicMatcher.matchQATopicEntry("ENRICH/MORE/PID", 1))))
|
||||||
|
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(3)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findAllUnauthorizedTest() throws Exception {
|
||||||
|
getClient().perform(get("/api/integration/qualityassurancetopics")).andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findAllForbiddenTest() throws Exception {
|
||||||
|
String authToken = getAuthToken(eperson.getEmail(), password);
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancetopics")).andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findAllPaginationTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
//create collection
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom 3")
|
||||||
|
.withTopic("ENRICH/MORE/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"10.2307/2144302\"}").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
|
||||||
|
.withTopic("ENRICH/MISSING/ABSTRACT")
|
||||||
|
.withMessage(
|
||||||
|
"{\"abstracts[0]\": \"Descrizione delle caratteristiche...\"}")
|
||||||
|
.build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancetopics").param("size", "2"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$._embedded.qualityassurancetopics", Matchers.hasSize(2)))
|
||||||
|
.andExpect(jsonPath("$.page.size", is(2))).andExpect(jsonPath("$.page.totalElements", is(3)));
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(get("/api/integration/qualityassurancetopics")
|
||||||
|
.param("size", "2")
|
||||||
|
.param("page", "1"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$._embedded.qualityassurancetopics", Matchers.hasSize(1)))
|
||||||
|
.andExpect(jsonPath("$.page.size", is(2))).andExpect(jsonPath("$.page.totalElements", is(3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findOneTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom 3")
|
||||||
|
.withTopic("ENRICH/MORE/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"10.2307/2144302\"}").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
|
||||||
|
.withTopic("ENRICH/MISSING/ABSTRACT")
|
||||||
|
.withMessage(
|
||||||
|
"{\"abstracts[0]\": \"Descrizione delle caratteristiche...\"}")
|
||||||
|
.build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancetopics/ENRICH!MISSING!PID"))
|
||||||
|
.andExpect(jsonPath("$", QATopicMatcher.matchQATopicEntry("ENRICH/MISSING/PID", 2)));
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancetopics/ENRICH!MISSING!ABSTRACT"))
|
||||||
|
.andExpect(jsonPath("$", QATopicMatcher.matchQATopicEntry("ENRICH/MISSING/ABSTRACT", 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findOneUnauthorizedTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
getClient().perform(get("/api/integration/qualityassurancetopics/ENRICH!MISSING!PID"))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
getClient().perform(get("/api/integration/qualityassurancetopics/ENRICH!MISSING!ABSTRACT"))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findOneForbiddenTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String authToken = getAuthToken(eperson.getEmail(), password);
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancetopics/ENRICH!MISSING!PID"))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancetopics/ENRICH!MISSING!ABSTRACT"))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findBySourceTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
configurationService.setProperty("qaevent.sources",
|
||||||
|
new String[] { "openaire", "test-source", "test-source-2" });
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
|
||||||
|
.withTopic("ENRICH/MISSING/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom 3")
|
||||||
|
.withTopic("ENRICH/MORE/PID")
|
||||||
|
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"10.2307/2144302\"}").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
|
||||||
|
.withTopic("ENRICH/MISSING/ABSTRACT")
|
||||||
|
.withMessage(
|
||||||
|
"{\"abstracts[0]\": \"Descrizione delle caratteristiche...\"}")
|
||||||
|
.build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom 5")
|
||||||
|
.withTopic("TEST/TOPIC")
|
||||||
|
.withSource("test-source")
|
||||||
|
.build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom 6")
|
||||||
|
.withTopic("TEST/TOPIC")
|
||||||
|
.withSource("test-source")
|
||||||
|
.build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom 7")
|
||||||
|
.withTopic("TEST/TOPIC/2")
|
||||||
|
.withSource("test-source")
|
||||||
|
.build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String authToken = getAuthToken(admin.getEmail(), password);
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancetopics/search/bySource")
|
||||||
|
.param("source", "openaire"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$._embedded.qualityassurancetopics",
|
||||||
|
Matchers.containsInAnyOrder(QATopicMatcher.matchQATopicEntry("ENRICH/MISSING/PID", 2),
|
||||||
|
QATopicMatcher.matchQATopicEntry("ENRICH/MISSING/ABSTRACT", 1),
|
||||||
|
QATopicMatcher.matchQATopicEntry("ENRICH/MORE/PID", 1))))
|
||||||
|
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(3)));
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancetopics/search/bySource")
|
||||||
|
.param("source", "test-source"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$._embedded.qualityassurancetopics",
|
||||||
|
Matchers.containsInAnyOrder(QATopicMatcher.matchQATopicEntry("TEST/TOPIC/2", 1),
|
||||||
|
QATopicMatcher.matchQATopicEntry("TEST/TOPIC", 2))))
|
||||||
|
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(2)));
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancetopics/search/bySource")
|
||||||
|
.param("source", "test-source-2"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(contentType))
|
||||||
|
.andExpect(jsonPath("$._embedded.qualityassurancetopics").doesNotExist())
|
||||||
|
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findBySourceUnauthorizedTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
getClient().perform(get("/api/integration/qualityassurancetopics/search/bySource")
|
||||||
|
.param("source", "openaire"))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findBySourceForbiddenTest() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||||
|
.withName("Parent Community")
|
||||||
|
.build();
|
||||||
|
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||||
|
QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||||
|
.withTopic("ENRICH/MISSING/PID").build();
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
String authToken = getAuthToken(eperson.getEmail(), password);
|
||||||
|
getClient(authToken).perform(get("/api/integration/qualityassurancetopics/search/bySource")
|
||||||
|
.param("source", "openaire"))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.authorization;
|
||||||
|
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.authorization.impl.QAAuthorizationFeature;
|
||||||
|
import org.dspace.app.rest.converter.SiteConverter;
|
||||||
|
import org.dspace.app.rest.matcher.AuthorizationMatcher;
|
||||||
|
import org.dspace.app.rest.model.SiteRest;
|
||||||
|
import org.dspace.app.rest.projection.DefaultProjection;
|
||||||
|
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||||
|
import org.dspace.content.Site;
|
||||||
|
import org.dspace.content.service.SiteService;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test suite for the Quality Assurance Authorization feature
|
||||||
|
*
|
||||||
|
* @author Francesco Bacchelli (francesco.bacchelli at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QAAuthorizationFeatureIT extends AbstractControllerIntegrationTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthorizationFeatureService authorizationFeatureService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SiteService siteService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SiteConverter siteConverter;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ConfigurationService configurationService;
|
||||||
|
|
||||||
|
|
||||||
|
private AuthorizationFeature qaAuthorizationFeature;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
qaAuthorizationFeature = authorizationFeatureService.find(QAAuthorizationFeature.NAME);
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testQAAuthorizationSuccess() throws Exception {
|
||||||
|
configurationService.setProperty("qaevents.enabled", true);
|
||||||
|
Site site = siteService.findSite(context);
|
||||||
|
SiteRest siteRest = siteConverter.convert(site, DefaultProjection.DEFAULT);
|
||||||
|
String tokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||||
|
Authorization authAdminSite = new Authorization(admin, qaAuthorizationFeature, siteRest);
|
||||||
|
|
||||||
|
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + authAdminSite.getID()))
|
||||||
|
.andExpect(jsonPath("$", Matchers.is(
|
||||||
|
AuthorizationMatcher.matchAuthorization(authAdminSite))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testQAAuthorizationFail() throws Exception {
|
||||||
|
configurationService.setProperty("qaevents.enabled", false);
|
||||||
|
Site site = siteService.findSite(context);
|
||||||
|
SiteRest siteRest = siteConverter.convert(site, DefaultProjection.DEFAULT);
|
||||||
|
String tokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||||
|
Authorization authAdminSite = new Authorization(admin, qaAuthorizationFeature, siteRest);
|
||||||
|
|
||||||
|
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + authAdminSite.getID()))
|
||||||
|
.andExpect(status().isNotFound());
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* 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.emptyOrNullString;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dspace.content.QAEvent;
|
||||||
|
import org.dspace.qaevent.service.dto.OpenaireMessageDTO;
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
import org.hamcrest.core.IsAnything;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matcher related to {@link QAEventResource}.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QAEventMatcher {
|
||||||
|
|
||||||
|
private QAEventMatcher() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Matcher<? super Object> matchQAEventFullEntry(QAEvent event) {
|
||||||
|
return allOf(
|
||||||
|
matchQAEventEntry(event),
|
||||||
|
hasJsonPath("$._embedded.topic.name", is(event.getTopic())),
|
||||||
|
hasJsonPath("$._embedded.target.id", is(event.getTarget())),
|
||||||
|
event.getRelated() != null ?
|
||||||
|
hasJsonPath("$._embedded.related.id", is(event.getRelated())) :
|
||||||
|
hasJsonPath("$._embedded.related", is(emptyOrNullString()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Matcher<? super Object> matchQAEventEntry(QAEvent event) {
|
||||||
|
try {
|
||||||
|
ObjectMapper jsonMapper = new JsonMapper();
|
||||||
|
return allOf(hasJsonPath("$.id", is(event.getEventId())),
|
||||||
|
hasJsonPath("$.originalId", is(event.getOriginalId())),
|
||||||
|
hasJsonPath("$.title", is(event.getTitle())),
|
||||||
|
hasJsonPath("$.trust", is(new DecimalFormat("0.000").format(event.getTrust()))),
|
||||||
|
hasJsonPath("$.status", Matchers.equalToIgnoringCase(event.getStatus())),
|
||||||
|
hasJsonPath("$.message",
|
||||||
|
matchMessage(event.getTopic(), jsonMapper.readValue(event.getMessage(),
|
||||||
|
OpenaireMessageDTO.class))),
|
||||||
|
hasJsonPath("$._links.target.href", Matchers.endsWith(event.getEventId() + "/target")),
|
||||||
|
hasJsonPath("$._links.related.href", Matchers.endsWith(event.getEventId() + "/related")),
|
||||||
|
hasJsonPath("$._links.topic.href", Matchers.endsWith(event.getEventId() + "/topic")),
|
||||||
|
hasJsonPath("$.type", is("qualityassuranceevent")));
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Matcher<? super Object> matchMessage(String topic, OpenaireMessageDTO message) {
|
||||||
|
if (StringUtils.endsWith(topic, "/ABSTRACT")) {
|
||||||
|
return allOf(hasJsonPath("$.abstract", is(message.getAbstracts())));
|
||||||
|
} else if (StringUtils.endsWith(topic, "/PID")) {
|
||||||
|
return allOf(
|
||||||
|
hasJsonPath("$.value", is(message.getValue())),
|
||||||
|
hasJsonPath("$.type", is(message.getType())),
|
||||||
|
hasJsonPath("$.pidHref", is(calculateOpenairePidHref(message.getType(), message.getValue()))));
|
||||||
|
} else if (StringUtils.endsWith(topic, "/PROJECT")) {
|
||||||
|
return allOf(
|
||||||
|
hasJsonPath("$.openaireId", is(message.getOpenaireId())),
|
||||||
|
hasJsonPath("$.acronym", is(message.getAcronym())),
|
||||||
|
hasJsonPath("$.code", is(message.getCode())),
|
||||||
|
hasJsonPath("$.funder", is(message.getFunder())),
|
||||||
|
hasJsonPath("$.fundingProgram", is(message.getFundingProgram())),
|
||||||
|
hasJsonPath("$.jurisdiction", is(message.getJurisdiction())),
|
||||||
|
hasJsonPath("$.title", is(message.getTitle())));
|
||||||
|
}
|
||||||
|
return IsAnything.anything();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String calculateOpenairePidHref(String type, String value) {
|
||||||
|
if (type == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String hrefPrefix = null;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "arxiv":
|
||||||
|
hrefPrefix = "https://arxiv.org/abs/";
|
||||||
|
break;
|
||||||
|
case "handle":
|
||||||
|
hrefPrefix = "https://hdl.handle.net/";
|
||||||
|
break;
|
||||||
|
case "urn":
|
||||||
|
hrefPrefix = "";
|
||||||
|
break;
|
||||||
|
case "doi":
|
||||||
|
hrefPrefix = "https://doi.org/";
|
||||||
|
break;
|
||||||
|
case "pmc":
|
||||||
|
hrefPrefix = "https://www.ncbi.nlm.nih.gov/pmc/articles/";
|
||||||
|
break;
|
||||||
|
case "pmid":
|
||||||
|
hrefPrefix = "https://pubmed.ncbi.nlm.nih.gov/";
|
||||||
|
break;
|
||||||
|
case "ncid":
|
||||||
|
hrefPrefix = "https://ci.nii.ac.jp/ncid/";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hrefPrefix != null ? hrefPrefix + value : null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.matcher;
|
||||||
|
|
||||||
|
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
||||||
|
import static org.hamcrest.Matchers.allOf;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.model.hateoas.QASourceResource;
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matcher related to {@link QASourceResource}.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QASourceMatcher {
|
||||||
|
|
||||||
|
private QASourceMatcher() { }
|
||||||
|
|
||||||
|
public static Matcher<? super Object> matchQASourceEntry(String key, int totalEvents) {
|
||||||
|
return allOf(
|
||||||
|
hasJsonPath("$.type", is("qualityassurancesource")),
|
||||||
|
hasJsonPath("$.id", is(key)),
|
||||||
|
hasJsonPath("$.totalEvents", is(totalEvents))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Matcher<? super Object> matchQASourceEntry(String key) {
|
||||||
|
return allOf(
|
||||||
|
hasJsonPath("$.type", is("qualityassurancesource")),
|
||||||
|
hasJsonPath("$.id", is(key))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.matcher;
|
||||||
|
|
||||||
|
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
||||||
|
import static org.hamcrest.Matchers.allOf;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.model.hateoas.QATopicResource;
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matcher related to {@link QATopicResource}.
|
||||||
|
*
|
||||||
|
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class QATopicMatcher {
|
||||||
|
|
||||||
|
private QATopicMatcher() { }
|
||||||
|
|
||||||
|
public static Matcher<? super Object> matchQATopicEntry(String key, int totalEvents) {
|
||||||
|
return allOf(
|
||||||
|
hasJsonPath("$.type", is("qualityassurancetopic")),
|
||||||
|
hasJsonPath("$.name", is(key)),
|
||||||
|
hasJsonPath("$.id", is(key.replace("/", "!"))),
|
||||||
|
hasJsonPath("$.totalEvents", is(totalEvents))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Matcher<? super Object> matchQATopicEntry(String key) {
|
||||||
|
return allOf(
|
||||||
|
hasJsonPath("$.type", is("qualityassurancetopic")),
|
||||||
|
hasJsonPath("$.name", is(key)),
|
||||||
|
hasJsonPath("$.id", is(key.replace("/", "/")))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -14,7 +14,7 @@ import javax.xml.bind.JAXBException;
|
|||||||
|
|
||||||
import eu.openaire.jaxb.helper.OpenAIREHandler;
|
import eu.openaire.jaxb.helper.OpenAIREHandler;
|
||||||
import eu.openaire.jaxb.model.Response;
|
import eu.openaire.jaxb.model.Response;
|
||||||
import org.dspace.external.OpenAIRERestConnector;
|
import org.dspace.external.OpenaireRestConnector;
|
||||||
import org.mockito.AdditionalMatchers;
|
import org.mockito.AdditionalMatchers;
|
||||||
import org.mockito.ArgumentMatchers;
|
import org.mockito.ArgumentMatchers;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
@@ -22,14 +22,14 @@ import org.mockito.invocation.InvocationOnMock;
|
|||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mock the OpenAIRE external source using a mock rest connector so that query
|
* Mock the Openaire external source using a mock rest connector so that query
|
||||||
* will be resolved against static test files
|
* will be resolved against static test files
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class MockOpenAIREFundingDataProvider extends OpenAIREFundingDataProvider {
|
public class MockOpenaireFundingDataProvider extends OpenaireFundingDataProvider {
|
||||||
@Override
|
@Override
|
||||||
public void init() throws IOException {
|
public void init() throws IOException {
|
||||||
OpenAIRERestConnector restConnector = Mockito.mock(OpenAIRERestConnector.class);
|
OpenaireRestConnector restConnector = Mockito.mock(OpenaireRestConnector.class);
|
||||||
|
|
||||||
when(restConnector.searchProjectByKeywords(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt(),
|
when(restConnector.searchProjectByKeywords(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt(),
|
||||||
ArgumentMatchers.startsWith("mushroom"))).thenAnswer(new Answer<Response>() {
|
ArgumentMatchers.startsWith("mushroom"))).thenAnswer(new Answer<Response>() {
|
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Following OpenAIRE Guidelines 4 -->
|
<!-- Following Openaire Guidelines 4 -->
|
||||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||||
xmlns:doc="http://www.lyncode.com/xoai">
|
xmlns:doc="http://www.lyncode.com/xoai">
|
||||||
<xsl:output indent="yes" method="xml" omit-xml-declaration="yes"/>
|
<xsl:output indent="yes" method="xml" omit-xml-declaration="yes"/>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<!--
|
<!--
|
||||||
Formatting dc.date.issued
|
Formatting dc.date.issued
|
||||||
based on what OpenAIRE4 specifies for issued dates
|
based on what Openaire4 specifies for issued dates
|
||||||
https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/field_publicationdate.html
|
https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/field_publicationdate.html
|
||||||
-->
|
-->
|
||||||
<xsl:template
|
<xsl:template
|
||||||
|
@@ -47,7 +47,7 @@
|
|||||||
</Context>
|
</Context>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
OpenAIRE Guidelines 3.0:
|
Openaire Guidelines 3.0:
|
||||||
|
|
||||||
- https://guidelines.openaire.eu/
|
- https://guidelines.openaire.eu/
|
||||||
|
|
||||||
@@ -55,10 +55,10 @@
|
|||||||
|
|
||||||
- Predefined DSpace fields don't allow to set this up with a default.
|
- Predefined DSpace fields don't allow to set this up with a default.
|
||||||
-->
|
-->
|
||||||
<Context baseurl="openaire" name="OpenAIRE Context">
|
<Context baseurl="openaire" name="Openaire Context">
|
||||||
<!-- Date format, field prefixes, etc are ensured by the transformer -->
|
<!-- Date format, field prefixes, etc are ensured by the transformer -->
|
||||||
<Transformer ref="openaireTransformer"/>
|
<Transformer ref="openaireTransformer"/>
|
||||||
<!-- OpenAIRE filter -->
|
<!-- Openaire filter -->
|
||||||
<Filter ref="openAireFilter"/>
|
<Filter ref="openAireFilter"/>
|
||||||
<!-- Just an alias, in fact it returns all items within the driver context -->
|
<!-- Just an alias, in fact it returns all items within the driver context -->
|
||||||
<Set ref="openaireSet"/>
|
<Set ref="openaireSet"/>
|
||||||
@@ -66,12 +66,12 @@
|
|||||||
<Format ref="oaidc"/>
|
<Format ref="oaidc"/>
|
||||||
<Format ref="mets"/>
|
<Format ref="mets"/>
|
||||||
<Description>
|
<Description>
|
||||||
This contexts complies with OpenAIRE Guidelines for Literature Repositories v3.0.
|
This contexts complies with Openaire Guidelines for Literature Repositories v3.0.
|
||||||
</Description>
|
</Description>
|
||||||
</Context>
|
</Context>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
OpenAIRE Guidelines 4.0:
|
Openaire Guidelines 4.0:
|
||||||
|
|
||||||
- https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/
|
- https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/
|
||||||
|
|
||||||
@@ -79,15 +79,15 @@
|
|||||||
|
|
||||||
- Predefined DSpace fields don't allow to set this up with a default.
|
- Predefined DSpace fields don't allow to set this up with a default.
|
||||||
-->
|
-->
|
||||||
<Context baseurl="openaire4" name="OpenAIRE 4 Context">
|
<Context baseurl="openaire4" name="Openaire 4 Context">
|
||||||
<!-- Date format, field prefixes, etc are ensured by the transformer -->
|
<!-- Date format, field prefixes, etc are ensured by the transformer -->
|
||||||
<Transformer ref="openaire4Transformer"/>
|
<Transformer ref="openaire4Transformer"/>
|
||||||
<!-- OpenAIRE filter -->
|
<!-- Openaire filter -->
|
||||||
<Filter ref="openAIRE4Filter"/>
|
<Filter ref="openaire4Filter"/>
|
||||||
<!-- Metadata Formats -->
|
<!-- Metadata Formats -->
|
||||||
<Format ref="oaiopenaire"/>
|
<Format ref="oaiopenaire"/>
|
||||||
<Description>
|
<Description>
|
||||||
This contexts complies with OpenAIRE Guidelines for Literature Repositories v4.0.
|
This contexts complies with Openaire Guidelines for Literature Repositories v4.0.
|
||||||
</Description>
|
</Description>
|
||||||
</Context>
|
</Context>
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@
|
|||||||
<Namespace>http://irdb.nii.ac.jp/oai</Namespace>
|
<Namespace>http://irdb.nii.ac.jp/oai</Namespace>
|
||||||
<SchemaLocation>http://irdb.nii.ac.jp/oai/junii2-3-1.xsd</SchemaLocation>
|
<SchemaLocation>http://irdb.nii.ac.jp/oai/junii2-3-1.xsd</SchemaLocation>
|
||||||
</Format>
|
</Format>
|
||||||
<!-- Metadata format based on the OpenAIRE 4.0 guidelines
|
<!-- Metadata format based on the Openaire 4.0 guidelines
|
||||||
https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/use_of_oai_pmh.html
|
https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/use_of_oai_pmh.html
|
||||||
-->
|
-->
|
||||||
<Format id="oaiopenaire">
|
<Format id="oaiopenaire">
|
||||||
@@ -250,13 +250,13 @@
|
|||||||
</Definition>
|
</Definition>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
|
||||||
<!-- OpenAIRE filter for records returned by OAI-PMH.
|
<!-- Openaire filter for records returned by OAI-PMH.
|
||||||
By default, return an Item record:
|
By default, return an Item record:
|
||||||
* If a Title & Author field both exist
|
* If a Title & Author field both exist
|
||||||
* AND a valid DRIVER Document Type exists
|
* AND a valid DRIVER Document Type exists
|
||||||
* AND Item is either publicly accessible OR Withdrawn (for tombstones)
|
* AND Item is either publicly accessible OR Withdrawn (for tombstones)
|
||||||
* AND the OpenAIRE "dc.relation" is specified
|
* AND the Openaire "dc.relation" is specified
|
||||||
This filter is only used in the OpenAIRE context ([oai]/openaire).
|
This filter is only used in the Openaire context ([oai]/openaire).
|
||||||
-->
|
-->
|
||||||
<Filter id="openAireFilter">
|
<Filter id="openAireFilter">
|
||||||
<Definition>
|
<Definition>
|
||||||
@@ -319,7 +319,7 @@
|
|||||||
</Definition>
|
</Definition>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
|
||||||
<!-- OpenAIRE4 filter for records returned by OAI-PMH.
|
<!-- Openaire4 filter for records returned by OAI-PMH.
|
||||||
By default, return an Item record:
|
By default, return an Item record:
|
||||||
* If it is publicly accessible
|
* If it is publicly accessible
|
||||||
* * OR it has been withdrawn (in order to display a tombstone record).
|
* * OR it has been withdrawn (in order to display a tombstone record).
|
||||||
@@ -328,7 +328,7 @@
|
|||||||
* limiting the results only to Publications as expected
|
* limiting the results only to Publications as expected
|
||||||
This filter is used by the default context ([oai]/request).
|
This filter is used by the default context ([oai]/request).
|
||||||
-->
|
-->
|
||||||
<Filter id="openAIRE4Filter">
|
<Filter id="openaire4Filter">
|
||||||
<Definition>
|
<Definition>
|
||||||
<And>
|
<And>
|
||||||
<LeftCondition>
|
<LeftCondition>
|
||||||
@@ -457,7 +457,7 @@
|
|||||||
|
|
||||||
<!-- This condition determines if an Item has a "dc.rights" field
|
<!-- This condition determines if an Item has a "dc.rights" field
|
||||||
specifying "open access", which is required for DRIVER
|
specifying "open access", which is required for DRIVER
|
||||||
OR "openAccess", which is required by OpenAIRE. -->
|
OR "openAccess", which is required by Openaire. -->
|
||||||
<CustomCondition id="driverAccessCondition">
|
<CustomCondition id="driverAccessCondition">
|
||||||
<Class>org.dspace.xoai.filter.DSpaceAtLeastOneMetadataFilter</Class>
|
<Class>org.dspace.xoai.filter.DSpaceAtLeastOneMetadataFilter</Class>
|
||||||
<Configuration>
|
<Configuration>
|
||||||
@@ -483,7 +483,7 @@
|
|||||||
</CustomCondition>
|
</CustomCondition>
|
||||||
|
|
||||||
<!-- This condition determines if an Item has a "dc.relation" field
|
<!-- This condition determines if an Item has a "dc.relation" field
|
||||||
which specifies the openAIRE project ID. -->
|
which specifies the openaire project ID. -->
|
||||||
<CustomCondition id="openaireRelationCondition">
|
<CustomCondition id="openaireRelationCondition">
|
||||||
<Class>org.dspace.xoai.filter.DSpaceAtLeastOneMetadataFilter</Class>
|
<Class>org.dspace.xoai.filter.DSpaceAtLeastOneMetadataFilter</Class>
|
||||||
<Configuration>
|
<Configuration>
|
||||||
|
@@ -780,7 +780,7 @@ event.dispatcher.default.class = org.dspace.event.BasicDispatcher
|
|||||||
# Add rdf here, if you are using dspace-rdf to export your repository content as RDF.
|
# Add rdf here, if you are using dspace-rdf to export your repository content as RDF.
|
||||||
# Add iiif here, if you are using dspace-iiif.
|
# Add iiif here, if you are using dspace-iiif.
|
||||||
# Add orcidqueue here, if the integration with ORCID is configured and wish to enable the synchronization queue functionality
|
# Add orcidqueue here, if the integration with ORCID is configured and wish to enable the synchronization queue functionality
|
||||||
event.dispatcher.default.consumers = versioning, discovery, eperson, submissionconfig
|
event.dispatcher.default.consumers = versioning, discovery, eperson, submissionconfig, qaeventsdelete
|
||||||
|
|
||||||
# The noindex dispatcher will not create search or browse indexes (useful for batch item imports)
|
# The noindex dispatcher will not create search or browse indexes (useful for batch item imports)
|
||||||
event.dispatcher.noindex.class = org.dspace.event.BasicDispatcher
|
event.dispatcher.noindex.class = org.dspace.event.BasicDispatcher
|
||||||
@@ -806,6 +806,10 @@ event.consumer.rdf.filters = Community|Collection|Item|Bundle|Bitstream|Site+Add
|
|||||||
#event.consumer.test.class = org.dspace.event.TestConsumer
|
#event.consumer.test.class = org.dspace.event.TestConsumer
|
||||||
#event.consumer.test.filters = All+All
|
#event.consumer.test.filters = All+All
|
||||||
|
|
||||||
|
# qaevents consumer to delete events related to deleted items
|
||||||
|
event.consumer.qaeventsdelete.class = org.dspace.qaevent.QAEventsDeleteCascadeConsumer
|
||||||
|
event.consumer.qaeventsdelete.filters = Item+Delete
|
||||||
|
|
||||||
# consumer to maintain versions
|
# consumer to maintain versions
|
||||||
event.consumer.versioning.class = org.dspace.versioning.VersioningConsumer
|
event.consumer.versioning.class = org.dspace.versioning.VersioningConsumer
|
||||||
event.consumer.versioning.filters = Item+Install
|
event.consumer.versioning.filters = Item+Install
|
||||||
@@ -1646,6 +1650,7 @@ include = ${module_dir}/irus-statistics.cfg
|
|||||||
include = ${module_dir}/oai.cfg
|
include = ${module_dir}/oai.cfg
|
||||||
include = ${module_dir}/openaire-client.cfg
|
include = ${module_dir}/openaire-client.cfg
|
||||||
include = ${module_dir}/orcid.cfg
|
include = ${module_dir}/orcid.cfg
|
||||||
|
include = ${module_dir}/qaevents.cfg
|
||||||
include = ${module_dir}/rdf.cfg
|
include = ${module_dir}/rdf.cfg
|
||||||
include = ${module_dir}/rest.cfg
|
include = ${module_dir}/rest.cfg
|
||||||
include = ${module_dir}/iiif.cfg
|
include = ${module_dir}/iiif.cfg
|
||||||
|
@@ -62,6 +62,8 @@
|
|||||||
<mapping class="org.dspace.content.Site"/>
|
<mapping class="org.dspace.content.Site"/>
|
||||||
<mapping class="org.dspace.content.WorkspaceItem"/>
|
<mapping class="org.dspace.content.WorkspaceItem"/>
|
||||||
|
|
||||||
|
<mapping class="org.dspace.content.QAEventProcessed" />
|
||||||
|
|
||||||
<mapping class="org.dspace.eperson.EPerson"/>
|
<mapping class="org.dspace.eperson.EPerson"/>
|
||||||
<mapping class="org.dspace.eperson.Group"/>
|
<mapping class="org.dspace.eperson.Group"/>
|
||||||
<mapping class="org.dspace.eperson.Group2GroupCache"/>
|
<mapping class="org.dspace.eperson.Group2GroupCache"/>
|
||||||
|
@@ -185,28 +185,28 @@
|
|||||||
<type>extract</type>
|
<type>extract</type>
|
||||||
</step-definition>
|
</step-definition>
|
||||||
|
|
||||||
<!-- OpenAIRE submission steps/forms -->
|
<!-- Openaire submission steps/forms -->
|
||||||
<step-definition id="openAIREProjectForm" mandatory="true">
|
<step-definition id="openaireProjectForm" mandatory="true">
|
||||||
<heading>submit.progressbar.describe.stepone</heading>
|
<heading>submit.progressbar.describe.stepone</heading>
|
||||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||||
<type>submission-form</type>
|
<type>submission-form</type>
|
||||||
</step-definition>
|
</step-definition>
|
||||||
<step-definition id="openAIREPersonForm" mandatory="true">
|
<step-definition id="openairePersonForm" mandatory="true">
|
||||||
<heading>submit.progressbar.describe.stepone</heading>
|
<heading>submit.progressbar.describe.stepone</heading>
|
||||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||||
<type>submission-form</type>
|
<type>submission-form</type>
|
||||||
</step-definition>
|
</step-definition>
|
||||||
<step-definition id="openAIREOrganizationForm" mandatory="true">
|
<step-definition id="openaireOrganizationForm" mandatory="true">
|
||||||
<heading>submit.progressbar.describe.stepone</heading>
|
<heading>submit.progressbar.describe.stepone</heading>
|
||||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||||
<type>submission-form</type>
|
<type>submission-form</type>
|
||||||
</step-definition>
|
</step-definition>
|
||||||
<step-definition id="openAIREPublicationPageoneForm" mandatory="true">
|
<step-definition id="openairePublicationPageoneForm" mandatory="true">
|
||||||
<heading>submit.progressbar.describe.stepone</heading>
|
<heading>submit.progressbar.describe.stepone</heading>
|
||||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||||
<type>submission-form</type>
|
<type>submission-form</type>
|
||||||
</step-definition>
|
</step-definition>
|
||||||
<step-definition id="openAIREPublicationPagetwoForm" mandatory="true">
|
<step-definition id="openairePublicationPagetwoForm" mandatory="true">
|
||||||
<heading>submit.progressbar.describe.stepone</heading>
|
<heading>submit.progressbar.describe.stepone</heading>
|
||||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||||
<type>submission-form</type>
|
<type>submission-form</type>
|
||||||
@@ -361,13 +361,13 @@
|
|||||||
<step id="license"/>
|
<step id="license"/>
|
||||||
</submission-process>
|
</submission-process>
|
||||||
|
|
||||||
<!-- OpenAIRE submission processes -->
|
<!-- Openaire submission processes -->
|
||||||
<submission-process name="openAIREPublicationSubmission">
|
<submission-process name="openairePublicationSubmission">
|
||||||
<step id="collection"/>
|
<step id="collection"/>
|
||||||
|
|
||||||
<!--Step will be to Describe the item. -->
|
<!--Step will be to Describe the item. -->
|
||||||
<step id="openAIREPublicationPageoneForm"/>
|
<step id="openairePublicationPageoneForm"/>
|
||||||
<step id="openAIREPublicationPagetwoForm"/>
|
<step id="openairePublicationPagetwoForm"/>
|
||||||
|
|
||||||
<!--Step will be to Upload the item -->
|
<!--Step will be to Upload the item -->
|
||||||
<!-- step id="upload-with-embargo"/-->
|
<!-- step id="upload-with-embargo"/-->
|
||||||
@@ -376,17 +376,17 @@
|
|||||||
<!--Step will be to Sign off on the License -->
|
<!--Step will be to Sign off on the License -->
|
||||||
<step id="license"/>
|
<step id="license"/>
|
||||||
</submission-process>
|
</submission-process>
|
||||||
<submission-process name="openAIREPersonSubmission">
|
<submission-process name="openairePersonSubmission">
|
||||||
<step id="collection"/>
|
<step id="collection"/>
|
||||||
<step id="openAIREPersonForm"/>
|
<step id="openairePersonForm"/>
|
||||||
</submission-process>
|
</submission-process>
|
||||||
<submission-process name="openAIREProjectSubmission">
|
<submission-process name="openaireProjectSubmission">
|
||||||
<step id="collection"/>
|
<step id="collection"/>
|
||||||
<step id="openAIREProjectForm"/>
|
<step id="openaireProjectForm"/>
|
||||||
</submission-process>
|
</submission-process>
|
||||||
<submission-process name="openAIREOrganizationSubmission">
|
<submission-process name="openaireOrganizationSubmission">
|
||||||
<step id="collection"/>
|
<step id="collection"/>
|
||||||
<step id="openAIREOrganizationForm"/>
|
<step id="openaireOrganizationForm"/>
|
||||||
</submission-process>
|
</submission-process>
|
||||||
</submission-definitions>
|
</submission-definitions>
|
||||||
|
|
||||||
|
@@ -16,20 +16,20 @@
|
|||||||
# The accessToken it only has a validity of one hour
|
# The accessToken it only has a validity of one hour
|
||||||
# For more details about the token, please check: https://develop.openaire.eu/personalToken.html
|
# For more details about the token, please check: https://develop.openaire.eu/personalToken.html
|
||||||
#
|
#
|
||||||
# the current OpenAIRE Rest client implementation uses basic authentication
|
# the current Openaire Rest client implementation uses basic authentication
|
||||||
# Described here: https://develop.openaire.eu/basic.html
|
# Described here: https://develop.openaire.eu/basic.html
|
||||||
#
|
#
|
||||||
# ---- Token usage required definitions ----
|
# ---- Token usage required definitions ----
|
||||||
# you can override this settings in your local.cfg file - can be true/false
|
# you can override this settings in your local.cfg file - can be true/false
|
||||||
openaire.token.enabled = false
|
openaire.token.enabled = false
|
||||||
|
|
||||||
# URL of the OpenAIRE authentication and authorization service
|
# URL of the Openaire authentication and authorization service
|
||||||
openaire.token.url = https://aai.openaire.eu/oidc/token
|
openaire.token.url = https://aai.openaire.eu/oidc/token
|
||||||
|
|
||||||
# you will be required to register at OpenAIRE (https://services.openaire.eu/uoa-user-management/registeredServices)
|
# you will be required to register at Openaire (https://services.openaire.eu/uoa-user-management/registeredServices)
|
||||||
# and create your service in order to get the following data:
|
# and create your service in order to get the following data:
|
||||||
openaire.token.clientId = CLIENT_ID_HERE
|
openaire.token.clientId = CLIENT_ID_HERE
|
||||||
openaire.token.clientSecret = CLIENT_SECRET_HERE
|
openaire.token.clientSecret = CLIENT_SECRET_HERE
|
||||||
|
|
||||||
# URL of OpenAIRE Rest API
|
# URL of Openaire Rest API
|
||||||
openaire.api.url = https://api.openaire.eu
|
openaire.api.url = https://api.openaire.eu
|
34
dspace/config/modules/qaevents.cfg
Normal file
34
dspace/config/modules/qaevents.cfg
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#---------------------------------------------------------------#
|
||||||
|
#------- Quality Assurance Broker Events CONFIGURATIONS --------#
|
||||||
|
#---------------------------------------------------------------#
|
||||||
|
# Configuration properties used by data correction service #
|
||||||
|
#---------------------------------------------------------------#
|
||||||
|
# Quality Assurance enable property, false by default
|
||||||
|
qaevents.enabled = false
|
||||||
|
qaevents.solr.server = ${solr.server}/${solr.multicorePrefix}qaevent
|
||||||
|
# A POST to these url(s) will be done to notify oaire of decision taken for each qaevents
|
||||||
|
# qaevents.openaire.acknowledge-url = https://beta.api-broker.openaire.eu/feedback/events
|
||||||
|
|
||||||
|
# The list of the supported events incoming from openaire (see also dspace/config/spring/api/qaevents.xml)
|
||||||
|
# add missing abstract suggestion
|
||||||
|
qaevents.openaire.import.topic = ENRICH/MISSING/ABSTRACT
|
||||||
|
# add missing publication id suggestion
|
||||||
|
qaevents.openaire.import.topic = ENRICH/MISSING/PID
|
||||||
|
# add more publication id suggestion
|
||||||
|
qaevents.openaire.import.topic = ENRICH/MORE/PID
|
||||||
|
# add missing project suggestion
|
||||||
|
qaevents.openaire.import.topic = ENRICH/MISSING/PROJECT
|
||||||
|
# add more project suggestion
|
||||||
|
qaevents.openaire.import.topic = ENRICH/MORE/PROJECT
|
||||||
|
|
||||||
|
# The list of the supported pid href for the OPENAIRE events
|
||||||
|
qaevents.openaire.pid-href-prefix.arxiv = https://arxiv.org/abs/
|
||||||
|
qaevents.openaire.pid-href-prefix.handle = https://hdl.handle.net/
|
||||||
|
qaevents.openaire.pid-href-prefix.urn =
|
||||||
|
qaevents.openaire.pid-href-prefix.doi = https://doi.org/
|
||||||
|
qaevents.openaire.pid-href-prefix.pmc = https://www.ncbi.nlm.nih.gov/pmc/articles/
|
||||||
|
qaevents.openaire.pid-href-prefix.pmid = https://pubmed.ncbi.nlm.nih.gov/
|
||||||
|
qaevents.openaire.pid-href-prefix.ncid = https://ci.nii.ac.jp/ncid/
|
||||||
|
|
||||||
|
# The URI used by the OPENAIRE broker client to import QA events
|
||||||
|
qaevents.openaire.broker-url = http://api.openaire.eu/broker
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user