mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 18:14:26 +00:00
[CST-5588] ORCID settings (REST) .
This commit is contained in:
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.orcid.model;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.EnumUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entity types of the ORCID objects that can be synchronized.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public enum OrcidEntityType {
|
||||||
|
|
||||||
|
PUBLICATION("/work"),
|
||||||
|
FUNDING("/funding");
|
||||||
|
|
||||||
|
private final String path;
|
||||||
|
|
||||||
|
private OrcidEntityType(String path) {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isValid(String entityType) {
|
||||||
|
return entityType != null ? EnumUtils.isValidEnum(OrcidEntityType.class, entityType.toUpperCase()) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OrcidEntityType fromString(String entityType) {
|
||||||
|
return isValid(entityType) ? OrcidEntityType.valueOf(entityType.toUpperCase()) : null;
|
||||||
|
}
|
||||||
|
}
|
@@ -8,9 +8,15 @@
|
|||||||
package org.dspace.app.orcid.service;
|
package org.dspace.app.orcid.service;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.dspace.app.orcid.model.OrcidEntityType;
|
||||||
import org.dspace.app.orcid.model.OrcidTokenResponseDTO;
|
import org.dspace.app.orcid.model.OrcidTokenResponseDTO;
|
||||||
|
import org.dspace.app.profile.OrcidEntitySyncPreference;
|
||||||
import org.dspace.app.profile.OrcidProfileDisconnectionMode;
|
import org.dspace.app.profile.OrcidProfileDisconnectionMode;
|
||||||
|
import org.dspace.app.profile.OrcidProfileSyncPreference;
|
||||||
|
import org.dspace.app.profile.OrcidSynchronizationMode;
|
||||||
import org.dspace.content.Item;
|
import org.dspace.content.Item;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
|
|
||||||
@@ -22,6 +28,14 @@ import org.dspace.core.Context;
|
|||||||
*/
|
*/
|
||||||
public interface OrcidSynchronizationService {
|
public interface OrcidSynchronizationService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given item is linked to an ORCID profile.
|
||||||
|
*
|
||||||
|
* @param item the item to check
|
||||||
|
* @return true if the given item is linked to ORCID
|
||||||
|
*/
|
||||||
|
boolean isLinkedToOrcid(Item item);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure the given profile with the data present in the given ORCID token.
|
* Configure the given profile with the data present in the given ORCID token.
|
||||||
* This action is required to synchronize profile and related entities with
|
* This action is required to synchronize profile and related entities with
|
||||||
@@ -43,6 +57,83 @@ public interface OrcidSynchronizationService {
|
|||||||
*/
|
*/
|
||||||
public void unlinkProfile(Context context, Item profile) throws SQLException;
|
public void unlinkProfile(Context context, Item profile) throws SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the synchronization preference for the given profile related to the given
|
||||||
|
* ORCID entity type.
|
||||||
|
*
|
||||||
|
* @param context the relevant DSpace Context.
|
||||||
|
* @param profile the researcher profile to update
|
||||||
|
* @param entityType the orcid entity type
|
||||||
|
* @param value the new synchronization preference value
|
||||||
|
* @return true if the value has actually been updated,
|
||||||
|
* false if the value to be set is the same as
|
||||||
|
* the one already configured
|
||||||
|
* @throws SQLException if a SQL error occurs during the profile
|
||||||
|
* update
|
||||||
|
* @throws IllegalArgumentException if the given researcher profile is no linked
|
||||||
|
* with an ORCID account
|
||||||
|
*/
|
||||||
|
public boolean setEntityPreference(Context context, Item profile, OrcidEntityType entityType,
|
||||||
|
OrcidEntitySyncPreference value) throws SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the profile's synchronization preference for the given profile.
|
||||||
|
*
|
||||||
|
* @param context the relevant DSpace Context.
|
||||||
|
* @param profile the researcher profile to update
|
||||||
|
* @param value the new synchronization preference value
|
||||||
|
* @return true if the value has actually been updated,
|
||||||
|
* false if the value to be set is the same as
|
||||||
|
* the one already configured
|
||||||
|
* @throws SQLException if a SQL error occurs during the profile
|
||||||
|
* update
|
||||||
|
* @throws IllegalArgumentException if the given researcher profile is no linked
|
||||||
|
* with an ORCID account
|
||||||
|
*/
|
||||||
|
public boolean setProfilePreference(Context context, Item profile,
|
||||||
|
List<OrcidProfileSyncPreference> values) throws SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the ORCID synchronization mode for the given profile.
|
||||||
|
*
|
||||||
|
* @param context the relevant DSpace Context.
|
||||||
|
* @param profile the researcher profile to update
|
||||||
|
* @param value the new synchronization mode value
|
||||||
|
* @return true if the value has actually been updated, false if
|
||||||
|
* the value to be set is the same as the one already
|
||||||
|
* configured
|
||||||
|
* @throws SQLException if a SQL error occurs during the profile update
|
||||||
|
*/
|
||||||
|
public boolean setSynchronizationMode(Context context, Item profile, OrcidSynchronizationMode value)
|
||||||
|
throws SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ORCID synchronization mode configured for the given profile item.
|
||||||
|
*
|
||||||
|
* @param profile the researcher profile item
|
||||||
|
* @return the synchronization mode
|
||||||
|
*/
|
||||||
|
Optional<OrcidSynchronizationMode> getSynchronizationMode(Item profile);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ORCID synchronization preference related to the given entity type
|
||||||
|
* configured for the given profile item.
|
||||||
|
*
|
||||||
|
* @param profile the researcher profile item
|
||||||
|
* @param entityType the orcid entity type
|
||||||
|
* @return the configured preference
|
||||||
|
*/
|
||||||
|
Optional<OrcidEntitySyncPreference> getEntityPreference(Item profile, OrcidEntityType entityType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ORCID synchronization preferences related to the profile itself
|
||||||
|
* configured for the given profile item.
|
||||||
|
*
|
||||||
|
* @param profile the researcher profile item
|
||||||
|
* @return the synchronization mode
|
||||||
|
*/
|
||||||
|
List<OrcidProfileSyncPreference> getProfilePreferences(Item profile);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the configuration ORCID profile's disconnection mode. If that mode is
|
* Returns the configuration ORCID profile's disconnection mode. If that mode is
|
||||||
* not configured or the configuration is wrong, the value DISABLED is returned.
|
* not configured or the configuration is wrong, the value DISABLED is returned.
|
||||||
|
@@ -9,14 +9,26 @@ package org.dspace.app.orcid.service.impl;
|
|||||||
|
|
||||||
import static java.time.LocalDateTime.now;
|
import static java.time.LocalDateTime.now;
|
||||||
import static java.time.format.DateTimeFormatter.ISO_DATE_TIME;
|
import static java.time.format.DateTimeFormatter.ISO_DATE_TIME;
|
||||||
|
import static java.util.List.of;
|
||||||
|
import static org.apache.commons.lang3.EnumUtils.isValidEnum;
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
import static org.dspace.content.Item.ANY;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.StringUtils;
|
||||||
|
import org.dspace.app.orcid.model.OrcidEntityType;
|
||||||
import org.dspace.app.orcid.model.OrcidTokenResponseDTO;
|
import org.dspace.app.orcid.model.OrcidTokenResponseDTO;
|
||||||
import org.dspace.app.orcid.service.OrcidSynchronizationService;
|
import org.dspace.app.orcid.service.OrcidSynchronizationService;
|
||||||
|
import org.dspace.app.profile.OrcidEntitySyncPreference;
|
||||||
import org.dspace.app.profile.OrcidProfileDisconnectionMode;
|
import org.dspace.app.profile.OrcidProfileDisconnectionMode;
|
||||||
|
import org.dspace.app.profile.OrcidProfileSyncPreference;
|
||||||
|
import org.dspace.app.profile.OrcidSynchronizationMode;
|
||||||
import org.dspace.authorize.AuthorizeException;
|
import org.dspace.authorize.AuthorizeException;
|
||||||
import org.dspace.content.Item;
|
import org.dspace.content.Item;
|
||||||
import org.dspace.content.MetadataValue;
|
import org.dspace.content.MetadataValue;
|
||||||
@@ -88,6 +100,77 @@ public class OrcidSynchronizationServiceImpl implements OrcidSynchronizationServ
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setEntityPreference(Context context, Item profile, OrcidEntityType type,
|
||||||
|
OrcidEntitySyncPreference value) throws SQLException {
|
||||||
|
String metadataQualifier = "sync-" + type.name().toLowerCase() + "s";
|
||||||
|
return updatePreferenceForSynchronizingWithOrcid(context, profile, metadataQualifier, of(value.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setProfilePreference(Context context, Item profile, List<OrcidProfileSyncPreference> values)
|
||||||
|
throws SQLException {
|
||||||
|
|
||||||
|
List<String> valuesAsString = values.stream()
|
||||||
|
.map(OrcidProfileSyncPreference::name)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return updatePreferenceForSynchronizingWithOrcid(context, profile, "sync-profile", valuesAsString);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setSynchronizationMode(Context context, Item profile, OrcidSynchronizationMode value)
|
||||||
|
throws SQLException {
|
||||||
|
|
||||||
|
if (!isLinkedToOrcid(profile)) {
|
||||||
|
throw new IllegalArgumentException("The given profile cannot be configured for the ORCID "
|
||||||
|
+ "synchronization because it is not linked to any ORCID account: "
|
||||||
|
+ profile.getID());
|
||||||
|
}
|
||||||
|
|
||||||
|
String newValue = value.name();
|
||||||
|
String oldValue = itemService.getMetadataFirstValue(profile, "dspace", "orcid", "sync-mode", Item.ANY);
|
||||||
|
|
||||||
|
if (StringUtils.equals(oldValue, newValue)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
itemService.setMetadataSingleValue(context, profile, "dspace", "orcid", "sync-mode", null, value.name());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<OrcidSynchronizationMode> getSynchronizationMode(Item item) {
|
||||||
|
return getMetadataValue(item, "dspace.orcid.sync-mode")
|
||||||
|
.map(metadataValue -> metadataValue.getValue())
|
||||||
|
.filter(value -> isValidEnum(OrcidSynchronizationMode.class, value))
|
||||||
|
.map(value -> OrcidSynchronizationMode.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<OrcidEntitySyncPreference> getEntityPreference(Item item, OrcidEntityType entityType) {
|
||||||
|
return getMetadataValue(item, "dspace.orcid.sync-" + entityType.name().toLowerCase() + "s")
|
||||||
|
.map(metadataValue -> metadataValue.getValue())
|
||||||
|
.filter(value -> isValidEnum(OrcidEntitySyncPreference.class, value))
|
||||||
|
.map(value -> OrcidEntitySyncPreference.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<OrcidProfileSyncPreference> getProfilePreferences(Item item) {
|
||||||
|
return getMetadataValues(item, "dspace.orcid.sync-profile")
|
||||||
|
.map(MetadataValue::getValue)
|
||||||
|
.filter(value -> isValidEnum(OrcidProfileSyncPreference.class, value))
|
||||||
|
.map(value -> OrcidProfileSyncPreference.valueOf(value))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLinkedToOrcid(Item item) {
|
||||||
|
return getOrcidAccessToken(item).isPresent() && getOrcid(item).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OrcidProfileDisconnectionMode getDisconnectionMode() {
|
public OrcidProfileDisconnectionMode getDisconnectionMode() {
|
||||||
String value = configurationService.getProperty("orcid.disconnection.allowed-users");
|
String value = configurationService.getProperty("orcid.disconnection.allowed-users");
|
||||||
@@ -97,9 +180,58 @@ public class OrcidSynchronizationServiceImpl implements OrcidSynchronizationServ
|
|||||||
return OrcidProfileDisconnectionMode.fromString(value);
|
return OrcidProfileDisconnectionMode.fromString(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean updatePreferenceForSynchronizingWithOrcid(Context context, Item profile,
|
||||||
|
String metadataQualifier,
|
||||||
|
List<String> values) throws SQLException {
|
||||||
|
|
||||||
|
if (!isLinkedToOrcid(profile)) {
|
||||||
|
throw new IllegalArgumentException("The given profile cannot be configured for the ORCID "
|
||||||
|
+ "synchronization because it is not linked to any ORCID account: "
|
||||||
|
+ profile.getID());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> oldValues = itemService.getMetadata(profile, "dspace", "orcid", metadataQualifier, ANY).stream()
|
||||||
|
.map(metadataValue -> metadataValue.getValue())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (containsSameValues(oldValues, values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
itemService.clearMetadata(context, profile, "dspace", "orcid", metadataQualifier, ANY);
|
||||||
|
for (String value : values) {
|
||||||
|
itemService.addMetadata(context, profile, "dspace", "orcid", metadataQualifier, null, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsSameValues(List<String> firstList, List<String> secondList) {
|
||||||
|
return new HashSet<>(firstList).equals(new HashSet<>(secondList));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<String> getOrcidAccessToken(Item item) {
|
||||||
|
return getMetadataValue(item, "dspace.orcid.access-token")
|
||||||
|
.map(metadataValue -> metadataValue.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getOrcid(Item item) {
|
||||||
|
return getMetadataValue(item, "person.identifier.orcid")
|
||||||
|
.map(metadataValue -> metadataValue.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<MetadataValue> getMetadataValue(Item item, String metadataField) {
|
||||||
|
return getMetadataValues(item, metadataField).findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
private Stream<MetadataValue> getMetadataValues(Item item, String metadataField) {
|
private Stream<MetadataValue> getMetadataValues(Item item, String metadataField) {
|
||||||
return item.getMetadata().stream()
|
return item.getMetadata().stream()
|
||||||
.filter(metadata -> metadataField.equals(metadata.getMetadataField().toString('.')));
|
.filter(metadata -> metadataField.equals(metadata.getMetadataField().toString('.')));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getProfileType() {
|
||||||
|
return configurationService.getProperty("researcher-profile.type", "Person");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateItem(Context context, Item item) throws SQLException {
|
private void updateItem(Context context, Item item) throws SQLException {
|
||||||
|
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.profile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum that model the allowed values to configure the ORCID synchronization
|
||||||
|
* preferences.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public enum OrcidEntitySyncPreference {
|
||||||
|
|
||||||
|
DISABLED,
|
||||||
|
ALL
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.profile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum that model the allowed values to configure the ORCID synchronization
|
||||||
|
* preferences for the user's profile.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public enum OrcidProfileSyncPreference {
|
||||||
|
|
||||||
|
BIOGRAPHICAL,
|
||||||
|
IDENTIFIERS;
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.profile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum that model the allowed values to configure the ORCID synchronization
|
||||||
|
* mode.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public enum OrcidSynchronizationMode {
|
||||||
|
|
||||||
|
MANUAL,
|
||||||
|
BATCH;
|
||||||
|
}
|
@@ -62,6 +62,11 @@ public class ResearcherProfile {
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<String> getOrcid() {
|
||||||
|
return getMetadataValue(item, "person.identifier.orcid")
|
||||||
|
.map(metadataValue -> metadataValue.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
private MetadataValue getDspaceObjectOwnerMetadata(Item item) {
|
private MetadataValue getDspaceObjectOwnerMetadata(Item item) {
|
||||||
return getMetadataValue(item, "dspace.object.owner")
|
return getMetadataValue(item, "dspace.object.owner")
|
||||||
.filter(metadata -> UUIDUtils.fromString(metadata.getAuthority()) != null)
|
.filter(metadata -> UUIDUtils.fromString(metadata.getAuthority()) != null)
|
||||||
|
@@ -7,10 +7,22 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest.converter;
|
package org.dspace.app.rest.converter;
|
||||||
|
|
||||||
|
import static org.dspace.app.orcid.model.OrcidEntityType.FUNDING;
|
||||||
|
import static org.dspace.app.orcid.model.OrcidEntityType.PUBLICATION;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.dspace.app.orcid.service.OrcidSynchronizationService;
|
||||||
|
import org.dspace.app.profile.OrcidEntitySyncPreference;
|
||||||
|
import org.dspace.app.profile.OrcidProfileSyncPreference;
|
||||||
|
import org.dspace.app.profile.OrcidSynchronizationMode;
|
||||||
import org.dspace.app.profile.ResearcherProfile;
|
import org.dspace.app.profile.ResearcherProfile;
|
||||||
import org.dspace.app.rest.model.ResearcherProfileRest;
|
import org.dspace.app.rest.model.ResearcherProfileRest;
|
||||||
|
import org.dspace.app.rest.model.ResearcherProfileRest.OrcidSynchronizationRest;
|
||||||
import org.dspace.app.rest.projection.Projection;
|
import org.dspace.app.rest.projection.Projection;
|
||||||
import org.dspace.content.Item;
|
import org.dspace.content.Item;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,6 +35,9 @@ import org.springframework.stereotype.Component;
|
|||||||
@Component
|
@Component
|
||||||
public class ResearcherProfileConverter implements DSpaceConverter<ResearcherProfile, ResearcherProfileRest> {
|
public class ResearcherProfileConverter implements DSpaceConverter<ResearcherProfile, ResearcherProfileRest> {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrcidSynchronizationService orcidSynchronizationService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResearcherProfileRest convert(ResearcherProfile profile, Projection projection) {
|
public ResearcherProfileRest convert(ResearcherProfile profile, Projection projection) {
|
||||||
ResearcherProfileRest researcherProfileRest = new ResearcherProfileRest();
|
ResearcherProfileRest researcherProfileRest = new ResearcherProfileRest();
|
||||||
@@ -33,12 +48,47 @@ public class ResearcherProfileConverter implements DSpaceConverter<ResearcherPro
|
|||||||
|
|
||||||
Item item = profile.getItem();
|
Item item = profile.getItem();
|
||||||
|
|
||||||
|
if (orcidSynchronizationService.isLinkedToOrcid(item)) {
|
||||||
|
profile.getOrcid().ifPresent(researcherProfileRest::setOrcid);
|
||||||
|
|
||||||
|
OrcidSynchronizationRest orcidSynchronization = new OrcidSynchronizationRest();
|
||||||
|
orcidSynchronization.setMode(getMode(item));
|
||||||
|
orcidSynchronization.setProfilePreferences(getProfilePreferences(item));
|
||||||
|
orcidSynchronization.setFundingsPreference(getFundingsPreference(item));
|
||||||
|
orcidSynchronization.setPublicationsPreference(getPublicationsPreference(item));
|
||||||
|
researcherProfileRest.setOrcidSynchronization(orcidSynchronization);
|
||||||
|
}
|
||||||
|
|
||||||
return researcherProfileRest;
|
return researcherProfileRest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getPublicationsPreference(Item item) {
|
||||||
|
return orcidSynchronizationService.getEntityPreference(item, PUBLICATION)
|
||||||
|
.map(OrcidEntitySyncPreference::name)
|
||||||
|
.orElse(OrcidEntitySyncPreference.DISABLED.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFundingsPreference(Item item) {
|
||||||
|
return orcidSynchronizationService.getEntityPreference(item, FUNDING)
|
||||||
|
.map(OrcidEntitySyncPreference::name)
|
||||||
|
.orElse(OrcidEntitySyncPreference.DISABLED.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getProfilePreferences(Item item) {
|
||||||
|
return orcidSynchronizationService.getProfilePreferences(item).stream()
|
||||||
|
.map(OrcidProfileSyncPreference::name)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMode(Item item) {
|
||||||
|
return orcidSynchronizationService.getSynchronizationMode(item)
|
||||||
|
.map(OrcidSynchronizationMode::name)
|
||||||
|
.orElse(OrcidSynchronizationMode.MANUAL.name());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<ResearcherProfile> getModelClass() {
|
public Class<ResearcherProfile> getModelClass() {
|
||||||
return ResearcherProfile.class;
|
return ResearcherProfile.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -7,8 +7,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest.model;
|
package org.dspace.app.rest.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import org.dspace.app.rest.RestResourceController;
|
import org.dspace.app.rest.RestResourceController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,6 +34,12 @@ public class ResearcherProfileRest extends BaseObjectRest<UUID> {
|
|||||||
|
|
||||||
private boolean visible;
|
private boolean visible;
|
||||||
|
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
private String orcid;
|
||||||
|
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
private OrcidSynchronizationRest orcidSynchronization;
|
||||||
|
|
||||||
public boolean isVisible() {
|
public boolean isVisible() {
|
||||||
return visible;
|
return visible;
|
||||||
}
|
}
|
||||||
@@ -40,6 +48,22 @@ public class ResearcherProfileRest extends BaseObjectRest<UUID> {
|
|||||||
this.visible = visible;
|
this.visible = visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OrcidSynchronizationRest getOrcidSynchronization() {
|
||||||
|
return orcidSynchronization;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrcidSynchronization(OrcidSynchronizationRest orcidSynchronization) {
|
||||||
|
this.orcidSynchronization = orcidSynchronization;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrcid() {
|
||||||
|
return orcid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrcid(String orcid) {
|
||||||
|
this.orcid = orcid;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return NAME;
|
return NAME;
|
||||||
@@ -54,4 +78,55 @@ public class ResearcherProfileRest extends BaseObjectRest<UUID> {
|
|||||||
public Class<?> getController() {
|
public Class<?> getController() {
|
||||||
return RestResourceController.class;
|
return RestResourceController.class;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Inner class to model ORCID synchronization preferences and mode.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static class OrcidSynchronizationRest {
|
||||||
|
|
||||||
|
private String mode;
|
||||||
|
|
||||||
|
private String publicationsPreference;
|
||||||
|
|
||||||
|
private String fundingsPreference;
|
||||||
|
|
||||||
|
private List<String> profilePreferences;
|
||||||
|
|
||||||
|
public String getMode() {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMode(String mode) {
|
||||||
|
this.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getProfilePreferences() {
|
||||||
|
return profilePreferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProfilePreferences(List<String> profilePreferences) {
|
||||||
|
this.profilePreferences = profilePreferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPublicationsPreference() {
|
||||||
|
return publicationsPreference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublicationsPreference(String publicationsPreference) {
|
||||||
|
this.publicationsPreference = publicationsPreference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFundingsPreference() {
|
||||||
|
return fundingsPreference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFundingsPreference(String fundingsPreference) {
|
||||||
|
this.fundingsPreference = fundingsPreference;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,156 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.repository.patch.operation;
|
||||||
|
|
||||||
|
import static org.dspace.app.orcid.model.OrcidEntityType.FUNDING;
|
||||||
|
import static org.dspace.app.orcid.model.OrcidEntityType.PUBLICATION;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dspace.app.orcid.service.OrcidSynchronizationService;
|
||||||
|
import org.dspace.app.profile.OrcidEntitySyncPreference;
|
||||||
|
import org.dspace.app.profile.OrcidProfileSyncPreference;
|
||||||
|
import org.dspace.app.profile.OrcidSynchronizationMode;
|
||||||
|
import org.dspace.app.profile.ResearcherProfile;
|
||||||
|
import org.dspace.app.profile.service.ResearcherProfileService;
|
||||||
|
import org.dspace.app.rest.exception.RESTAuthorizationException;
|
||||||
|
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||||
|
import org.dspace.app.rest.model.patch.Operation;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation for ResearcherProfile ORCID synchronization preferences
|
||||||
|
* patches.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* <code> curl -X PATCH http://${dspace.server.url}/api/cris/profiles/<:id-eperson> -H "
|
||||||
|
* Content-Type: application/json" -d '[{
|
||||||
|
* "op": "replace",
|
||||||
|
* "path": "/orcid/publications",
|
||||||
|
* "value": "ALL"
|
||||||
|
* }]'
|
||||||
|
* </code>
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class ResearcherProfileReplaceOrcidSyncPreferencesOperation extends PatchOperation<ResearcherProfile> {
|
||||||
|
|
||||||
|
private static final String OPERATION_ORCID_SYNCH = "/orcid";
|
||||||
|
|
||||||
|
private static final String PUBLICATIONS_PREFERENCES = "/publications";
|
||||||
|
|
||||||
|
private static final String FUNDINGS_PREFERENCES = "/fundings";
|
||||||
|
|
||||||
|
private static final String PROFILE_PREFERENCES = "/profile";
|
||||||
|
|
||||||
|
private static final String MODE_PREFERENCES = "/mode";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ResearcherProfileService profileService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrcidSynchronizationService synchronizationService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResearcherProfile perform(Context context, ResearcherProfile profile, Operation operation)
|
||||||
|
throws SQLException {
|
||||||
|
|
||||||
|
String path = StringUtils.removeStart(operation.getPath(), OPERATION_ORCID_SYNCH);
|
||||||
|
String value = getNewValueFromOperation(operation);
|
||||||
|
|
||||||
|
Item profileItem = profile.getItem();
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
try {
|
||||||
|
updatePreferences(context, path, value, profileItem);
|
||||||
|
|
||||||
|
return profileService.findById(context, profile.getId());
|
||||||
|
|
||||||
|
} catch (AuthorizeException e) {
|
||||||
|
throw new RESTAuthorizationException(e);
|
||||||
|
} finally {
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getNewValueFromOperation(Operation operation) {
|
||||||
|
Object valueObject = operation.getValue();
|
||||||
|
if (valueObject == null | !(valueObject instanceof String)) {
|
||||||
|
throw new UnprocessableEntityException("The /orcid value must be a string");
|
||||||
|
}
|
||||||
|
return (String) valueObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean updatePreferences(Context context, String path, String value, Item profileItem)
|
||||||
|
throws SQLException {
|
||||||
|
switch (path) {
|
||||||
|
case PUBLICATIONS_PREFERENCES:
|
||||||
|
OrcidEntitySyncPreference preference = parsePreference(value);
|
||||||
|
return synchronizationService.setEntityPreference(context, profileItem, PUBLICATION, preference);
|
||||||
|
case FUNDINGS_PREFERENCES:
|
||||||
|
OrcidEntitySyncPreference fundingPreference = parsePreference(value);
|
||||||
|
return synchronizationService.setEntityPreference(context, profileItem, FUNDING, fundingPreference);
|
||||||
|
case PROFILE_PREFERENCES:
|
||||||
|
List<OrcidProfileSyncPreference> profilePreferences = parseProfilePreferences(value);
|
||||||
|
return synchronizationService.setProfilePreference(context, profileItem, profilePreferences);
|
||||||
|
case MODE_PREFERENCES:
|
||||||
|
return synchronizationService.setSynchronizationMode(context, profileItem, parseMode(value));
|
||||||
|
default:
|
||||||
|
throw new UnprocessableEntityException("Invalid path starting with " + OPERATION_ORCID_SYNCH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(Object objectToMatch, Operation operation) {
|
||||||
|
return objectToMatch instanceof ResearcherProfile
|
||||||
|
&& operation.getOp().trim().equalsIgnoreCase(OPERATION_REPLACE)
|
||||||
|
&& operation.getPath().trim().toLowerCase().startsWith(OPERATION_ORCID_SYNCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<OrcidProfileSyncPreference> parseProfilePreferences(String value) {
|
||||||
|
return Arrays.stream(value.split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.filter(StringUtils::isNotEmpty)
|
||||||
|
.map(this::parseProfilePreference)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private OrcidProfileSyncPreference parseProfilePreference(String value) {
|
||||||
|
try {
|
||||||
|
return OrcidProfileSyncPreference.valueOf(value.toUpperCase());
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
throw new UnprocessableEntityException("Invalid profile's synchronization preference value: " + value, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OrcidSynchronizationMode parseMode(String value) {
|
||||||
|
try {
|
||||||
|
return OrcidSynchronizationMode.valueOf(value.toUpperCase());
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
throw new UnprocessableEntityException("Invalid synchronization mode value: " + value, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OrcidEntitySyncPreference parsePreference(String value) {
|
||||||
|
try {
|
||||||
|
return OrcidEntitySyncPreference.valueOf(value.toUpperCase());
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
throw new UnprocessableEntityException("Invalid synchronization preference value: " + value, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -11,14 +11,19 @@ import static com.jayway.jsonpath.JsonPath.read;
|
|||||||
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.UUID.fromString;
|
import static java.util.UUID.fromString;
|
||||||
|
import static org.dspace.app.matcher.MetadataValueMatcher.with;
|
||||||
|
import static org.dspace.app.profile.OrcidEntitySyncPreference.ALL;
|
||||||
import static org.dspace.app.rest.matcher.HalMatcher.matchLinks;
|
import static org.dspace.app.rest.matcher.HalMatcher.matchLinks;
|
||||||
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadata;
|
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadata;
|
||||||
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadataDoesNotExist;
|
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadataDoesNotExist;
|
||||||
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadataNotEmpty;
|
import static org.dspace.app.rest.matcher.MetadataMatcher.matchMetadataNotEmpty;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
import static org.hamcrest.Matchers.empty;
|
import static org.hamcrest.Matchers.empty;
|
||||||
|
import static org.hamcrest.Matchers.hasItem;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.springframework.data.rest.webmvc.RestMediaTypes.TEXT_URI_LIST;
|
import static org.springframework.data.rest.webmvc.RestMediaTypes.TEXT_URI_LIST;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||||
@@ -52,6 +57,7 @@ import org.dspace.content.MetadataValue;
|
|||||||
import org.dspace.content.service.ItemService;
|
import org.dspace.content.service.ItemService;
|
||||||
import org.dspace.eperson.EPerson;
|
import org.dspace.eperson.EPerson;
|
||||||
import org.dspace.services.ConfigurationService;
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.dspace.util.UUIDUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@@ -1030,19 +1036,318 @@ public class ResearcherProfileRestRepositoryIT extends AbstractControllerIntegra
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCloneFromExternalProfileAlreadyAssociated() throws Exception {
|
public void testOrcidMetadataOfEpersonAreCopiedOnProfile() throws Exception {
|
||||||
|
|
||||||
String id = user.getID().toString();
|
context.turnOffAuthorisationSystem();
|
||||||
String authToken = getAuthToken(user.getEmail(), password);
|
|
||||||
|
|
||||||
getClient(authToken).perform(post("/api/eperson/profiles/").contentType(MediaType.APPLICATION_JSON_VALUE))
|
EPerson ePerson = EPersonBuilder.createEPerson(context)
|
||||||
.andExpect(status().isCreated()).andExpect(jsonPath("$.id", is(id)))
|
.withCanLogin(true)
|
||||||
.andExpect(jsonPath("$.visible", is(false))).andExpect(jsonPath("$.type", is("profile")));
|
.withOrcid("0000-1111-2222-3333")
|
||||||
|
.withEmail("test@email.it")
|
||||||
|
.withPassword(password)
|
||||||
|
.withNameInMetadata("Test", "User")
|
||||||
|
.withOrcidAccessToken("af097328-ac1c-4a3e-9eb4-069897874910")
|
||||||
|
.withOrcidRefreshToken("32aadae0-829e-49c5-824f-ccaf4d1913e4")
|
||||||
|
.withOrcidScope("/first-scope")
|
||||||
|
.withOrcidScope("/second-scope")
|
||||||
|
.build();
|
||||||
|
|
||||||
getClient(authToken)
|
context.restoreAuthSystemState();
|
||||||
.perform(post("/api/eperson/profiles/").contentType(TEXT_URI_LIST)
|
|
||||||
.content("http://localhost:8080/server/api/core/items/" + id))
|
String ePersonId = ePerson.getID().toString();
|
||||||
.andExpect(status().isConflict());
|
String authToken = getAuthToken(ePerson.getEmail(), password);
|
||||||
|
|
||||||
|
getClient(authToken).perform(post("/api/eperson/profiles/")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isCreated())
|
||||||
|
.andExpect(jsonPath("$.id", is(ePersonId.toString())))
|
||||||
|
.andExpect(jsonPath("$.visible", is(false)))
|
||||||
|
.andExpect(jsonPath("$.type", is("profile")));
|
||||||
|
|
||||||
|
getClient(authToken).perform(get("/api/eperson/profiles/{id}", ePersonId))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.orcid", is("0000-1111-2222-3333")))
|
||||||
|
.andExpect(jsonPath("$.orcidSynchronization.mode", is("MANUAL")))
|
||||||
|
.andExpect(jsonPath("$.orcidSynchronization.publicationsPreference", is("DISABLED")))
|
||||||
|
.andExpect(jsonPath("$.orcidSynchronization.fundingsPreference", is("DISABLED")))
|
||||||
|
.andExpect(jsonPath("$.orcidSynchronization.profilePreferences", empty()));
|
||||||
|
|
||||||
|
String itemId = getItemIdByProfileId(authToken, ePersonId);
|
||||||
|
|
||||||
|
Item profileItem = itemService.find(context, UUIDUtils.fromString(itemId));
|
||||||
|
assertThat(profileItem, notNullValue());
|
||||||
|
|
||||||
|
List<MetadataValue> metadata = profileItem.getMetadata();
|
||||||
|
assertThat(metadata, hasItem(with("person.identifier.orcid", "0000-1111-2222-3333")));
|
||||||
|
assertThat(metadata, hasItem(with("dspace.orcid.access-token", "af097328-ac1c-4a3e-9eb4-069897874910")));
|
||||||
|
assertThat(metadata, hasItem(with("dspace.orcid.refresh-token", "32aadae0-829e-49c5-824f-ccaf4d1913e4")));
|
||||||
|
assertThat(metadata, hasItem(with("dspace.orcid.scope", "/first-scope", 0)));
|
||||||
|
assertThat(metadata, hasItem(with("dspace.orcid.scope", "/second-scope", 1)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPatchToSetOrcidSynchronizationPreferenceForPublications() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
EPerson ePerson = EPersonBuilder.createEPerson(context)
|
||||||
|
.withCanLogin(true)
|
||||||
|
.withOrcid("0000-1111-2222-3333")
|
||||||
|
.withEmail("test@email.it")
|
||||||
|
.withPassword(password)
|
||||||
|
.withNameInMetadata("Test", "User")
|
||||||
|
.withOrcidAccessToken("af097328-ac1c-4a3e-9eb4-069897874910")
|
||||||
|
.withOrcidRefreshToken("32aadae0-829e-49c5-824f-ccaf4d1913e4")
|
||||||
|
.withOrcidScope("/first-scope")
|
||||||
|
.withOrcidScope("/second-scope")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
String ePersonId = ePerson.getID().toString();
|
||||||
|
String authToken = getAuthToken(ePerson.getEmail(), password);
|
||||||
|
|
||||||
|
getClient(authToken).perform(post("/api/eperson/profiles/")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isCreated());
|
||||||
|
|
||||||
|
List<Operation> operations = asList(new ReplaceOperation("/orcid/publications", ALL.name()));
|
||||||
|
|
||||||
|
getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId)
|
||||||
|
.content(getPatchContent(operations))
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.orcidSynchronization.publicationsPreference", is(ALL.name())));
|
||||||
|
|
||||||
|
getClient(authToken).perform(get("/api/eperson/profiles/{id}", ePersonId))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.orcidSynchronization.publicationsPreference", is(ALL.name())));
|
||||||
|
|
||||||
|
operations = asList(new ReplaceOperation("/orcid/publications", "INVALID_VALUE"));
|
||||||
|
|
||||||
|
getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId)
|
||||||
|
.content(getPatchContent(operations))
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isUnprocessableEntity());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPatchToSetOrcidSynchronizationPreferenceForFundings() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
EPerson ePerson = EPersonBuilder.createEPerson(context)
|
||||||
|
.withCanLogin(true)
|
||||||
|
.withOrcid("0000-1111-2222-3333")
|
||||||
|
.withEmail("test@email.it")
|
||||||
|
.withPassword(password)
|
||||||
|
.withNameInMetadata("Test", "User")
|
||||||
|
.withOrcidAccessToken("af097328-ac1c-4a3e-9eb4-069897874910")
|
||||||
|
.withOrcidRefreshToken("32aadae0-829e-49c5-824f-ccaf4d1913e4")
|
||||||
|
.withOrcidScope("/first-scope")
|
||||||
|
.withOrcidScope("/second-scope")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
String ePersonId = ePerson.getID().toString();
|
||||||
|
String authToken = getAuthToken(ePerson.getEmail(), password);
|
||||||
|
|
||||||
|
getClient(authToken).perform(post("/api/eperson/profiles/")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isCreated());
|
||||||
|
|
||||||
|
List<Operation> operations = asList(new ReplaceOperation("/orcid/fundings", ALL.name()));
|
||||||
|
|
||||||
|
getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId)
|
||||||
|
.content(getPatchContent(operations))
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.orcidSynchronization.fundingsPreference", is(ALL.name())));
|
||||||
|
|
||||||
|
getClient(authToken).perform(get("/api/eperson/profiles/{id}", ePersonId))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.orcidSynchronization.fundingsPreference", is(ALL.name())));
|
||||||
|
|
||||||
|
operations = asList(new ReplaceOperation("/orcid/fundings", "INVALID_VALUE"));
|
||||||
|
|
||||||
|
getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId)
|
||||||
|
.content(getPatchContent(operations))
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isUnprocessableEntity());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPatchToSetOrcidSynchronizationPreferenceForProfile() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
EPerson ePerson = EPersonBuilder.createEPerson(context)
|
||||||
|
.withCanLogin(true)
|
||||||
|
.withOrcid("0000-1111-2222-3333")
|
||||||
|
.withEmail("test@email.it")
|
||||||
|
.withPassword(password)
|
||||||
|
.withNameInMetadata("Test", "User")
|
||||||
|
.withOrcidAccessToken("af097328-ac1c-4a3e-9eb4-069897874910")
|
||||||
|
.withOrcidRefreshToken("32aadae0-829e-49c5-824f-ccaf4d1913e4")
|
||||||
|
.withOrcidScope("/first-scope")
|
||||||
|
.withOrcidScope("/second-scope")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
String ePersonId = ePerson.getID().toString();
|
||||||
|
String authToken = getAuthToken(ePerson.getEmail(), password);
|
||||||
|
|
||||||
|
getClient(authToken).perform(post("/api/eperson/profiles/")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isCreated());
|
||||||
|
|
||||||
|
List<Operation> operations = asList(new ReplaceOperation("/orcid/profile", "IDENTIFIERS"));
|
||||||
|
|
||||||
|
getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId)
|
||||||
|
.content(getPatchContent(operations))
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.orcidSynchronization.profilePreferences",
|
||||||
|
containsInAnyOrder("IDENTIFIERS")));
|
||||||
|
|
||||||
|
getClient(authToken).perform(get("/api/eperson/profiles/{id}", ePersonId))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.orcidSynchronization.profilePreferences",
|
||||||
|
containsInAnyOrder("IDENTIFIERS")));
|
||||||
|
|
||||||
|
operations = asList(new ReplaceOperation("/orcid/profiles", "INVALID_VALUE"));
|
||||||
|
|
||||||
|
getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId)
|
||||||
|
.content(getPatchContent(operations))
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isUnprocessableEntity());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPatchToSetOrcidSynchronizationMode() throws Exception {
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
EPerson ePerson = EPersonBuilder.createEPerson(context)
|
||||||
|
.withCanLogin(true)
|
||||||
|
.withOrcid("0000-1111-2222-3333")
|
||||||
|
.withEmail("test@email.it")
|
||||||
|
.withPassword(password)
|
||||||
|
.withNameInMetadata("Test", "User")
|
||||||
|
.withOrcidAccessToken("af097328-ac1c-4a3e-9eb4-069897874910")
|
||||||
|
.withOrcidRefreshToken("32aadae0-829e-49c5-824f-ccaf4d1913e4")
|
||||||
|
.withOrcidScope("/first-scope")
|
||||||
|
.withOrcidScope("/second-scope")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
String ePersonId = ePerson.getID().toString();
|
||||||
|
String authToken = getAuthToken(ePerson.getEmail(), password);
|
||||||
|
|
||||||
|
getClient(authToken).perform(post("/api/eperson/profiles/")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isCreated());
|
||||||
|
|
||||||
|
List<Operation> operations = asList(new ReplaceOperation("/orcid/mode", "BATCH"));
|
||||||
|
|
||||||
|
getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId)
|
||||||
|
.content(getPatchContent(operations))
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.orcidSynchronization.mode", is("BATCH")));
|
||||||
|
|
||||||
|
getClient(authToken).perform(get("/api/eperson/profiles/{id}", ePersonId))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.orcidSynchronization.mode", is("BATCH")));
|
||||||
|
|
||||||
|
operations = asList(new ReplaceOperation("/orcid/mode", "MANUAL"));
|
||||||
|
|
||||||
|
getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId)
|
||||||
|
.content(getPatchContent(operations))
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.orcidSynchronization.mode", is("MANUAL")));
|
||||||
|
|
||||||
|
getClient(authToken).perform(get("/api/eperson/profiles/{id}", ePersonId))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.orcidSynchronization.mode", is("MANUAL")));
|
||||||
|
|
||||||
|
operations = asList(new ReplaceOperation("/orcid/mode", "INVALID_VALUE"));
|
||||||
|
|
||||||
|
getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId)
|
||||||
|
.content(getPatchContent(operations))
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isUnprocessableEntity());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPatchToSetOrcidSynchronizationPreferenceWithWrongPath() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
EPerson ePerson = EPersonBuilder.createEPerson(context)
|
||||||
|
.withCanLogin(true)
|
||||||
|
.withOrcid("0000-1111-2222-3333")
|
||||||
|
.withEmail("test@email.it")
|
||||||
|
.withPassword(password)
|
||||||
|
.withNameInMetadata("Test", "User")
|
||||||
|
.withOrcidAccessToken("af097328-ac1c-4a3e-9eb4-069897874910")
|
||||||
|
.withOrcidRefreshToken("32aadae0-829e-49c5-824f-ccaf4d1913e4")
|
||||||
|
.withOrcidScope("/first-scope")
|
||||||
|
.withOrcidScope("/second-scope")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
String ePersonId = ePerson.getID().toString();
|
||||||
|
String authToken = getAuthToken(ePerson.getEmail(), password);
|
||||||
|
|
||||||
|
getClient(authToken).perform(post("/api/eperson/profiles/")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isCreated());
|
||||||
|
|
||||||
|
List<Operation> operations = asList(new ReplaceOperation("/orcid/wrong-path", "BATCH"));
|
||||||
|
|
||||||
|
getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId)
|
||||||
|
.content(getPatchContent(operations))
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isUnprocessableEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPatchToSetOrcidSynchronizationPreferenceWithProfileNotLinkedToOrcid() throws Exception {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
EPerson ePerson = EPersonBuilder.createEPerson(context)
|
||||||
|
.withCanLogin(true)
|
||||||
|
.withOrcid("0000-1111-2222-3333")
|
||||||
|
.withEmail("test@email.it")
|
||||||
|
.withPassword(password)
|
||||||
|
.withNameInMetadata("Test", "User")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
String ePersonId = ePerson.getID().toString();
|
||||||
|
String authToken = getAuthToken(ePerson.getEmail(), password);
|
||||||
|
|
||||||
|
getClient(authToken).perform(post("/api/eperson/profiles/")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isCreated());
|
||||||
|
|
||||||
|
List<Operation> operations = asList(new ReplaceOperation("/orcid/mode", "BATCH"));
|
||||||
|
|
||||||
|
getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId)
|
||||||
|
.content(getPatchContent(operations))
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isBadRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1528,6 +1833,22 @@ public class ResearcherProfileRestRepositoryIT extends AbstractControllerIntegra
|
|||||||
assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), not(empty()));
|
assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), not(empty()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCloneFromExternalProfileAlreadyAssociated() throws Exception {
|
||||||
|
|
||||||
|
String id = user.getID().toString();
|
||||||
|
String authToken = getAuthToken(user.getEmail(), password);
|
||||||
|
|
||||||
|
getClient(authToken).perform(post("/api/eperson/profiles/").contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||||
|
.andExpect(status().isCreated()).andExpect(jsonPath("$.id", is(id)))
|
||||||
|
.andExpect(jsonPath("$.visible", is(false))).andExpect(jsonPath("$.type", is("profile")));
|
||||||
|
|
||||||
|
getClient(authToken)
|
||||||
|
.perform(post("/api/eperson/profiles/").contentType(TEXT_URI_LIST)
|
||||||
|
.content("http://localhost:8080/server/api/core/items/" + id))
|
||||||
|
.andExpect(status().isConflict());
|
||||||
|
}
|
||||||
|
|
||||||
private Item createProfile(EPerson ePerson) throws Exception {
|
private Item createProfile(EPerson ePerson) throws Exception {
|
||||||
|
|
||||||
String authToken = getAuthToken(ePerson.getEmail(), password);
|
String authToken = getAuthToken(ePerson.getEmail(), password);
|
||||||
|
@@ -72,6 +72,34 @@
|
|||||||
<scope_note></scope_note>
|
<scope_note></scope_note>
|
||||||
</dc-type>
|
</dc-type>
|
||||||
|
|
||||||
|
<dc-type>
|
||||||
|
<schema>dspace</schema>
|
||||||
|
<element>orcid</element>
|
||||||
|
<qualifier>sync-mode</qualifier>
|
||||||
|
<scope_note></scope_note>
|
||||||
|
</dc-type>
|
||||||
|
|
||||||
|
<dc-type>
|
||||||
|
<schema>dspace</schema>
|
||||||
|
<element>orcid</element>
|
||||||
|
<qualifier>sync-publications</qualifier>
|
||||||
|
<scope_note></scope_note>
|
||||||
|
</dc-type>
|
||||||
|
|
||||||
|
<dc-type>
|
||||||
|
<schema>dspace</schema>
|
||||||
|
<element>orcid</element>
|
||||||
|
<qualifier>sync-fundings</qualifier>
|
||||||
|
<scope_note></scope_note>
|
||||||
|
</dc-type>
|
||||||
|
|
||||||
|
<dc-type>
|
||||||
|
<schema>dspace</schema>
|
||||||
|
<element>orcid</element>
|
||||||
|
<qualifier>sync-profile</qualifier>
|
||||||
|
<scope_note></scope_note>
|
||||||
|
</dc-type>
|
||||||
|
|
||||||
<dc-type>
|
<dc-type>
|
||||||
<schema>dspace</schema>
|
<schema>dspace</schema>
|
||||||
<element>orcid</element>
|
<element>orcid</element>
|
||||||
|
Reference in New Issue
Block a user