mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 01:54:22 +00:00
Merge remote-tracking branch 'origin/main' into coar-notify-7
This commit is contained in:
@@ -14,6 +14,7 @@ import java.util.Date;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import org.dspace.qaevent.service.dto.NotifyMessageDTO;
|
||||
import org.dspace.qaevent.service.dto.CorrectionTypeMessageDTO;
|
||||
import org.dspace.qaevent.service.dto.OpenaireMessageDTO;
|
||||
import org.dspace.qaevent.service.dto.QAMessageDTO;
|
||||
import org.dspace.util.RawJsonDeserializer;
|
||||
@@ -31,7 +32,8 @@ public class QAEvent {
|
||||
public static final String REJECTED = "rejected";
|
||||
public static final String DISCARDED = "discarded";
|
||||
|
||||
public static final String OPENAIRE_SOURCE = "openaire";
|
||||
public static final String OPENAIRE_SOURCE = "OpenAIRE";
|
||||
public static final String DSPACE_USERS_SOURCE = "DSpaceUsers";
|
||||
public static final String COAR_NOTIFY_SOURCE = "coar-notify";
|
||||
|
||||
private String source;
|
||||
@@ -65,8 +67,7 @@ public class QAEvent {
|
||||
|
||||
private String status = "PENDING";
|
||||
|
||||
public QAEvent() {
|
||||
}
|
||||
public QAEvent() {}
|
||||
|
||||
public QAEvent(String source, String originalId, String target, String title,
|
||||
String topic, double trust, String message, Date lastUpdate) {
|
||||
@@ -84,7 +85,6 @@ public class QAEvent {
|
||||
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getOriginalId() {
|
||||
@@ -209,10 +209,10 @@ public class QAEvent {
|
||||
switch (getSource()) {
|
||||
case OPENAIRE_SOURCE:
|
||||
result = OpenaireMessageDTO.class;
|
||||
break;
|
||||
break;
|
||||
case COAR_NOTIFY_SOURCE:
|
||||
result = NotifyMessageDTO.class;
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown event's source: " + getSource());
|
||||
}
|
||||
|
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.correctiontype;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.QAEvent;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.qaevent.service.dto.QAMessageDTO;
|
||||
|
||||
/**
|
||||
* Interface class that model the CorrectionType.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.com)
|
||||
*/
|
||||
public interface CorrectionType {
|
||||
|
||||
/**
|
||||
* Retrieves the unique identifier associated to the CorrectionType.
|
||||
*/
|
||||
public String getId();
|
||||
|
||||
/**
|
||||
* Retrieves the topic associated with the to the CorrectionType.
|
||||
*/
|
||||
public String getTopic();
|
||||
|
||||
/**
|
||||
* Checks whether the CorrectionType required related item.
|
||||
*/
|
||||
public boolean isRequiredRelatedItem();
|
||||
|
||||
/**
|
||||
* Checks whether target item is allowed for current CorrectionType
|
||||
*
|
||||
* @param context Current DSpace session
|
||||
* @param targetItem Target item
|
||||
* @throws AuthorizeException if authorize error
|
||||
* @throws SQLException if there's a database problem
|
||||
*/
|
||||
public boolean isAllowed(Context context, Item targetItem) throws AuthorizeException, SQLException;
|
||||
|
||||
/**
|
||||
* Checks whether target item and related item are allowed for current CorrectionType
|
||||
*
|
||||
* @param context Current DSpace session
|
||||
* @param targetItem Target item
|
||||
* @param relatedItem Related item
|
||||
* @throws AuthorizeException if authorize error
|
||||
* @throws SQLException if there's a database problem
|
||||
*/
|
||||
public boolean isAllowed(Context context, Item targetItem, Item relatedItem) throws AuthorizeException,SQLException;
|
||||
|
||||
/**
|
||||
* Creates a QAEvent for a specific target item.
|
||||
*
|
||||
* @param context Current DSpace session
|
||||
* @param targetItem Target item
|
||||
* @param reason Reason
|
||||
* @return QAEvent
|
||||
*/
|
||||
public QAEvent createCorrection(Context context, Item targetItem, QAMessageDTO reason);
|
||||
|
||||
/**
|
||||
* Creates a QAEvent for a target item and related item.
|
||||
* @param context Current DSpace session
|
||||
* @param targetItem Target item
|
||||
* @param relatedItem Related item
|
||||
* @param reason Reason
|
||||
* @return QAEvent
|
||||
*/
|
||||
public QAEvent createCorrection(Context context, Item targetItem, Item relatedItem, QAMessageDTO reason);
|
||||
|
||||
}
|
@@ -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.correctiontype;
|
||||
|
||||
import static org.dspace.content.QAEvent.DSPACE_USERS_SOURCE;
|
||||
import static org.dspace.correctiontype.WithdrawnCorrectionType.WITHDRAWAL_REINSTATE_GROUP;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.QAEvent;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.Group;
|
||||
import org.dspace.eperson.service.GroupService;
|
||||
import org.dspace.qaevent.service.QAEventService;
|
||||
import org.dspace.qaevent.service.dto.CorrectionTypeMessageDTO;
|
||||
import org.dspace.qaevent.service.dto.QAMessageDTO;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Implementation class for {@link CorrectionType}
|
||||
* that will reinstate target item if it's withdrawn.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
public class ReinstateCorrectionType implements CorrectionType, InitializingBean {
|
||||
|
||||
private String id;
|
||||
private String topic;
|
||||
private String creationForm;
|
||||
|
||||
@Autowired
|
||||
private GroupService groupService;
|
||||
@Autowired
|
||||
private QAEventService qaEventService;
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
||||
|
||||
@Override
|
||||
public boolean isAllowed(Context context, Item targetItem) throws SQLException {
|
||||
if (!targetItem.isWithdrawn()) {
|
||||
return false;
|
||||
}
|
||||
boolean isAdmin = authorizeService.isAdmin(context);
|
||||
if (!currentUserIsMemberOfwithdrawalReinstateGroup(context) && !isAdmin) {
|
||||
return false;
|
||||
}
|
||||
long tot = qaEventService.countSourcesByTarget(context, targetItem.getID());
|
||||
return tot == 0;
|
||||
}
|
||||
|
||||
private boolean currentUserIsMemberOfwithdrawalReinstateGroup(Context context) throws SQLException {
|
||||
String groupName = configurationService.getProperty(WITHDRAWAL_REINSTATE_GROUP);
|
||||
if (StringUtils.isBlank(groupName)) {
|
||||
return false;
|
||||
}
|
||||
Group withdrawalReinstateGroup = groupService.findByName(context, groupName);
|
||||
return withdrawalReinstateGroup != null && groupService.isMember(context, withdrawalReinstateGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowed(Context context, Item targetItem, Item relatedItem) throws AuthorizeException,
|
||||
SQLException {
|
||||
return isAllowed(context, targetItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QAEvent createCorrection(Context context, Item targetItem, QAMessageDTO reason) {
|
||||
ObjectNode reasonJson = createReasonJson(reason);
|
||||
QAEvent qaEvent = new QAEvent(DSPACE_USERS_SOURCE,
|
||||
context.getCurrentUser().getID().toString(),
|
||||
targetItem.getID().toString(),
|
||||
targetItem.getName(),
|
||||
this.getTopic(),
|
||||
1.0,
|
||||
reasonJson.toString(),
|
||||
new Date()
|
||||
);
|
||||
|
||||
qaEventService.store(context, qaEvent);
|
||||
return qaEvent;
|
||||
}
|
||||
|
||||
private ObjectNode createReasonJson(QAMessageDTO reason) {
|
||||
CorrectionTypeMessageDTO mesasge = (CorrectionTypeMessageDTO) reason;
|
||||
ObjectNode jsonNode = new ObjectMapper().createObjectNode();
|
||||
jsonNode.put("reason", mesasge.getReason());
|
||||
return jsonNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QAEvent createCorrection(Context context, Item targetItem, Item relatedItem, QAMessageDTO reason) {
|
||||
return this.createCorrection(context, targetItem, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRequiredRelatedItem() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {}
|
||||
|
||||
public void setCreationForm(String creationForm) {
|
||||
this.creationForm = creationForm;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* 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.correctiontype;
|
||||
|
||||
import static org.dspace.content.QAEvent.DSPACE_USERS_SOURCE;
|
||||
import static org.dspace.core.Constants.READ;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.QAEvent;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.Group;
|
||||
import org.dspace.eperson.service.GroupService;
|
||||
import org.dspace.qaevent.service.QAEventService;
|
||||
import org.dspace.qaevent.service.dto.CorrectionTypeMessageDTO;
|
||||
import org.dspace.qaevent.service.dto.QAMessageDTO;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Implementation class for {@link CorrectionType}
|
||||
* that will withdrawn target item if it archived and wasn't withdrawn alredy.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.com)
|
||||
*/
|
||||
public class WithdrawnCorrectionType implements CorrectionType, InitializingBean {
|
||||
|
||||
public static final String WITHDRAWAL_REINSTATE_GROUP = "qaevents.withdraw-reinstate.group";
|
||||
|
||||
private String id;
|
||||
private String topic;
|
||||
private String creationForm;
|
||||
|
||||
@Autowired
|
||||
private GroupService groupService;
|
||||
@Autowired
|
||||
private QAEventService qaEventService;
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isAllowed(Context context, Item targetItem) throws SQLException {
|
||||
if (targetItem.isWithdrawn() || !targetItem.isArchived()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
authorizeService.authorizeAction(context, targetItem, READ);
|
||||
} catch (AuthorizeException e) {
|
||||
return false;
|
||||
}
|
||||
boolean isAdmin = authorizeService.isAdmin(context);
|
||||
if (!currentUserIsMemberOfwithdrawalReinstateGroup(context) && !isAdmin) {
|
||||
return false;
|
||||
}
|
||||
long tot = qaEventService.countSourcesByTarget(context, targetItem.getID());
|
||||
return tot == 0;
|
||||
}
|
||||
|
||||
private boolean currentUserIsMemberOfwithdrawalReinstateGroup(Context context) throws SQLException {
|
||||
String groupName = configurationService.getProperty(WITHDRAWAL_REINSTATE_GROUP);
|
||||
if (StringUtils.isBlank(groupName)) {
|
||||
return false;
|
||||
}
|
||||
Group withdrawalReinstateGroup = groupService.findByName(context, groupName);
|
||||
return withdrawalReinstateGroup != null && groupService.isMember(context, withdrawalReinstateGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QAEvent createCorrection(Context context, Item targetItem, QAMessageDTO reason) {
|
||||
ObjectNode reasonJson = createReasonJson(reason);
|
||||
QAEvent qaEvent = new QAEvent(DSPACE_USERS_SOURCE,
|
||||
context.getCurrentUser().getID().toString(),
|
||||
targetItem.getID().toString(),
|
||||
targetItem.getName(),
|
||||
this.getTopic(),
|
||||
1.0,
|
||||
reasonJson.toString(),
|
||||
new Date()
|
||||
);
|
||||
|
||||
qaEventService.store(context, qaEvent);
|
||||
return qaEvent;
|
||||
}
|
||||
|
||||
private ObjectNode createReasonJson(QAMessageDTO reason) {
|
||||
CorrectionTypeMessageDTO mesasge = (CorrectionTypeMessageDTO) reason;
|
||||
ObjectNode jsonNode = new ObjectMapper().createObjectNode();
|
||||
jsonNode.put("reason", mesasge.getReason());
|
||||
return jsonNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowed(Context context, Item targetItem, Item relatedItem)
|
||||
throws AuthorizeException, SQLException {
|
||||
return isAllowed(context, targetItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QAEvent createCorrection(Context context, Item targetItem, Item relatedItem, QAMessageDTO reason) {
|
||||
return createCorrection(context, targetItem, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRequiredRelatedItem() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {}
|
||||
|
||||
public void setCreationForm(String creationForm) {
|
||||
this.creationForm = creationForm;
|
||||
}
|
||||
|
||||
}
|
@@ -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.correctiontype.service;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.correctiontype.CorrectionType;
|
||||
|
||||
/**
|
||||
* Service interface class for the CorrectionType object.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public interface CorrectionTypeService {
|
||||
|
||||
/**
|
||||
* Retrieves a CorrectionType object from the system based on a unique identifier.
|
||||
*
|
||||
* @param id The unique identifier of the CorrectionType object to be retrieved.
|
||||
* @return The CorrectionType object corresponding to the provided identifier,
|
||||
* or null if no object is found.
|
||||
*/
|
||||
public CorrectionType findOne(String id);
|
||||
|
||||
/**
|
||||
* Retrieves a list of all CorrectionType objects available in the system.
|
||||
*
|
||||
* @return Returns a List containing all CorrectionType objects in the system.
|
||||
*/
|
||||
public List<CorrectionType> findAll();
|
||||
|
||||
/**
|
||||
* Retrieves a list of CorrectionType objects related to the provided Item.
|
||||
*
|
||||
* @param context Current DSpace session.
|
||||
* @param item Target item
|
||||
* @throws AuthorizeException If authorize error
|
||||
* @throws SQLException If a database error occurs during the operation.
|
||||
*/
|
||||
public List<CorrectionType> findByItem(Context context, Item item) throws AuthorizeException, SQLException;
|
||||
|
||||
/**
|
||||
* Retrieves a CorrectionType object associated with a specific topic.
|
||||
*
|
||||
* @param topic The topic for which the CorrectionType object is to be retrieved.
|
||||
*/
|
||||
public CorrectionType findByTopic(String topic);
|
||||
|
||||
}
|
@@ -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.correctiontype.service.impl;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.correctiontype.CorrectionType;
|
||||
import org.dspace.correctiontype.service.CorrectionTypeService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Service implementation class for the CorrectionType object.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class CorrectionTypeServiceImpl implements CorrectionTypeService {
|
||||
|
||||
@Autowired
|
||||
private List<CorrectionType> correctionTypes;
|
||||
|
||||
@Override
|
||||
public CorrectionType findOne(String id) {
|
||||
return findAll().stream()
|
||||
.filter(correctionType -> correctionType.getId().equals(id))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CorrectionType> findAll() {
|
||||
return CollectionUtils.isNotEmpty(correctionTypes) ? correctionTypes : List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CorrectionType> findByItem(Context context, Item item) throws AuthorizeException, SQLException {
|
||||
List<CorrectionType> correctionTypes = new ArrayList<>();
|
||||
for (CorrectionType correctionType : findAll()) {
|
||||
if (correctionType.isAllowed(context, item)) {
|
||||
correctionTypes.add(correctionType);
|
||||
}
|
||||
}
|
||||
return correctionTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CorrectionType findByTopic(String topic) {
|
||||
return findAll().stream()
|
||||
.filter(correctionType -> correctionType.getTopic().equals(topic))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
}
|
@@ -11,9 +11,9 @@ package org.dspace.qaevent;
|
||||
* Constants for Quality Assurance configurations to be used into cfg and xml spring.
|
||||
*
|
||||
* @author Francesco Bacchelli (francesco.bacchelli at 4science.it)
|
||||
*
|
||||
*/
|
||||
public class QANotifyPatterns {
|
||||
|
||||
public static final String TOPIC_ENRICH_MORE_PROJECT = "ENRICH/MORE/PROJECT";
|
||||
public static final String TOPIC_ENRICH_MISSING_PROJECT = "ENRICH/MISSING/PROJECT";
|
||||
public static final String TOPIC_ENRICH_MISSING_ABSTRACT = "ENRICH/MISSING/ABSTRACT";
|
||||
@@ -21,10 +21,9 @@ public class QANotifyPatterns {
|
||||
public static final String TOPIC_ENRICH_MORE_ENDORSEMENT = "ENRICH/MORE/ENDORSEMENT";
|
||||
public static final String TOPIC_ENRICH_MORE_PID = "ENRICH/MORE/PID";
|
||||
public static final String TOPIC_ENRICH_MISSING_PID = "ENRICH/MISSING/PID";
|
||||
public static final String TOPIC_ENRICH_MORE_LINK = "ENRICH/MORE/LINK";
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
private QANotifyPatterns() { }
|
||||
}
|
||||
}
|
@@ -17,16 +17,16 @@ import java.util.UUID;
|
||||
*
|
||||
*/
|
||||
public class QASource {
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* if the QASource stats (see next attributes) are related to a specific target
|
||||
* The focus attributes specify if the QASource object is describing the status of a specific
|
||||
* quality assurance source for the whole repository (focus = null) or for a specific
|
||||
* DSpaceObject (focus = uuid of the DSpaceObject). This would mostly affect the totalEvents attribute below.
|
||||
*/
|
||||
private UUID focus;
|
||||
|
||||
private long totalEvents;
|
||||
|
||||
private String name;
|
||||
private Date lastEvent;
|
||||
private long totalEvents;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
@@ -36,14 +36,6 @@ public class QASource {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public UUID getFocus() {
|
||||
return focus;
|
||||
}
|
||||
|
||||
public void setFocus(UUID focus) {
|
||||
this.focus = focus;
|
||||
}
|
||||
|
||||
public long getTotalEvents() {
|
||||
return totalEvents;
|
||||
}
|
||||
@@ -60,6 +52,14 @@ public class QASource {
|
||||
this.lastEvent = lastEvent;
|
||||
}
|
||||
|
||||
public UUID getFocus() {
|
||||
return focus;
|
||||
}
|
||||
|
||||
public void setFocus(UUID focus) {
|
||||
this.focus = focus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + focus + totalEvents;
|
||||
|
@@ -15,17 +15,22 @@ import java.util.UUID;
|
||||
* 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 source;
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* if the QASource stats (see next attributes) are related to a specific target
|
||||
* The focus attributes specify if the QATopic object is describing the status of a specific
|
||||
* quality assurance topic for the whole repository (focus = null) or for a specific
|
||||
* DSpaceObject (focus = uuid of the DSpaceObject). This would mostly affect the totalEvents attribute below.
|
||||
*/
|
||||
private UUID focus;
|
||||
private long totalEvents;
|
||||
private String key;
|
||||
/**
|
||||
* The source attributes contains the name of the QA source like: OpenAIRE, DSpaceUsers
|
||||
*/
|
||||
private String source;
|
||||
private Date lastEvent;
|
||||
private long totalEvents;
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
@@ -65,5 +70,4 @@ public class QATopic {
|
||||
public void setLastEvent(Date lastEvent) {
|
||||
this.lastEvent = lastEvent;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.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.QAMessageDTO;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* QAReinstateRequestAction is an implementation of the QualityAssuranceAction interface.
|
||||
* It is responsible for applying a correction to reinstate a specified item.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
public class QAReinstateRequestAction implements QualityAssuranceAction {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(QAReinstateRequestAction.class);
|
||||
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
|
||||
@Override
|
||||
public void applyCorrection(Context context, Item item, Item relatedItem, QAMessageDTO message) {
|
||||
try {
|
||||
itemService.reinstate(context, item);
|
||||
} catch (SQLException | AuthorizeException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.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.QAMessageDTO;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* QAWithdrawnRequestAction is an implementation of the QualityAssuranceAction interface.
|
||||
* It is responsible for applying a correction to withdraw a specified item.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
public class QAWithdrawnRequestAction implements QualityAssuranceAction {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(QAWithdrawnRequestAction.class);
|
||||
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
|
||||
@Override
|
||||
public void applyCorrection(Context context, Item item, Item relatedItem, QAMessageDTO message) {
|
||||
try {
|
||||
itemService.withdraw(context, item);
|
||||
} catch (SQLException | AuthorizeException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -7,9 +7,9 @@
|
||||
*/
|
||||
package org.dspace.qaevent.script;
|
||||
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.substringAfter;
|
||||
import static org.dspace.core.Constants.ITEM;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -28,10 +28,14 @@ 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.DSpaceObject;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.QAEvent;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.factory.EPersonServiceFactory;
|
||||
import org.dspace.handle.HandleServiceImpl;
|
||||
import org.dspace.handle.service.HandleService;
|
||||
import org.dspace.qaevent.service.OpenaireClientFactory;
|
||||
import org.dspace.qaevent.service.QAEventService;
|
||||
import org.dspace.scripts.DSpaceRunnable;
|
||||
@@ -71,6 +75,8 @@ import org.dspace.utils.DSpace;
|
||||
public class OpenaireEventsImport
|
||||
extends DSpaceRunnable<OpenaireEventsImportScriptConfiguration<OpenaireEventsImport>> {
|
||||
|
||||
private HandleService handleService;
|
||||
|
||||
private QAEventService qaEventService;
|
||||
|
||||
private String[] topicsToImport;
|
||||
@@ -103,7 +109,9 @@ public class OpenaireEventsImport
|
||||
jsonMapper = new JsonMapper();
|
||||
jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
|
||||
qaEventService = new DSpace().getSingletonService(QAEventService.class);
|
||||
DSpace dspace = new DSpace();
|
||||
handleService = dspace.getSingletonService(HandleServiceImpl.class);
|
||||
qaEventService = dspace.getSingletonService(QAEventService.class);
|
||||
configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
|
||||
brokerClient = OpenaireClientFactory.getInstance().getBrokerClient();
|
||||
|
||||
@@ -236,14 +244,47 @@ public class OpenaireEventsImport
|
||||
private void storeOpenaireQAEvents(List<QAEvent> events) {
|
||||
for (QAEvent event : events) {
|
||||
try {
|
||||
final String resourceUUID = getResourceUUID(context, event.getOriginalId());
|
||||
if (resourceUUID == null) {
|
||||
throw new IllegalArgumentException("Skipped event " + event.getEventId() +
|
||||
" related to the oai record " + event.getOriginalId() + " as the record was not found");
|
||||
}
|
||||
event.setTarget(resourceUUID);
|
||||
storeOpenaireQAEvent(event);
|
||||
} catch (RuntimeException e) {
|
||||
} catch (RuntimeException | SQLException e) {
|
||||
handler.logWarning("An error occurs storing the event with id "
|
||||
+ event.getEventId() + ": " + getMessage(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getResourceUUID(Context context, String originalId) throws IllegalStateException, SQLException {
|
||||
String id = getHandleFromOriginalId(originalId);
|
||||
if (StringUtils.isNotBlank(id)) {
|
||||
DSpaceObject dso = handleService.resolveToObject(context, id);
|
||||
if (dso != null && dso.getType() == ITEM) {
|
||||
Item item = (Item) dso;
|
||||
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 originalId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the given QAEvent, skipping it if it is not supported.
|
||||
*
|
||||
|
@@ -20,9 +20,9 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
* QASecurity that restrict access to the QA Source and related events only to repository administrators
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.com)
|
||||
*
|
||||
*/
|
||||
public class AdministratorsOnlyQASecurity implements QASecurity {
|
||||
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
|
||||
@@ -30,6 +30,7 @@ public class AdministratorsOnlyQASecurity implements QASecurity {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSeeQASource(Context context, EPerson user) {
|
||||
try {
|
||||
return authorizeService.isAdmin(context, user);
|
||||
@@ -38,6 +39,7 @@ public class AdministratorsOnlyQASecurity implements QASecurity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSeeQAEvent(Context context, EPerson user, QAEvent qaEvent) {
|
||||
try {
|
||||
return authorizeService.isAdmin(context, user);
|
||||
|
@@ -14,7 +14,9 @@ import org.dspace.core.Context;
|
||||
import org.dspace.eperson.EPerson;
|
||||
|
||||
/**
|
||||
*
|
||||
* The QASecurity interface defines methods for implementing security strategies
|
||||
* related to Quality Assurance (QA) events. Classes implementing this interface should
|
||||
* provide logic to filter and determine visibility of QA events based on the user's permissions.
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.com)
|
||||
*
|
||||
|
@@ -23,18 +23,16 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
* uuid
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.com)
|
||||
*
|
||||
*/
|
||||
public class UserBasedFilterQASecurity implements QASecurity {
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
|
||||
private String filterTemplate;
|
||||
private boolean allowAdmins = true;
|
||||
|
||||
@Autowired
|
||||
private QAEventService qaEventService;
|
||||
|
||||
private String filterTemplate;
|
||||
|
||||
private boolean allowAdmins = true;
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
|
||||
public Optional<String> generateFilterQuery(Context context, EPerson user) {
|
||||
try {
|
||||
@@ -68,4 +66,5 @@ public class UserBasedFilterQASecurity implements QASecurity {
|
||||
public void setAllowAdmins(boolean allowAdmins) {
|
||||
this.allowAdmins = allowAdmins;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -23,20 +23,20 @@ public interface QAEventSecurityService {
|
||||
|
||||
/**
|
||||
* Check if the specified user can see a specific QASource
|
||||
* @param context the context
|
||||
* @param user the eperson to consider
|
||||
* @param qaSource the qaSource involved
|
||||
* @param context the context
|
||||
* @param user the eperson to consider
|
||||
* @param sourceName the source name
|
||||
* @return <code>true</code> if the specified user can eventually see events in the QASource
|
||||
*/
|
||||
boolean canSeeSource(Context context, EPerson user, String qaSource);
|
||||
boolean canSeeSource(Context context, EPerson user, String sourceName);
|
||||
|
||||
/**
|
||||
* Check if the specified user can see a specific QAEvent. It is expected that a QAEvent in a not visible QASource
|
||||
* cannot be accessed. So implementation of this method should enforce this rule.
|
||||
*
|
||||
* @param context the context
|
||||
* @param user the eperson to consider
|
||||
* @param qaEvent the qaevent to check
|
||||
* @param context the context
|
||||
* @param user the eperson to consider
|
||||
* @param qaEvent the qaevent to check
|
||||
* @return <code>true</code> if the specified user can see the specified event
|
||||
*/
|
||||
boolean canSeeEvent(Context context, EPerson user, QAEvent qaEvent);
|
||||
@@ -45,11 +45,11 @@ public interface QAEventSecurityService {
|
||||
* Generate a query to restrict the qa events returned by other search/find method to the only ones visible to the
|
||||
* specified user
|
||||
*
|
||||
* @param context the context
|
||||
* @param user the eperson to consider
|
||||
* @param qaSource the qaSource involved
|
||||
* @param context the context
|
||||
* @param user the eperson to consider
|
||||
* @param sourceName the source name
|
||||
* @return the solr filter query
|
||||
*/
|
||||
public Optional<String> generateQAEventFilterQuery(Context context, EPerson user, String qaSource);
|
||||
public Optional<String> generateQAEventFilterQuery(Context context, EPerson user, String sourceName);
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -27,8 +27,11 @@ public interface QAEventService {
|
||||
/**
|
||||
* Find all the event's topics.
|
||||
*
|
||||
* @param offset the offset to apply
|
||||
* @return the topics list
|
||||
* @param context the DSpace context
|
||||
* @param offset the offset to apply
|
||||
* @param orderField the field to order for
|
||||
* @param ascending true if the order should be ascending, false otherwise
|
||||
* @return the topics list
|
||||
*/
|
||||
public List<QATopic> findAllTopics(Context context, long offset, long count, String orderField, boolean ascending);
|
||||
|
||||
@@ -36,13 +39,15 @@ public interface QAEventService {
|
||||
* Find all the event's topics related to the given source.
|
||||
*
|
||||
* @param context the DSpace context
|
||||
* @param source the source to search for
|
||||
* @param offset the offset to apply
|
||||
* @param count the page size
|
||||
* @return the topics list
|
||||
* @param source the source to search for
|
||||
* @param offset the offset to apply
|
||||
* @param count the page size
|
||||
* @param orderField the field to order for
|
||||
* @param ascending true if the order should be ascending, false otherwise
|
||||
* @return the topics list
|
||||
*/
|
||||
public List<QATopic> findAllTopicsBySource(Context context, String source, long offset, long count,
|
||||
String orderField, boolean ascending);
|
||||
String orderField, boolean ascending);
|
||||
|
||||
/**
|
||||
* Find a specific topic by its name, source and optionally a target.
|
||||
@@ -67,30 +72,30 @@ public interface QAEventService {
|
||||
/**
|
||||
* Find all the events by topic sorted by trust descending.
|
||||
*
|
||||
* @param context the DSpace context
|
||||
* @param source the source name
|
||||
* @param context the DSpace context
|
||||
* @param sourceName the source name
|
||||
* @param topic the topic to search for
|
||||
* @param offset the offset to apply
|
||||
* @param pageSize the page size
|
||||
* @param size the page size
|
||||
* @return the events
|
||||
*/
|
||||
public List<QAEvent> findEventsByTopicAndPage(Context context, String source, String topic, long offset,
|
||||
int pageSize);
|
||||
public List<QAEvent> findEventsByTopic(Context context, String sourceName, String topic, long offset, int size,
|
||||
String orderField, boolean ascending);
|
||||
|
||||
/**
|
||||
* Find all the events by topic.
|
||||
*
|
||||
* @param context the DSpace context
|
||||
* @param topic the topic to search for
|
||||
* @return the events count
|
||||
* @param context the DSpace context
|
||||
* @param sourceName the source name
|
||||
* @param topic the topic to search for
|
||||
* @return the events count
|
||||
*/
|
||||
public long countEventsByTopic(Context context, String source, String topic);
|
||||
public long countEventsByTopic(Context context, String sourceName, String topic);
|
||||
|
||||
/**
|
||||
* Find an event by the given id. Please note that no security filter are applied by this method.
|
||||
*
|
||||
* @param context the DSpace context
|
||||
* Find an event by the given id.
|
||||
* @param id the id of the event to search for
|
||||
*
|
||||
* @return the event
|
||||
*/
|
||||
public QAEvent findEventByEventId(Context context, String id);
|
||||
@@ -139,7 +144,7 @@ public interface QAEventService {
|
||||
/**
|
||||
* Find all the event's sources.
|
||||
*
|
||||
* @param context the DSpace context
|
||||
* @param context the DSpace context
|
||||
* @param offset the offset to apply
|
||||
* @param pageSize the page size
|
||||
* @return the sources list
|
||||
@@ -154,6 +159,25 @@ public interface QAEventService {
|
||||
*/
|
||||
public long countSources(Context context);
|
||||
|
||||
/**
|
||||
* Count all the event's sources related to a specific item
|
||||
*
|
||||
* @param context the DSpace context
|
||||
* @param target the item uuid
|
||||
* @return the count result
|
||||
*/
|
||||
public long countSourcesByTarget(Context context, UUID target);
|
||||
|
||||
/**
|
||||
* Count all the event's topics related to the given source referring to a specific item
|
||||
*
|
||||
* @param context the DSpace context
|
||||
* @param target the item uuid
|
||||
* @param source the source to search for
|
||||
* @return countTopicsBySourceAndTarget the count result
|
||||
*/
|
||||
public long countTopicsBySourceAndTarget(Context context, String source, UUID target);
|
||||
|
||||
/**
|
||||
* Check if the given QA event supports a related item.
|
||||
*
|
||||
@@ -165,7 +189,7 @@ public interface QAEventService {
|
||||
/**
|
||||
* Find a list of QA events according to the pagination parameters for the specified topic and target sorted by
|
||||
* trust descending
|
||||
*
|
||||
*
|
||||
* @param context the DSpace context
|
||||
* @param source the source name
|
||||
* @param topic the topic to search for
|
||||
@@ -203,16 +227,6 @@ public interface QAEventService {
|
||||
public List<QATopic> findAllTopicsBySourceAndTarget(Context context, String source, UUID target, long offset,
|
||||
long count, String orderField, boolean ascending);
|
||||
|
||||
/**
|
||||
* Count all the event's topics related to the given source referring to a specific item
|
||||
*
|
||||
* @param context the DSpace context
|
||||
* @param target the item uuid
|
||||
* @param source the source to search for
|
||||
* @return the count result
|
||||
*/
|
||||
public long countTopicsBySourceAndTarget(Context context, String source, UUID target);
|
||||
|
||||
/**
|
||||
* Find all the event's sources related to a specific item
|
||||
*
|
||||
@@ -224,15 +238,6 @@ public interface QAEventService {
|
||||
*/
|
||||
public List<QASource> findAllSourcesByTarget(Context context, UUID target, long offset, int pageSize);
|
||||
|
||||
/**
|
||||
* Count all the event's sources related to a specific item
|
||||
*
|
||||
* @param context the DSpace context
|
||||
* @param target the item uuid
|
||||
* @return the count result
|
||||
*/
|
||||
public long countSourcesByTarget(Context context, UUID target);
|
||||
|
||||
/**
|
||||
* Check if a qaevent with the provided id is visible to the current user according to the source security
|
||||
*
|
||||
|
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.qaevent.service.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* The CorrectionTypeMessageDTO class implements the QAMessageDTO interface
|
||||
* and represents a Data Transfer Object (DTO) for holding information
|
||||
* related to a correction type message in the context of Quality Assurance (QA).
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
|
||||
*/
|
||||
public class CorrectionTypeMessageDTO implements QAMessageDTO, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 2718151302291303796L;
|
||||
|
||||
private String reason;
|
||||
|
||||
public CorrectionTypeMessageDTO() {}
|
||||
|
||||
public CorrectionTypeMessageDTO(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
}
|
@@ -17,5 +17,4 @@ import org.dspace.content.QAEvent;
|
||||
*/
|
||||
public interface QAMessageDTO {
|
||||
|
||||
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ 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.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.QAEvent;
|
||||
@@ -41,7 +42,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
*
|
||||
*/
|
||||
public class QAEventActionServiceImpl implements QAEventActionService {
|
||||
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(QAEventActionServiceImpl.class);
|
||||
|
||||
private static final Logger log = LogManager.getLogger(QAEventActionServiceImpl.class);
|
||||
|
||||
private ObjectMapper jsonMapper;
|
||||
|
||||
|
@@ -17,14 +17,27 @@ import org.dspace.eperson.EPerson;
|
||||
import org.dspace.qaevent.security.QASecurity;
|
||||
import org.dspace.qaevent.service.QAEventSecurityService;
|
||||
|
||||
/**
|
||||
* Implementation of the security service for QAEvents.
|
||||
* This implementation manages a configuration of {@link QASecurity} instances,
|
||||
* each responsible for security checks for a specific QA source.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
public class QAEventSecurityServiceImpl implements QAEventSecurityService {
|
||||
|
||||
/**
|
||||
* The default security settings to be used when specific configurations are not available for a QA source.
|
||||
*/
|
||||
private QASecurity defaultSecurity;
|
||||
|
||||
/**
|
||||
* A mapping of QA source names to their corresponding QASecurity configurations.
|
||||
*/
|
||||
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(QAEventSecurityServiceImpl.class);
|
||||
|
||||
private Map<String, QASecurity> qaSecurityConfiguration;
|
||||
|
||||
private QASecurity defaultSecurity;
|
||||
|
||||
|
||||
public void setQaSecurityConfiguration(Map<String, QASecurity> qaSecurityConfiguration) {
|
||||
this.qaSecurityConfiguration = qaSecurityConfiguration;
|
||||
}
|
||||
@@ -47,8 +60,7 @@ public class QAEventSecurityServiceImpl implements QAEventSecurityService {
|
||||
public boolean canSeeEvent(Context context, EPerson user, QAEvent qaEvent) {
|
||||
String source = qaEvent.getSource();
|
||||
QASecurity qaSecurity = getQASecurity(source);
|
||||
return qaSecurity.canSeeQASource(context, user)
|
||||
&& qaSecurity.canSeeQAEvent(context, user, qaEvent);
|
||||
return qaSecurity.canSeeQASource(context, user) && qaSecurity.canSeeQAEvent(context, user, qaEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -23,6 +23,7 @@ import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
@@ -41,12 +42,13 @@ 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.eperson.EPerson;
|
||||
import org.dspace.handle.service.HandleService;
|
||||
import org.dspace.core.Email;
|
||||
import org.dspace.core.I18nUtil;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.qaevent.AutomaticProcessingAction;
|
||||
import org.dspace.qaevent.QAEventAutomaticProcessingEvaluation;
|
||||
import org.dspace.qaevent.QASource;
|
||||
@@ -58,6 +60,8 @@ import org.dspace.qaevent.service.QAEventSecurityService;
|
||||
import org.dspace.qaevent.service.QAEventService;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
@@ -84,9 +88,6 @@ public class QAEventServiceImpl implements QAEventService {
|
||||
@Autowired(required = true)
|
||||
protected ItemService itemService;
|
||||
|
||||
@Autowired
|
||||
private HandleService handleService;
|
||||
|
||||
@Autowired
|
||||
private QAEventsDAOImpl qaEventsDao;
|
||||
|
||||
@@ -154,23 +155,19 @@ public class QAEventServiceImpl implements QAEventService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countTopicsBySourceAndTarget(Context context, String source, UUID target) {
|
||||
if (isNotSupportedSource(source)
|
||||
|| !qaSecurityService.canSeeSource(context, context.getCurrentUser(), source)) {
|
||||
public long countTopicsBySource(Context context, String sourceName) {
|
||||
var currentUser = context.getCurrentUser();
|
||||
if (isNotSupportedSource(sourceName) || !qaSecurityService.canSeeSource(context, currentUser, sourceName)) {
|
||||
return 0;
|
||||
}
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
solrQuery.setRows(0);
|
||||
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context,
|
||||
context.getCurrentUser(), source);
|
||||
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context, currentUser, sourceName);
|
||||
solrQuery.setQuery(securityQuery.orElse("*:*"));
|
||||
solrQuery.setFacet(true);
|
||||
solrQuery.setFacetMinCount(1);
|
||||
solrQuery.addFacetField(TOPIC);
|
||||
solrQuery.addFilterQuery(SOURCE + ":\"" + source + "\"");
|
||||
if (target != null) {
|
||||
solrQuery.addFilterQuery(RESOURCE_UUID + ":\"" + target.toString() + "\"");
|
||||
}
|
||||
solrQuery.addFilterQuery("source:" + sourceName);
|
||||
QueryResponse response;
|
||||
try {
|
||||
response = getSolr().query(solrQuery);
|
||||
@@ -180,6 +177,44 @@ public class QAEventServiceImpl implements QAEventService {
|
||||
return response.getFacetField(TOPIC).getValueCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public QATopic findTopicBySourceAndNameAndTarget(Context context, String sourceName, String topicName,UUID target) {
|
||||
var currentUser = context.getCurrentUser();
|
||||
if (isNotSupportedSource(sourceName) || !qaSecurityService.canSeeSource(context, currentUser, sourceName)) {
|
||||
return null;
|
||||
}
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context, currentUser, sourceName);
|
||||
solrQuery.setRows(0);
|
||||
solrQuery.setQuery(securityQuery.orElse("*:*"));
|
||||
solrQuery.addFilterQuery(SOURCE + ":\"" + sourceName + "\"");
|
||||
solrQuery.addFilterQuery(TOPIC + ":\"" + topicName + "\"");
|
||||
if (target != null) {
|
||||
solrQuery.addFilterQuery(RESOURCE_UUID + ":\"" + target.toString() + "\"");
|
||||
}
|
||||
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(topicName)) {
|
||||
QATopic topic = new QATopic();
|
||||
topic.setSource(sourceName);
|
||||
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 void deleteEventByEventId(String id) {
|
||||
try {
|
||||
@@ -281,7 +316,6 @@ public class QAEventServiceImpl implements QAEventService {
|
||||
try {
|
||||
response = getSolr().query(solrQuery);
|
||||
FacetField facetField = response.getFacetField(TOPIC);
|
||||
topics = new ArrayList<>();
|
||||
int idx = 0;
|
||||
for (Count c : facetField.getValues()) {
|
||||
if (idx < offset) {
|
||||
@@ -366,13 +400,45 @@ public class QAEventServiceImpl implements QAEventService {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public QAEvent findEventByEventId(Context context, String eventId) {
|
||||
SolrQuery param = new SolrQuery("*:*");
|
||||
param.addFilterQuery(EVENT_ID + ":\"" + eventId + "\"");
|
||||
QueryResponse response;
|
||||
/**
|
||||
* Sends an email notification to the system administrator about a new
|
||||
* Quality Assurance (QA) request event. The email includes details such as the
|
||||
* topic, target, and message associated with the QA event.
|
||||
*
|
||||
* @param qaEvent The Quality Assurance event for which the notification is generated.
|
||||
*/
|
||||
public void sentEmailToAdminAboutNewRequest(QAEvent qaEvent) {
|
||||
try {
|
||||
response = getSolr().query(param);
|
||||
String uiUrl = configurationService.getProperty("dspace.ui.url");
|
||||
Email email = Email.getEmail(I18nUtil.getEmailFilename(Locale.getDefault(), "qaevent_admin_notification"));
|
||||
email.addRecipient(configurationService.getProperty("qaevents.mail.notification"));
|
||||
email.addArgument(qaEvent.getTopic());
|
||||
email.addArgument(uiUrl + "/items/" + qaEvent.getTarget());
|
||||
email.addArgument(parsJson(qaEvent.getMessage()));
|
||||
email.send();
|
||||
} catch (Exception e) {
|
||||
log.warn("Error during sending email of Withdrawn/Reinstate request for item with uuid:"
|
||||
+ qaEvent.getTarget(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String parsJson(String jsonString) {
|
||||
try {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
JsonNode jsonNode = objectMapper.readTree(jsonString);
|
||||
return jsonNode.get("reason").asText();
|
||||
} catch (Exception e) {
|
||||
log.warn("Unable to parse the JSON:" + jsonString);
|
||||
return jsonString;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public QAEvent findEventByEventId(String eventId) {
|
||||
SolrQuery solrQuery = new SolrQuery("*:*");
|
||||
solrQuery.addFilterQuery(EVENT_ID + ":\"" + eventId + "\"");
|
||||
try {
|
||||
QueryResponse response = getSolr().query(solrQuery);
|
||||
if (response != null) {
|
||||
SolrDocumentList list = response.getResults();
|
||||
if (list != null && list.size() == 1) {
|
||||
@@ -386,28 +452,9 @@ public class QAEventServiceImpl implements QAEventService {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean qaEventsInSource(Context context, EPerson user, String eventId, String source) {
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context,
|
||||
user, source);
|
||||
solrQuery.setQuery(securityQuery.orElse("*:*"));
|
||||
solrQuery.addFilterQuery(EVENT_ID + ":\"" + eventId + "\"");
|
||||
QueryResponse response;
|
||||
try {
|
||||
response = getSolr().query(solrQuery);
|
||||
if (response != null) {
|
||||
return response.getResults().getNumFound() == 1;
|
||||
}
|
||||
} catch (SolrServerException | IOException e) {
|
||||
throw new RuntimeException("Exception querying Solr", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<QAEvent> findEventsByTopicAndPage(Context context, String source, String topic, long offset,
|
||||
int pageSize) {
|
||||
int pageSize) {
|
||||
if (isNotSupportedSource(source)
|
||||
|| !qaSecurityService.canSeeSource(context, context.getCurrentUser(), source)) {
|
||||
return List.of();
|
||||
@@ -442,7 +489,7 @@ public class QAEventServiceImpl implements QAEventService {
|
||||
|
||||
@Override
|
||||
public List<QAEvent> findEventsByTopicAndPageAndTarget(Context context, String source, String topic, long offset,
|
||||
int pageSize, UUID target) {
|
||||
int pageSize, UUID target) {
|
||||
if (isNotSupportedSource(source)
|
||||
|| !qaSecurityService.canSeeSource(context, context.getCurrentUser(), source)) {
|
||||
return List.of();
|
||||
@@ -480,49 +527,23 @@ public class QAEventServiceImpl implements QAEventService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countEventsByTopic(Context context, String source, String topic) {
|
||||
if (isNotSupportedSource(source)
|
||||
|| !qaSecurityService.canSeeSource(context, context.getCurrentUser(), source)) {
|
||||
public long countEventsByTopic(Context context, String sourceName, String topic) {
|
||||
EPerson currentUser = context.getCurrentUser();
|
||||
if (isNotSupportedSource(sourceName) || !qaSecurityService.canSeeSource(context, currentUser, sourceName)) {
|
||||
return 0;
|
||||
}
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
solrQuery.setRows(0);
|
||||
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context, context.getCurrentUser(),
|
||||
source);
|
||||
solrQuery.setQuery(securityQuery.orElse("*:*"));
|
||||
solrQuery.addFilterQuery(TOPIC + ":\"" + topic + "\"");
|
||||
solrQuery.addFilterQuery(SOURCE + ":\"" + source + "\"");
|
||||
QueryResponse response = null;
|
||||
try {
|
||||
response = getSolr().query(solrQuery);
|
||||
return response.getResults().getNumFound();
|
||||
} catch (SolrServerException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countEventsByTopicAndTarget(Context context, String source, String topic, UUID target) {
|
||||
if (isNotSupportedSource(source)
|
||||
|| !qaSecurityService.canSeeSource(context, context.getCurrentUser(), source)) {
|
||||
return 0;
|
||||
}
|
||||
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context, currentUser, sourceName);
|
||||
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
solrQuery.setRows(0);
|
||||
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context, context.getCurrentUser(),
|
||||
source);
|
||||
solrQuery.setQuery(securityQuery.orElse("*:*"));
|
||||
if (target != null) {
|
||||
solrQuery.addFilterQuery(RESOURCE_UUID + ":\"" + target.toString() + "\"");
|
||||
}
|
||||
solrQuery.addFilterQuery(SOURCE + ":\"" + source + "\"");
|
||||
solrQuery.addFilterQuery(TOPIC + ":\"" + topic + "\"");
|
||||
QueryResponse response = null;
|
||||
solrQuery.addFilterQuery(SOURCE + ":\"" + sourceName + "\"");
|
||||
solrQuery.setQuery(TOPIC + ":" + topic.replaceAll("!", "/"));
|
||||
try {
|
||||
response = getSolr().query(solrQuery);
|
||||
return response.getResults().getNumFound();
|
||||
return getSolr().query(solrQuery).getResults().getNumFound();
|
||||
} catch (SolrServerException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,15 +555,14 @@ public class QAEventServiceImpl implements QAEventService {
|
||||
|
||||
@Override
|
||||
public QASource findSource(Context context, String sourceName, UUID target) {
|
||||
|
||||
if (isNotSupportedSource(sourceName)
|
||||
|| !qaSecurityService.canSeeSource(context, context.getCurrentUser(), sourceName)) {
|
||||
EPerson currentUser = context.getCurrentUser();
|
||||
if (isNotSupportedSource(sourceName) || !qaSecurityService.canSeeSource(context, currentUser, sourceName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context, currentUser, sourceName);
|
||||
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context, context.getCurrentUser(),
|
||||
sourceName);
|
||||
solrQuery.setQuery(securityQuery.orElse("*:*"));
|
||||
solrQuery.setRows(0);
|
||||
solrQuery.addFilterQuery(SOURCE + ":\"" + sourceName + "\"");
|
||||
@@ -553,9 +573,8 @@ public class QAEventServiceImpl implements QAEventService {
|
||||
solrQuery.setFacetMinCount(1);
|
||||
solrQuery.addFacetField(SOURCE);
|
||||
|
||||
QueryResponse response;
|
||||
try {
|
||||
response = getSolr().query(solrQuery);
|
||||
QueryResponse response = getSolr().query(solrQuery);
|
||||
FacetField facetField = response.getFacetField(SOURCE);
|
||||
for (Count c : facetField.getValues()) {
|
||||
if (c.getName().equalsIgnoreCase(sourceName)) {
|
||||
@@ -568,7 +587,7 @@ public class QAEventServiceImpl implements QAEventService {
|
||||
}
|
||||
}
|
||||
} catch (SolrServerException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
QASource source = new QASource();
|
||||
@@ -581,12 +600,13 @@ public class QAEventServiceImpl implements QAEventService {
|
||||
@Override
|
||||
public List<QASource> findAllSources(Context context, long offset, int pageSize) {
|
||||
return Arrays.stream(getSupportedSources())
|
||||
.map((sourceName) -> findSource(context, sourceName))
|
||||
.map((sourceName) -> findSource(context, sourceName))
|
||||
.filter(Objects::nonNull)
|
||||
.sorted(comparing(QASource::getTotalEvents).reversed())
|
||||
.skip(offset)
|
||||
.limit(pageSize)
|
||||
.collect(Collectors.toList());
|
||||
.sorted(comparing(QASource::getTotalEvents)
|
||||
.reversed())
|
||||
.skip(offset)
|
||||
.limit(pageSize)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -635,12 +655,7 @@ public class QAEventServiceImpl implements QAEventService {
|
||||
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(RESOURCE_UUID, dto.getTarget());
|
||||
doc.addField(RELATED_UUID, dto.getRelated());
|
||||
return doc;
|
||||
}
|
||||
@@ -686,13 +701,173 @@ public class QAEventServiceImpl implements QAEventService {
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean qaEventsInSource(Context context, EPerson user, String eventId, String source) {
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context, user, source);
|
||||
solrQuery.setQuery(securityQuery.orElse("*:*"));
|
||||
solrQuery.addFilterQuery(EVENT_ID + ":\"" + eventId + "\"");
|
||||
QueryResponse response;
|
||||
try {
|
||||
response = getSolr().query(solrQuery);
|
||||
if (response != null) {
|
||||
return response.getResults().getNumFound() == 1;
|
||||
}
|
||||
} catch (SolrServerException | IOException e) {
|
||||
throw new RuntimeException("Exception querying Solr", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countEventsByTopicAndTarget(Context context, String sourceName, String topic, UUID target) {
|
||||
var currentUser = context.getCurrentUser();
|
||||
if (isNotSupportedSource(sourceName) || !qaSecurityService.canSeeSource(context, currentUser, sourceName)) {
|
||||
return 0;
|
||||
}
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
solrQuery.setRows(0);
|
||||
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context, currentUser, sourceName);
|
||||
solrQuery.setQuery(securityQuery.orElse("*:*"));
|
||||
if (target != null) {
|
||||
solrQuery.addFilterQuery(RESOURCE_UUID + ":\"" + target.toString() + "\"");
|
||||
}
|
||||
solrQuery.addFilterQuery(SOURCE + ":\"" + sourceName + "\"");
|
||||
solrQuery.addFilterQuery(TOPIC + ":\"" + topic + "\"");
|
||||
QueryResponse response = null;
|
||||
try {
|
||||
response = getSolr().query(solrQuery);
|
||||
return response.getResults().getNumFound();
|
||||
} catch (SolrServerException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<QAEvent> findEventsByTopicAndTarget(Context context, String source, String topic, UUID target,
|
||||
long offset, int pageSize) {
|
||||
var currentUser = context.getCurrentUser();
|
||||
if (isNotSupportedSource(source) || !qaSecurityService.canSeeSource(context, currentUser, source)) {
|
||||
return List.of();
|
||||
}
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
solrQuery.setStart(((Long) offset).intValue());
|
||||
solrQuery.setRows(pageSize);
|
||||
solrQuery.setSort(TRUST, ORDER.desc);
|
||||
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context, currentUser, source);
|
||||
solrQuery.setQuery(securityQuery.orElse("*:*"));
|
||||
if (target != null) {
|
||||
solrQuery.addFilterQuery(RESOURCE_UUID + ":\"" + target.toString() + "\"");
|
||||
}
|
||||
solrQuery.addFilterQuery(SOURCE + ":\"" + source + "\"");
|
||||
solrQuery.addFilterQuery(TOPIC + ":\"" + topic + "\"");
|
||||
|
||||
try {
|
||||
QueryResponse 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();
|
||||
}
|
||||
|
||||
private boolean isNotSupportedSource(String source) {
|
||||
return !ArrayUtils.contains(getSupportedSources(), source);
|
||||
}
|
||||
|
||||
private String[] getSupportedSources() {
|
||||
return configurationService.getArrayProperty("qaevent.sources",
|
||||
return configurationService.getArrayProperty(QAEVENTS_SOURCES,
|
||||
new String[] { QAEvent.OPENAIRE_SOURCE, QAEvent.COAR_NOTIFY_SOURCE });
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countTopicsBySourceAndTarget(Context context, String source, UUID target) {
|
||||
var currentUser = context.getCurrentUser();
|
||||
if (isNotSupportedSource(source) || !qaSecurityService.canSeeSource(context, currentUser, source)) {
|
||||
return 0;
|
||||
}
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
solrQuery.setRows(0);
|
||||
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context, currentUser, source);
|
||||
solrQuery.setQuery(securityQuery.orElse("*:*"));
|
||||
solrQuery.setFacet(true);
|
||||
solrQuery.setFacetMinCount(1);
|
||||
solrQuery.addFacetField(TOPIC);
|
||||
solrQuery.addFilterQuery(SOURCE + ":\"" + source + "\"");
|
||||
if (target != null) {
|
||||
solrQuery.addFilterQuery(RESOURCE_UUID + ":\"" + target.toString() + "\"");
|
||||
}
|
||||
try {
|
||||
QueryResponse response = getSolr().query(solrQuery);
|
||||
return response.getFacetField(TOPIC).getValueCount();
|
||||
} catch (SolrServerException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<QASource> findAllSourcesByTarget(Context context, UUID target, long offset, int pageSize) {
|
||||
return Arrays.stream(getSupportedSources())
|
||||
.map((sourceName) -> findSource(context, sourceName, target))
|
||||
.filter(Objects::nonNull)
|
||||
.sorted(comparing(QASource::getTotalEvents).reversed())
|
||||
.filter(source -> source.getTotalEvents() > 0)
|
||||
.skip(offset)
|
||||
.limit(pageSize)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<QATopic> findAllTopicsBySourceAndTarget(Context context, String source, UUID target, long offset,
|
||||
int pageSize, String orderField, boolean ascending) {
|
||||
var currentUser = context.getCurrentUser();
|
||||
if (isNotSupportedSource(source) || !qaSecurityService.canSeeSource(context, currentUser, source)) {
|
||||
return List.of();
|
||||
}
|
||||
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context, currentUser, source);
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
solrQuery.setRows(0);
|
||||
solrQuery.setSort(orderField, ascending ? ORDER.asc : ORDER.desc);
|
||||
solrQuery.setQuery(securityQuery.orElse("*:*"));
|
||||
solrQuery.setFacet(true);
|
||||
solrQuery.setFacetMinCount(1);
|
||||
solrQuery.setFacetLimit((int) (offset + pageSize));
|
||||
solrQuery.addFacetField(TOPIC);
|
||||
solrQuery.addFilterQuery(SOURCE + ":\"" + source + "\"");
|
||||
if (target != null) {
|
||||
solrQuery.addFilterQuery(RESOURCE_UUID + ":" + target.toString());
|
||||
}
|
||||
try {
|
||||
List<QATopic> topics = new ArrayList<>();
|
||||
QueryResponse response = getSolr().query(solrQuery);
|
||||
FacetField facetField = response.getFacetField(TOPIC);
|
||||
int idx = 0;
|
||||
for (Count c : facetField.getValues()) {
|
||||
if (idx < offset) {
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
QATopic topic = new QATopic();
|
||||
topic.setSource(source);
|
||||
topic.setKey(c.getName());
|
||||
topic.setFocus(target);
|
||||
topic.setTotalEvents(c.getCount());
|
||||
topic.setLastEvent(new Date());
|
||||
topics.add(topic);
|
||||
idx++;
|
||||
}
|
||||
return topics;
|
||||
} catch (SolrServerException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -169,7 +169,7 @@ public class S3BitStoreService extends BaseBitStoreService {
|
||||
@Override
|
||||
public void init() throws IOException {
|
||||
|
||||
if (this.isInitialized()) {
|
||||
if (this.isInitialized() || !this.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -11,6 +11,7 @@ import static java.util.List.of;
|
||||
import static org.dspace.content.QAEvent.COAR_NOTIFY_SOURCE;
|
||||
import static org.dspace.content.QAEvent.OPENAIRE_SOURCE;
|
||||
import static org.dspace.matcher.QAEventMatcher.pendingOpenaireEventWith;
|
||||
import static org.dspace.qaevent.service.impl.QAEventServiceImpl.QAEVENTS_SOURCES;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
@@ -57,10 +58,8 @@ import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.utils.DSpace;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
/**
|
||||
* Integration tests for {@link OpenaireEventsImport}.
|
||||
*
|
||||
@@ -69,9 +68,8 @@ import org.junit.Test;
|
||||
*/
|
||||
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 static final String BASE_JSON_DIR_PATH = "org/dspace/app/openaire-events/";
|
||||
|
||||
private QAEventService qaEventService = new DSpace().getSingletonService(QAEventService.class);
|
||||
|
||||
@@ -101,8 +99,7 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
|
||||
.build();
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
configurationService.setProperty("qaevent.sources", new String[]
|
||||
{ QAEvent.OPENAIRE_SOURCE });
|
||||
configurationService.setProperty(QAEVENTS_SOURCES, new String[] { QAEvent.OPENAIRE_SOURCE });
|
||||
((OpenaireClientFactoryImpl) OpenaireClientFactory.getInstance()).setBrokerClient(mockBrokerClient);
|
||||
}
|
||||
|
||||
@@ -151,7 +148,6 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testManyEventsImportFromFile() throws Exception {
|
||||
|
||||
context.turnOffAuthorisationSystem();
|
||||
@@ -207,7 +203,6 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
|
||||
abstractMessage, QANotifyPatterns.TOPIC_ENRICH_MISSING_ABSTRACT, 1.00d)));
|
||||
|
||||
verifyNoInteractions(mockBrokerClient);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -226,12 +221,12 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
|
||||
|
||||
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"));
|
||||
contains("An error occurs storing the event with id 406fb9c5656c7f11cac8995abb746887: "
|
||||
+ "Skipped event 406fb9c5656c7f11cac8995abb746887 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 eafd747feee49cca7603d30ba4e768dc: "
|
||||
+ "Skipped event eafd747feee49cca7603d30ba4e768dc 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"));
|
||||
@@ -246,14 +241,11 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
|
||||
|
||||
String abstractMessage = "{\"abstracts[0]\":\"Missing Abstract\"}";
|
||||
|
||||
assertThat(qaEventService.findEventsByTopicAndPage(context, OPENAIRE_SOURCE,
|
||||
QANotifyPatterns.TOPIC_ENRICH_MISSING_ABSTRACT, 0, 20),
|
||||
contains(
|
||||
assertThat(qaEventService.findEventsByTopic("ENRICH/MISSING/ABSTRACT"), contains(
|
||||
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/99999", item, "Test Publication",
|
||||
abstractMessage, QANotifyPatterns.TOPIC_ENRICH_MISSING_ABSTRACT, 1.00d)));
|
||||
|
||||
verifyNoInteractions(mockBrokerClient);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -273,7 +265,9 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
|
||||
|
||||
assertThat(handler.getErrorMessages(), empty());
|
||||
assertThat(handler.getWarningMessages(),
|
||||
contains("Event for topic ENRICH/MORE/UNKNOWN is not allowed in the qaevents.cfg"));
|
||||
contains("An error occurs storing the event with id 8307aa56769deba961faed7162d91aab:"
|
||||
+ " Skipped event 8307aa56769deba961faed7162d91aab 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 2 events in the given file"));
|
||||
@@ -285,14 +279,11 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
|
||||
|
||||
String abstractMessage = "{\"abstracts[0]\":\"Missing Abstract\"}";
|
||||
|
||||
assertThat(qaEventService.findEventsByTopicAndPage(context, OPENAIRE_SOURCE,
|
||||
QANotifyPatterns.TOPIC_ENRICH_MISSING_ABSTRACT, 0, 20),
|
||||
contains(
|
||||
assertThat(qaEventService.findEventsByTopic("ENRICH/MISSING/ABSTRACT"), contains(
|
||||
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/999991", secondItem, "Test Publication 2",
|
||||
abstractMessage, org.dspace.qaevent.QANotifyPatterns.TOPIC_ENRICH_MISSING_ABSTRACT, 1.00d)));
|
||||
|
||||
verifyNoInteractions(mockBrokerClient);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -310,13 +301,12 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
|
||||
|
||||
assertThat(qaEventService.findAllSources(context, 0, 20), hasItem(QASourceMatcher.with(OPENAIRE_SOURCE, 0L)));
|
||||
|
||||
assertThat(qaEventService.findAllTopicsBySource(context, OPENAIRE_SOURCE, 0, 20, ORDER_FIELD, false), empty());
|
||||
assertThat(qaEventService.findAllTopics(0, 20, ORDER_FIELD, false), empty());
|
||||
|
||||
verifyNoInteractions(mockBrokerClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testImportFromOpenaireBroker() throws Exception {
|
||||
|
||||
context.turnOffAuthorisationSystem();
|
||||
@@ -371,18 +361,14 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
|
||||
+ "\"projects[0].openaireId\":\"40|corda__h2020::6e32f5eb912688f2424c68b851483ea4\","
|
||||
+ "\"projects[0].title\":\"Tracking Papyrus and Parchment Paths\"}";
|
||||
|
||||
assertThat(qaEventService.findEventsByTopicAndPage(context, OPENAIRE_SOURCE,
|
||||
QANotifyPatterns.TOPIC_ENRICH_MORE_PROJECT, 0, 20),
|
||||
contains(
|
||||
assertThat(qaEventService.findEventsByTopic("ENRICH/MORE/PROJECT"), contains(
|
||||
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/99998", firstItem,
|
||||
"Egypt, crossroad of translations and literary interweavings", projectMessage,
|
||||
QANotifyPatterns.TOPIC_ENRICH_MORE_PROJECT, 1.00d)));
|
||||
|
||||
String abstractMessage = "{\"abstracts[0]\":\"Missing Abstract\"}";
|
||||
|
||||
List<QAEvent> eventList = qaEventService.findEventsByTopicAndPage(context, OPENAIRE_SOURCE,
|
||||
QANotifyPatterns.TOPIC_ENRICH_MISSING_ABSTRACT, 0, 20);
|
||||
assertThat(eventList, hasItem(
|
||||
assertThat(qaEventService.findEventsByTopic("ENRICH/MISSING/ABSTRACT"), containsInAnyOrder(
|
||||
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/99999", secondItem, "Test Publication",
|
||||
abstractMessage, QANotifyPatterns.TOPIC_ENRICH_MISSING_ABSTRACT, 1.00d)));
|
||||
assertThat(eventList, hasItem(
|
||||
@@ -417,7 +403,7 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
|
||||
|
||||
assertThat(qaEventService.findAllSources(context, 0, 20), hasItem(QASourceMatcher.with(OPENAIRE_SOURCE, 0L)));
|
||||
|
||||
assertThat(qaEventService.findAllTopicsBySource(context, OPENAIRE_SOURCE, 0, 20, ORDER_FIELD, false), empty());
|
||||
assertThat(qaEventService.findAllTopics(0, 20, ORDER_FIELD, false), empty());
|
||||
|
||||
verify(mockBrokerClient).listSubscriptions(openaireURL, "user@test.com");
|
||||
|
||||
@@ -426,7 +412,6 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testImportFromOpenaireBrokerWithErrorDuringEventsDownload() throws Exception {
|
||||
|
||||
context.turnOffAuthorisationSystem();
|
||||
@@ -476,12 +461,8 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
|
||||
assertThat(topicList, hasItem(QATopicMatcher.with(QANotifyPatterns.TOPIC_ENRICH_MISSING_PROJECT, 1L)));
|
||||
assertThat(topicList, hasItem(QATopicMatcher.with(QANotifyPatterns.TOPIC_ENRICH_MISSING_ABSTRACT, 2L)));
|
||||
|
||||
assertThat(qaEventService.findEventsByTopicAndPage(context, OPENAIRE_SOURCE,
|
||||
QANotifyPatterns.TOPIC_ENRICH_MORE_PROJECT, 0, 20),
|
||||
hasSize(1));
|
||||
assertThat(qaEventService.findEventsByTopicAndPage(context, OPENAIRE_SOURCE,
|
||||
QANotifyPatterns.TOPIC_ENRICH_MISSING_ABSTRACT, 0, 20),
|
||||
hasSize(2));
|
||||
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());
|
||||
@@ -489,7 +470,6 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
|
||||
verify(mockBrokerClient).downloadEvents(eq(openaireURL), eq("sub3"), any());
|
||||
|
||||
verifyNoMoreInteractions(mockBrokerClient);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -19,6 +19,7 @@ import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@@ -42,6 +43,7 @@ import com.amazonaws.services.s3.model.ObjectMetadata;
|
||||
import io.findify.s3mock.S3Mock;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.dspace.AbstractIntegrationTestWithDatabase;
|
||||
import org.dspace.app.matcher.LambdaMatcher;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
@@ -53,6 +55,8 @@ import org.dspace.content.Bitstream;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Utils;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
@@ -60,6 +64,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @author Luca Giamminonni (luca.giamminonni at 4science.com)
|
||||
*/
|
||||
@@ -77,9 +82,13 @@ public class S3BitStoreServiceIT extends AbstractIntegrationTestWithDatabase {
|
||||
|
||||
private File s3Directory;
|
||||
|
||||
private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
|
||||
configurationService.setProperty("assetstore.s3.enabled", "true");
|
||||
s3Directory = new File(System.getProperty("java.io.tmpdir"), "s3");
|
||||
|
||||
s3Mock = S3Mock.create(8001, s3Directory.getAbsolutePath());
|
||||
@@ -88,7 +97,8 @@ public class S3BitStoreServiceIT extends AbstractIntegrationTestWithDatabase {
|
||||
amazonS3Client = createAmazonS3Client();
|
||||
|
||||
s3BitStoreService = new S3BitStoreService(amazonS3Client);
|
||||
|
||||
s3BitStoreService.setEnabled(BooleanUtils.toBoolean(
|
||||
configurationService.getProperty("assetstore.s3.enabled")));
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
@@ -382,6 +392,17 @@ public class S3BitStoreServiceIT extends AbstractIntegrationTestWithDatabase {
|
||||
assertThat(computedPath, Matchers.not(Matchers.containsString(File.separator)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoNotInitializeConfigured() throws Exception {
|
||||
String assetstores3enabledOldValue = configurationService.getProperty("assetstore.s3.enabled");
|
||||
configurationService.setProperty("assetstore.s3.enabled", "false");
|
||||
s3BitStoreService = new S3BitStoreService(amazonS3Client);
|
||||
s3BitStoreService.init();
|
||||
assertFalse(s3BitStoreService.isInitialized());
|
||||
assertFalse(s3BitStoreService.isEnabled());
|
||||
configurationService.setProperty("assetstore.s3.enabled", assetstores3enabledOldValue);
|
||||
}
|
||||
|
||||
private byte[] generateChecksum(String content) {
|
||||
try {
|
||||
MessageDigest m = MessageDigest.getInstance("MD5");
|
||||
|
@@ -78,7 +78,6 @@ public class QAEventRelatedRestController {
|
||||
@RequestParam(name = "item") UUID relatedItemUUID) throws SQLException, AuthorizeException {
|
||||
|
||||
Context context = ContextUtil.obtainCurrentRequestContext();
|
||||
|
||||
QAEvent qaevent = qaEventService.findEventByEventId(context, qaeventId);
|
||||
if (qaevent == null) {
|
||||
throw new ResourceNotFoundException("No such qa event: " + qaeventId);
|
||||
|
@@ -15,7 +15,6 @@ import org.dspace.app.rest.model.BaseObjectRest;
|
||||
import org.dspace.app.rest.model.ItemRest;
|
||||
import org.dspace.app.rest.model.VersionRest;
|
||||
import org.dspace.app.rest.projection.DefaultProjection;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.versioning.Version;
|
||||
@@ -34,8 +33,6 @@ import org.springframework.stereotype.Component;
|
||||
description = "It can be used to verify if the user can delete a version of an Item")
|
||||
public class CanDeleteVersionFeature extends DeleteFeature {
|
||||
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
@Autowired
|
||||
private ItemConverter itemConverter;
|
||||
@Autowired
|
||||
|
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.CorrectionTypeRest;
|
||||
import org.dspace.app.rest.projection.Projection;
|
||||
import org.dspace.correctiontype.CorrectionType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* This class provides the method to convert a CorrectionType to its REST representation, the
|
||||
* CorrectionTypeRest
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
@Component
|
||||
public class CorrectionTypeConverter implements DSpaceConverter<CorrectionType, CorrectionTypeRest> {
|
||||
|
||||
@Override
|
||||
public CorrectionTypeRest convert(CorrectionType target, Projection projection) {
|
||||
CorrectionTypeRest targetRest = new CorrectionTypeRest();
|
||||
targetRest.setProjection(projection);
|
||||
targetRest.setId(target.getId());
|
||||
targetRest.setTopic(target.getTopic());
|
||||
return targetRest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<CorrectionType> getModelClass() {
|
||||
return CorrectionType.class;
|
||||
}
|
||||
|
||||
}
|
@@ -8,6 +8,8 @@
|
||||
package org.dspace.app.rest.converter;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.Locale;
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
@@ -15,12 +17,14 @@ 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.NotifyQAEventMessageRest;
|
||||
import org.dspace.app.rest.model.CorrectionTypeQAEventMessageRest;
|
||||
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.NotifyMessageDTO;
|
||||
import org.dspace.qaevent.service.dto.CorrectionTypeMessageDTO;
|
||||
import org.dspace.qaevent.service.dto.OpenaireMessageDTO;
|
||||
import org.dspace.qaevent.service.dto.QAMessageDTO;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
@@ -56,9 +60,9 @@ public class QAEventConverter implements DSpaceConverter<QAEvent, QAEventRest> {
|
||||
rest.setId(modelObject.getEventId());
|
||||
try {
|
||||
rest.setMessage(convertMessage(jsonMapper.readValue(modelObject.getMessage(),
|
||||
modelObject.getMessageDtoClass())));
|
||||
modelObject.getMessageDtoClass())));
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
rest.setSource(modelObject.getSource());
|
||||
rest.setOriginalId(modelObject.getOriginalId());
|
||||
@@ -66,7 +70,8 @@ public class QAEventConverter implements DSpaceConverter<QAEvent, QAEventRest> {
|
||||
rest.setTitle(modelObject.getTitle());
|
||||
rest.setTopic(modelObject.getTopic());
|
||||
rest.setEventDate(modelObject.getLastUpdate());
|
||||
rest.setTrust(new DecimalFormat("0.000").format(modelObject.getTrust()));
|
||||
DecimalFormat decimalFormat = new DecimalFormat("0.000", new DecimalFormatSymbols(Locale.ENGLISH));
|
||||
rest.setTrust(decimalFormat.format(modelObject.getTrust()));
|
||||
// right now only the pending status can be found in persisted qa events
|
||||
rest.setStatus(modelObject.getStatus());
|
||||
return rest;
|
||||
@@ -78,6 +83,9 @@ public class QAEventConverter implements DSpaceConverter<QAEvent, QAEventRest> {
|
||||
} else if (dto instanceof NotifyMessageDTO) {
|
||||
return convertNotifyMessage(dto);
|
||||
}
|
||||
if (dto instanceof CorrectionTypeMessageDTO) {
|
||||
return convertCorrectionTypeMessage(dto);
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown message type: " + dto.getClass());
|
||||
}
|
||||
|
||||
@@ -91,6 +99,13 @@ public class QAEventConverter implements DSpaceConverter<QAEvent, QAEventRest> {
|
||||
return message;
|
||||
}
|
||||
|
||||
private QAEventMessageRest convertCorrectionTypeMessage(QAMessageDTO dto) {
|
||||
CorrectionTypeMessageDTO correctionTypeDto = (CorrectionTypeMessageDTO) dto;
|
||||
CorrectionTypeQAEventMessageRest message = new CorrectionTypeQAEventMessageRest();
|
||||
message.setReason(correctionTypeDto.getReason());
|
||||
return message;
|
||||
}
|
||||
|
||||
private QAEventMessageRest convertOpenaireMessage(QAMessageDTO dto) {
|
||||
OpenaireMessageDTO openaireDto = (OpenaireMessageDTO) dto;
|
||||
OpenaireQAEventMessageRest message = new OpenaireQAEventMessageRest();
|
||||
|
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* The CorrectionTypeQAEventMessageRest class implements the QAEventMessageRest
|
||||
* interface and represents a message structure for Quality Assurance (QA)
|
||||
* events related to correction types.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
public class CorrectionTypeQAEventMessageRest implements QAEventMessageRest {
|
||||
|
||||
private String reason;
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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;
|
||||
import org.dspace.app.rest.RestResourceController;
|
||||
|
||||
/**
|
||||
* The CorrectionType REST Resource
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class CorrectionTypeRest extends BaseObjectRest<String> {
|
||||
|
||||
private static final long serialVersionUID = -8297846719538025938L;
|
||||
|
||||
public static final String NAME = "correctiontype";
|
||||
public static final String PLURAL_NAME = "correctiontypes";
|
||||
public static final String CATEGORY = RestAddressableModel.CONFIGURATION;
|
||||
|
||||
private String topic;
|
||||
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCategory() {
|
||||
return CATEGORY;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public String getType() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypePlural() {
|
||||
return PLURAL_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class getController() {
|
||||
return RestResourceController.class;
|
||||
}
|
||||
|
||||
}
|
@@ -19,9 +19,9 @@ import org.dspace.app.rest.RestResourceController;
|
||||
*/
|
||||
@LinksRest(
|
||||
links = {
|
||||
@LinkRest(name = "topic", method = "getTopic"),
|
||||
@LinkRest(name = "target", method = "getTarget"),
|
||||
@LinkRest(name = "related", method = "getRelated")
|
||||
@LinkRest(name = "topic", method = "getTopic"),
|
||||
@LinkRest(name = "target", method = "getTarget"),
|
||||
@LinkRest(name = "related", method = "getRelated")
|
||||
})
|
||||
public class QAEventRest extends BaseObjectRest<String> {
|
||||
|
||||
|
@@ -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.CorrectionTypeRest;
|
||||
import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource;
|
||||
import org.dspace.app.rest.utils.Utils;
|
||||
|
||||
/**
|
||||
* CorrectionType Rest HAL Resource. The HAL Resource wraps the REST Resource
|
||||
* adding support for the links and embedded resources
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
@RelNameDSpaceResource(CorrectionTypeRest.NAME)
|
||||
public class CorrectionTypeResource extends DSpaceResource<CorrectionTypeRest> {
|
||||
|
||||
public CorrectionTypeResource(CorrectionTypeRest target, Utils utils) {
|
||||
super(target, utils);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* 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 java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.dspace.app.rest.Parameter;
|
||||
import org.dspace.app.rest.SearchRestMethod;
|
||||
import org.dspace.app.rest.exception.RESTAuthorizationException;
|
||||
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||
import org.dspace.app.rest.model.CorrectionTypeRest;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.correctiontype.CorrectionType;
|
||||
import org.dspace.correctiontype.service.CorrectionTypeService;
|
||||
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;
|
||||
|
||||
/**
|
||||
* The CorrectionType REST Repository
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
@Component(CorrectionTypeRest.CATEGORY + "." + CorrectionTypeRest.PLURAL_NAME)
|
||||
public class CorrectionTypeRestRepository extends DSpaceRestRepository<CorrectionTypeRest, String> {
|
||||
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
@Autowired
|
||||
private CorrectionTypeService correctionTypeService;
|
||||
|
||||
@PreAuthorize("hasAuthority('AUTHENTICATED')")
|
||||
@Override
|
||||
public CorrectionTypeRest findOne(Context context, String id) {
|
||||
CorrectionType correctionType = correctionTypeService.findOne(id);
|
||||
return Objects.nonNull(correctionType) ? converter.toRest(correctionType, utils.obtainProjection()) : null;
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('AUTHENTICATED')")
|
||||
@Override
|
||||
public Page<CorrectionTypeRest> findAll(Context context, Pageable pageable) {
|
||||
return converter.toRestPage(correctionTypeService.findAll(), pageable, utils.obtainProjection());
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('AUTHENTICATED')")
|
||||
@SearchRestMethod(name = "findByItem")
|
||||
public Page<CorrectionTypeRest> findByItem(@Parameter(value = "uuid",required = true) UUID uuid,Pageable pageable) {
|
||||
Context context = obtainContext();
|
||||
try {
|
||||
Item item = itemService.find(context, uuid);
|
||||
if (Objects.isNull(item)) {
|
||||
throw new UnprocessableEntityException("Item with uuid:" + uuid + " not found");
|
||||
}
|
||||
|
||||
List<CorrectionType> correctionTypes;
|
||||
try {
|
||||
correctionTypes = correctionTypeService.findByItem(context, item);
|
||||
} catch (AuthorizeException e) {
|
||||
throw new RESTAuthorizationException(e);
|
||||
}
|
||||
|
||||
return converter.toRestPage(correctionTypes, pageable, utils.obtainProjection());
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('AUTHENTICATED')")
|
||||
@SearchRestMethod(name = "findByTopic")
|
||||
public CorrectionTypeRest findByTopic(@Parameter(value = "topic", required = true) String topic) {
|
||||
CorrectionType correctionType = correctionTypeService.findByTopic(topic);
|
||||
return Objects.nonNull(correctionType) ? converter.toRest(correctionType, utils.obtainProjection()) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<CorrectionTypeRest> getDomainClass() {
|
||||
return CorrectionTypeRest.class;
|
||||
}
|
||||
|
||||
}
|
@@ -7,14 +7,18 @@
|
||||
*/
|
||||
package org.dspace.app.rest.repository;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.dspace.app.rest.Parameter;
|
||||
import org.dspace.app.rest.SearchRestMethod;
|
||||
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
|
||||
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||
import org.dspace.app.rest.model.QAEventRest;
|
||||
import org.dspace.app.rest.model.patch.Patch;
|
||||
import org.dspace.app.rest.repository.patch.ResourcePatch;
|
||||
@@ -23,9 +27,12 @@ import org.dspace.content.Item;
|
||||
import org.dspace.content.QAEvent;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.correctiontype.CorrectionType;
|
||||
import org.dspace.correctiontype.service.CorrectionTypeService;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.qaevent.dao.QAEventsDAO;
|
||||
import org.dspace.qaevent.service.QAEventService;
|
||||
import org.dspace.qaevent.service.dto.CorrectionTypeMessageDTO;
|
||||
import org.dspace.util.UUIDUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
@@ -56,6 +63,14 @@ public class QAEventRestRepository extends DSpaceRestRepository<QAEventRest, Str
|
||||
@Autowired
|
||||
private ResourcePatch<QAEvent> resourcePatch;
|
||||
|
||||
@Autowired
|
||||
private CorrectionTypeService correctionTypeService;
|
||||
|
||||
@Override
|
||||
public Page<QAEventRest> findAll(Context context, Pageable pageable) {
|
||||
throw new RepositoryMethodNotImplementedException(QAEventRest.NAME, "findAll");
|
||||
}
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasPermission(#id, 'QUALITYASSURANCEEVENT', 'READ')")
|
||||
public QAEventRest findOne(Context context, String id) {
|
||||
@@ -84,11 +99,9 @@ public class QAEventRestRepository extends DSpaceRestRepository<QAEventRest, Str
|
||||
String sourceName = topicIdSplitted[0];
|
||||
String topicName = topicIdSplitted[1].replaceAll("!", "/");
|
||||
UUID target = topicIdSplitted.length == 3 ? UUID.fromString(topicIdSplitted[2]) : null;
|
||||
List<QAEvent> qaEvents = null;
|
||||
long count = 0L;
|
||||
qaEvents = qaEventService.findEventsByTopicAndPageAndTarget(context, sourceName, topicName,
|
||||
List<QAEvent> qaEvents = qaEventService.findEventsByTopicAndPageAndTarget(context, sourceName, topicName,
|
||||
pageable.getOffset(), pageable.getPageSize(), target);
|
||||
count = qaEventService.countEventsByTopicAndTarget(context, sourceName, topicName, target);
|
||||
long count = qaEventService.countEventsByTopicAndTarget(context, sourceName, topicName, target);
|
||||
if (qaEvents == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -104,11 +117,6 @@ public class QAEventRestRepository extends DSpaceRestRepository<QAEventRest, Str
|
||||
qaEventDao.storeEvent(context, id, eperson, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<QAEventRest> findAll(Context context, Pageable pageable) {
|
||||
throw new RepositoryMethodNotImplementedException(QAEventRest.NAME, "findAll");
|
||||
}
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasPermission(#id, 'QUALITYASSURANCEEVENT', 'WRITE')")
|
||||
protected void patch(Context context, HttpServletRequest request, String apiCategory, String model,
|
||||
@@ -130,6 +138,64 @@ public class QAEventRestRepository extends DSpaceRestRepository<QAEventRest, Str
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('AUTHENTICATED')")
|
||||
protected QAEventRest createAndReturn(Context context) throws SQLException, AuthorizeException {
|
||||
ServletRequest request = getRequestService().getCurrentRequest().getServletRequest();
|
||||
|
||||
String itemUUID = request.getParameter("target");
|
||||
String relatedItemUUID = request.getParameter("related");
|
||||
String correctionTypeStr = request.getParameter("correctionType");
|
||||
|
||||
|
||||
if (StringUtils.isBlank(correctionTypeStr) || StringUtils.isBlank(itemUUID)) {
|
||||
throw new UnprocessableEntityException("The target item and correctionType must be provided!");
|
||||
}
|
||||
|
||||
Item targetItem = null;
|
||||
Item relatedItem = null;
|
||||
try {
|
||||
targetItem = itemService.find(context, UUID.fromString(itemUUID));
|
||||
relatedItem = StringUtils.isNotBlank(relatedItemUUID) ?
|
||||
itemService.find(context, UUID.fromString(relatedItemUUID)) : null;
|
||||
} catch (Exception e) {
|
||||
throw new UnprocessableEntityException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
if (Objects.isNull(targetItem)) {
|
||||
throw new UnprocessableEntityException("The target item UUID is not valid!");
|
||||
}
|
||||
|
||||
CorrectionType correctionType = correctionTypeService.findOne(correctionTypeStr);
|
||||
if (Objects.isNull(correctionType)) {
|
||||
throw new UnprocessableEntityException("The given correction type in the request is not valid!");
|
||||
}
|
||||
|
||||
if (correctionType.isRequiredRelatedItem() && Objects.isNull(relatedItem)) {
|
||||
throw new UnprocessableEntityException("The given correction type in the request is not valid!");
|
||||
}
|
||||
|
||||
if (!correctionType.isAllowed(context, targetItem)) {
|
||||
throw new UnprocessableEntityException("This item cannot be processed by this correction type!");
|
||||
}
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
CorrectionTypeMessageDTO reason = null;
|
||||
try {
|
||||
reason = mapper.readValue(request.getInputStream(), CorrectionTypeMessageDTO.class);
|
||||
} catch (IOException exIO) {
|
||||
throw new UnprocessableEntityException("error parsing the body " + exIO.getMessage(), exIO);
|
||||
}
|
||||
|
||||
QAEvent qaEvent;
|
||||
if (correctionType.isRequiredRelatedItem()) {
|
||||
qaEvent = correctionType.createCorrection(context, targetItem, relatedItem, reason);
|
||||
} else {
|
||||
qaEvent = correctionType.createCorrection(context, targetItem, reason);
|
||||
}
|
||||
return converter.toRest(qaEvent, utils.obtainProjection());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<QAEventRest> getDomainClass() {
|
||||
return QAEventRest.class;
|
||||
|
@@ -48,7 +48,7 @@ public class QAEventTargetLinkRepository extends AbstractDSpaceRestRepository im
|
||||
* @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
|
||||
* @return the item rest representation of the qa event target
|
||||
*/
|
||||
@PreAuthorize("hasPermission(#id, 'QUALITYASSURANCEEVENT', 'READ')")
|
||||
public ItemRest getTarget(@Nullable HttpServletRequest request, String id, @Nullable Pageable pageable,
|
||||
|
@@ -42,7 +42,7 @@ public class QAEventTopicLinkRepository extends AbstractDSpaceRestRepository imp
|
||||
* @param id the qa event id
|
||||
* @param pageable the optional pageable
|
||||
* @param projection the projection object
|
||||
* @return the qa topic rest representation
|
||||
* @return the qa topic rest representation
|
||||
*/
|
||||
@PreAuthorize("hasPermission(#id, 'QUALITYASSURANCEEVENT', 'READ')")
|
||||
public QATopicRest getTopic(@Nullable HttpServletRequest request, String id, @Nullable Pageable pageable,
|
||||
|
@@ -55,10 +55,10 @@ public class QASourceRestRepository extends DSpaceRestRepository<QASourceRest, S
|
||||
@SearchRestMethod(name = "byTarget")
|
||||
@PreAuthorize("hasAuthority('AUTHENTICATED')")
|
||||
public Page<QASourceRest> findByTarget(@Parameter(value = "target", required = true) UUID target,
|
||||
Pageable pageable) {
|
||||
Pageable pageable) {
|
||||
Context context = obtainContext();
|
||||
List<QASource> topics = qaEventService
|
||||
.findAllSourcesByTarget(context, target, pageable.getOffset(), pageable.getPageSize());
|
||||
List<QASource> topics = qaEventService.findAllSourcesByTarget(context, target,
|
||||
pageable.getOffset(), pageable.getPageSize());
|
||||
long count = qaEventService.countSourcesByTarget(context, target);
|
||||
if (topics == null) {
|
||||
return null;
|
||||
|
@@ -42,6 +42,11 @@ public class QATopicRestRepository extends DSpaceRestRepository<QATopicRest, Str
|
||||
|
||||
private static final Logger log = LogManager.getLogger();
|
||||
|
||||
@Override
|
||||
public Page<QATopicRest> findAll(Context context, Pageable pageable) {
|
||||
throw new RepositoryMethodNotImplementedException("Method not allowed!", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasPermission(#id, 'QUALITYASSURANCETOPIC', 'READ')")
|
||||
public QATopicRest findOne(Context context, String id) {
|
||||
@@ -53,30 +58,21 @@ public class QATopicRestRepository extends DSpaceRestRepository<QATopicRest, Str
|
||||
String topicName = topicIdSplitted[1].replaceAll("!", "/");
|
||||
UUID target = topicIdSplitted.length == 3 ? UUID.fromString(topicIdSplitted[2]) : null;
|
||||
QATopic topic = qaEventService.findTopicBySourceAndNameAndTarget(context, sourceName, topicName, target);
|
||||
if (topic == null) {
|
||||
return null;
|
||||
}
|
||||
return converter.toRest(topic, utils.obtainProjection());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<QATopicRest> findAll(Context context, Pageable pageable) {
|
||||
throw new RepositoryMethodNotImplementedException("Method not allowed!", "");
|
||||
return (topic != null) ? converter.toRest(topic, utils.obtainProjection()) : null;
|
||||
}
|
||||
|
||||
@SearchRestMethod(name = "bySource")
|
||||
@PreAuthorize("hasPermission(#source, 'QUALITYASSURANCETOPIC', 'READ')")
|
||||
public Page<QATopicRest> findBySource(@Parameter(value = "source", required = true) String source,
|
||||
Pageable pageable) {
|
||||
Pageable pageable) {
|
||||
Context context = obtainContext();
|
||||
long count = qaEventService.countTopicsBySource(context, source);
|
||||
boolean ascending = false;
|
||||
if (pageable.getSort() != null && pageable.getSort().getOrderFor(ORDER_FIELD) != null) {
|
||||
ascending = pageable.getSort()
|
||||
.getOrderFor(ORDER_FIELD).getDirection() == Direction.ASC;
|
||||
ascending = pageable.getSort().getOrderFor(ORDER_FIELD).getDirection() == Direction.ASC;
|
||||
}
|
||||
List<QATopic> topics = qaEventService.findAllTopicsBySource(context, source,
|
||||
pageable.getOffset(), pageable.getPageSize(), ORDER_FIELD, ascending);
|
||||
pageable.getOffset(), pageable.getPageSize(), ORDER_FIELD, ascending);
|
||||
long count = qaEventService.countTopicsBySource(context, source);
|
||||
if (topics == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -86,20 +82,15 @@ public class QATopicRestRepository extends DSpaceRestRepository<QATopicRest, Str
|
||||
@SearchRestMethod(name = "byTarget")
|
||||
@PreAuthorize("hasPermission(#target, 'ITEM', 'READ')")
|
||||
public Page<QATopicRest> findByTarget(@Parameter(value = "target", required = true) UUID target,
|
||||
@Parameter(value = "source", required = true) String source,
|
||||
Pageable pageable) {
|
||||
@Parameter(value = "source", required = true) String source, Pageable pageable) {
|
||||
Context context = obtainContext();
|
||||
List<QATopic> topics = qaEventService
|
||||
.findAllTopicsBySourceAndTarget(context, source, target, pageable.getOffset(),
|
||||
pageable.getPageSize(), null, true);
|
||||
long count = qaEventService.countTopicsBySourceAndTarget(context, source, target);
|
||||
|
||||
if (topics == null) {
|
||||
return null;
|
||||
boolean ascending = false;
|
||||
if (pageable.getSort() != null && pageable.getSort().getOrderFor(ORDER_FIELD) != null) {
|
||||
ascending = pageable.getSort().getOrderFor(ORDER_FIELD).getDirection() == Direction.ASC;
|
||||
}
|
||||
return converter.toRestPage(topics, pageable, count, utils.obtainProjection());
|
||||
}
|
||||
|
||||
List<QATopic> topics = qaEventService.findAllTopicsBySourceAndTarget(context, source, target,
|
||||
pageable.getOffset(), pageable.getPageSize(), ORDER_FIELD, ascending);
|
||||
long count = qaEventService.countTopicsBySourceAndTarget(context, source, target);
|
||||
@Override
|
||||
public Class<QATopicRest> getDomainClass() {
|
||||
return QATopicRest.class;
|
||||
|
@@ -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.app.rest.repository.handler;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.dspace.app.rest.exception.DSpaceBadRequestException;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.correctiontype.CorrectionType;
|
||||
import org.dspace.correctiontype.service.CorrectionTypeService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* This class extends the {@link ExternalSourceEntryItemUriListHandler} abstract class and implements it specifically
|
||||
* for the List<{@link CorrectionType}> objects.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
@Component
|
||||
public class ExternalSourceCorrectionTypeUriListHandler extends ExternalSourceEntryItemUriListHandler<CorrectionType> {
|
||||
|
||||
@Autowired
|
||||
private CorrectionTypeService correctionTypeService;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public boolean supports(List<String> uriList, String method,Class clazz) {
|
||||
return clazz != CorrectionType.class ? false : true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CorrectionType handle(Context context, HttpServletRequest request, List<String> uriList)
|
||||
throws SQLException, AuthorizeException {
|
||||
return getObjectFromUriList(context, uriList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(Context context, HttpServletRequest request, List<String> uriList)
|
||||
throws AuthorizeException {
|
||||
return uriList.size() > 1 ? false : true;
|
||||
}
|
||||
|
||||
|
||||
private CorrectionType getObjectFromUriList(Context context, List<String> uriList) {
|
||||
CorrectionType correctionType = null;
|
||||
String url = uriList.get(0);
|
||||
Pattern pattern = Pattern.compile("\\/api\\/config\\/correctiontypes\\/(.*)");
|
||||
Matcher matcher = pattern.matcher(url);
|
||||
if (!matcher.find()) {
|
||||
throw new DSpaceBadRequestException("The uri: " + url + " doesn't resolve to an correction type");
|
||||
}
|
||||
String id = matcher.group(1);
|
||||
try {
|
||||
correctionType = correctionTypeService.findOne(id);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
return correctionType;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,308 @@
|
||||
/**
|
||||
* 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 org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
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.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.dspace.app.rest.repository.CorrectionTypeRestRepository;
|
||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
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.content.service.ItemService;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
/**
|
||||
* Test suite for {@link CorrectionTypeRestRepository}
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.com)
|
||||
*/
|
||||
public class CorrectionTypeRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
|
||||
@Test
|
||||
public void findAllAdminTest() throws Exception {
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(adminToken).perform(get("/api/config/correctiontypes"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", greaterThanOrEqualTo(2)))
|
||||
.andExpect(jsonPath("$._embedded.correctiontypes", containsInAnyOrder(
|
||||
allOf(
|
||||
hasJsonPath("$.id", equalTo("request-withdrawn")),
|
||||
hasJsonPath("$.topic", equalTo("REQUEST/WITHDRAWN")),
|
||||
hasJsonPath("$.type", equalTo("correctiontype"))
|
||||
),
|
||||
allOf(
|
||||
hasJsonPath("$.id", equalTo("request-reinstate")),
|
||||
hasJsonPath("$.topic", equalTo("REQUEST/REINSTATE")),
|
||||
hasJsonPath("$.type", equalTo("correctiontype"))
|
||||
)
|
||||
)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAllEPersonTest() throws Exception {
|
||||
String ePersonToken = getAuthToken(eperson.getEmail(), password);
|
||||
getClient(ePersonToken).perform(get("/api/config/correctiontypes"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", greaterThanOrEqualTo(2)))
|
||||
.andExpect(jsonPath("$._embedded.correctiontypes", containsInAnyOrder(
|
||||
allOf(
|
||||
hasJsonPath("$.id", equalTo("request-withdrawn")),
|
||||
hasJsonPath("$.topic", equalTo("REQUEST/WITHDRAWN")),
|
||||
hasJsonPath("$.type", equalTo("correctiontype"))
|
||||
),
|
||||
allOf(
|
||||
hasJsonPath("$.id", equalTo("request-reinstate")),
|
||||
hasJsonPath("$.topic", equalTo("REQUEST/REINSTATE")),
|
||||
hasJsonPath("$.type", equalTo("correctiontype"))
|
||||
)
|
||||
)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAllUnauthorizedTest() throws Exception {
|
||||
getClient().perform(get("/api/config/correctiontypes"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findOneAdminTest() throws Exception {
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(adminToken).perform(get("/api/config/correctiontypes/request-withdrawn"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id", equalTo("request-withdrawn")))
|
||||
.andExpect(jsonPath("$.topic", equalTo("REQUEST/WITHDRAWN")))
|
||||
.andExpect(jsonPath("$.type", equalTo("correctiontype")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findOneEPersonTest() throws Exception {
|
||||
String epersonToken = getAuthToken(eperson.getEmail(), password);
|
||||
getClient(epersonToken).perform(get("/api/config/correctiontypes/request-withdrawn"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id", equalTo("request-withdrawn")))
|
||||
.andExpect(jsonPath("$.topic", equalTo("REQUEST/WITHDRAWN")))
|
||||
.andExpect(jsonPath("$.type", equalTo("correctiontype")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findOneUnauthorizedTest() throws Exception {
|
||||
getClient().perform(get("/api/config/correctiontypes/request-withdrawn"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findOneNotFoundTest() throws Exception {
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(adminToken).perform(get("/api/config/correctiontypes/test"))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByItemWithoutUUIDParameterTest() throws Exception {
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(adminToken).perform(get("/api/config/correctiontypes/search/findByItem"))
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByItemNotFoundTest() throws Exception {
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(adminToken).perform(get("/api/config/correctiontypes/search/findByItem")
|
||||
.param("uuid", UUID.randomUUID().toString()))
|
||||
.andExpect(status().isUnprocessableEntity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByItemUnAuthorizedTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
parentCommunity = CommunityBuilder.createCommunity(context).build();
|
||||
Collection collection = CollectionBuilder.createCollection(context, parentCommunity).build();
|
||||
Item privateItem = ItemBuilder.createItem(context, collection).build();
|
||||
authorizeService.removeAllPolicies(context, privateItem);
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
getClient().perform(get("/api/config/correctiontypes/search/findByItem")
|
||||
.param("uuid", privateItem.getID().toString()))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByNotArchivedItemTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
parentCommunity = CommunityBuilder.createCommunity(context).build();
|
||||
Collection collection = CollectionBuilder.createCollection(context, parentCommunity).build();
|
||||
Item item = ItemBuilder.createItem(context, collection).build();
|
||||
item.setArchived(false);
|
||||
itemService.update(context, item);
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(adminToken).perform(get("/api/config/correctiontypes/search/findByItem")
|
||||
.param("uuid", item.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByWithdrawnItemTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
.build();
|
||||
Collection collection = CollectionBuilder.createCollection(context, parentCommunity)
|
||||
.build();
|
||||
Item item = ItemBuilder.createItem(context, collection)
|
||||
.withdrawn()
|
||||
.build();
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String tokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||
getClient(tokenAdmin).perform(get("/api/config/correctiontypes/search/findByItem")
|
||||
.param("uuid", item.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", is(1)))
|
||||
.andExpect(jsonPath("$._embedded.correctiontypes", containsInAnyOrder(allOf(
|
||||
hasJsonPath("$.id", equalTo("request-reinstate")),
|
||||
hasJsonPath("$.topic", equalTo("REQUEST/REINSTATE"))
|
||||
))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByNotDiscoverableItemTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
parentCommunity = CommunityBuilder.createCommunity(context).build();
|
||||
Collection collection = CollectionBuilder.createCollection(context, parentCommunity).build();
|
||||
Item item = ItemBuilder.createItem(context, collection).build();
|
||||
item.setDiscoverable(false);
|
||||
itemService.update(context, item);
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(adminToken).perform(get("/api/config/correctiontypes/search/findByItem")
|
||||
.param("uuid", item.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", is(1)))
|
||||
.andExpect(jsonPath("$._embedded.correctiontypes", containsInAnyOrder(
|
||||
allOf(
|
||||
hasJsonPath("$.id", equalTo("request-withdrawn")),
|
||||
hasJsonPath("$.topic", equalTo("REQUEST/WITHDRAWN")),
|
||||
hasJsonPath("$.type", equalTo("correctiontype"))
|
||||
)
|
||||
)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByPersonalArchiveItemTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
parentCommunity = CommunityBuilder.createCommunity(context).build();
|
||||
Collection collection = CollectionBuilder.createCollection(context, parentCommunity).build();
|
||||
Item itemOne = ItemBuilder.createItem(context, collection)
|
||||
.build();
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(adminToken).perform(get("/api/config/correctiontypes/search/findByItem")
|
||||
.param("uuid", itemOne.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", is(1)))
|
||||
.andExpect(jsonPath("$._embedded.correctiontypes", containsInAnyOrder(
|
||||
allOf(
|
||||
hasJsonPath("$.id", equalTo("request-withdrawn")),
|
||||
hasJsonPath("$.topic", equalTo("REQUEST/WITHDRAWN")),
|
||||
hasJsonPath("$.type", equalTo("correctiontype"))
|
||||
)
|
||||
)));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByItemTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
parentCommunity = CommunityBuilder.createCommunity(context).build();
|
||||
Collection collection = CollectionBuilder.createCollection(context, parentCommunity).build();
|
||||
Item item = ItemBuilder.createItem(context, collection).build();
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(adminToken).perform(get("/api/config/correctiontypes/search/findByItem")
|
||||
.param("uuid", item.getID().toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", is(1)))
|
||||
.andExpect(jsonPath("$._embedded.correctiontypes", containsInAnyOrder(
|
||||
allOf(
|
||||
hasJsonPath("$.id", equalTo("request-withdrawn")),
|
||||
hasJsonPath("$.topic", equalTo("REQUEST/WITHDRAWN")),
|
||||
hasJsonPath("$.type", equalTo("correctiontype"))
|
||||
)
|
||||
)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByTopicWithoutTopicParameterTest() throws Exception {
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(adminToken).perform(get("/api/config/correctiontypes/search/findByTopic"))
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByWrongTopicTest() throws Exception {
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(adminToken).perform(get("/api/config/correctiontypes/search/findByTopic")
|
||||
.param("topic", "wrongValue"))
|
||||
.andExpect(status().isNoContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByTopicAdminTest() throws Exception {
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(adminToken).perform(get("/api/config/correctiontypes/search/findByTopic")
|
||||
.param("topic", "REQUEST/WITHDRAWN"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id", equalTo("request-withdrawn")))
|
||||
.andExpect(jsonPath("$.topic", equalTo("REQUEST/WITHDRAWN")))
|
||||
.andExpect(jsonPath("$.type", equalTo("correctiontype")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByTopicEPersonTest() throws Exception {
|
||||
String ePersonToken = getAuthToken(eperson.getEmail(), password);
|
||||
getClient(ePersonToken).perform(get("/api/config/correctiontypes/search/findByTopic")
|
||||
.param("topic", "REQUEST/WITHDRAWN"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id", equalTo("request-withdrawn")))
|
||||
.andExpect(jsonPath("$.topic", equalTo("REQUEST/WITHDRAWN")))
|
||||
.andExpect(jsonPath("$.type", equalTo("correctiontype")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByTopicUnauthorizedTest() throws Exception {
|
||||
getClient().perform(get("/api/config/correctiontypes/search/findByTopic")
|
||||
.param("topic", "REQUEST/WITHDRAWN"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@
|
||||
package org.dspace.app.rest;
|
||||
|
||||
import static org.dspace.app.rest.matcher.QASourceMatcher.matchQASourceEntry;
|
||||
import static org.dspace.qaevent.service.impl.QAEventServiceImpl.QAEVENTS_SOURCES;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
@@ -45,35 +46,31 @@ public class QASourceRestRepositoryIT extends AbstractControllerIntegrationTest
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
.withTitle("Community")
|
||||
.build();
|
||||
.withTitle("Community")
|
||||
.build();
|
||||
|
||||
Collection collection = CollectionBuilder.createCollection(context, parentCommunity)
|
||||
.withName("Collection")
|
||||
.build();
|
||||
.withName("Collection")
|
||||
.build();
|
||||
|
||||
target = ItemBuilder.createItem(context, collection)
|
||||
.withTitle("Item")
|
||||
.build();
|
||||
.withTitle("Item")
|
||||
.build();
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
configurationService.setProperty("qaevent.sources",
|
||||
new String[] { "openaire", "coar-notify", "test-source", "test-source-2" });
|
||||
|
||||
configurationService.setProperty(QAEVENTS_SOURCES, new String[] { "openaire","coar-notify", "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");
|
||||
context.setCurrentUser(eperson);
|
||||
createEvent("openaire", "TOPIC/OPENAIRE/2", "Title 3");
|
||||
createEvent("openaire", "TOPIC/OPENAIRE/2", "Title 4");
|
||||
|
||||
@@ -111,7 +108,6 @@ public class QASourceRestRepositoryIT extends AbstractControllerIntegrationTest
|
||||
|
||||
@Test
|
||||
public void testFindAllUnauthorized() throws Exception {
|
||||
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
createEvent("openaire", "TOPIC/OPENAIRE/1", "Title 1");
|
||||
@@ -120,8 +116,7 @@ public class QASourceRestRepositoryIT extends AbstractControllerIntegrationTest
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
getClient().perform(get("/api/integration/qualityassurancesources"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -7,6 +7,8 @@
|
||||
*/
|
||||
package org.dspace.app.rest;
|
||||
|
||||
import static org.dspace.content.QAEvent.OPENAIRE_SOURCE;
|
||||
import static org.dspace.qaevent.service.impl.QAEventServiceImpl.QAEVENTS_SOURCES;
|
||||
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;
|
||||
@@ -44,74 +46,60 @@ public class QATopicRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void findAllNotImplementedTest() 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(QANotifyPatterns.TOPIC_ENRICH_MISSING_PID)
|
||||
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||
QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
|
||||
.withTopic(QANotifyPatterns.TOPIC_ENRICH_MISSING_PID)
|
||||
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
|
||||
QAEventBuilder.createTarget(context, col1, "Science and Freedom 3")
|
||||
.withTopic(QANotifyPatterns.TOPIC_ENRICH_MORE_PID)
|
||||
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"10.2307/2144302\"}").build();
|
||||
QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
|
||||
.withTopic(org.dspace.qaevent.QANotifyPatterns.TOPIC_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().isMethodNotAllowed());
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(adminToken).perform(get("/api/integration/qualityassurancetopics"))
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findOneTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
configurationService.setProperty("qaevent.sources",
|
||||
new String[] { "openaire", "test-source" });
|
||||
configurationService.setProperty(QAEVENTS_SOURCES, new String[] { OPENAIRE_SOURCE, "test-source" });
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
|
||||
.withName("Collection 1")
|
||||
.build();
|
||||
QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||
.withTopic(QANotifyPatterns.TOPIC_ENRICH_MISSING_PID)
|
||||
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||
.withTopic(QANotifyPatterns.TOPIC_ENRICH_MISSING_PID)
|
||||
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}")
|
||||
.build();
|
||||
QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
|
||||
.withTopic(QANotifyPatterns.TOPIC_ENRICH_MISSING_PID)
|
||||
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
|
||||
.withTopic(QANotifyPatterns.TOPIC_ENRICH_MISSING_PID)
|
||||
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}")
|
||||
.build();
|
||||
QAEventBuilder.createTarget(context, col1, "Science and Freedom 3")
|
||||
.withTopic(QANotifyPatterns.TOPIC_ENRICH_MORE_PID)
|
||||
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"10.2307/2144302\"}").build();
|
||||
.withTopic(QANotifyPatterns.TOPIC_ENRICH_MORE_PID)
|
||||
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"10.2307/2144302\"}")
|
||||
.build();
|
||||
QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
|
||||
.withTopic(org.dspace.qaevent.QANotifyPatterns.TOPIC_ENRICH_MISSING_ABSTRACT)
|
||||
.withMessage(
|
||||
"{\"test\": \"Test...\"}")
|
||||
.build();
|
||||
.withTopic(org.dspace.qaevent.QANotifyPatterns.TOPIC_ENRICH_MISSING_ABSTRACT)
|
||||
.withMessage("{\"test\": \"Test...\"}")
|
||||
.build();
|
||||
QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
|
||||
.withSource("test-source")
|
||||
.withTopic("TOPIC/TEST")
|
||||
.withMessage(
|
||||
"{\"abstracts[0]\": \"Descrizione delle caratteristiche...\"}")
|
||||
.build();
|
||||
.withSource("test-source")
|
||||
.withTopic("TOPIC/TEST")
|
||||
.withMessage("{\"abstracts[0]\": \"Descrizione delle caratteristiche...\"}")
|
||||
.build();
|
||||
context.restoreAuthSystemState();
|
||||
String authToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(authToken).perform(get("/api/integration/qualityassurancetopics/openaire:ENRICH!MISSING!PID"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$",
|
||||
QATopicMatcher.matchQATopicEntry(QANotifyPatterns.TOPIC_ENRICH_MISSING_PID, 2)));
|
||||
getClient(authToken).perform(get("/api/integration/qualityassurancetopics/openaire:ENRICH!MISSING!ABSTRACT"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$",
|
||||
QATopicMatcher.matchQATopicEntry(QANotifyPatterns.TOPIC_ENRICH_MISSING_ABSTRACT, 1)));
|
||||
getClient(authToken).perform(get("/api/integration/qualityassurancetopics/test-source:TOPIC!TEST"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$",
|
||||
QATopicMatcher.matchQATopicEntry("test-source", "TOPIC/TEST", 1)));
|
||||
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(adminToken).perform(
|
||||
get("/api/integration/qualityassurancetopics/" + OPENAIRE_SOURCE + ":ENRICH!MISSING!PID"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", QATopicMatcher.matchQATopicEntry(
|
||||
QANotifyPatterns.TOPIC_ENRICH_MISSING_PID, 2)));
|
||||
|
||||
getClient(adminToken).perform(get("/api/integration/qualityassurancetopics/"
|
||||
+ OPENAIRE_SOURCE + ":ENRICH!MISSING!ABSTRACT"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", QATopicMatcher.matchQATopicEntry(
|
||||
QANotifyPatterns.TOPIC_ENRICH_MISSING_ABSTRACT, 1)));
|
||||
|
||||
getClient(adminToken).perform(get("/api/integration/qualityassurancetopics/test-source:TOPIC!TEST"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$",QATopicMatcher.matchQATopicEntry("test-source", "TOPIC/TEST", 1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -176,68 +164,77 @@ public class QATopicRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||
@Test
|
||||
public void findBySourceTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
configurationService.setProperty("qaevent.sources",
|
||||
new String[] { QAEvent.OPENAIRE_SOURCE, "test-source", "test-source-2" });
|
||||
configurationService.setProperty(QAEVENTS_SOURCES, new String[] {
|
||||
OPENAIRE_SOURCE, "test-source", "test-source-2" });
|
||||
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
|
||||
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
|
||||
.withName("Collection 1")
|
||||
.build();
|
||||
|
||||
QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||
.withTopic(QANotifyPatterns.TOPIC_ENRICH_MISSING_PID)
|
||||
.withSource(QAEvent.OPENAIRE_SOURCE)
|
||||
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
|
||||
.withTopic(QANotifyPatterns.TOPIC_ENRICH_MISSING_PID)
|
||||
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}")
|
||||
.build();
|
||||
QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
|
||||
.withTopic(QANotifyPatterns.TOPIC_ENRICH_MISSING_PID)
|
||||
.withSource(QAEvent.OPENAIRE_SOURCE)
|
||||
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
|
||||
.withTopic(QANotifyPatterns.TOPIC_ENRICH_MISSING_PID)
|
||||
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}")
|
||||
.build();
|
||||
QAEventBuilder.createTarget(context, col1, "Science and Freedom 3")
|
||||
.withTopic(QANotifyPatterns.TOPIC_ENRICH_MORE_PID)
|
||||
.withSource(QAEvent.OPENAIRE_SOURCE)
|
||||
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"10.2307/2144302\"}").build();
|
||||
.withTopic(QANotifyPatterns.TOPIC_ENRICH_MORE_PID)
|
||||
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"10.2307/2144302\"}")
|
||||
.build();
|
||||
QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
|
||||
.withTopic(org.dspace.qaevent.QANotifyPatterns.TOPIC_ENRICH_MISSING_ABSTRACT)
|
||||
.withSource(QAEvent.OPENAIRE_SOURCE)
|
||||
.withMessage(
|
||||
"{\"abstracts[0]\": \"Descrizione delle caratteristiche...\"}")
|
||||
.build();
|
||||
.withTopic(org.dspace.qaevent.QANotifyPatterns.TOPIC_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();
|
||||
.withTopic("TEST/TOPIC")
|
||||
.withSource("test-source")
|
||||
.build();
|
||||
QAEventBuilder.createTarget(context, col1, "Science and Freedom 6")
|
||||
.withTopic("TEST/TOPIC")
|
||||
.withSource("test-source")
|
||||
.build();
|
||||
.withTopic("TEST/TOPIC")
|
||||
.withSource("test-source")
|
||||
.build();
|
||||
QAEventBuilder.createTarget(context, col1, "Science and Freedom 7")
|
||||
.withTopic("TEST/TOPIC/2")
|
||||
.withSource("test-source")
|
||||
.build();
|
||||
.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"))
|
||||
.param("source", OPENAIRE_SOURCE))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.qualityassurancetopics",
|
||||
Matchers.containsInAnyOrder(
|
||||
QATopicMatcher.matchQATopicEntry(QANotifyPatterns.TOPIC_ENRICH_MISSING_PID, 2),
|
||||
QATopicMatcher.matchQATopicEntry(QANotifyPatterns.TOPIC_ENRICH_MISSING_ABSTRACT, 1),
|
||||
QATopicMatcher.matchQATopicEntry(QANotifyPatterns.TOPIC_ENRICH_MORE_PID, 1))))
|
||||
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(3)));
|
||||
QATopicMatcher.matchQATopicEntry(QANotifyPatterns.TOPIC_ENRICH_MISSING_ABSTRACT, 1),
|
||||
QATopicMatcher.matchQATopicEntry(QANotifyPatterns.TOPIC_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-source", "TEST/TOPIC/2", 1),
|
||||
QATopicMatcher.matchQATopicEntry("test-source", "TEST/TOPIC", 2))))
|
||||
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(2)));
|
||||
.param("source", "test-source"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.qualityassurancetopics", Matchers.containsInAnyOrder(
|
||||
QATopicMatcher.matchQATopicEntry("test-source", "TEST/TOPIC/2", 1),
|
||||
QATopicMatcher.matchQATopicEntry("test-source", "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)));
|
||||
.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
|
||||
@@ -310,15 +307,19 @@ public class QATopicRestRepositoryIT extends AbstractControllerIntegrationTest {
|
||||
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();
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
|
||||
.withName("Collection 1")
|
||||
.build();
|
||||
QAEventBuilder.createTarget(context, col1, "Science and Freedom")
|
||||
.withTopic(QANotifyPatterns.TOPIC_ENRICH_MISSING_PID).build();
|
||||
.withTopic(QANotifyPatterns.TOPIC_ENRICH_MISSING_PID)
|
||||
.build();
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
getClient().perform(get("/api/integration/qualityassurancetopics/search/bySource")
|
||||
.param("source", "openaire"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
.param("source", OPENAIRE_SOURCE))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -13,6 +13,8 @@ import static org.hamcrest.Matchers.emptyOrNullString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -53,10 +55,11 @@ public class QAEventMatcher {
|
||||
public static Matcher<? super Object> matchQAEventEntry(QAEvent event) {
|
||||
try {
|
||||
ObjectMapper jsonMapper = new JsonMapper();
|
||||
DecimalFormat decimalFormat = new DecimalFormat("0.000", new DecimalFormatSymbols(Locale.ENGLISH));
|
||||
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("$.trust", is(decimalFormat.format(event.getTrust()))),
|
||||
hasJsonPath("$.status", Matchers.equalToIgnoringCase(event.getStatus())),
|
||||
hasJsonPath("$.message",
|
||||
matchMessage(event.getTopic(), jsonMapper.readValue(event.getMessage(),
|
||||
|
@@ -43,7 +43,6 @@ public class QATopicMatcher {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public static Matcher<? super Object> matchQATopicEntry(String source, String topicName) {
|
||||
return allOf(
|
||||
hasJsonPath("$.type", is("qualityassurancetopic")),
|
||||
@@ -52,7 +51,8 @@ public class QATopicMatcher {
|
||||
);
|
||||
}
|
||||
|
||||
public static Matcher matchQATopicEntry(String source, String topicName, String itemUuid, int totalEvents) {
|
||||
public static Matcher<? super Object> matchQATopicEntry(String source, String topicName, String itemUuid,
|
||||
int totalEvents) {
|
||||
return allOf(
|
||||
hasJsonPath("$.type", is("qualityassurancetopic")),
|
||||
hasJsonPath("$.name", is(topicName)),
|
||||
|
@@ -19,6 +19,7 @@ import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
@@ -94,6 +95,15 @@ public class AbstractWebClientIntegrationTest extends AbstractIntegrationTestWit
|
||||
return getClient().getForEntity(getURL(path), String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a request (defined by RequestEntity) and return response as a String
|
||||
* @param request RequestEntity object which defines the GET request
|
||||
* @return ResponseEntity with a String body
|
||||
*/
|
||||
public ResponseEntity<String> responseAsString(RequestEntity request) {
|
||||
return getClient().exchange(request, String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an authenticated (via Basic Auth) GET request and return response as a String
|
||||
* @param path path to perform GET against
|
||||
@@ -107,10 +117,10 @@ public class AbstractWebClientIntegrationTest extends AbstractIntegrationTestWit
|
||||
|
||||
/**
|
||||
* Perform an authenticated (via Basic Auth) POST request and return response as a String.
|
||||
* @param path path to perform GET against
|
||||
* @param path path to perform POST against
|
||||
* @param username Username (may be null to perform an unauthenticated POST)
|
||||
* @param password Password
|
||||
* @param requestEntity unknown -- not used.
|
||||
* @param requestEntity HttpEntity to specify content/headers to POST
|
||||
* @return ResponseEntity with a String body
|
||||
*/
|
||||
public ResponseEntity<String> postResponseAsString(String path, String username, String password,
|
||||
|
@@ -9,19 +9,36 @@
|
||||
package org.dspace.app.sword2;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.app.rest.test.AbstractWebClientIntegrationTest;
|
||||
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.services.ConfigurationService;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.ContentDisposition;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
|
||||
/**
|
||||
* Integration test to verify the /swordv2 endpoint is responding as a valid SWORDv2 endpoint.
|
||||
@@ -41,11 +58,24 @@ public class Swordv2IT extends AbstractWebClientIntegrationTest {
|
||||
private ConfigurationService configurationService;
|
||||
|
||||
// All SWORD v2 paths that we test against
|
||||
private final String SERVICE_DOC_PATH = "/swordv2/servicedocument";
|
||||
private final String COLLECTION_PATH = "/swordv2/collection";
|
||||
private final String MEDIA_RESOURCE_PATH = "/swordv2/edit-media";
|
||||
private final String CONTAINER_PATH = "/swordv2/edit";
|
||||
private final String STATEMENT_PATH = "/swordv2/statement";
|
||||
private final String SWORD_PATH = "/swordv2";
|
||||
private final String SERVICE_DOC_PATH = SWORD_PATH + "/servicedocument";
|
||||
private final String COLLECTION_PATH = SWORD_PATH + "/collection";
|
||||
private final String MEDIA_RESOURCE_PATH = SWORD_PATH + "/edit-media";
|
||||
private final String EDIT_PATH = SWORD_PATH + "/edit";
|
||||
private final String STATEMENT_PATH = SWORD_PATH + "/statement";
|
||||
|
||||
// Content Types used
|
||||
private final String ATOM_SERVICE_CONTENT_TYPE = "application/atomserv+xml;charset=UTF-8";
|
||||
private final String ATOM_FEED_CONTENT_TYPE = "application/atom+xml;type=feed;charset=UTF-8";
|
||||
private final String ATOM_ENTRY_CONTENT_TYPE = "application/atom+xml;type=entry;charset=UTF-8";
|
||||
|
||||
/**
|
||||
* Create a global temporary upload folder which will be cleaned up automatically by JUnit.
|
||||
* NOTE: As a ClassRule, this temp folder is shared by ALL tests below.
|
||||
**/
|
||||
@ClassRule
|
||||
public static final TemporaryFolder uploadTempFolder = new TemporaryFolder();
|
||||
|
||||
@Before
|
||||
public void onlyRunIfConfigExists() {
|
||||
@@ -60,7 +90,18 @@ public class Swordv2IT extends AbstractWebClientIntegrationTest {
|
||||
|
||||
// Ensure SWORDv2 URL configurations are set correctly (based on our integration test server's paths)
|
||||
// SWORDv2 validates requests against these configs, and throws a 404 if they don't match the request path
|
||||
configurationService.setProperty("swordv2-server.url", getURL(SWORD_PATH));
|
||||
configurationService.setProperty("swordv2-server.servicedocument.url", getURL(SERVICE_DOC_PATH));
|
||||
configurationService.setProperty("swordv2-server.collection.url", getURL(COLLECTION_PATH));
|
||||
|
||||
// Override default value of SWORD upload directory to point at our JUnit TemporaryFolder (see above).
|
||||
// This ensures uploaded files are saved in a place where JUnit can clean them up automatically.
|
||||
configurationService.setProperty("swordv2-server.upload.tempdir",
|
||||
uploadTempFolder.getRoot().getAbsolutePath());
|
||||
|
||||
// MUST be set to allow DELETE requests on Items which are in the archive. (This isn't enabled by default)
|
||||
configurationService.setProperty("plugin.single.org.dspace.sword2.WorkflowManager",
|
||||
"org.dspace.sword2.WorkflowManagerUnrestricted");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -68,22 +109,20 @@ public class Swordv2IT extends AbstractWebClientIntegrationTest {
|
||||
// Attempt to GET the ServiceDocument without first authenticating
|
||||
ResponseEntity<String> response = getResponseAsString(SERVICE_DOC_PATH);
|
||||
// Expect a 401 response code
|
||||
assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED));
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serviceDocumentTest() throws Exception {
|
||||
// Attempt to GET the ServiceDocument as an Admin user.
|
||||
// Attempt to GET the ServiceDocument as any user account
|
||||
ResponseEntity<String> response = getResponseAsString(SERVICE_DOC_PATH,
|
||||
admin.getEmail(), password);
|
||||
// Expect a 200 response code, and an ATOM UTF-8 document
|
||||
assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
|
||||
assertThat(response.getHeaders().getContentType().toString(),
|
||||
equalTo("application/atomserv+xml;charset=UTF-8"));
|
||||
eperson.getEmail(), password);
|
||||
// Expect a 200 response code, and an ATOM service document
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertEquals(ATOM_SERVICE_CONTENT_TYPE, response.getHeaders().getContentType().toString());
|
||||
|
||||
// Check for correct SWORD version in response body
|
||||
assertThat(response.getBody(),
|
||||
containsString("<version xmlns=\"http://purl.org/net/sword/terms/\">2.0</version>"));
|
||||
assertThat(response.getBody(), containsString("<version xmlns=\"http://purl.org/net/sword/terms/\">2.0</version>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -91,44 +130,204 @@ public class Swordv2IT extends AbstractWebClientIntegrationTest {
|
||||
// Attempt to POST to /collection endpoint without sending authentication information
|
||||
ResponseEntity<String> response = postResponseAsString(COLLECTION_PATH, null, null, null);
|
||||
// Expect a 401 response code
|
||||
assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED));
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* In DSpace the /collections/[handle-prefix]/[handle-suffix] endpoint gives a list of all Items
|
||||
* which were deposited BY THE AUTHENTICATED USER into the given collection
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void collectionTest() throws Exception {
|
||||
// TODO: Actually test collection endpoint via SWORDv2.
|
||||
// Currently, we are just ensuring the /collection endpoint exists (see above) and isn't throwing a 404
|
||||
context.turnOffAuthorisationSystem();
|
||||
// Create all content as the SAME EPERSON we will use to authenticate on this endpoint.
|
||||
// THIS IS REQUIRED as the /collections endpoint will only show YOUR ITEM SUBMISSIONS.
|
||||
context.setCurrentUser(eperson);
|
||||
// Create a top level community and one Collection
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
Collection collection = CollectionBuilder.createCollection(context, parentCommunity)
|
||||
.withName("Test SWORDv2 Collection")
|
||||
.build();
|
||||
|
||||
// Add one Item into that Collection.
|
||||
String itemTitle = "Test SWORDv2 Item";
|
||||
Item item = ItemBuilder.createItem(context, collection)
|
||||
.withTitle(itemTitle)
|
||||
.withAuthor("Smith, Sam")
|
||||
.build();
|
||||
|
||||
// Above changes MUST be committed to the database for SWORDv2 to see them.
|
||||
context.commit();
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
// This Collection should exist on the /collection endpoint via its handle.
|
||||
// Authenticate as the same user we used to create the test content above.
|
||||
ResponseEntity<String> response = getResponseAsString(COLLECTION_PATH + "/" + collection.getHandle(),
|
||||
eperson.getEmail(), password);
|
||||
|
||||
// Expect a 200 response code, and an ATOM feed document
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertEquals(ATOM_FEED_CONTENT_TYPE, response.getHeaders().getContentType().toString());
|
||||
|
||||
// Check for response body to include the Item edit link
|
||||
// NOTE: This endpoint will only list items which were submitted by the authenticated EPerson.
|
||||
assertThat(response.getBody(), containsString(EDIT_PATH + "/" + item.getID().toString()));
|
||||
// Check for response body to include the Item title text
|
||||
assertThat(response.getBody(), containsString("<title type=\"text\">" + itemTitle + "</title>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mediaResourceUnauthorizedTest() throws Exception {
|
||||
// Attempt to POST to /mediaresource endpoint without sending authentication information
|
||||
// Attempt to POST to /edit-media endpoint without sending authentication information
|
||||
ResponseEntity<String> response = postResponseAsString(MEDIA_RESOURCE_PATH, null, null, null);
|
||||
// Expect a 401 response code
|
||||
assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED));
|
||||
assertEquals(response.getStatusCode(), HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests four different SWORDv2 actions, as these all require starting with a new deposit.
|
||||
* 1. Depositing a new item via SWORD (via POST /collections/[collection-uuid])
|
||||
* 2. Reading the deposited item (via GET /edit/[item-uuid])
|
||||
* 3. Updating the deposited item's metadata (via PUT /edit/[item-uuid])
|
||||
* 4. Deleting the deposited item (via DELETE /edit/[item-uuid]).
|
||||
*/
|
||||
@Test
|
||||
public void depositAndEditViaSwordTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
// Create a top level community and one Collection
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
// Make sure our Collection allows the "eperson" user to submit into it
|
||||
Collection collection = CollectionBuilder.createCollection(context, parentCommunity)
|
||||
.withName("Test SWORDv2 Collection")
|
||||
.withSubmitterGroup(eperson)
|
||||
.build();
|
||||
// Above changes MUST be committed to the database for SWORDv2 to see them.
|
||||
context.commit();
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
// Add file
|
||||
LinkedMultiValueMap<Object, Object> multipart = new LinkedMultiValueMap<>();
|
||||
multipart.add("file", new FileSystemResource(Path.of("src", "test", "resources",
|
||||
"org", "dspace", "app", "sword2", "example.zip")));
|
||||
// Add required headers
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||
headers.setContentDisposition(ContentDisposition.attachment().filename("example.zip").build());
|
||||
headers.set("Packaging", "http://purl.org/net/sword/package/METSDSpaceSIP");
|
||||
headers.setAccept(List.of(MediaType.APPLICATION_ATOM_XML));
|
||||
|
||||
//----
|
||||
// STEP 1: Verify upload/submit via SWORDv2 works
|
||||
//----
|
||||
// Send POST to upload Zip file via SWORD
|
||||
ResponseEntity<String> response = postResponseAsString(COLLECTION_PATH + "/" + collection.getHandle(),
|
||||
eperson.getEmail(), password,
|
||||
new HttpEntity<>(multipart, headers));
|
||||
|
||||
// Expect a 201 CREATED response with ATOM "entry" content returned
|
||||
assertEquals(HttpStatus.CREATED, response.getStatusCode());
|
||||
assertEquals(ATOM_ENTRY_CONTENT_TYPE, response.getHeaders().getContentType().toString());
|
||||
// MUST return a "Location" header which is the "/swordv2/edit/[uuid]" URI of the created item
|
||||
assertNotNull(response.getHeaders().getLocation());
|
||||
|
||||
String editLink = response.getHeaders().getLocation().toString();
|
||||
|
||||
// Body should include that link as the rel="edit" URL
|
||||
assertThat(response.getBody(), containsString("<link href=\"" + editLink + "\" rel=\"edit\"/>"));
|
||||
|
||||
//----
|
||||
// STEP 2: Verify uploaded content can be read via SWORDv2
|
||||
//----
|
||||
// Edit URI should work when requested by the EPerson who did the deposit
|
||||
HttpHeaders authHeaders = new HttpHeaders();
|
||||
authHeaders.setBasicAuth(eperson.getEmail(), password);
|
||||
RequestEntity request = RequestEntity.get(editLink)
|
||||
.accept(MediaType.valueOf("application/atom+xml"))
|
||||
.headers(authHeaders)
|
||||
.build();
|
||||
response = responseAsString(request);
|
||||
|
||||
// Expect a 200 response with ATOM feed content returned
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertEquals(ATOM_FEED_CONTENT_TYPE, response.getHeaders().getContentType().toString());
|
||||
// Body should include links to bitstreams from the zip.
|
||||
// This just verifies at least one /swordv2/edit-media/bitstream/* link exists.
|
||||
assertThat(response.getBody(), containsString(getURL(MEDIA_RESOURCE_PATH + "/bitstream")));
|
||||
// Verify Item title also is returned in the body
|
||||
assertThat(response.getBody(), containsString("Attempts to detect retrotransposition"));
|
||||
|
||||
//----
|
||||
// STEP 3: Verify uploaded content can be UPDATED via SWORDv2 (by an Admin ONLY)
|
||||
//----
|
||||
// Edit URI can be used with PUT to update the metadata of the Item.
|
||||
// Since we submitted to a collection WITHOUT a workflow, this item is in archive. That means DELETE
|
||||
// must be done via a user with Admin privileges on the Item.
|
||||
authHeaders = new HttpHeaders();
|
||||
authHeaders.setBasicAuth(admin.getEmail(), password);
|
||||
// This example simply changes the title.
|
||||
String newTitle = "This is a new title updated via PUT";
|
||||
String newTitleEntry = "<entry xmlns=\"http://www.w3.org/2005/Atom\"><title>" + newTitle + "</title></entry>";
|
||||
request = RequestEntity.put(editLink)
|
||||
.headers(authHeaders)
|
||||
.contentType(MediaType.APPLICATION_ATOM_XML)
|
||||
.body(newTitleEntry);
|
||||
response = responseAsString(request);
|
||||
// Expect a 200 OK response
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
|
||||
//----
|
||||
// STEP 4: Verify content was successfully updated by reading content again
|
||||
//----
|
||||
// Edit URI should work when requested by the EPerson who did the deposit
|
||||
authHeaders = new HttpHeaders();
|
||||
authHeaders.setBasicAuth(eperson.getEmail(), password);
|
||||
request = RequestEntity.get(editLink)
|
||||
.accept(MediaType.valueOf("application/atom+xml"))
|
||||
.headers(authHeaders)
|
||||
.build();
|
||||
response = responseAsString(request);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
// Verify the new Item title is now included in the response body
|
||||
assertThat(response.getBody(), containsString(newTitle));
|
||||
|
||||
//----
|
||||
// STEP 5: Verify uploaded content can be DELETED via SWORDv2 (by an Admin ONLY)
|
||||
//----
|
||||
// Edit URI should also allow user to DELETE the uploaded content
|
||||
// Since we submitted to a collection WITHOUT a workflow, this item is in archive. That means DELETE
|
||||
// must be done via a user with Admin privileges on the Item.
|
||||
authHeaders = new HttpHeaders();
|
||||
authHeaders.setBasicAuth(admin.getEmail(), password);
|
||||
request = RequestEntity.delete(editLink)
|
||||
.headers(authHeaders)
|
||||
.build();
|
||||
response = responseAsString(request);
|
||||
|
||||
// Expect a 204 No Content response
|
||||
assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
|
||||
|
||||
// Verify that Edit URI now returns a 404 (using eperson login info)
|
||||
authHeaders = new HttpHeaders();
|
||||
authHeaders.setBasicAuth(eperson.getEmail(), password);
|
||||
request = RequestEntity.get(editLink)
|
||||
.accept(MediaType.valueOf("application/atom+xml"))
|
||||
.headers(authHeaders)
|
||||
.build();
|
||||
response = responseAsString(request);
|
||||
// Expect a 404 response as content was deleted
|
||||
assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void mediaResourceTest() throws Exception {
|
||||
// TODO: Actually test this endpoint via SWORDv2.
|
||||
// Currently, we are just ensuring the /mediaresource endpoint exists (see above) and isn't throwing a 404
|
||||
}
|
||||
|
||||
@Test
|
||||
public void containerUnauthorizedTest() throws Exception {
|
||||
// Attempt to POST to /container endpoint without sending authentication information
|
||||
ResponseEntity<String> response = postResponseAsString(CONTAINER_PATH, null, null, null);
|
||||
public void editUnauthorizedTest() throws Exception {
|
||||
// Attempt to POST to /edit endpoint without sending authentication information
|
||||
ResponseEntity<String> response = postResponseAsString(EDIT_PATH, null, null, null);
|
||||
// Expect a 401 response code
|
||||
assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void containerTest() throws Exception {
|
||||
// TODO: Actually test this endpoint via SWORDv2.
|
||||
// Currently, we are just ensuring the /container endpoint exists (see above) and isn't throwing a 404
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -136,14 +335,64 @@ public class Swordv2IT extends AbstractWebClientIntegrationTest {
|
||||
// Attempt to GET /statement endpoint without sending authentication information
|
||||
ResponseEntity<String> response = getResponseAsString(STATEMENT_PATH);
|
||||
// Expect a 401 response code
|
||||
assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED));
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Statements exist for Items in DSpace (/statements/[item-uuid])
|
||||
* https://swordapp.github.io/SWORDv2-Profile/SWORDProfile.html#statement
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void statementTest() throws Exception {
|
||||
// TODO: Actually test this endpoint via SWORDv2.
|
||||
// Currently, we are just ensuring the /statement endpoint exists (see above) and isn't throwing a 404
|
||||
context.turnOffAuthorisationSystem();
|
||||
// Create all content as the SAME EPERSON we will use to authenticate on this endpoint.
|
||||
// THIS IS REQUIRED as the /statements endpoint will only show YOUR ITEM SUBMISSIONS.
|
||||
context.setCurrentUser(eperson);
|
||||
// Create a top level community and one Collection
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
Collection collection = CollectionBuilder.createCollection(context, parentCommunity)
|
||||
.withName("Test SWORDv2 Collection")
|
||||
.build();
|
||||
|
||||
// Add one Item into that Collection.
|
||||
String itemTitle = "Test SWORDv2 Item";
|
||||
String itemAuthor = "Smith, Samantha";
|
||||
Item item = ItemBuilder.createItem(context, collection)
|
||||
.withTitle(itemTitle)
|
||||
.withAuthor(itemAuthor)
|
||||
.build();
|
||||
|
||||
// Above changes MUST be committed to the database for SWORDv2 to see them.
|
||||
context.commit();
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
HttpHeaders authHeaders = new HttpHeaders();
|
||||
authHeaders.setBasicAuth(eperson.getEmail(), password);
|
||||
// GET call to /statement MUST include an "Accept" header that matches one of the formats
|
||||
// supported by 'SwordStatementDisseminator' (configured in swordv2-server.cfg)
|
||||
RequestEntity request = RequestEntity.get(getURL(STATEMENT_PATH + "/" + item.getID().toString()))
|
||||
.accept(MediaType.valueOf("application/atom+xml"))
|
||||
.headers(authHeaders)
|
||||
.build();
|
||||
ResponseEntity<String> response = responseAsString(request);
|
||||
|
||||
// Expect a 200 response with ATOM feed content returned
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertEquals(ATOM_FEED_CONTENT_TYPE, response.getHeaders().getContentType().toString());
|
||||
|
||||
|
||||
// Body should include the statement path of the Item, as well as the title & author information
|
||||
assertThat(response.getBody(),
|
||||
containsString(STATEMENT_PATH + "/" + item.getID().toString()));
|
||||
assertThat(response.getBody(),
|
||||
containsString("<title type=\"text\">" + itemTitle + "</title>"));
|
||||
assertThat(response.getBody(),
|
||||
containsString("<author><name>" + itemAuthor + "</name></author>"));
|
||||
// Also verify Item is in "archived" state
|
||||
assertThat(response.getBody(),
|
||||
containsString("<category term=\"http://dspace.org/state/archived\""));
|
||||
}
|
||||
}
|
||||
|
||||
|
Binary file not shown.
@@ -150,11 +150,7 @@ public class CollectionDepositor extends Depositor {
|
||||
// for a moment
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
String bundleName = configurationService.getProperty(
|
||||
"sword-server", "bundle.name");
|
||||
if (bundleName == null || "".equals(bundleName)) {
|
||||
bundleName = "SWORD";
|
||||
}
|
||||
String bundleName = configurationService.getProperty("sword-server.bundle.name", "SWORD");
|
||||
Item item = result.getItem();
|
||||
List<Bundle> bundles = item.getBundles();
|
||||
Bundle swordBundle = null;
|
||||
|
@@ -431,8 +431,7 @@ public class SWORDUrlManager {
|
||||
*/
|
||||
public String getBaseMediaLinkUrl()
|
||||
throws DSpaceSWORDException {
|
||||
String mlUrl = configurationService.getProperty(
|
||||
"sword-server", "media-link.url");
|
||||
String mlUrl = configurationService.getProperty("sword-server.media-link.url");
|
||||
if (StringUtils.isBlank(mlUrl)) {
|
||||
if (dspaceUrl == null || "".equals(dspaceUrl)) {
|
||||
throw new DSpaceSWORDException(
|
||||
|
@@ -458,10 +458,9 @@ public class SwordUrlManager {
|
||||
throws DSpaceSwordException {
|
||||
WorkflowTools wft = new WorkflowTools();
|
||||
|
||||
// if the item is in the workspace, we need to give it it's own special identifier
|
||||
// if the item is in the workspace, we need to give it its own special identifier
|
||||
if (wft.isItemInWorkspace(context, item)) {
|
||||
String urlTemplate = configurationService
|
||||
.getProperty("swordv2-server", "workspace.url-template");
|
||||
String urlTemplate = configurationService.getProperty("swordv2-server.workspace.url-template");
|
||||
if (urlTemplate != null) {
|
||||
return urlTemplate.replace("#wsid#", Integer.toString(
|
||||
wft.getWorkspaceItem(context, item).getID()));
|
||||
|
@@ -1094,7 +1094,6 @@ webui.preview.brand.fontpoint = 12
|
||||
# Solr:
|
||||
# ItemCountDAO.class = org.dspace.browse.ItemCountDAOSolr
|
||||
|
||||
|
||||
###### Browse Configuration ######
|
||||
#
|
||||
# Define the DAO class to use this must meet your storage choice for
|
||||
|
13
dspace/config/emails/qaevent_admin_notification
Normal file
13
dspace/config/emails/qaevent_admin_notification
Normal file
@@ -0,0 +1,13 @@
|
||||
## E-mail sent to notify Administrator of new Withdrawn/Reinstate request
|
||||
##
|
||||
## Parameters: {0} Type of request 'topic'
|
||||
## {1} resource id
|
||||
## {2} reason
|
||||
##
|
||||
#set($subject = "Notification about ${params[0]} Request")
|
||||
|
||||
Item Details:
|
||||
|
||||
Type of request: ${params[0]}
|
||||
Relatem to item: ${params[1]}
|
||||
Reason: ${params[2]}
|
@@ -37,4 +37,18 @@ 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
|
||||
qaevents.openaire.broker-url = http://api.openaire.eu/broker
|
||||
|
||||
###### QAEvent source Configuration ######
|
||||
qaevents.sources = OpenAIRE, DSpaceUsers
|
||||
|
||||
### Withdrawal&Reinstate correction Group ###
|
||||
# Members of this group enabled to make requests for the Withdrawn or Reinstate of an item.
|
||||
# By default this property is empty, so only Administrators will see the button to make these requests.
|
||||
# If you want to allow all authenticated users to have this feature,
|
||||
# you can configure this setting to use the Anonymous group.
|
||||
qaevents.withdraw-reinstate.group =
|
||||
|
||||
# Withdrawal&Reinstate email to notify the system administrator about a new
|
||||
# Quality Assurance (QA) request event.
|
||||
qaevents.mail.notification = ${mail.admin}
|
||||
|
@@ -297,6 +297,7 @@ plugin.named.org.dspace.sword2.SwordContentDisseminator = \
|
||||
plugin.named.org.dspace.sword2.SwordStatementDisseminator = \
|
||||
org.dspace.sword2.AtomStatementDisseminator = atom, \
|
||||
org.dspace.sword2.OreStatementDisseminator = rdf, \
|
||||
org.dspace.sword2.AtomStatementDisseminator = application/atom+xml, \
|
||||
org.dspace.sword2.AtomStatementDisseminator = application/atom+xml_type_feed, \
|
||||
org.dspace.sword2.OreStatementDisseminator = application/rdf+xml
|
||||
|
||||
|
@@ -157,5 +157,7 @@
|
||||
<!-- Submission Config Service -->
|
||||
<bean class="org.dspace.submit.service.SubmissionConfigServiceImpl"/>
|
||||
|
||||
<bean class="org.dspace.correctiontype.service.impl.CorrectionTypeServiceImpl"/>
|
||||
|
||||
</beans>
|
||||
|
||||
|
17
dspace/config/spring/api/correction-types.xml
Normal file
17
dspace/config/spring/api/correction-types.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
|
||||
default-lazy-init="true">
|
||||
|
||||
<bean id="withdrawnRequest" class="org.dspace.correctiontype.WithdrawnCorrectionType" >
|
||||
<property name="id" value="request-withdrawn"/>
|
||||
<property name="topic" value="REQUEST/WITHDRAWN"/>
|
||||
</bean>
|
||||
|
||||
<bean id="reinstateRequest" class="org.dspace.correctiontype.ReinstateCorrectionType" >
|
||||
<property name="id" value="request-reinstate"/>
|
||||
<property name="topic" value="REQUEST/REINSTATE"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
@@ -4,7 +4,7 @@
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
|
||||
|
||||
<!-- Identifier Service Application Interface. Will be autowired with
|
||||
<!-- Identifier Service Application Interface. Will be autowired with
|
||||
any Identifier Providers present in Spring context.
|
||||
-->
|
||||
<bean id="org.dspace.identifier.service.IdentifierService"
|
||||
@@ -12,6 +12,13 @@
|
||||
autowire="byType"
|
||||
scope="singleton"/>
|
||||
|
||||
<!-- If you disable versioning, you need to use the default HandleIdentifierProvider. -->
|
||||
<!--
|
||||
<bean id="org.dspace.identifier.HandleIdentifierProvider" class="org.dspace.identifier.HandleIdentifierProvider" scope="singleton">
|
||||
<property name="configurationService" ref="org.dspace.services.ConfigurationService"/>
|
||||
</bean>
|
||||
-->
|
||||
|
||||
<!-- If you enabled versioning, you should use one of the versioned
|
||||
handle identifier provider instead of the default one.
|
||||
The VersionedHandleIdentifierProvider creates a new versioned
|
||||
@@ -26,7 +33,7 @@
|
||||
a new version is created the previous version gets a new
|
||||
handle. This leads to a handle that points always to the
|
||||
newest version, but there is no permanent handle, that
|
||||
will always keep pointing to the acutal newest one.
|
||||
will always keep pointing to the actual newest one.
|
||||
-->
|
||||
<!--
|
||||
<bean id="org.dspace.identifier.HandleIdentifierProvider" class="org.dspace.identifier.VersionedHandleIdentifierProviderWithCanonicalHandles" scope="singleton">
|
||||
|
@@ -21,8 +21,7 @@
|
||||
<bean id="org.dspace.qaevent.service.QAEventActionService" class="org.dspace.qaevent.service.impl.QAEventActionServiceImpl">
|
||||
<property name="topicsToActions">
|
||||
<map>
|
||||
<!--The key are the TOPIC, the value must be a valid implementation of the
|
||||
org.dspace.qaevent.QAEventAction interface -->
|
||||
<!--The key are the TOPIC, the value must be a valid implementation of the org.dspace.qaevent.QAEventAction interface -->
|
||||
<entry value-ref="ProjectLinkedEntityAction">
|
||||
<key><util:constant static-field="org.dspace.qaevent.QANotifyPatterns.TOPIC_ENRICH_MORE_PROJECT"/></key>
|
||||
</entry>
|
||||
@@ -47,7 +46,9 @@
|
||||
<entry value-ref="RelationMetadataAction">
|
||||
<key><util:constant static-field="org.dspace.qaevent.QANotifyPatterns.TOPIC_ENRICH_MORE_LINK"/></key>
|
||||
</entry>
|
||||
</map>
|
||||
<entry key="REQUEST/WITHDRAWN" value-ref="WithdrawnRequestAction" />
|
||||
<entry key="REQUEST/REINSTATE" value-ref="ReinstateRequestAction" />
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
@@ -100,7 +101,7 @@
|
||||
<entry key="default" value="datacite.relation.isReferencedBy" />
|
||||
<entry key="http://purl.org/vocab/frbr/core#supplement" value="datacite.relation.isSupplementedBy" />
|
||||
</map>
|
||||
</property>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="org.dspace.qaevent.service.QAEventSecurityService" class="org.dspace.qaevent.service.impl.QAEventSecurityServiceImpl">
|
||||
@@ -111,7 +112,7 @@
|
||||
<map>
|
||||
<entry key="coar-notify" value-ref="submitterQASecurity" />
|
||||
</map>
|
||||
</property>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="submitterQASecurity" class="org.dspace.qaevent.security.UserBasedFilterQASecurity">
|
||||
@@ -119,11 +120,11 @@
|
||||
<!-- we need to escape the { as it as a special meaning for the message format -->
|
||||
<!-- argument {0} will be replaced with the uuid of the loggedin user -->
|
||||
<value>'{'!join from=search.resourceid to=resource_uuid fromIndex=${solr.multicorePrefix}search}submitter_authority:{0}</value>
|
||||
</property>
|
||||
</property>
|
||||
</bean>
|
||||
<!--
|
||||
<!--
|
||||
To configure rules to automatic process specific qaevent you must provide a qaAutomaticProcessingMap
|
||||
where the keys are the qaevent source provider name and the value is a reference to a
|
||||
where the keys are the qaevent source provider name and the value is a reference to a
|
||||
AutomaticProcessingEvaluation implementation. Below you can find an example of configuration defining
|
||||
some thresholds rules for the coar-notify generated QAEvent to be approved, rejected and ignored
|
||||
-->
|
||||
@@ -141,4 +142,28 @@
|
||||
<property name="itemFilterToApprove" ref="simple-demo_filter" />
|
||||
</bean>
|
||||
-->
|
||||
|
||||
<bean id="WithdrawnRequestAction" class="org.dspace.qaevent.action.QAWithdrawnRequestAction" />
|
||||
|
||||
<bean id="ReinstateRequestAction" class="org.dspace.qaevent.action.QAReinstateRequestAction" />
|
||||
|
||||
<bean id="org.dspace.qaevent.service.QAEventSecurityService" class="org.dspace.qaevent.service.impl.QAEventSecurityServiceImpl">
|
||||
<property name="defaultSecurity">
|
||||
<bean class="org.dspace.qaevent.security.AdministratorsOnlyQASecurity" />
|
||||
</property>
|
||||
<property name="qaSecurityConfiguration">
|
||||
<map>
|
||||
<entry key="DSpaceUsers" value-ref="submitterWithdrawnReinstateQASecurity" />
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="submitterWithdrawnReinstateQASecurity" class="org.dspace.qaevent.security.UserBasedFilterQASecurity">
|
||||
<property name="filterTemplate">
|
||||
<!-- we need to escape the { as it as a special meaning for the message format -->
|
||||
<!-- argument {0} will be replaced with the uuid of the loggedin user -->
|
||||
<value>original_id:{0}</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
Reference in New Issue
Block a user