Update DSpace integration to use ROR API v2

(cherry picked from commit 53713629a6)
This commit is contained in:
Jesiel Viana
2025-07-14 13:47:49 -03:00
committed by github-actions[bot]
parent ba5b147889
commit fc93298676
3 changed files with 183 additions and 7 deletions

View File

@@ -0,0 +1,132 @@
/**
* 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.importer.external.metadatamapping.contributor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* This Processor extracts values from a JSON array, but only when a condition
* on another attribute is met. For example, to extract all values of
* /names/value where /names/types contains "ror_display".
*
* Configurable via:
* pathToArray: e.g., /items/0/names
* elementAttribute: e.g., /value
* filterAttribute: e.g., /types
* requiredValueInFilter: e.g., ror_display
*
* Supports filtering when the filter attribute is either a JSON array or a single string.
*
* Example JSON:
* {
* "items": [{
* "names": [
* { "types": ["label", "ror_display"], "value": "Universidade Federal do Piauí" },
* { "types": ["label"], "value": "UFPI" }
* ]
* }]
* }
* This processor can extract "Universidade Federal do Piauí" using proper configuration.
*
* Author: Jesiel (based on Mykhaylo Boychuks original processor)
*/
public class ConditionalArrayElementAttributeProcessor implements JsonPathMetadataProcessor {
private static final Logger log = LogManager.getLogger();
private String pathToArray;
private String elementAttribute;
private String filterAttribute;
private String requiredValueInFilter;
@Override
public Collection<String> processMetadata(String json) {
System.out.println("pathToArray: " + pathToArray);
System.out.println("elementAttribute: " + elementAttribute);
System.out.println("filterAttribute: " + filterAttribute);
System.out.println("requiredValueInFilter: " + requiredValueInFilter);
JsonNode rootNode = convertStringJsonToJsonNode(json);
Collection<String> results = new ArrayList<>();
if (rootNode == null) {
return results;
}
Iterator<JsonNode> array = rootNode.at(pathToArray).iterator();
System.out.println("array: " + array.toString());
while (array.hasNext()) {
JsonNode element = array.next();
JsonNode filterNode = element.at(filterAttribute);
boolean match = false;
if (filterNode.isArray()) {
for (JsonNode filterValue : filterNode) {
if (requiredValueInFilter.equalsIgnoreCase(filterValue.textValue())) {
match = true;
break;
}
}
} else if (filterNode.isTextual()) {
if (requiredValueInFilter.equalsIgnoreCase(filterNode.textValue())) {
match = true;
}
}
if (match) {
JsonNode valueNode = element.at(elementAttribute);
if (valueNode.isTextual()) {
results.add(valueNode.textValue());
} else if (valueNode.isArray()) {
for (JsonNode item : valueNode) {
if (item.isTextual() && StringUtils.isNotBlank(item.textValue())) {
results.add(item.textValue());
}
}
}
}
}
return results;
}
private JsonNode convertStringJsonToJsonNode(String json) {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.readTree(json);
} catch (JsonProcessingException e) {
log.error("Unable to process JSON response.", e);
return null;
}
}
public void setPathToArray(String pathToArray) {
this.pathToArray = pathToArray;
}
public void setElementAttribute(String elementAttribute) {
this.elementAttribute = elementAttribute;
}
public void setFilterAttribute(String filterAttribute) {
this.filterAttribute = filterAttribute;
}
public void setRequiredValueInFilter(String requiredValueInFilter) {
this.requiredValueInFilter = requiredValueInFilter;
}
}

View File

@@ -98,5 +98,5 @@ datacite.timeout = 180000
#--------------------------- ROR -------------------------------#
#---------------------------------------------------------------#
ror.orgunit-import.api-url = https://api.ror.org/organizations
ror.orgunit-import.api-url = https://api.ror.org/v2/organizations
#################################################################

View File

@@ -26,8 +26,17 @@
<bean id="rorTitleContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
<property name="field" ref="ror.title"/>
<property name="query" value="/name"/>
<!-- <property name="query" value="/name"/>-->
<property name="metadataProcessor" ref="rorNamesProcessor"/>
</bean>
<bean name="rorNamesProcessor" class="org.dspace.importer.external.metadatamapping.contributor.ConditionalArrayElementAttributeProcessor">
<property name="pathToArray" value="/names" />
<property name="elementAttribute" value="/value" />
<property name="filterAttribute" value="/types" />
<property name="requiredValueInFilter" value="ror_display" />
</bean>
<bean id="ror.title" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="organization.legalName"/>
</bean>
@@ -42,15 +51,31 @@
<bean id="rorAcronymContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
<property name="field" ref="ror.acronym"/>
<property name="query" value="/acronyms"/>
<!-- <property name="query" value="/acronyms"/>-->
<property name="metadataProcessor" ref="rorAcronymProcessor"/>
</bean>
<bean name="rorAcronymProcessor" class="org.dspace.importer.external.metadatamapping.contributor.ConditionalArrayElementAttributeProcessor">
<property name="pathToArray" value="/names" />
<property name="elementAttribute" value="/value" />
<property name="filterAttribute" value="/types" />
<property name="requiredValueInFilter" value="acronym" />
</bean>
<bean id="ror.acronym" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="organization.alternateName"/>
</bean>
<bean id="rorLinksContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
<property name="field" ref="ror.links"/>
<property name="query" value="/links"/>
<!-- <property name="query" value="/links"/>-->
<property name="metadataProcessor" ref="rorLinksProcessor"/>
</bean>
<bean name="rorLinksProcessor" class="org.dspace.importer.external.metadatamapping.contributor.ConditionalArrayElementAttributeProcessor">
<property name="pathToArray" value="/links" />
<property name="elementAttribute" value="/value" />
<property name="filterAttribute" value="/type" />
<property name="requiredValueInFilter" value="website" />
</bean>
<bean id="ror.links" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="organization.url"/>
@@ -66,7 +91,12 @@
<bean id="rorCountryContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
<property name="field" ref="ror.country"/>
<property name="query" value="/country/country_code"/>
<!-- <property name="query" value="/country/country_code"/>-->
<property name="metadataProcessor" ref="rorCountryProcessor"/>
</bean>
<bean name="rorCountryProcessor" class="org.dspace.importer.external.metadatamapping.contributor.ArrayElementAttributeProcessor">
<property name="pathToArray" value="/locations"/>
<property name="elementAttribute" value="/geonames_details/country_code"/>
</bean>
<bean id="ror.country" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="organization.address.addressCountry"/>
@@ -82,7 +112,14 @@
<bean id="rorFundRefContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
<property name="field" ref="ror.fundref"/>
<property name="query" value="/external_ids/FundRef/all"/>
<!-- <property name="query" value="/external_ids/FundRef/all"/>-->
<property name="metadataProcessor" ref="rorFundRefProcessor"/>
</bean>
<bean name="rorFundRefProcessor" class="org.dspace.importer.external.metadatamapping.contributor.ConditionalArrayElementAttributeProcessor">
<property name="pathToArray" value="/external_ids" />
<property name="elementAttribute" value="/all" />
<property name="filterAttribute" value="/type" />
<property name="requiredValueInFilter" value="fundref" />
</bean>
<bean id="ror.fundref" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="organization.identifier.crossrefid"/>
@@ -90,7 +127,14 @@
<bean id="rorISNIContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
<property name="field" ref="ror.isni"/>
<property name="query" value="/external_ids/ISNI/all"/>
<!-- <property name="query" value="/external_ids/ISNI/all"/>-->
<property name="metadataProcessor" ref="rorISNIProcessor"/>
</bean>
<bean name="rorISNIProcessor" class="org.dspace.importer.external.metadatamapping.contributor.ConditionalArrayElementAttributeProcessor">
<property name="pathToArray" value="/external_ids" />
<property name="elementAttribute" value="/all" />
<property name="filterAttribute" value="/type" />
<property name="requiredValueInFilter" value="isni" />
</bean>
<bean id="ror.isni" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="organization.identifier.isni"/>