From 34f136b67ffc56376f9c46cdfeea7b590124b7ab Mon Sep 17 00:00:00 2001 From: "Gantner, Florian Klaus" Date: Wed, 21 Feb 2024 12:40:44 +0100 Subject: [PATCH] CST-14906 Cherrypick for Orcid Push for Patent and Product Entities --- .../dspace/orcid/model/OrcidEntityType.java | 10 + .../model/OrcidPatentWorkFieldMapping.java | 287 ++++++ .../model/OrcidProductWorkFieldMapping.java | 287 ++++++ .../factory/impl/OrcidPatentWorkFactory.java | 397 +++++++++ .../factory/impl/OrcidProductWorkFactory.java | 398 +++++++++ .../java/org/dspace/builder/ItemBuilder.java | 16 + .../dspace/orcid/OrcidQueueConsumerIT.java | 397 ++++++++- .../service/OrcidEntityFactoryServiceIT.java | 816 +++++++++++++++++- .../converter/ResearcherProfileConverter.java | 19 +- .../app/rest/model/ResearcherProfileRest.java | 22 +- ...eReplaceOrcidSyncPreferencesOperation.java | 26 +- .../dspace/app/rest/ItemRestRepositoryIT.java | 263 ++++++ .../ResearcherProfileRestRepositoryIT.java | 263 +++++- ...ter-dspace-to-orcid-patent-type.properties | 3 + ...er-dspace-to-orcid-product-type.properties | 23 + dspace/config/modules/orcid.cfg | 35 +- dspace/config/registries/dspace-types.xml | 14 + dspace/config/spring/api/orcid-services.xml | 112 ++- 18 files changed, 3338 insertions(+), 50 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/orcid/model/OrcidPatentWorkFieldMapping.java create mode 100644 dspace-api/src/main/java/org/dspace/orcid/model/OrcidProductWorkFieldMapping.java create mode 100644 dspace-api/src/main/java/org/dspace/orcid/model/factory/impl/OrcidPatentWorkFactory.java create mode 100644 dspace-api/src/main/java/org/dspace/orcid/model/factory/impl/OrcidProductWorkFactory.java create mode 100644 dspace/config/crosswalks/orcid/mapConverter-dspace-to-orcid-patent-type.properties create mode 100644 dspace/config/crosswalks/orcid/mapConverter-dspace-to-orcid-product-type.properties diff --git a/dspace-api/src/main/java/org/dspace/orcid/model/OrcidEntityType.java b/dspace-api/src/main/java/org/dspace/orcid/model/OrcidEntityType.java index 6b32818f7673..d035867370e7 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/model/OrcidEntityType.java +++ b/dspace-api/src/main/java/org/dspace/orcid/model/OrcidEntityType.java @@ -22,6 +22,16 @@ public enum OrcidEntityType { */ PUBLICATION("Publication", "/work"), + /** + * The ORCID product/work activity. + */ + PRODUCT("Product", "/work"), + + /** + * The ORCID patent/work activity. + */ + PATENT("Patent", "/work"), + /** * The ORCID funding activity. */ diff --git a/dspace-api/src/main/java/org/dspace/orcid/model/OrcidPatentWorkFieldMapping.java b/dspace-api/src/main/java/org/dspace/orcid/model/OrcidPatentWorkFieldMapping.java new file mode 100644 index 000000000000..4de7bb287570 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/orcid/model/OrcidPatentWorkFieldMapping.java @@ -0,0 +1,287 @@ +/** + * 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.orcid.model; + +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; +import static org.dspace.orcid.model.factory.OrcidFactoryUtils.parseConfigurations; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.dspace.content.integration.crosswalks.CSLItemDataCrosswalk; +import org.dspace.util.SimpleMapConverter; +import org.orcid.jaxb.model.common.CitationType; +import org.orcid.jaxb.model.common.ContributorRole; +import org.orcid.jaxb.model.v3.release.record.Work; + +/** + * Class that contains all the mapping between {@link Work} and DSpace metadata + * fields. Adapted from {@link OrcidWorkFieldMapping} for Patent entity + */ +public class OrcidPatentWorkFieldMapping { + + /** + * The metadata fields related to the work contributors. + */ + private Map contributorFields = new HashMap<>(); + + /** + * The metadata fields related to the work external identifiers. + */ + private Map externalIdentifierFields = new HashMap<>(); + + /** + * The metadata field related to the work publication date. + */ + private String publicationDateField; + + /** + * The metadata field related to the work title. + */ + private String titleField; + + /** + * The metadata field related to the work type. + */ + private String typeField; + + /** + * The metadata field related to the work journal title. + */ + private String journalTitleField; + + /** + * The metadata field related to the work description. + */ + private String shortDescriptionField; + + /** + * The metadata field related to the work language. + */ + private String languageField; + + /** + * The metadata field related to the work sub title. + */ + private String subTitleField; + + private CitationType citationType; + + /** + * The work type converter. + */ + private SimpleMapConverter typeConverter; + + /** + * The work language converter. + */ + private SimpleMapConverter languageConverter; + + private Map citationCrosswalks; + + private String fundingField; + + private String fundingExternalIdType; + + private String fundingExternalId; + + private String fundingEntityExternalId; + + private String fundingUrlField; + + public String convertType(String type) { + return typeConverter != null ? typeConverter.getValue(type) : type; + } + + public String convertLanguage(String language) { + return languageConverter != null ? languageConverter.getValue(language) : language; + } + + public String getTitleField() { + return titleField; + } + + public void setTitleField(String titleField) { + this.titleField = titleField; + } + + public String getTypeField() { + return typeField; + } + + public void setTypeField(String typeField) { + this.typeField = typeField; + } + + public void setTypeConverter(SimpleMapConverter typeConverter) { + this.typeConverter = typeConverter; + } + + public Map getContributorFields() { + return contributorFields; + } + + public void setContributorFields(String contributorFields) { + this.contributorFields = parseContributors(contributorFields); + } + + public Map getExternalIdentifierFields() { + return externalIdentifierFields; + } + + public void setExternalIdentifierFields(String externalIdentifierFields) { + this.externalIdentifierFields = parseConfigurations(externalIdentifierFields); + } + + public String getPublicationDateField() { + return publicationDateField; + } + + public void setPublicationDateField(String publicationDateField) { + this.publicationDateField = publicationDateField; + } + + public String getJournalTitleField() { + return journalTitleField; + } + + public void setJournalTitleField(String journalTitleField) { + this.journalTitleField = journalTitleField; + } + + public String getShortDescriptionField() { + return shortDescriptionField; + } + + public void setShortDescriptionField(String shortDescriptionField) { + this.shortDescriptionField = shortDescriptionField; + } + + public String getLanguageField() { + return languageField; + } + + public void setLanguageField(String languageField) { + this.languageField = languageField; + } + + public void setLanguageConverter(SimpleMapConverter languageConverter) { + this.languageConverter = languageConverter; + } + + public String getSubTitleField() { + return subTitleField; + } + + public void setSubTitleField(String subTitleField) { + this.subTitleField = subTitleField; + } + + public Map getCitationCrosswalks() { + return citationCrosswalks; + } + + public void setCitationCrosswalks(Map citationCrosswalks) { + this.citationCrosswalks = citationCrosswalks; + } + + public String getFundingField() { + return fundingField; + } + + public void setFundingField(String fundingField) { + this.fundingField = fundingField; + } + + public String getFundingExternalIdType() { + return fundingExternalIdType; + } + + public void setFundingExternalIdType(String fundingExternalIdType) { + this.fundingExternalIdType = fundingExternalIdType; + } + + public String getFundingExternalId() { + return fundingExternalId; + } + + public void setFundingExternalId(String fundingExternalId) { + this.fundingExternalId = fundingExternalId; + } + + public String getFundingEntityExternalId() { + return fundingEntityExternalId; + } + + public void setFundingEntityExternalId(String fundingEntityExternalId) { + this.fundingEntityExternalId = fundingEntityExternalId; + } + + public String getFundingUrlField() { + return fundingUrlField; + } + + public void setFundingUrlField(String fundingUrlField) { + this.fundingUrlField = fundingUrlField; + } + + public CitationType getCitationType() { + return citationType; + } + + public void setCitationType(String citationType) { + this.citationType = parseCitationType(citationType); + } + + private CitationType parseCitationType(String citationType) { + + if (StringUtils.isBlank(citationType)) { + return null; + } + + try { + return CitationType.fromValue(citationType); + } catch (IllegalArgumentException ex) { + throw new IllegalArgumentException("The citation type " + citationType + " is invalid, " + + "allowed values are " + getAllowedCitationTypes(), ex); + } + } + + private Map parseContributors(String contributors) { + Map contributorsMap = parseConfigurations(contributors); + return contributorsMap.keySet().stream() + .collect(toMap(identity(), field -> parseContributorRole(contributorsMap.get(field)))); + } + + private ContributorRole parseContributorRole(String contributorRole) { + try { + return ContributorRole.fromValue(contributorRole); + } catch (IllegalArgumentException ex) { + throw new IllegalArgumentException("The contributor role " + contributorRole + + " is invalid, allowed values are " + getAllowedContributorRoles(), ex); + } + } + + private List getAllowedContributorRoles() { + return Arrays.asList(ContributorRole.values()).stream() + .map(ContributorRole::value) + .collect(Collectors.toList()); + } + + private List getAllowedCitationTypes() { + return Arrays.asList(CitationType.values()).stream() + .map(CitationType::value) + .collect(Collectors.toList()); + } + +} diff --git a/dspace-api/src/main/java/org/dspace/orcid/model/OrcidProductWorkFieldMapping.java b/dspace-api/src/main/java/org/dspace/orcid/model/OrcidProductWorkFieldMapping.java new file mode 100644 index 000000000000..c3b77a59733b --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/orcid/model/OrcidProductWorkFieldMapping.java @@ -0,0 +1,287 @@ +/** + * 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.orcid.model; + +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; +import static org.dspace.orcid.model.factory.OrcidFactoryUtils.parseConfigurations; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.dspace.content.integration.crosswalks.CSLItemDataCrosswalk; +import org.dspace.util.SimpleMapConverter; +import org.orcid.jaxb.model.common.CitationType; +import org.orcid.jaxb.model.common.ContributorRole; +import org.orcid.jaxb.model.v3.release.record.Work; + +/** + * Class that contains all the mapping between {@link Work} and DSpace metadata + * fields. Adapted from {@link org.dspace.orcid.model.OrcidWorkFieldMapping} for Product entity + */ +public class OrcidProductWorkFieldMapping { + + /** + * The metadata fields related to the work contributors. + */ + private Map contributorFields = new HashMap<>(); + + /** + * The metadata fields related to the work external identifiers. + */ + private Map externalIdentifierFields = new HashMap<>(); + + /** + * The metadata field related to the work publication date. + */ + private String publicationDateField; + + /** + * The metadata field related to the work title. + */ + private String titleField; + + /** + * The metadata field related to the work type. + */ + private String typeField; + + /** + * The metadata field related to the work journal title. + */ + private String journalTitleField; + + /** + * The metadata field related to the work description. + */ + private String shortDescriptionField; + + /** + * The metadata field related to the work language. + */ + private String languageField; + + /** + * The metadata field related to the work sub title. + */ + private String subTitleField; + + private CitationType citationType; + + /** + * The work type converter. + */ + private SimpleMapConverter typeConverter; + + /** + * The work language converter. + */ + private SimpleMapConverter languageConverter; + + private Map citationCrosswalks; + + private String fundingField; + + private String fundingExternalIdType; + + private String fundingExternalId; + + private String fundingEntityExternalId; + + private String fundingUrlField; + + public String convertType(String type) { + return typeConverter != null ? typeConverter.getValue(type) : type; + } + + public String convertLanguage(String language) { + return languageConverter != null ? languageConverter.getValue(language) : language; + } + + public String getTitleField() { + return titleField; + } + + public void setTitleField(String titleField) { + this.titleField = titleField; + } + + public String getTypeField() { + return typeField; + } + + public void setTypeField(String typeField) { + this.typeField = typeField; + } + + public void setTypeConverter(SimpleMapConverter typeConverter) { + this.typeConverter = typeConverter; + } + + public Map getContributorFields() { + return contributorFields; + } + + public void setContributorFields(String contributorFields) { + this.contributorFields = parseContributors(contributorFields); + } + + public Map getExternalIdentifierFields() { + return externalIdentifierFields; + } + + public void setExternalIdentifierFields(String externalIdentifierFields) { + this.externalIdentifierFields = parseConfigurations(externalIdentifierFields); + } + + public String getPublicationDateField() { + return publicationDateField; + } + + public void setPublicationDateField(String publicationDateField) { + this.publicationDateField = publicationDateField; + } + + public String getJournalTitleField() { + return journalTitleField; + } + + public void setJournalTitleField(String journalTitleField) { + this.journalTitleField = journalTitleField; + } + + public String getShortDescriptionField() { + return shortDescriptionField; + } + + public void setShortDescriptionField(String shortDescriptionField) { + this.shortDescriptionField = shortDescriptionField; + } + + public String getLanguageField() { + return languageField; + } + + public void setLanguageField(String languageField) { + this.languageField = languageField; + } + + public void setLanguageConverter(SimpleMapConverter languageConverter) { + this.languageConverter = languageConverter; + } + + public String getSubTitleField() { + return subTitleField; + } + + public void setSubTitleField(String subTitleField) { + this.subTitleField = subTitleField; + } + + public Map getCitationCrosswalks() { + return citationCrosswalks; + } + + public void setCitationCrosswalks(Map citationCrosswalks) { + this.citationCrosswalks = citationCrosswalks; + } + + public String getFundingField() { + return fundingField; + } + + public void setFundingField(String fundingField) { + this.fundingField = fundingField; + } + + public String getFundingExternalIdType() { + return fundingExternalIdType; + } + + public void setFundingExternalIdType(String fundingExternalIdType) { + this.fundingExternalIdType = fundingExternalIdType; + } + + public String getFundingExternalId() { + return fundingExternalId; + } + + public void setFundingExternalId(String fundingExternalId) { + this.fundingExternalId = fundingExternalId; + } + + public String getFundingEntityExternalId() { + return fundingEntityExternalId; + } + + public void setFundingEntityExternalId(String fundingEntityExternalId) { + this.fundingEntityExternalId = fundingEntityExternalId; + } + + public String getFundingUrlField() { + return fundingUrlField; + } + + public void setFundingUrlField(String fundingUrlField) { + this.fundingUrlField = fundingUrlField; + } + + public CitationType getCitationType() { + return citationType; + } + + public void setCitationType(String citationType) { + this.citationType = parseCitationType(citationType); + } + + private CitationType parseCitationType(String citationType) { + + if (StringUtils.isBlank(citationType)) { + return null; + } + + try { + return CitationType.fromValue(citationType); + } catch (IllegalArgumentException ex) { + throw new IllegalArgumentException("The citation type " + citationType + " is invalid, " + + "allowed values are " + getAllowedCitationTypes(), ex); + } + } + + private Map parseContributors(String contributors) { + Map contributorsMap = parseConfigurations(contributors); + return contributorsMap.keySet().stream() + .collect(toMap(identity(), field -> parseContributorRole(contributorsMap.get(field)))); + } + + private ContributorRole parseContributorRole(String contributorRole) { + try { + return ContributorRole.fromValue(contributorRole); + } catch (IllegalArgumentException ex) { + throw new IllegalArgumentException("The contributor role " + contributorRole + + " is invalid, allowed values are " + getAllowedContributorRoles(), ex); + } + } + + private List getAllowedContributorRoles() { + return Arrays.asList(ContributorRole.values()).stream() + .map(ContributorRole::value) + .collect(Collectors.toList()); + } + + private List getAllowedCitationTypes() { + return Arrays.asList(CitationType.values()).stream() + .map(CitationType::value) + .collect(Collectors.toList()); + } + +} diff --git a/dspace-api/src/main/java/org/dspace/orcid/model/factory/impl/OrcidPatentWorkFactory.java b/dspace-api/src/main/java/org/dspace/orcid/model/factory/impl/OrcidPatentWorkFactory.java new file mode 100644 index 000000000000..34d78e4346e4 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/orcid/model/factory/impl/OrcidPatentWorkFactory.java @@ -0,0 +1,397 @@ +/** + * 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.orcid.model.factory.impl; + +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.dspace.core.CrisConstants.PLACEHOLDER_PARENT_METADATA_VALUE; +import static org.orcid.jaxb.model.common.Relationship.FUNDED_BY; +import static org.orcid.jaxb.model.common.Relationship.SELF; + +import java.io.ByteArrayOutputStream; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; +import org.dspace.authority.service.AuthorityValueService; +import org.dspace.content.Item; +import org.dspace.content.MetadataValue; +import org.dspace.content.integration.crosswalks.CSLItemDataCrosswalk; +import org.dspace.content.service.ItemService; +import org.dspace.core.Context; +import org.dspace.orcid.model.OrcidEntityType; +import org.dspace.orcid.model.OrcidPatentWorkFieldMapping; +import org.dspace.orcid.model.factory.OrcidCommonObjectFactory; +import org.dspace.orcid.model.factory.OrcidEntityFactory; +import org.dspace.util.UUIDUtils; +import org.orcid.jaxb.model.common.CitationType; +import org.orcid.jaxb.model.common.ContributorRole; +import org.orcid.jaxb.model.common.LanguageCode; +import org.orcid.jaxb.model.common.Relationship; +import org.orcid.jaxb.model.common.WorkType; +import org.orcid.jaxb.model.v3.release.common.Contributor; +import org.orcid.jaxb.model.v3.release.common.PublicationDate; +import org.orcid.jaxb.model.v3.release.common.Subtitle; +import org.orcid.jaxb.model.v3.release.common.Title; +import org.orcid.jaxb.model.v3.release.common.Url; +import org.orcid.jaxb.model.v3.release.record.Activity; +import org.orcid.jaxb.model.v3.release.record.Citation; +import org.orcid.jaxb.model.v3.release.record.ExternalID; +import org.orcid.jaxb.model.v3.release.record.ExternalIDs; +import org.orcid.jaxb.model.v3.release.record.Work; +import org.orcid.jaxb.model.v3.release.record.WorkContributors; +import org.orcid.jaxb.model.v3.release.record.WorkTitle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Implementation of {@link OrcidEntityFactory} that creates instances of + * {@link Work}. Copy of {@link OrcidWorkFactory} + * Adapted for Product Entity with own mapping in {@link org.dspace.orcid.model.OrcidProductWorkFieldMapping} + */ +public class OrcidPatentWorkFactory extends OrcidWorkFactory implements OrcidEntityFactory { + + private static final Logger LOGGER = LoggerFactory.getLogger(OrcidPatentWorkFactory.class); + + @Autowired + private ItemService itemService; + + @Autowired + private OrcidCommonObjectFactory orcidCommonObjectFactory; + + private OrcidPatentWorkFieldMapping fieldMapping; + + @Override + public OrcidEntityType getEntityType() { + return OrcidEntityType.PATENT; + } + + @Override + public Activity createOrcidObject(Context context, Item item) { + Work work = new Work(); + work.setJournalTitle(getJournalTitle(context, item)); + work.setWorkContributors(getWorkContributors(context, item)); + work.setWorkTitle(getWorkTitle(context, item)); + work.setPublicationDate(getPublicationDate(context, item)); + work.setWorkExternalIdentifiers(getWorkExternalIds(context, item)); + work.setWorkType(getWorkType(context, item)); + work.setWorkCitation(getWorkCitation(context, item)); + work.setShortDescription(getShortDescription(context, item)); + work.setLanguageCode(getLanguageCode(context, item)); + work.setUrl(getUrl(context, item)); + return work; + } + + private Title getJournalTitle(Context context, Item item) { + return getMetadataValue(context, item, fieldMapping.getJournalTitleField()) + .map(metadataValue -> new Title(metadataValue.getValue())) + .orElse(null); + } + + private WorkContributors getWorkContributors(Context context, Item item) { + Map contributorFields = fieldMapping.getContributorFields(); + List contributors = getMetadataValues(context, item, contributorFields.keySet()).stream() + .map(metadataValue -> getContributor(context, metadataValue)) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + return new WorkContributors(contributors); + } + + private Optional getContributor(Context context, MetadataValue metadataValue) { + Map contributorFields = fieldMapping.getContributorFields(); + ContributorRole role = contributorFields.get(metadataValue.getMetadataField().toString('.')); + return orcidCommonObjectFactory.createContributor(context, metadataValue, role); + } + + /** + * Create an instance of WorkTitle from the given item. + */ + private WorkTitle getWorkTitle(Context context, Item item) { + Optional workTitleValue = getWorkTitleValue(context, item); + if (workTitleValue.isEmpty()) { + return null; + } + + WorkTitle workTitle = new WorkTitle(); + workTitle.setTitle(new Title(workTitleValue.get())); + getSubTitle(context, item).ifPresent(workTitle::setSubtitle); + return workTitle; + } + + /** + * Take the work title from the configured metadata field of the given item + * (orcid.mapping.work.title), if any. + */ + private Optional getWorkTitleValue(Context context, Item item) { + return getMetadataValue(context, item, fieldMapping.getTitleField()) + .map(MetadataValue::getValue); + } + + /** + * Take the work title from the configured metadata field of the given item + * (orcid.mapping.work.sub-title), if any. + */ + private Optional getSubTitle(Context context, Item item) { + return getMetadataValue(context, item, fieldMapping.getSubTitleField()) + .map(MetadataValue::getValue) + .map(Subtitle::new); + } + + private PublicationDate getPublicationDate(Context context, Item item) { + return getMetadataValue(context, item, fieldMapping.getPublicationDateField()) + .flatMap(orcidCommonObjectFactory::createFuzzyDate) + .map(PublicationDate::new) + .orElse(null); + } + + /** + * Creates an instance of ExternalIDs from the metadata values of the given + * item, using the orcid.mapping.funding.external-ids configuration. + */ + private ExternalIDs getWorkExternalIds(Context context, Item item) { + ExternalIDs externalIdentifiers = new ExternalIDs(); + externalIdentifiers.getExternalIdentifier().addAll(getWorkSelfExternalIds(context, item)); + externalIdentifiers.getExternalIdentifier().addAll(getWorkFundedByExternalIds(context, item)); + return externalIdentifiers; + } + + /** + * Creates a list of ExternalID, one for orcid.mapping.funding.external-ids + * value, taking the values from the given item. + */ + private List getWorkSelfExternalIds(Context context, Item item) { + + List selfExternalIds = new ArrayList(); + + Map externalIdentifierFields = fieldMapping.getExternalIdentifierFields(); + + if (externalIdentifierFields.containsKey(SIMPLE_HANDLE_PLACEHOLDER)) { + String handleType = externalIdentifierFields.get(SIMPLE_HANDLE_PLACEHOLDER); + selfExternalIds.add(getExternalId(handleType, item.getHandle(), SELF)); + } + + getMetadataValues(context, item, externalIdentifierFields.keySet()).stream() + .map(this::getSelfExternalId) + .forEach(selfExternalIds::add); + + return selfExternalIds; + } + + /** + * Creates an instance of ExternalID taking the value from the given + * metadataValue. The type of the ExternalID is calculated using the + * orcid.mapping.funding.external-ids configuration. The relationship of the + * ExternalID is SELF. + */ + private ExternalID getSelfExternalId(MetadataValue metadataValue) { + Map externalIdentifierFields = fieldMapping.getExternalIdentifierFields(); + String metadataField = metadataValue.getMetadataField().toString('.'); + return getExternalId(externalIdentifierFields.get(metadataField), metadataValue.getValue(), SELF); + } + + private List getWorkFundedByExternalIds(Context context, Item item) { + + if (isBlank(fieldMapping.getFundingExternalIdType())) { + return Collections.emptyList(); + } + + return getMetadataValues(context, item, fieldMapping.getFundingField()).stream() + .map(metadataValue -> getWorkFundedByExternalId(context, item, metadataValue)) + .flatMap(Optional::stream) + .collect(Collectors.toList()); + } + + private Optional getWorkFundedByExternalId(Context context, Item work, MetadataValue fundingMetadata) { + return getFundedByExternalIdFromFunding(context, fundingMetadata) + .or(() -> getFundedByExternalIdFromWork(context, work, fundingMetadata.getPlace())); + } + + private Optional getFundedByExternalIdFromFunding(Context context, MetadataValue fundingMetadata) { + + if (isAuthoritySet(fundingMetadata.getAuthority())) { + return findItemById(context, UUIDUtils.fromString(fundingMetadata.getAuthority())) + .map(funding -> getFundingExternalId(context, funding)); + } + + return Optional.empty(); + } + + private Optional getFundedByExternalIdFromWork(Context context, Item work, int fundingPlace) { + List externalIdValues = getMetadataValues(context, work, fieldMapping.getFundingExternalId()); + + if (externalIdValues.size() > fundingPlace && isNotPlaceholder(externalIdValues.get(fundingPlace))) { + String value = externalIdValues.get(fundingPlace).getValue(); + return Optional.of(getExternalId(fieldMapping.getFundingExternalIdType(), value, FUNDED_BY)); + } + + return Optional.empty(); + } + + private ExternalID getFundingExternalId(Context context, Item funding) { + + String externalIdValue = getMetadataValue(context, funding, fieldMapping.getFundingEntityExternalId()) + .map(MetadataValue::getValue) + .orElse(null); + + if (externalIdValue == null) { + return null; + } + + Optional fundingUrl = getMetadataValue(context, funding, fieldMapping.getFundingUrlField()) + .map(fundingUrlMetadata -> new Url(fundingUrlMetadata.getValue())) + .or(() -> orcidCommonObjectFactory.createUrl(context, funding)); + + ExternalID externalId = getExternalId(fieldMapping.getFundingExternalIdType(), externalIdValue, FUNDED_BY); + fundingUrl.ifPresent(externalId::setUrl); + return externalId; + } + + private boolean isAuthoritySet(String authority) { + return isNotBlank(authority) && !StringUtils.startsWith(authority, AuthorityValueService.REFERENCE); + } + + /** + * Creates an instance of ExternalID with the given type, value and + * relationship. + */ + private ExternalID getExternalId(String type, String value, Relationship relationship) { + ExternalID externalID = new ExternalID(); + externalID.setType(type); + externalID.setValue(value); + externalID.setRelationship(relationship); + return externalID; + } + + /** + * Creates an instance of WorkType from the given item, taking the value fom the + * configured metadata field (orcid.mapping.work.type). + */ + private WorkType getWorkType(Context context, Item item) { + return getMetadataValue(context, item, fieldMapping.getTypeField()) + .map(MetadataValue::getValue) + .map(type -> fieldMapping.convertType(type)) + .flatMap(this::getWorkType) + .orElse(WorkType.UNDEFINED); + } + + /** + * Creates an instance of WorkType from the given workType value, if valid. + */ + private Optional getWorkType(String workType) { + try { + return Optional.ofNullable(WorkType.fromValue(workType)); + } catch (IllegalArgumentException ex) { + LOGGER.warn("The type {} is not valid for ORCID works", workType); + return Optional.empty(); + } + } + + private Citation getWorkCitation(Context context, Item item) { + + CSLItemDataCrosswalk citationCrosswalk = getCitationCrosswalk(); + + if (citationCrosswalk == null || !citationCrosswalk.canDisseminate(context, item)) { + return null; + } + + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + citationCrosswalk.disseminate(context, item, out); + return new Citation(out.toString(), fieldMapping.getCitationType()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private CSLItemDataCrosswalk getCitationCrosswalk() { + CitationType citationType = fieldMapping.getCitationType(); + return citationType != null ? fieldMapping.getCitationCrosswalks().get(citationType.value()) : null; + } + + private String getShortDescription(Context context, Item item) { + return getMetadataValue(context, item, fieldMapping.getShortDescriptionField()) + .map(MetadataValue::getValue) + .orElse(null); + } + + private String getLanguageCode(Context context, Item item) { + return getMetadataValue(context, item, fieldMapping.getLanguageField()) + .map(MetadataValue::getValue) + .map(language -> fieldMapping.convertLanguage(language)) + .filter(language -> isValidLanguage(language)) + .orElse(null); + } + + private boolean isValidLanguage(String language) { + + if (isBlank(language)) { + return false; + } + + boolean isValid = EnumUtils.isValidEnum(LanguageCode.class, language); + if (!isValid) { + LOGGER.warn("The language {} is not a valid language code for ORCID works", language); + } + return isValid; + } + + private Url getUrl(Context context, Item item) { + return orcidCommonObjectFactory.createUrl(context, item).orElse(null); + } + + private List getMetadataValues(Context context, Item item, String metadataField) { + if (isBlank(metadataField)) { + return Collections.emptyList(); + } + return itemService.getMetadataByMetadataString(item, metadataField); + } + + private boolean isNotPlaceholder(MetadataValue metadata) { + return metadata != null && metadata.getValue() != null + && !metadata.getValue().equals(PLACEHOLDER_PARENT_METADATA_VALUE); + } + + private List getMetadataValues(Context context, Item item, Collection metadataFields) { + return metadataFields.stream() + .flatMap(metadataField -> itemService.getMetadataByMetadataString(item, metadataField).stream()) + .collect(Collectors.toList()); + } + + private Optional findItemById(Context context, UUID id) { + try { + return Optional.ofNullable(itemService.find(context, id)); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + private Optional getMetadataValue(Context context, Item item, String metadataField) { + + if (isBlank(metadataField)) { + return Optional.empty(); + } + + return itemService.getMetadataByMetadataString(item, metadataField).stream() + .filter(metadataValue -> isNotBlank(metadataValue.getValue())) + .findFirst(); + } + + public void setFieldMapping(OrcidPatentWorkFieldMapping fieldMapping) { + this.fieldMapping = fieldMapping; + } + +} diff --git a/dspace-api/src/main/java/org/dspace/orcid/model/factory/impl/OrcidProductWorkFactory.java b/dspace-api/src/main/java/org/dspace/orcid/model/factory/impl/OrcidProductWorkFactory.java new file mode 100644 index 000000000000..2636592cfae4 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/orcid/model/factory/impl/OrcidProductWorkFactory.java @@ -0,0 +1,398 @@ +/** + * 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.orcid.model.factory.impl; + +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.dspace.core.CrisConstants.PLACEHOLDER_PARENT_METADATA_VALUE; +import static org.orcid.jaxb.model.common.Relationship.FUNDED_BY; +import static org.orcid.jaxb.model.common.Relationship.SELF; + +import java.io.ByteArrayOutputStream; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; +import org.dspace.authority.service.AuthorityValueService; +import org.dspace.content.Item; +import org.dspace.content.MetadataValue; +import org.dspace.content.integration.crosswalks.CSLItemDataCrosswalk; +import org.dspace.content.service.ItemService; +import org.dspace.core.Context; +import org.dspace.orcid.model.OrcidEntityType; +import org.dspace.orcid.model.OrcidProductWorkFieldMapping; +import org.dspace.orcid.model.factory.OrcidCommonObjectFactory; +import org.dspace.orcid.model.factory.OrcidEntityFactory; +import org.dspace.util.UUIDUtils; +import org.orcid.jaxb.model.common.CitationType; +import org.orcid.jaxb.model.common.ContributorRole; +import org.orcid.jaxb.model.common.LanguageCode; +import org.orcid.jaxb.model.common.Relationship; +import org.orcid.jaxb.model.common.WorkType; +import org.orcid.jaxb.model.v3.release.common.Contributor; +import org.orcid.jaxb.model.v3.release.common.PublicationDate; +import org.orcid.jaxb.model.v3.release.common.Subtitle; +import org.orcid.jaxb.model.v3.release.common.Title; +import org.orcid.jaxb.model.v3.release.common.Url; +import org.orcid.jaxb.model.v3.release.record.Activity; +import org.orcid.jaxb.model.v3.release.record.Citation; +import org.orcid.jaxb.model.v3.release.record.ExternalID; +import org.orcid.jaxb.model.v3.release.record.ExternalIDs; +import org.orcid.jaxb.model.v3.release.record.Work; +import org.orcid.jaxb.model.v3.release.record.WorkContributors; +import org.orcid.jaxb.model.v3.release.record.WorkTitle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Implementation of {@link OrcidEntityFactory} that creates instances of + * {@link Work}. Copy of {@link OrcidWorkFactory} + * Adapted for Product Entity with own mapping in {@link org.dspace.orcid.model.OrcidProductWorkFieldMapping} + * + */ +public class OrcidProductWorkFactory implements OrcidEntityFactory { + + private static final Logger LOGGER = LoggerFactory.getLogger(OrcidProductWorkFactory.class); + + @Autowired + private ItemService itemService; + + @Autowired + private OrcidCommonObjectFactory orcidCommonObjectFactory; + + private OrcidProductWorkFieldMapping fieldMapping; + + @Override + public OrcidEntityType getEntityType() { + return OrcidEntityType.PRODUCT; + } + + @Override + public Activity createOrcidObject(Context context, Item item) { + Work work = new Work(); + work.setJournalTitle(getJournalTitle(context, item)); + work.setWorkContributors(getWorkContributors(context, item)); + work.setWorkTitle(getWorkTitle(context, item)); + work.setPublicationDate(getPublicationDate(context, item)); + work.setWorkExternalIdentifiers(getWorkExternalIds(context, item)); + work.setWorkType(getWorkType(context, item)); + work.setWorkCitation(getWorkCitation(context, item)); + work.setShortDescription(getShortDescription(context, item)); + work.setLanguageCode(getLanguageCode(context, item)); + work.setUrl(getUrl(context, item)); + return work; + } + + private Title getJournalTitle(Context context, Item item) { + return getMetadataValue(context, item, fieldMapping.getJournalTitleField()) + .map(metadataValue -> new Title(metadataValue.getValue())) + .orElse(null); + } + + private WorkContributors getWorkContributors(Context context, Item item) { + Map contributorFields = fieldMapping.getContributorFields(); + List contributors = getMetadataValues(context, item, contributorFields.keySet()).stream() + .map(metadataValue -> getContributor(context, metadataValue)) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + return new WorkContributors(contributors); + } + + private Optional getContributor(Context context, MetadataValue metadataValue) { + Map contributorFields = fieldMapping.getContributorFields(); + ContributorRole role = contributorFields.get(metadataValue.getMetadataField().toString('.')); + return orcidCommonObjectFactory.createContributor(context, metadataValue, role); + } + + /** + * Create an instance of WorkTitle from the given item. + */ + private WorkTitle getWorkTitle(Context context, Item item) { + Optional workTitleValue = getWorkTitleValue(context, item); + if (workTitleValue.isEmpty()) { + return null; + } + + WorkTitle workTitle = new WorkTitle(); + workTitle.setTitle(new Title(workTitleValue.get())); + getSubTitle(context, item).ifPresent(workTitle::setSubtitle); + return workTitle; + } + + /** + * Take the work title from the configured metadata field of the given item + * (orcid.mapping.work.title), if any. + */ + private Optional getWorkTitleValue(Context context, Item item) { + return getMetadataValue(context, item, fieldMapping.getTitleField()) + .map(MetadataValue::getValue); + } + + /** + * Take the work title from the configured metadata field of the given item + * (orcid.mapping.work.sub-title), if any. + */ + private Optional getSubTitle(Context context, Item item) { + return getMetadataValue(context, item, fieldMapping.getSubTitleField()) + .map(MetadataValue::getValue) + .map(Subtitle::new); + } + + private PublicationDate getPublicationDate(Context context, Item item) { + return getMetadataValue(context, item, fieldMapping.getPublicationDateField()) + .flatMap(orcidCommonObjectFactory::createFuzzyDate) + .map(PublicationDate::new) + .orElse(null); + } + + /** + * Creates an instance of ExternalIDs from the metadata values of the given + * item, using the orcid.mapping.funding.external-ids configuration. + */ + private ExternalIDs getWorkExternalIds(Context context, Item item) { + ExternalIDs externalIdentifiers = new ExternalIDs(); + externalIdentifiers.getExternalIdentifier().addAll(getWorkSelfExternalIds(context, item)); + externalIdentifiers.getExternalIdentifier().addAll(getWorkFundedByExternalIds(context, item)); + return externalIdentifiers; + } + + /** + * Creates a list of ExternalID, one for orcid.mapping.funding.external-ids + * value, taking the values from the given item. + */ + private List getWorkSelfExternalIds(Context context, Item item) { + + List selfExternalIds = new ArrayList(); + + Map externalIdentifierFields = fieldMapping.getExternalIdentifierFields(); + + if (externalIdentifierFields.containsKey(SIMPLE_HANDLE_PLACEHOLDER)) { + String handleType = externalIdentifierFields.get(SIMPLE_HANDLE_PLACEHOLDER); + selfExternalIds.add(getExternalId(handleType, item.getHandle(), SELF)); + } + + getMetadataValues(context, item, externalIdentifierFields.keySet()).stream() + .map(this::getSelfExternalId) + .forEach(selfExternalIds::add); + + return selfExternalIds; + } + + /** + * Creates an instance of ExternalID taking the value from the given + * metadataValue. The type of the ExternalID is calculated using the + * orcid.mapping.funding.external-ids configuration. The relationship of the + * ExternalID is SELF. + */ + private ExternalID getSelfExternalId(MetadataValue metadataValue) { + Map externalIdentifierFields = fieldMapping.getExternalIdentifierFields(); + String metadataField = metadataValue.getMetadataField().toString('.'); + return getExternalId(externalIdentifierFields.get(metadataField), metadataValue.getValue(), SELF); + } + + private List getWorkFundedByExternalIds(Context context, Item item) { + + if (isBlank(fieldMapping.getFundingExternalIdType())) { + return Collections.emptyList(); + } + + return getMetadataValues(context, item, fieldMapping.getFundingField()).stream() + .map(metadataValue -> getWorkFundedByExternalId(context, item, metadataValue)) + .flatMap(Optional::stream) + .collect(Collectors.toList()); + } + + private Optional getWorkFundedByExternalId(Context context, Item work, MetadataValue fundingMetadata) { + return getFundedByExternalIdFromFunding(context, fundingMetadata) + .or(() -> getFundedByExternalIdFromWork(context, work, fundingMetadata.getPlace())); + } + + private Optional getFundedByExternalIdFromFunding(Context context, MetadataValue fundingMetadata) { + + if (isAuthoritySet(fundingMetadata.getAuthority())) { + return findItemById(context, UUIDUtils.fromString(fundingMetadata.getAuthority())) + .map(funding -> getFundingExternalId(context, funding)); + } + + return Optional.empty(); + } + + private Optional getFundedByExternalIdFromWork(Context context, Item work, int fundingPlace) { + List externalIdValues = getMetadataValues(context, work, fieldMapping.getFundingExternalId()); + + if (externalIdValues.size() > fundingPlace && isNotPlaceholder(externalIdValues.get(fundingPlace))) { + String value = externalIdValues.get(fundingPlace).getValue(); + return Optional.of(getExternalId(fieldMapping.getFundingExternalIdType(), value, FUNDED_BY)); + } + + return Optional.empty(); + } + + private ExternalID getFundingExternalId(Context context, Item funding) { + + String externalIdValue = getMetadataValue(context, funding, fieldMapping.getFundingEntityExternalId()) + .map(MetadataValue::getValue) + .orElse(null); + + if (externalIdValue == null) { + return null; + } + + Optional fundingUrl = getMetadataValue(context, funding, fieldMapping.getFundingUrlField()) + .map(fundingUrlMetadata -> new Url(fundingUrlMetadata.getValue())) + .or(() -> orcidCommonObjectFactory.createUrl(context, funding)); + + ExternalID externalId = getExternalId(fieldMapping.getFundingExternalIdType(), externalIdValue, FUNDED_BY); + fundingUrl.ifPresent(externalId::setUrl); + return externalId; + } + + private boolean isAuthoritySet(String authority) { + return isNotBlank(authority) && !StringUtils.startsWith(authority, AuthorityValueService.REFERENCE); + } + + /** + * Creates an instance of ExternalID with the given type, value and + * relationship. + */ + private ExternalID getExternalId(String type, String value, Relationship relationship) { + ExternalID externalID = new ExternalID(); + externalID.setType(type); + externalID.setValue(value); + externalID.setRelationship(relationship); + return externalID; + } + + /** + * Creates an instance of WorkType from the given item, taking the value fom the + * configured metadata field (orcid.mapping.work.type). + */ + private WorkType getWorkType(Context context, Item item) { + return getMetadataValue(context, item, fieldMapping.getTypeField()) + .map(MetadataValue::getValue) + .map(type -> fieldMapping.convertType(type)) + .flatMap(this::getWorkType) + .orElse(WorkType.UNDEFINED); + } + + /** + * Creates an instance of WorkType from the given workType value, if valid. + */ + private Optional getWorkType(String workType) { + try { + return Optional.ofNullable(WorkType.fromValue(workType)); + } catch (IllegalArgumentException ex) { + LOGGER.warn("The type {} is not valid for ORCID works", workType); + return Optional.empty(); + } + } + + private Citation getWorkCitation(Context context, Item item) { + + CSLItemDataCrosswalk citationCrosswalk = getCitationCrosswalk(); + + if (citationCrosswalk == null || !citationCrosswalk.canDisseminate(context, item)) { + return null; + } + + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + citationCrosswalk.disseminate(context, item, out); + return new Citation(out.toString(), fieldMapping.getCitationType()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private CSLItemDataCrosswalk getCitationCrosswalk() { + CitationType citationType = fieldMapping.getCitationType(); + return citationType != null ? fieldMapping.getCitationCrosswalks().get(citationType.value()) : null; + } + + private String getShortDescription(Context context, Item item) { + return getMetadataValue(context, item, fieldMapping.getShortDescriptionField()) + .map(MetadataValue::getValue) + .orElse(null); + } + + private String getLanguageCode(Context context, Item item) { + return getMetadataValue(context, item, fieldMapping.getLanguageField()) + .map(MetadataValue::getValue) + .map(language -> fieldMapping.convertLanguage(language)) + .filter(language -> isValidLanguage(language)) + .orElse(null); + } + + private boolean isValidLanguage(String language) { + + if (isBlank(language)) { + return false; + } + + boolean isValid = EnumUtils.isValidEnum(LanguageCode.class, language); + if (!isValid) { + LOGGER.warn("The language {} is not a valid language code for ORCID works", language); + } + return isValid; + } + + private Url getUrl(Context context, Item item) { + return orcidCommonObjectFactory.createUrl(context, item).orElse(null); + } + + private List getMetadataValues(Context context, Item item, String metadataField) { + if (isBlank(metadataField)) { + return Collections.emptyList(); + } + return itemService.getMetadataByMetadataString(item, metadataField); + } + + private boolean isNotPlaceholder(MetadataValue metadata) { + return metadata != null && metadata.getValue() != null + && !metadata.getValue().equals(PLACEHOLDER_PARENT_METADATA_VALUE); + } + + private List getMetadataValues(Context context, Item item, Collection metadataFields) { + return metadataFields.stream() + .flatMap(metadataField -> itemService.getMetadataByMetadataString(item, metadataField).stream()) + .collect(Collectors.toList()); + } + + private Optional findItemById(Context context, UUID id) { + try { + return Optional.ofNullable(itemService.find(context, id)); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + private Optional getMetadataValue(Context context, Item item, String metadataField) { + + if (isBlank(metadataField)) { + return Optional.empty(); + } + + return itemService.getMetadataByMetadataString(item, metadataField).stream() + .filter(metadataValue -> isNotBlank(metadataValue.getValue())) + .findFirst(); + } + + public void setFieldMapping(OrcidProductWorkFieldMapping fieldMapping) { + this.fieldMapping = fieldMapping; + } + +} diff --git a/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java b/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java index 3e5ab0f38f5b..4d3dd4bac98e 100644 --- a/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java @@ -234,6 +234,22 @@ public ItemBuilder withOrcidSynchronizationFundingsPreference(String value) { return setMetadataSingleValue(item, "dspace", "orcid", "sync-fundings", value); } + public ItemBuilder withOrcidSynchronizationProductsPreference(OrcidEntitySyncPreference value) { + return withOrcidSynchronizationProductsPreference(value.name()); + } + + public ItemBuilder withOrcidSynchronizationProductsPreference(String value) { + return setMetadataSingleValue(item, "dspace", "orcid", "sync-products", value); + } + + public ItemBuilder withOrcidSynchronizationPatentsPreference(OrcidEntitySyncPreference value) { + return withOrcidSynchronizationPatentsPreference(value.name()); + } + + public ItemBuilder withOrcidSynchronizationPatentsPreference(String value) { + return setMetadataSingleValue(item, "dspace", "orcid", "sync-patents", value); + } + public ItemBuilder withOrcidSynchronizationProfilePreference(OrcidProfileSyncPreference value) { return withOrcidSynchronizationProfilePreference(value.name()); } diff --git a/dspace-api/src/test/java/org/dspace/orcid/OrcidQueueConsumerIT.java b/dspace-api/src/test/java/org/dspace/orcid/OrcidQueueConsumerIT.java index f2e528d78cd6..c12869459ddf 100644 --- a/dspace-api/src/test/java/org/dspace/orcid/OrcidQueueConsumerIT.java +++ b/dspace-api/src/test/java/org/dspace/orcid/OrcidQueueConsumerIT.java @@ -575,7 +575,112 @@ public void testNoOrcidQueueRecordCreationOccursIfPublicationSynchronizationIsDi } @Test - public void testOrcidQueueRecordCreationToUpdateProject() throws Exception { + public void testOrcidQueueRecordCreationForFunding() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .withOrcidSynchronizationFundingsPreference(ALL) + .build(); + + Collection fundingCollection = createCollection("Fundings", "Funding"); + + Item funding = ItemBuilder.createItem(context, fundingCollection) + .withTitle("Test funding") + .withFundingInvestigator("Test User", profile.getID().toString()) + .build(); + + context.restoreAuthSystemState(); + context.commit(); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, funding, "Funding", null, INSERT)); + + addMetadata(funding, "dc", "type", null, "Funding type", null); + context.commit(); + + List newOrcidQueueRecords = orcidQueueService.findAll(context); + assertThat(newOrcidQueueRecords, hasSize(1)); + + assertThat(orcidQueueRecords.get(0), equalTo(newOrcidQueueRecords.get(0))); + } + + @Test + public void testOrcidQueueRecordCreationForProduct() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .withOrcidSynchronizationProductsPreference(ALL) + .build(); + + Collection productCollection = createCollection("Products", "Product"); + + Item product = ItemBuilder.createItem(context, productCollection) + .withTitle("Test product") + .withAuthor("Test User", profile.getID().toString()) + .build(); + + context.restoreAuthSystemState(); + context.commit(); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, product, "Product", null, INSERT)); + + addMetadata(product, "dc", "type", null, "http://purl.org/coar/resource_type/scheme/c_12cc", null); + context.commit(); + + List newOrcidQueueRecords = orcidQueueService.findAll(context); + assertThat(newOrcidQueueRecords, hasSize(1)); + + assertThat(orcidQueueRecords.get(0), equalTo(newOrcidQueueRecords.get(0))); + } + + @Test + public void testOrcidQueueRecordCreationForPatent() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .withOrcidSynchronizationPatentsPreference(ALL) + .build(); + + Collection patentCollection = createCollection("Patents", "Patent"); + + Item patent = ItemBuilder.createItem(context, patentCollection) + .withTitle("Test patent") + .withAuthor("Test User", profile.getID().toString()) + .build(); + + context.restoreAuthSystemState(); + context.commit(); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, patent, "Patent", null, INSERT)); + + addMetadata(patent, "dc", "type", null, "http://purl.org/coar/resource_type/scheme/Z907-YMBB", null); + context.commit(); + + List newOrcidQueueRecords = orcidQueueService.findAll(context); + assertThat(newOrcidQueueRecords, hasSize(1)); + + assertThat(orcidQueueRecords.get(0), equalTo(newOrcidQueueRecords.get(0))); + } + + @Test + public void testOrcidQueueRecordCreationToUpdateFunding() throws Exception { context.turnOffAuthorisationSystem(); @@ -609,7 +714,225 @@ public void testOrcidQueueRecordCreationToUpdateProject() throws Exception { List orcidQueueRecords = orcidQueueService.findAll(context); assertThat(orcidQueueRecords, hasSize(1)); - assertThat(orcidQueueRecords.get(0), matches(profile, project, "Project", "123456", UPDATE)); + assertThat(orcidQueueRecords.get(0), matches(profile, funding, "Funding", "123456", UPDATE)); + } + + @Test + public void testOrcidQueueRecordCreationToUpdateProduct() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .withOrcidSynchronizationProductsPreference(ALL) + .build(); + + Collection productCollection = createCollection("Products", "Product"); + + Item product = ItemBuilder.createItem(context, productCollection) + .withTitle("Test product") + .build(); + + createOrcidHistory(context, profile, product) + .withPutCode("123456") + .build(); + + addMetadata(product, "dc", "contributor", "author", "Test User", profile.getID().toString()); + + context.restoreAuthSystemState(); + context.commit(); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, product, "Product", "123456", UPDATE)); + } + + @Test + public void testOrcidQueueRecordCreationToUpdatePatent() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .withOrcidSynchronizationPatentsPreference(ALL) + .build(); + + Collection patentCollection = createCollection("Patents", "Patent"); + + Item patent = ItemBuilder.createItem(context, patentCollection) + .withTitle("Test patent") + .build(); + + createOrcidHistory(context, profile, patent) + .withPutCode("123456") + .build(); + + addMetadata(patent, "dc", "contributor", "author", "Test User", profile.getID().toString()); + + context.restoreAuthSystemState(); + context.commit(); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, patent, "Patent", "123456", UPDATE)); + } + + @Test + public void testNoOrcidQueueRecordCreationOccursIfFundingSynchronizationIsDisabled() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .build(); + + Collection fundingCollection = createCollection("Fundings", "Funding"); + + Item funding = ItemBuilder.createItem(context, fundingCollection) + .withTitle("Test funding") + .withFundingInvestigator("Test User", profile.getID().toString()) + .build(); + + context.restoreAuthSystemState(); + context.commit(); + + assertThat(orcidQueueService.findAll(context), empty()); + + addMetadata(profile, "dspace", "orcid", "sync-fundings", DISABLED.name(), null); + addMetadata(funding, "crispj", "partnerou", null, "Partner", null); + context.commit(); + + assertThat(orcidQueueService.findAll(context), empty()); + } + + @Test + public void testNoOrcidQueueRecordCreationOccursIfProductSynchronizationIsDisabled() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .build(); + + Collection productCollection = createCollection("Products", "Product"); + + Item product = ItemBuilder.createItem(context, productCollection) + .withTitle("Test product") + .withAuthor("Test User", profile.getID().toString()) + .build(); + + context.restoreAuthSystemState(); + context.commit(); + + assertThat(orcidQueueService.findAll(context), empty()); + + addMetadata(profile, "dspace", "orcid", "sync-products", DISABLED.name(), null); + addMetadata(product, "dc", "description", "abstract", "Product Poduct Pduct Puct Pct Pt P", null); + context.commit(); + + assertThat(orcidQueueService.findAll(context), empty()); + } + + @Test + public void testNoOrcidQueueRecordCreationOccursIfPatentSynchronizationIsDisabled() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .build(); + + Collection patentCollection = createCollection("Patents", "Patent"); + + Item patent = ItemBuilder.createItem(context, patentCollection) + .withTitle("Test patent") + .withAuthor("Test User", profile.getID().toString()) + .build(); + + context.restoreAuthSystemState(); + context.commit(); + + assertThat(orcidQueueService.findAll(context), empty()); + + addMetadata(profile, "dspace", "orcid", "sync-patents", DISABLED.name(), null); + addMetadata(patent, "dc", "description", "abstract", "Patent Ptent Pent Pnt Pt P", null); + context.commit(); + + assertThat(orcidQueueService.findAll(context), empty()); + } + + @Test + public void testNoOrcidQueueRecordCreationOccursIfProfileHasNotOrcidIdentifier() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .withOrcidSynchronizationFundingsPreference(ALL) + .withOrcidSynchronizationProductsPreference(ALL) + .withOrcidSynchronizationPatentsPreference(ALL) + .build(); + + Collection fundingCollection = createCollection("Fundings", "Funding"); + + ItemBuilder.createItem(context, fundingCollection) + .withTitle("Test funding") + .withFundingInvestigator("Test User", profile.getID().toString()) + .build(); + + Collection productCollection = createCollection("Products", "Product"); + + ItemBuilder.createItem(context, productCollection) + .withTitle("Test product") + .withAuthor("Test User", profile.getID().toString()) + .build(); + + Collection patentsCollection = createCollection("Patents", "Patent"); + + ItemBuilder.createItem(context, patentsCollection) + .withTitle("Test patent") + .withAuthor("Test User", profile.getID().toString()) + .build(); + + context.restoreAuthSystemState(); + context.commit(); + + assertThat(orcidQueueService.findAll(context), empty()); + } + + @Test + public void testNoOrcidQueueRecordCreationOccursIfProfileHasNotOrcidAccessToken() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidSynchronizationFundingsPreference(ALL) + .build(); + + Collection fundingCollection = createCollection("Fundings", "Funding"); + + ItemBuilder.createItem(context, fundingCollection) + .withTitle("Test funding") + .withFundingInvestigator("Test User", profile.getID().toString()) + .build(); + + context.restoreAuthSystemState(); + context.commit(); + + assertThat(orcidQueueService.findAll(context), empty()); } @Test @@ -687,6 +1010,76 @@ public void testOrcidQueueRecalculationOnProfilePreferenceUpdate() throws Except } + @Test + public void testWithMetadataFieldToIgnore() throws Exception { + configurationService.addPropertyValue("orcid.linkable-metadata-fields.ignore", "dc.contributor.author"); + configurationService.addPropertyValue("orcid.linkable-metadata-fields.ignore", "crisfund.coinvestigators"); + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-0000-0012-2345") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .withOrcidSynchronizationFundingsPreference(ALL) + .withOrcidSynchronizationPublicationsPreference(ALL) + .withOrcidSynchronizationProductsPreference(ALL) + .withOrcidSynchronizationPatentsPreference(ALL) + .build(); + + Collection publicationCollection = createCollection("Publications", "Publication"); + + Item firstPublication = ItemBuilder.createItem(context, publicationCollection) + .withTitle("Another Test publication") + .withEditor("Test User", profile.getID().toString()) + .build(); + + Item secondPublication = ItemBuilder.createItem(context, publicationCollection) + .withTitle("Another Test publication") + .withAuthor("Test User", profile.getID().toString()) + .withEditor("Test User", profile.getID().toString()) + .build(); + + ItemBuilder.createItem(context, publicationCollection) + .withTitle("Test publication") + .withAuthor("Test User", profile.getID().toString()) + .build(); + + Collection fundingCollection = createCollection("Fundings", "Funding"); + + Item firstFunding = ItemBuilder.createItem(context, fundingCollection) + .withTitle("Test funding") + .withFundingInvestigator("Test User", profile.getID().toString()) + .build(); + + ItemBuilder.createItem(context, fundingCollection) + .withTitle("Another funding") + .withFundingCoInvestigator("Test User", profile.getID().toString()) + .build(); + + Collection productCollection = createCollection("Products", "Product"); + + Item firstProduct = ItemBuilder.createItem(context, productCollection) + .withTitle("Test product") + .withAuthor("Test User", profile.getID().toString()) + .build(); + + Collection patentCollection = createCollection("Patents", "Patent"); + + Item firstPatent = ItemBuilder.createItem(context, patentCollection) + .withTitle("Test patent") + .withAuthor("Test User", profile.getID().toString()) + .build(); + + context.restoreAuthSystemState(); + + List records = orcidQueueService.findAll(context); + assertThat(records, hasSize(3)); + assertThat(records, hasItem(matches(profile, firstPublication, "Publication", null, INSERT))); + assertThat(records, hasItem(matches(profile, secondPublication, "Publication", null, INSERT))); + assertThat(records, hasItem(matches(profile, firstFunding, "Funding", null, INSERT))); + } + @Test public void testWithManyInsertionAndDeletionOfSameMetadataValue() throws Exception { diff --git a/dspace-api/src/test/java/org/dspace/orcid/service/OrcidEntityFactoryServiceIT.java b/dspace-api/src/test/java/org/dspace/orcid/service/OrcidEntityFactoryServiceIT.java index 17bc6ee531c3..f1d054aba78a 100644 --- a/dspace-api/src/test/java/org/dspace/orcid/service/OrcidEntityFactoryServiceIT.java +++ b/dspace-api/src/test/java/org/dspace/orcid/service/OrcidEntityFactoryServiceIT.java @@ -69,8 +69,14 @@ public class OrcidEntityFactoryServiceIT extends AbstractIntegrationTestWithData private Collection orgUnits; + private Collection patents; + private Collection publications; + private Collection products; + + private Collection products; + private Collection projects; @Before @@ -89,11 +95,26 @@ public void setup() { .withEntityType("OrgUnit") .build(); + patents = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection") + .withEntityType("Patent") + .build(); + publications = CollectionBuilder.createCollection(context, parentCommunity) .withName("Collection") .withEntityType("Publication") .build(); + products = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection") + .withEntityType("Product") + .build(); + + products = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection") + .withEntityType("Product") + .build(); + projects = CollectionBuilder.createCollection(context, parentCommunity) .withName("Collection") .withEntityType("Project") @@ -157,36 +178,809 @@ public void testWorkCreation() { } @Test - public void testEmptyWorkWithUnknownTypeCreation() { + public void testProductWorkCreation() { context.turnOffAuthorisationSystem(); - Item publication = ItemBuilder.createItem(context, publications) - .withType("TYPE") + Item author = ItemBuilder.createItem(context, persons) + .withTitle("Jesse Pinkman") + .withOrcidIdentifier("0000-1111-2222-3333") + .withPersonEmail("test@test.it") + .build(); + + Item product = ItemBuilder.createItem(context, products) + .withTitle("Test dataset") + .withAuthor("Walter White") + .withAuthor("Jesse Pinkman", author.getID().toString()) + .withEditor("Editor") + .withIssueDate("2021-04-30") + .withDescriptionAbstract("Product description") + .withLanguage("en_US") + .withType("http://purl.org/coar/resource_type/c_ddb1") + .withIsPartOf("Collection of Products") + .withDoiIdentifier("doi-id") + .withScopusIdentifier("scopus-id") .build(); context.restoreAuthSystemState(); - Activity activity = entityFactoryService.createOrcidObject(context, publication); + Activity activity = entityFactoryService.createOrcidObject(context, product); assertThat(activity, instanceOf(Work.class)); Work work = (Work) activity; - assertThat(work.getJournalTitle(), nullValue()); - assertThat(work.getLanguageCode(), nullValue()); - assertThat(work.getPublicationDate(), nullValue()); - assertThat(work.getShortDescription(), nullValue()); + assertThat(work.getJournalTitle(), notNullValue()); + assertThat(work.getJournalTitle().getContent(), is("Collection of Products")); + assertThat(work.getLanguageCode(), is("en")); + assertThat(work.getPublicationDate(), matches(date("2021", "04", "30"))); + assertThat(work.getShortDescription(), is("Product description")); assertThat(work.getPutCode(), nullValue()); - assertThat(work.getWorkType(), is(WorkType.OTHER)); - assertThat(work.getWorkTitle(), nullValue()); + // assertThat(work.getWorkCitation(), notNullValue()); + // assertThat(work.getWorkCitation().getCitation(), containsString("Test product")); + assertThat(work.getWorkType(), is(WorkType.DATA_SET)); + assertThat(work.getWorkTitle(), notNullValue()); + assertThat(work.getWorkTitle().getTitle(), notNullValue()); + assertThat(work.getWorkTitle().getTitle().getContent(), is("Test dataset")); assertThat(work.getWorkContributors(), notNullValue()); - assertThat(work.getWorkContributors().getContributor(), empty()); + assertThat(work.getUrl(), matches(urlEndsWith(product.getHandle()))); + + List contributors = work.getWorkContributors().getContributor(); + assertThat(contributors, hasSize(2)); + assertThat(contributors, has(contributor("Walter White", AUTHOR, FIRST))); + // assertThat(contributors, has(contributor("Editor", EDITOR, FIRST))); + assertThat(contributors, has(contributor("Jesse Pinkman", AUTHOR, ADDITIONAL, + "0000-1111-2222-3333", "test@test.it"))); + + assertThat(work.getExternalIdentifiers(), notNullValue()); + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(3)); + assertThat(externalIds, has(selfExternalId("doi", "doi-id"))); + assertThat(externalIds, has(selfExternalId("eid", "scopus-id"))); + assertThat(externalIds, has(selfExternalId("handle", product.getHandle()))); + + } + + @Test + public void testProductWorkCreation() { + + context.turnOffAuthorisationSystem(); + + Item author = ItemBuilder.createItem(context, persons) + .withTitle("Jesse Pinkman") + .withOrcidIdentifier("0000-1111-2222-3333") + .withPersonEmail("test@test.it") + .build(); + + Item product = ItemBuilder.createItem(context, products) + .withTitle("Test dataset") + .withAuthor("Walter White") + .withAuthor("Jesse Pinkman", author.getID().toString()) + .withEditor("Editor") + .withIssueDate("2021-04-30") + .withDescriptionAbstract("Product description") + .withLanguage("en_US") + .withType("http://purl.org/coar/resource_type/c_ddb1") + .withIsPartOf("Collection of Products") + .withDoiIdentifier("doi-id") + .withScopusIdentifier("scopus-id") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, product); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + assertThat(work.getJournalTitle(), notNullValue()); + assertThat(work.getJournalTitle().getContent(), is("Collection of Products")); + assertThat(work.getLanguageCode(), is("en")); + assertThat(work.getPublicationDate(), matches(date("2021", "04", "30"))); + assertThat(work.getShortDescription(), is("Product description")); + assertThat(work.getPutCode(), nullValue()); + // assertThat(work.getWorkCitation(), notNullValue()); + // assertThat(work.getWorkCitation().getCitation(), containsString("Test product")); + assertThat(work.getWorkType(), is(WorkType.DATA_SET)); + assertThat(work.getWorkTitle(), notNullValue()); + assertThat(work.getWorkTitle().getTitle(), notNullValue()); + assertThat(work.getWorkTitle().getTitle().getContent(), is("Test dataset")); + assertThat(work.getWorkContributors(), notNullValue()); + assertThat(work.getUrl(), matches(urlEndsWith(product.getHandle()))); + + List contributors = work.getWorkContributors().getContributor(); + assertThat(contributors, hasSize(2)); + assertThat(contributors, has(contributor("Walter White", AUTHOR, FIRST))); + // assertThat(contributors, has(contributor("Editor", EDITOR, FIRST))); + assertThat(contributors, has(contributor("Jesse Pinkman", AUTHOR, ADDITIONAL, + "0000-1111-2222-3333", "test@test.it"))); + + assertThat(work.getExternalIdentifiers(), notNullValue()); + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(3)); + assertThat(externalIds, has(selfExternalId("doi", "doi-id"))); + assertThat(externalIds, has(selfExternalId("eid", "scopus-id"))); + assertThat(externalIds, has(selfExternalId("handle", product.getHandle()))); + + } + + @Test + public void testPatentWorkCreation() { + + context.turnOffAuthorisationSystem(); + + Item author = ItemBuilder.createItem(context, persons) + .withTitle("Jesse Pinkman") + .withOrcidIdentifier("0000-1111-2222-3333") + .withPersonEmail("test@test.it") + .build(); + + Item patent = ItemBuilder.createItem(context, patents) + .withTitle("Test patent") + .withAuthor("Jesse Pinkman", author.getID().toString()) + .withIssueDate("2021-04-30") + .withDescriptionAbstract("Patent description") + .withPublisher("Patent registration office") + .withLanguage("en_US") + .withPatentNo("2021.01.0111") + .withType("http://purl.org/coar/resource_type/c_15cd") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, patent); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + assertThat(work.getJournalTitle(), notNullValue()); + assertThat(work.getJournalTitle().getContent(), is("Patent registration office")); + assertThat(work.getLanguageCode(), is("en")); + assertThat(work.getPublicationDate(), matches(date("2021", "04", "30"))); + assertThat(work.getShortDescription(), is("Patent description")); + assertThat(work.getPutCode(), nullValue()); + // assertThat(work.getWorkCitation(), notNullValue()); + // assertThat(work.getWorkCitation().getCitation(), containsString("Test patent")); + assertThat(work.getWorkType(), is(WorkType.PATENT)); + assertThat(work.getWorkTitle(), notNullValue()); + assertThat(work.getWorkTitle().getTitle(), notNullValue()); + assertThat(work.getWorkTitle().getTitle().getContent(), is("Test patent")); + assertThat(work.getWorkContributors(), notNullValue()); + assertThat(work.getUrl(), matches(urlEndsWith(patent.getHandle()))); + + List contributors = work.getWorkContributors().getContributor(); + assertThat(contributors, hasSize(1)); + assertThat(contributors, has(contributor("Jesse Pinkman", AUTHOR, FIRST, + "0000-1111-2222-3333", "test@test.it"))); + assertThat(work.getExternalIdentifiers(), notNullValue()); + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(2)); + assertThat(externalIds, has(selfExternalId("handle", patent.getHandle()))); + assertThat(externalIds, has(selfExternalId("pat", "2021.01.0111"))); + + } + + @Test + public void testWorkWithFundingCreation() { + context.turnOffAuthorisationSystem(); + + Item publication = ItemBuilder.createItem(context, publications) + .withTitle("Test publication") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("Controlled Vocabulary for Resource Type Genres::text::book") + .withRelationFunding("Test funding") + .withRelationGrantno("123456") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, publication); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(2)); + assertThat(externalIds, has(selfExternalId("handle", publication.getHandle()))); + assertThat(externalIds, has(fundedByExternalId("grant_number", "123456"))); + } + + @Test + public void testProductWorkWithFundingCreation() { + context.turnOffAuthorisationSystem(); + + Item product = ItemBuilder.createItem(context, products) + .withTitle("Test dataset") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("http://purl.org/coar/resource_type/H6QP-SC1X") + .withRelationFunding("Test funding") + .withRelationGrantno("123456") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, product); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(2)); + assertThat(externalIds, has(selfExternalId("handle", product.getHandle()))); + assertThat(externalIds, has(fundedByExternalId("grant_number", "123456"))); + } + + @Test + public void testPatentsWorkWithFundingCreation() { + context.turnOffAuthorisationSystem(); + + Item patent = ItemBuilder.createItem(context, patents) + .withTitle("Test patent") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("http://purl.org/coar/resource_type/c_15cd") + .withRelationFunding("Test funding") + .withRelationGrantno("123456") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, patent); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(2)); + assertThat(externalIds, has(selfExternalId("handle", patent.getHandle()))); + assertThat(externalIds, has(fundedByExternalId("grant_number", "123456"))); + } + + + @Test + public void testWorkWithFundingWithoutGrantNumberCreation() { + context.turnOffAuthorisationSystem(); + + Item publication = ItemBuilder.createItem(context, publications) + .withTitle("Test publication") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("Controlled Vocabulary for Resource Type Genres::text::book") + .withRelationFunding("Test funding") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, publication); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); assertThat(externalIds, hasSize(1)); assertThat(externalIds, has(selfExternalId("handle", publication.getHandle()))); } + @Test + public void testProductWorkWithFundingWithoutGrantNumberCreation() { + context.turnOffAuthorisationSystem(); + + Item product = ItemBuilder.createItem(context, products) + .withTitle("Test product") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("http://purl.org/coar/resource_type/c_e9a0") + .withRelationFunding("Test funding") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, product); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(1)); + assertThat(externalIds, has(selfExternalId("handle", product.getHandle()))); + } + + @Test + public void testPatentWorkWithFundingWithoutGrantNumberCreation() { + context.turnOffAuthorisationSystem(); + + Item patent = ItemBuilder.createItem(context, patents) + .withTitle("Test patent") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("http://purl.org/coar/resource_type/c_15cd") + .withRelationFunding("Test funding") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, patent); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(1)); + assertThat(externalIds, has(selfExternalId("handle", patent.getHandle()))); + } + + @Test + public void testWorkWithFundingWithGrantNumberPlaceholderCreation() { + context.turnOffAuthorisationSystem(); + + Item publication = ItemBuilder.createItem(context, publications) + .withTitle("Test publication") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("Controlled Vocabulary for Resource Type Genres::text::book") + .withRelationFunding("Test funding") + .withRelationGrantno(CrisConstants.PLACEHOLDER_PARENT_METADATA_VALUE) + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, publication); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(1)); + assertThat(externalIds, has(selfExternalId("handle", publication.getHandle()))); + } + + @Test + public void testProductWorkWithFundingWithGrantNumberPlaceholderCreation() { + context.turnOffAuthorisationSystem(); + + Item product = ItemBuilder.createItem(context, products) + .withTitle("Test dataset") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("http://purl.org/coar/resource_type/c_7ad9") + .withRelationFunding("Test funding") + .withRelationGrantno(CrisConstants.PLACEHOLDER_PARENT_METADATA_VALUE) + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, product); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(1)); + assertThat(externalIds, has(selfExternalId("handle", product.getHandle()))); + } + + @Test + public void testPatentWorkWithFundingWithGrantNumberPlaceholderCreation() { + context.turnOffAuthorisationSystem(); + + Item patent = ItemBuilder.createItem(context, patents) + .withTitle("Test patent") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("http://purl.org/coar/resource_type/c_15cd") + .withRelationFunding("Test funding") + .withRelationGrantno(CrisConstants.PLACEHOLDER_PARENT_METADATA_VALUE) + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, patent); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(1)); + assertThat(externalIds, has(selfExternalId("handle", patent.getHandle()))); + } + + + @Test + public void testWorkWithFundingEntityWithoutGrantNumberCreation() { + + context.turnOffAuthorisationSystem(); + + Item funding = ItemBuilder.createItem(context, fundings) + .withTitle("Test funding") + .build(); + + Item publication = ItemBuilder.createItem(context, publications) + .withTitle("Test publication") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("Controlled Vocabulary for Resource Type Genres::text::book") + .withRelationFunding("Test funding", funding.getID().toString()) + .withRelationGrantno("123456") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, publication); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(2)); + assertThat(externalIds, has(selfExternalId("handle", publication.getHandle()))); + assertThat(externalIds, has(fundedByExternalId("grant_number", "123456"))); + } + + @Test + public void testProductWorkWithFundingEntityWithoutGrantNumberCreation() { + + context.turnOffAuthorisationSystem(); + + Item funding = ItemBuilder.createItem(context, fundings) + .withTitle("Test funding") + .build(); + + Item product = ItemBuilder.createItem(context, products) + .withTitle("Test product") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("http://purl.org/coar/resource_type/c_7ad9") + .withRelationFunding("Test funding", funding.getID().toString()) + .withRelationGrantno("123456") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, product); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(2)); + assertThat(externalIds, has(selfExternalId("handle", product.getHandle()))); + assertThat(externalIds, has(fundedByExternalId("grant_number", "123456"))); + } + + @Test + public void testPatentWorkWithFundingEntityWithoutGrantNumberCreation() { + + context.turnOffAuthorisationSystem(); + + Item funding = ItemBuilder.createItem(context, fundings) + .withTitle("Test funding") + .build(); + + Item patent = ItemBuilder.createItem(context, patents) + .withTitle("Test patent") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("http://purl.org/coar/resource_type/c_15cd") + .withRelationFunding("Test funding", funding.getID().toString()) + .withRelationGrantno("123456") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, patent); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(2)); + assertThat(externalIds, has(selfExternalId("handle", patent.getHandle()))); + assertThat(externalIds, has(fundedByExternalId("grant_number", "123456"))); + } + + @Test + public void testWorkWithFundingEntityWithGrantNumberCreation() { + + context.turnOffAuthorisationSystem(); + + Item funding = ItemBuilder.createItem(context, fundings) + .withHandle("123456789/0001") + .withTitle("Test funding") + .withFundingIdentifier("987654") + .build(); + + Item publication = ItemBuilder.createItem(context, publications) + .withTitle("Test publication") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("Controlled Vocabulary for Resource Type Genres::text::book") + .withRelationFunding("Test funding", funding.getID().toString()) + .withRelationGrantno("123456") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, publication); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(2)); + assertThat(externalIds, has(selfExternalId("handle", publication.getHandle()))); + assertThat(externalIds, has(fundedByExternalId("grant_number", "987654", + "http://localhost:4000/handle/123456789/0001"))); + } + + @Test + public void testProductWorkWithFundingEntityWithGrantNumberCreation() { + + context.turnOffAuthorisationSystem(); + + Item funding = ItemBuilder.createItem(context, fundings) + .withHandle("123456789/0001") + .withTitle("Test funding") + .withFundingIdentifier("987654") + .build(); + + Item product = ItemBuilder.createItem(context, products) + .withTitle("Test product") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("http://purl.org/coar/resource_type/c_18cc") + .withRelationFunding("Test funding", funding.getID().toString()) + .withRelationGrantno("123456") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, product); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(2)); + assertThat(externalIds, has(selfExternalId("handle", product.getHandle()))); + assertThat(externalIds, has(fundedByExternalId("grant_number", "987654", + "http://localhost:4000/handle/123456789/0001"))); + } + + @Test + public void testPatentWorkWithFundingEntityWithGrantNumberCreation() { + + context.turnOffAuthorisationSystem(); + + Item funding = ItemBuilder.createItem(context, fundings) + .withHandle("123456789/0001") + .withTitle("Test funding") + .withFundingIdentifier("987654") + .build(); + + Item patent = ItemBuilder.createItem(context, patents) + .withTitle("Test patent") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("http://purl.org/coar/resource_type/c_15cd") + .withRelationFunding("Test funding", funding.getID().toString()) + .withRelationGrantno("123456") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, patent); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(2)); + assertThat(externalIds, has(selfExternalId("handle", patent.getHandle()))); + assertThat(externalIds, has(fundedByExternalId("grant_number", "987654", + "http://localhost:4000/handle/123456789/0001"))); + } + + + @Test + public void testWorkWithFundingEntityWithGrantNumberAndUrlCreation() { + + context.turnOffAuthorisationSystem(); + + Item funding = ItemBuilder.createItem(context, fundings) + .withHandle("123456789/0001") + .withTitle("Test funding") + .withFundingIdentifier("987654") + .withFundingAwardUrl("http://test-funding") + .build(); + + Item publication = ItemBuilder.createItem(context, publications) + .withTitle("Test publication") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("Controlled Vocabulary for Resource Type Genres::text::book") + .withRelationFunding("Test funding", funding.getID().toString()) + .withRelationGrantno("123456") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, publication); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(2)); + assertThat(externalIds, has(selfExternalId("handle", publication.getHandle()))); + assertThat(externalIds, has(fundedByExternalId("grant_number", "987654", "http://test-funding"))); + } + + @Test + public void testProductWorkWithFundingEntityWithGrantNumberAndUrlCreation() { + + context.turnOffAuthorisationSystem(); + + Item funding = ItemBuilder.createItem(context, fundings) + .withHandle("123456789/0001") + .withTitle("Test funding") + .withFundingIdentifier("987654") + .withFundingAwardUrl("http://test-funding") + .build(); + + Item product = ItemBuilder.createItem(context, products) + .withTitle("Test product") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("http://purl.org/coar/resource_type/c_18cc") + .withRelationFunding("Test funding", funding.getID().toString()) + .withRelationGrantno("123456") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, product); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(2)); + assertThat(externalIds, has(selfExternalId("handle", product.getHandle()))); + assertThat(externalIds, has(fundedByExternalId("grant_number", "987654", "http://test-funding"))); + } + + @Test + public void testPatentWorkWithFundingEntityWithGrantNumberAndUrlCreation() { + + context.turnOffAuthorisationSystem(); + + Item funding = ItemBuilder.createItem(context, fundings) + .withHandle("123456789/0001") + .withTitle("Test funding") + .withFundingIdentifier("987654") + .withFundingAwardUrl("http://test-funding") + .build(); + + Item patent = ItemBuilder.createItem(context, patents) + .withTitle("Test patent") + .withAuthor("Walter White") + .withIssueDate("2021-04-30") + .withType("http://purl.org/coar/resource_type/c_15cd") + .withRelationFunding("Test funding", funding.getID().toString()) + .withRelationGrantno("123456") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, patent); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(2)); + assertThat(externalIds, has(selfExternalId("handle", patent.getHandle()))); + assertThat(externalIds, has(fundedByExternalId("grant_number", "987654", "http://test-funding"))); + } + + @Test + public void testEmptyWorkWithUnknownTypeCreation() { + + context.turnOffAuthorisationSystem(); + + Item publication = ItemBuilder.createItem(context, publications) + .withType("TYPE") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, publication); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + assertThat(work.getJournalTitle(), nullValue()); + assertThat(work.getLanguageCode(), nullValue()); + assertThat(work.getPublicationDate(), nullValue()); + assertThat(work.getShortDescription(), nullValue()); + assertThat(work.getPutCode(), nullValue()); + assertThat(work.getWorkType(), is(WorkType.OTHER)); + assertThat(work.getWorkTitle(), nullValue()); + assertThat(work.getWorkContributors(), notNullValue()); + assertThat(work.getWorkContributors().getContributor(), empty()); + assertThat(work.getExternalIdentifiers(), notNullValue()); + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(1)); + assertThat(externalIds, has(selfExternalId("handle", publication.getHandle()))); + } + + @Test + public void testEmptyProductWorkWithUnknownTypeCreation() { + + context.turnOffAuthorisationSystem(); + + Item product = ItemBuilder.createItem(context, products) + .withType("http://purl.org/coar/resource_type/") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, product); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + assertThat(work.getJournalTitle(), nullValue()); + assertThat(work.getLanguageCode(), nullValue()); + assertThat(work.getPublicationDate(), nullValue()); + assertThat(work.getShortDescription(), nullValue()); + assertThat(work.getPutCode(), nullValue()); + // assertThat(work.getWorkCitation(), notNullValue()); + assertThat(work.getWorkType(), is(WorkType.DATA_SET)); + assertThat(work.getWorkTitle(), nullValue()); + assertThat(work.getWorkContributors(), notNullValue()); + assertThat(work.getWorkContributors().getContributor(), empty()); + assertThat(work.getExternalIdentifiers(), notNullValue()); + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(1)); + assertThat(externalIds, has(selfExternalId("handle", product.getHandle()))); + } + + @Test + public void testEmptyPatentWorkWithUnknownTypeCreation() { + + context.turnOffAuthorisationSystem(); + + Item patent = ItemBuilder.createItem(context, patents) + .withType("http://purl.org/coar/resource_type/") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, patent); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + assertThat(work.getJournalTitle(), nullValue()); + assertThat(work.getLanguageCode(), nullValue()); + assertThat(work.getPublicationDate(), nullValue()); + assertThat(work.getShortDescription(), nullValue()); + assertThat(work.getPutCode(), nullValue()); + // assertThat(work.getWorkCitation(), notNullValue()); + assertThat(work.getWorkType(), is(WorkType.PATENT)); + assertThat(work.getWorkTitle(), nullValue()); + assertThat(work.getWorkContributors(), notNullValue()); + assertThat(work.getWorkContributors().getContributor(), empty()); + assertThat(work.getExternalIdentifiers(), notNullValue()); + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(1)); + assertThat(externalIds, has(selfExternalId("handle", patent.getHandle()))); + } + + @Test public void testFundingCreation() { context.turnOffAuthorisationSystem(); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ResearcherProfileConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ResearcherProfileConverter.java index 54dffa57881e..841a3b8eebd7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ResearcherProfileConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ResearcherProfileConverter.java @@ -8,6 +8,8 @@ package org.dspace.app.rest.converter; import static org.dspace.orcid.model.OrcidEntityType.FUNDING; +import static org.dspace.orcid.model.OrcidEntityType.PATENT; +import static org.dspace.orcid.model.OrcidEntityType.PRODUCT; import static org.dspace.orcid.model.OrcidEntityType.PUBLICATION; import java.util.List; @@ -60,6 +62,8 @@ public ResearcherProfileRest convert(ResearcherProfile profile, Projection proje orcidSynchronization.setProfilePreferences(getProfilePreferences(item)); orcidSynchronization.setFundingsPreference(getFundingsPreference(item)); orcidSynchronization.setPublicationsPreference(getPublicationsPreference(item)); + orcidSynchronization.setProductsPreference(getProductsPreference(item)); + orcidSynchronization.setPatentsPreference(getPatentsPreference(item)); researcherProfileRest.setOrcidSynchronization(orcidSynchronization); } @@ -72,6 +76,19 @@ private String getPublicationsPreference(Item item) { .orElse(OrcidEntitySyncPreference.DISABLED.name()); } + private String getProductsPreference(Item item) { + return orcidSynchronizationService.getEntityPreference(item, PRODUCT) + .map(OrcidEntitySyncPreference::name) + .orElse(OrcidEntitySyncPreference.DISABLED.name()); + } + + private String getPatentsPreference(Item item) { + return orcidSynchronizationService.getEntityPreference(item, PATENT) + .map(OrcidEntitySyncPreference::name) + .orElse(OrcidEntitySyncPreference.DISABLED.name()); + } + + private String getFundingsPreference(Item item) { return orcidSynchronizationService.getEntityPreference(item, FUNDING) .map(OrcidEntitySyncPreference::name) @@ -95,4 +112,4 @@ public Class getModelClass() { return ResearcherProfile.class; } -} \ No newline at end of file +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResearcherProfileRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResearcherProfileRest.java index 4224cfeeb924..6133ae36e266 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResearcherProfileRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResearcherProfileRest.java @@ -91,8 +91,12 @@ public static class OrcidSynchronizationRest { private String publicationsPreference; + private String productsPreference; + private String fundingsPreference; + private String patentsPreference; + private List profilePreferences; public String getMode() { @@ -119,6 +123,22 @@ public void setPublicationsPreference(String publicationsPreference) { this.publicationsPreference = publicationsPreference; } + public String getProductsPreference() { + return productsPreference; + } + + public void setProductsPreference(String productsPreference) { + this.productsPreference = productsPreference; + } + + public String getPatentsPreference() { + return patentsPreference; + } + + public void setPatentsPreference(String patentsPreference) { + this.patentsPreference = patentsPreference; + } + public String getFundingsPreference() { return fundingsPreference; } @@ -129,4 +149,4 @@ public void setFundingsPreference(String fundingsPreference) { } -} \ No newline at end of file +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/ResearcherProfileReplaceOrcidSyncPreferencesOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/ResearcherProfileReplaceOrcidSyncPreferencesOperation.java index 5084931382a5..e6e79684e84f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/ResearcherProfileReplaceOrcidSyncPreferencesOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/ResearcherProfileReplaceOrcidSyncPreferencesOperation.java @@ -8,6 +8,8 @@ package org.dspace.app.rest.repository.patch.operation; import static org.dspace.orcid.model.OrcidEntityType.FUNDING; +import static org.dspace.orcid.model.OrcidEntityType.PATENT; +import static org.dspace.orcid.model.OrcidEntityType.PRODUCT; import static org.dspace.orcid.model.OrcidEntityType.PUBLICATION; import java.sql.SQLException; @@ -54,6 +56,10 @@ public class ResearcherProfileReplaceOrcidSyncPreferencesOperation extends Patch private static final String PUBLICATIONS_PREFERENCES = "/publications"; + private static final String PRODUCTS_PREFERENCES = "/products"; + + private static final String PATENTS_PREFERENCES = "/patents"; + private static final String FUNDINGS_PREFERENCES = "/fundings"; private static final String PROFILE_PREFERENCES = "/profile"; @@ -121,6 +127,12 @@ private boolean updatePreferences(Context context, String path, String value, It case PUBLICATIONS_PREFERENCES: OrcidEntitySyncPreference preference = parsePreference(value); return synchronizationService.setEntityPreference(context, profileItem, PUBLICATION, preference); + case PRODUCTS_PREFERENCES: + OrcidEntitySyncPreference productPreference = parsePreference(value); + return synchronizationService.setEntityPreference(context, profileItem, PRODUCT, productPreference); + case PATENTS_PREFERENCES: + OrcidEntitySyncPreference patentsPreference = parsePreference(value); + return synchronizationService.setEntityPreference(context, profileItem, PATENT, patentsPreference); case FUNDINGS_PREFERENCES: OrcidEntitySyncPreference fundingPreference = parsePreference(value); return synchronizationService.setEntityPreference(context, profileItem, FUNDING, fundingPreference); @@ -137,9 +149,19 @@ private boolean updatePreferences(Context context, String path, String value, It private void reloadOrcidQueue(Context context, String path, String value, Item profileItem) throws SQLException, AuthorizeException { - if (path.equals(PUBLICATIONS_PREFERENCES) || path.equals(FUNDINGS_PREFERENCES)) { + if (path.equals(PUBLICATIONS_PREFERENCES) || path.equals(FUNDINGS_PREFERENCES) + || path.equals(PRODUCTS_PREFERENCES) || path.equals(PATENTS_PREFERENCES)) { OrcidEntitySyncPreference preference = parsePreference(value); - OrcidEntityType entityType = path.equals(PUBLICATIONS_PREFERENCES) ? PUBLICATION : FUNDING; + OrcidEntityType entityType = FUNDING; + if (path.equals(PUBLICATIONS_PREFERENCES)) { + entityType = PUBLICATION; + } + if (path.equals(PRODUCTS_PREFERENCES)) { + entityType = PRODUCT; + } + if (path.equals(PATENTS_PREFERENCES)) { + entityType = PATENT; + } orcidQueueService.recalculateOrcidQueue(context, profileItem, entityType, preference); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java index 714ad0b419a1..7164f2166da0 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java @@ -4040,6 +4040,269 @@ public void testDeletionOfFundingToBeSynchronizedWithOrcid() throws Exception { } + @Test + public void testDeletionOfProductToBeSynchronizedWithOrcid() throws Exception { + + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Collection profileCollection = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Profiles") + .withEntityType("Person") + .build(); + + Collection productCollection = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Products") + .withEntityType("Product") + .build(); + + EPerson firstOwner = EPersonBuilder.createEPerson(context) + .withEmail("owner2@test.com") + .build(); + + EPerson secondOwner = EPersonBuilder.createEPerson(context) + .withEmail("owner3@test.com") + .build(); + + EPerson thirdOwner = EPersonBuilder.createEPerson(context) + .withEmail("owner1@test.com") + .build(); + + Item firstProfile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withDspaceObjectOwner(firstOwner.getFullName(), firstOwner.getID().toString()) + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", firstOwner) + .withOrcidSynchronizationProductsPreference(ALL) + .build(); + + Item secondProfile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withDspaceObjectOwner(secondOwner.getFullName(), secondOwner.getID().toString()) + .withOrcidIdentifier("4444-1111-2222-3333") + .withOrcidAccessToken("bb4d18a0-8d9a-40f1-b601-a417255c8d20", secondOwner) + .build(); + + Item thirdProfile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withDspaceObjectOwner(thirdOwner.getFullName(), thirdOwner.getID().toString()) + .withOrcidIdentifier("5555-1111-2222-3333") + .withOrcidAccessToken("cb4d18a0-8d9a-40f1-b601-a417255c8d20", thirdOwner) + .withOrcidSynchronizationProductsPreference(ALL) + .build(); + + Item product = ItemBuilder.createItem(context, productCollection) + .withTitle("Test product") + + .build(); + + createOrcidQueue(context, firstProfile, product).build(); + createOrcidQueue(context, secondProfile, product).build(); + + List historyRecords = new ArrayList<>(); + historyRecords.add(createOrcidHistory(context, firstProfile, product).build()); + historyRecords.add(createOrcidHistory(context, firstProfile, product).withPutCode("12345").build()); + historyRecords.add(createOrcidHistory(context, secondProfile, product).build()); + historyRecords.add(createOrcidHistory(context, secondProfile, product).withPutCode("67891").build()); + historyRecords.add(createOrcidHistory(context, thirdProfile, product).build()); + + context.restoreAuthSystemState(); + + String token = getAuthToken(admin.getEmail(), password); + + getClient(token).perform(delete("/api/core/items/" + product.getID())) + .andExpect(status().is(204)); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords, hasItem(matches(firstProfile, null, "Product", "12345", DELETE))); + + for (OrcidHistory historyRecord : historyRecords) { + historyRecord = context.reloadEntity(historyRecord); + assertThat(historyRecord, notNullValue()); + assertThat(historyRecord.getEntity(), nullValue()); + } + + } + + @Test + public void testDeletionOfProductToBeSynchronizedWithOrcid() throws Exception { + + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Collection profileCollection = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Profiles") + .withEntityType("Person") + .build(); + + Collection productCollection = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Products") + .withEntityType("Product") + .build(); + + EPerson firstOwner = EPersonBuilder.createEPerson(context) + .withEmail("owner2@test.com") + .build(); + + EPerson secondOwner = EPersonBuilder.createEPerson(context) + .withEmail("owner3@test.com") + .build(); + + EPerson thirdOwner = EPersonBuilder.createEPerson(context) + .withEmail("owner1@test.com") + .build(); + + Item firstProfile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withDspaceObjectOwner(firstOwner.getFullName(), firstOwner.getID().toString()) + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", firstOwner) + .withOrcidSynchronizationProductsPreference(ALL) + .build(); + + Item secondProfile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withDspaceObjectOwner(secondOwner.getFullName(), secondOwner.getID().toString()) + .withOrcidIdentifier("4444-1111-2222-3333") + .withOrcidAccessToken("bb4d18a0-8d9a-40f1-b601-a417255c8d20", secondOwner) + .build(); + + Item thirdProfile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withDspaceObjectOwner(thirdOwner.getFullName(), thirdOwner.getID().toString()) + .withOrcidIdentifier("5555-1111-2222-3333") + .withOrcidAccessToken("cb4d18a0-8d9a-40f1-b601-a417255c8d20", thirdOwner) + .withOrcidSynchronizationProductsPreference(ALL) + .build(); + + Item product = ItemBuilder.createItem(context, productCollection) + .withTitle("Test product") + + .build(); + + createOrcidQueue(context, firstProfile, product).build(); + createOrcidQueue(context, secondProfile, product).build(); + + List historyRecords = new ArrayList<>(); + historyRecords.add(createOrcidHistory(context, firstProfile, product).build()); + historyRecords.add(createOrcidHistory(context, firstProfile, product).withPutCode("12345").build()); + historyRecords.add(createOrcidHistory(context, secondProfile, product).build()); + historyRecords.add(createOrcidHistory(context, secondProfile, product).withPutCode("67891").build()); + historyRecords.add(createOrcidHistory(context, thirdProfile, product).build()); + + context.restoreAuthSystemState(); + + String token = getAuthToken(admin.getEmail(), password); + + getClient(token).perform(delete("/api/core/items/" + product.getID())) + .andExpect(status().is(204)); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords, hasItem(matches(firstProfile, null, "Product", "12345", DELETE))); + + for (OrcidHistory historyRecord : historyRecords) { + historyRecord = context.reloadEntity(historyRecord); + assertThat(historyRecord, notNullValue()); + assertThat(historyRecord.getEntity(), nullValue()); + } + + } + + @Test + public void testDeletionOfPatentToBeSynchronizedWithOrcid() throws Exception { + + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Collection profileCollection = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Profiles") + .withEntityType("Person") + .build(); + + Collection patentCollection = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Patents") + .withEntityType("Patent") + .build(); + + EPerson firstOwner = EPersonBuilder.createEPerson(context) + .withEmail("owner2@test.com") + .build(); + + EPerson secondOwner = EPersonBuilder.createEPerson(context) + .withEmail("owner3@test.com") + .build(); + + EPerson thirdOwner = EPersonBuilder.createEPerson(context) + .withEmail("owner1@test.com") + .build(); + + Item firstProfile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withDspaceObjectOwner(firstOwner.getFullName(), firstOwner.getID().toString()) + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", firstOwner) + .withOrcidSynchronizationPatentsPreference(ALL) + .build(); + + Item secondProfile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withDspaceObjectOwner(secondOwner.getFullName(), secondOwner.getID().toString()) + .withOrcidIdentifier("4444-1111-2222-3333") + .withOrcidAccessToken("bb4d18a0-8d9a-40f1-b601-a417255c8d20", secondOwner) + .build(); + + Item thirdProfile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withDspaceObjectOwner(thirdOwner.getFullName(), thirdOwner.getID().toString()) + .withOrcidIdentifier("5555-1111-2222-3333") + .withOrcidAccessToken("cb4d18a0-8d9a-40f1-b601-a417255c8d20", thirdOwner) + .withOrcidSynchronizationPatentsPreference(ALL) + .build(); + + Item patent = ItemBuilder.createItem(context, patentCollection) + .withTitle("Test patent") + .build(); + + createOrcidQueue(context, firstProfile, patent).build(); + createOrcidQueue(context, secondProfile, patent).build(); + + List historyRecords = new ArrayList<>(); + historyRecords.add(createOrcidHistory(context, firstProfile, patent).build()); + historyRecords.add(createOrcidHistory(context, firstProfile, patent).withPutCode("12345").build()); + historyRecords.add(createOrcidHistory(context, secondProfile, patent).build()); + historyRecords.add(createOrcidHistory(context, secondProfile, patent).withPutCode("67891").build()); + historyRecords.add(createOrcidHistory(context, thirdProfile, patent).build()); + + context.restoreAuthSystemState(); + + String token = getAuthToken(admin.getEmail(), password); + + getClient(token).perform(delete("/api/core/items/" + patent.getID())) + .andExpect(status().is(204)); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords, hasItem(matches(firstProfile, null, "Patent", "12345", DELETE))); + + for (OrcidHistory historyRecord : historyRecords) { + historyRecord = context.reloadEntity(historyRecord); + assertThat(historyRecord, notNullValue()); + assertThat(historyRecord.getEntity(), nullValue()); + } + + } + private void initPublicationAuthorsRelationships() throws SQLException { context.turnOffAuthorisationSystem(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java index 70009d049fc5..a6e7f7965063 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java @@ -1346,7 +1346,9 @@ public void testOrcidMetadataOfEpersonAreCopiedOnProfile() throws Exception { .andExpect(jsonPath("$.orcidSynchronization.mode", is("MANUAL"))) .andExpect(jsonPath("$.orcidSynchronization.publicationsPreference", is("DISABLED"))) .andExpect(jsonPath("$.orcidSynchronization.fundingsPreference", is("DISABLED"))) - .andExpect(jsonPath("$.orcidSynchronization.profilePreferences", empty())); + .andExpect(jsonPath("$.orcidSynchronization.productsPreference", is("DISABLED"))) + .andExpect(jsonPath("$.orcidSynchronization.patentsPreference", is("DISABLED"))) + .andExpect(jsonPath("$.orcidSynchronization.profilePreferences", empty())); String itemId = getItemIdByProfileId(authToken, ePersonId); @@ -1450,9 +1452,127 @@ public void testPatchToSetOrcidSynchronizationPreferenceForFundings() throws Exc 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()); + .content(getPatchContent(operations)) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isUnprocessableEntity()); + + } + + @Test + public void testPatchToSetOrcidSynchronizationPreferenceForProduct() 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") + .withOrcidScope("/first-scope") + .withOrcidScope("/second-scope") + .build(); + + OrcidTokenBuilder.create(context, ePerson, "af097328-ac1c-4a3e-9eb4-069897874910").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 operations = asList(new ReplaceOperation("/orcid/products", 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.productsPreference", is(ALL.name()))); + + getClient(authToken).perform(get("/api/eperson/profiles/{id}", ePersonId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.orcidSynchronization.productsPreference", is(ALL.name()))); + + operations = asList(new ReplaceOperation("/orcid/products", MINE.name())); + + getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId) + .content(getPatchContent(operations)) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.orcidSynchronization.productsPreference", is(MINE.name()))); + + getClient(authToken).perform(get("/api/eperson/profiles/{id}", ePersonId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.orcidSynchronization.productsPreference", is(MINE.name()))); + + operations = asList(new ReplaceOperation("/orcid/products", "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 testPatchToSetOrcidSynchronizationPreferenceForPatent() 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") + .withOrcidScope("/first-scope") + .withOrcidScope("/second-scope") + .build(); + + OrcidTokenBuilder.create(context, ePerson, "af097328-ac1c-4a3e-9eb4-069897874910").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 operations = asList(new ReplaceOperation("/orcid/patents", 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.patentsPreference", is(ALL.name()))); + + getClient(authToken).perform(get("/api/eperson/profiles/{id}", ePersonId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.orcidSynchronization.patentsPreference", is(ALL.name()))); + + operations = asList(new ReplaceOperation("/orcid/patents", MINE.name())); + + getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId) + .content(getPatchContent(operations)) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.orcidSynchronization.patentsPreference", is(MINE.name()))); + + getClient(authToken).perform(get("/api/eperson/profiles/{id}", ePersonId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.orcidSynchronization.patentsPreference", is(MINE.name()))); + + operations = asList(new ReplaceOperation("/orcid/patents", "INVALID_VALUE")); + + getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId) + .content(getPatchContent(operations)) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isUnprocessableEntity()); } @@ -2267,14 +2387,14 @@ public void testOrcidSynchronizationPreferenceUpdateForceOrcidQueueRecalculation "isProjectOfPerson", "isPersonOfProject", 0, null, 0, null).build(); EPerson ePerson = EPersonBuilder.createEPerson(context) - .withCanLogin(true) - .withOrcid("0000-1111-2222-3333") - .withOrcidScope("/read") - .withOrcidScope("/write") - .withEmail("test@email.it") - .withPassword(password) - .withNameInMetadata("Test", "User") - .build(); + .withCanLogin(true) + .withOrcid("0000-1111-2222-3333") + .withOrcidScope("/read") + .withOrcidScope("/write") + .withEmail("test@email.it") + .withPassword(password) + .withNameInMetadata("Test", "User") + .build(); OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); @@ -2286,10 +2406,14 @@ public void testOrcidSynchronizationPreferenceUpdateForceOrcidQueueRecalculation Collection publications = createCollection("Publications", "Publication"); + Collection products = createCollection("Products", "Product"); + Collection orgUnits = createCollection("OrgUnits", "OrgUnit"); Item publication = createPublication(publications, "Test publication", profile, isAuthorOfPublication); + Item product = createProduct(products, "Test product", profile); + Collection projects = createCollection("Projects", "Project"); Item firstProject = createProject(projects, "First project", profile, isProjectOfPerson); @@ -2297,6 +2421,11 @@ public void testOrcidSynchronizationPreferenceUpdateForceOrcidQueueRecalculation createOrgUnit(orgUnits, "OrgUnit", profile, isOrgUnitOfPerson); + Collection patents = createCollection("Patents", "Patent"); + + Item firstPatent = createPatent(patents, "First patent", profile); + Item secondPatent = createPatent(patents, "Second patent", profile); + context.restoreAuthSystemState(); // no preferences configured, so no orcid queue records created @@ -2305,18 +2434,20 @@ public void testOrcidSynchronizationPreferenceUpdateForceOrcidQueueRecalculation String authToken = getAuthToken(ePerson.getEmail(), password); getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId.toString()) - .content(getPatchContent(asList(new ReplaceOperation("/orcid/publications", "ALL")))) - .contentType(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(status().isOk()); + .content(getPatchContent( + asList(new ReplaceOperation("/orcid/publications", "ALL")))) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()); List queueRecords = orcidQueueService.findByProfileItemId(context, profileItemId); assertThat(queueRecords, hasSize(1)); assertThat(queueRecords, has(orcidQueueRecordWithEntity(publication))); getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId.toString()) - .content(getPatchContent(asList(new ReplaceOperation("/orcid/fundings", "ALL")))) - .contentType(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(status().isOk()); + .content( + getPatchContent(asList(new ReplaceOperation("/orcid/fundings", "ALL")))) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()); queueRecords = orcidQueueService.findByProfileItemId(context, profileItemId); assertThat(queueRecords, hasSize(3)); @@ -2325,18 +2456,81 @@ public void testOrcidSynchronizationPreferenceUpdateForceOrcidQueueRecalculation assertThat(queueRecords, has(orcidQueueRecordWithEntity(secondProject))); getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId.toString()) - .content(getPatchContent(asList(new ReplaceOperation("/orcid/publications", "DISABLED")))) - .contentType(MediaType.APPLICATION_JSON_VALUE)) + .content( + getPatchContent(asList(new ReplaceOperation("/orcid/patents", "ALL")))) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()); + + queueRecords = orcidQueueService.findByProfileItemId(context, profileItemId); + assertThat(queueRecords, hasSize(6)); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(publication))); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(firstFunding))); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(secondFunding))); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(product))); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(firstPatent))); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(secondPatent))); + + getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId.toString()) + .content(getPatchContent( + asList(new ReplaceOperation("/orcid/products", "ALL")))) + .contentType(MediaType.APPLICATION_JSON_VALUE)) .andExpect(status().isOk()); + queueRecords = orcidQueueService.findByProfileItemId(context, profileItemId); + assertThat(queueRecords, hasSize(5)); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(firstFunding))); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(secondFunding))); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(product))); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(firstPatent))); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(secondPatent))); + + getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId.toString()) + .content(getPatchContent( + asList(new ReplaceOperation("/orcid/fundings", "DISABLED")))) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()); + + queueRecords = orcidQueueService.findByProfileItemId(context, profileItemId); + assertThat(queueRecords, hasSize(3)); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(product))); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(firstPatent))); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(secondPatent))); + + getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId.toString()) + .content(getPatchContent( + asList(new ReplaceOperation("/orcid/products", "DISABLED")))) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()); + queueRecords = orcidQueueService.findByProfileItemId(context, profileItemId); assertThat(queueRecords, hasSize(2)); - assertThat(queueRecords, has(orcidQueueRecordWithEntity(firstProject))); - assertThat(queueRecords, has(orcidQueueRecordWithEntity(secondProject))); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(firstPatent))); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(secondPatent))); getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId.toString()) - .content(getPatchContent(asList(new ReplaceOperation("/orcid/fundings", "DISABLED")))) - .contentType(MediaType.APPLICATION_JSON_VALUE)) + .content(getPatchContent( + asList(new ReplaceOperation("/orcid/patents", "DISABLED")))) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()); + + assertThat(orcidQueueService.findByProfileItemId(context, profileItemId), empty()); + + configurationService.setProperty("orcid.linkable-metadata-fields.ignore", "crisfund.coinvestigators"); + + getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId.toString()) + .content( + getPatchContent(asList(new ReplaceOperation("/orcid/fundings", "DISABLED")))) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()); + + queueRecords = orcidQueueService.findByProfileItemId(context, profileItemId); + assertThat(queueRecords, hasSize(1)); + assertThat(queueRecords, has(orcidQueueRecordWithEntity(product))); + + getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId.toString()) + .content( + getPatchContent(asList(new ReplaceOperation("/orcid/products", "DISABLED")))) + .contentType(MediaType.APPLICATION_JSON_VALUE)) .andExpect(status().isOk()); assertThat(orcidQueueService.findByProfileItemId(context, profileItemId), empty()); @@ -2583,6 +2777,27 @@ private Item createPublication(Collection collection, String title, Item author, } + private Item createProduct(Collection collection, String title, Item author) { + return ItemBuilder.createItem(context, collection) + .withTitle(title) + .withAuthor(author.getName(), author.getID().toString()) + .build(); + } + + private Item createProduct(Collection collection, String title, Item author) { + return ItemBuilder.createItem(context, collection) + .withTitle(title) + .withAuthor(author.getName(), author.getID().toString()) + .build(); + } + + private Item createPatent(Collection collection, String title, Item author) { + return ItemBuilder.createItem(context, collection) + .withTitle(title) + .withAuthor(author.getName(), author.getID().toString()) + .build(); + } + private Item createOrgUnit(Collection collection, String title, Item person, RelationshipType isOrgUnitOfPerson) { diff --git a/dspace/config/crosswalks/orcid/mapConverter-dspace-to-orcid-patent-type.properties b/dspace/config/crosswalks/orcid/mapConverter-dspace-to-orcid-patent-type.properties new file mode 100644 index 000000000000..50aa130ea351 --- /dev/null +++ b/dspace/config/crosswalks/orcid/mapConverter-dspace-to-orcid-patent-type.properties @@ -0,0 +1,3 @@ +# Mapping between DSpace common patent's types and the type supported by ORCID +http\://purl.org/coar/resource_type/c_15cd = patent +http\://purl.org/coar/resource_type/H6QP-SC1X = trademark diff --git a/dspace/config/crosswalks/orcid/mapConverter-dspace-to-orcid-product-type.properties b/dspace/config/crosswalks/orcid/mapConverter-dspace-to-orcid-product-type.properties new file mode 100644 index 000000000000..9757efd2afb3 --- /dev/null +++ b/dspace/config/crosswalks/orcid/mapConverter-dspace-to-orcid-product-type.properties @@ -0,0 +1,23 @@ +# Mapping between DSpace common product's types and the type supported by ORCID +http\://purl.org/coar/resource_type/c_ddb1 = data-set +http\://purl.org/coar/resource_type/NHD0-W6SY = data-set +http\://purl.org/coar/resource_type/W2XT-7017 = data-set +http\://purl.org/coar/resource_type/CQMR-7K63 = data-set +http\://purl.org/coar/resource_type/FF4C-28RK = data-set +http\://purl.org/coar/resource_type/DD58-GFSX = data-set +http\://purl.org/coar/resource_type/H41Y-FW7B = data-set +http\://purl.org/coar/resource_type/2H0M-X761 = data-set +http\://purl.org/coar/resource_type/A8F1-NPV9 = data-set +http\://purl.org/coar/resource_type/AM6W-6QAW = data-set +http\://purl.org/coar/resource_type/FXF3-D3G7 = data-set +http\://purl.org/coar/resource_type/c_cb28 = data-set +http\://purl.org/coar/resource_type/ACF7-8YT9 = data-set +http\://purl.org/coar/resource_type/c_5ce6 = software +http\://purl.org/coar/resource_type/c_18cc = lecture-speech +http\://purl.org/coar/resource_type/c_18cd = lecture-speech +http\://purl.org/coar/resource_type/c_7ad9 = website +http\://purl.org/coar/resource_type/c_e9a0 = online-resource +http\://purl.org/coar/resource_type/H6QP-SC1X = trademark + + + diff --git a/dspace/config/modules/orcid.cfg b/dspace/config/modules/orcid.cfg index cde819677447..13eb85f3c582 100644 --- a/dspace/config/modules/orcid.cfg +++ b/dspace/config/modules/orcid.cfg @@ -64,9 +64,42 @@ orcid.mapping.work.external-ids = $simple-handle::handle orcid.mapping.work.external-ids = dc.identifier.isi::wosuid orcid.mapping.work.external-ids = dc.identifier.issn::issn +orcid.mapping.work.funding = dc.relation.funding +orcid.mapping.work.funding.external-id.type = grant_number +orcid.mapping.work.funding.external-id.value = dc.relation.grantno +orcid.mapping.work.funding.external-id.entity-value = oairecerif.funding.identifier +orcid.mapping.work.funding.url = crisfund.award.url + +### Work (Product) mapping ### +# where is differs from publication mapping above +# see orcid-services.xml for properties being changed here! +# https://info.orcid.org/documentation/integration-and-api-faq/#easy-faq-2682 +# https://info.orcid.org/faq/what-contributor-information-should-i-include-when-adding-works-or-funding-items/ + +# aligned to default submittion form for "product" +orcid.mapping.work.product.contributors = dc.contributor.author::author + +# Additional Mapping to CRediT roles https://credit.niso.org/ possible +# The roles are not part of the current used orcid model and thus it is not possible to configure the NISO-roles + +orcid.mapping.work.product.type.converter = mapConverterDSpaceToOrcidProductType + +### Work (Patent) mapping ### +# where is differs from publication mapping above +# see orcid-services.xml for properties being changed here! +# https://info.orcid.org/documentation/integration-and-api-faq/#easy-faq-2682 +# https://info.orcid.org/faq/what-contributor-information-should-i-include-when-adding-works-or-funding-items/ +orcid.mapping.work.patent.contributors = dc.contributor.author::author + +orcid.mapping.work.patent.journal-title = dc.publisher + +orcid.mapping.work.patent.type.converter = mapConverterDSpaceToOrcidPatentType +orcid.mapping.work.patent.external-ids = $simple-handle::handle +orcid.mapping.work.patent.external-ids = dc.identifier.patentno::pat + ### Funding mapping ### orcid.mapping.funding.title = dc.title -orcid.mapping.funding.type = +orcid.mapping.funding.type = orcid.mapping.funding.type.converter = mapConverterDSpaceToOrcidFundingType ##orcid.mapping.funding.external-ids syntax is :: ##The full list of available external identifiers is available here https://pub.orcid.org/v3.0/identifiers diff --git a/dspace/config/registries/dspace-types.xml b/dspace/config/registries/dspace-types.xml index fcdbc9b2af9b..b1819fc3fbb1 100644 --- a/dspace/config/registries/dspace-types.xml +++ b/dspace/config/registries/dspace-types.xml @@ -72,6 +72,20 @@ Stores the publication synchronization with ORCID preference chosen by the user + + dspace + orcid + sync-products + Stores the product synchronization with ORCID preference chosen by the user + + + + dspace + orcid + sync-patents + Stores the patent synchronization with ORCID preference chosen by the user + + dspace orcid diff --git a/dspace/config/spring/api/orcid-services.xml b/dspace/config/spring/api/orcid-services.xml index e5fb002c314f..f77ca02030c7 100644 --- a/dspace/config/spring/api/orcid-services.xml +++ b/dspace/config/spring/api/orcid-services.xml @@ -13,9 +13,9 @@ - + - + @@ -44,6 +44,15 @@ + + + + + + + + + @@ -64,7 +73,84 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -89,12 +175,12 @@ - - + @@ -134,7 +220,17 @@ - + + + + + + + + + + + @@ -146,7 +242,7 @@ - + @@ -166,7 +262,7 @@ - +