mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 01:54:22 +00:00
Merge pull request #9238 from 4Science/main_CST-12825
ROR Integration - Live Import
This commit is contained in:
@@ -11,7 +11,9 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.dspace.content.MetadataFieldName;
|
||||
import org.dspace.importer.external.metadatamapping.MetadatumDTO;
|
||||
|
||||
/**
|
||||
@@ -94,6 +96,31 @@ public class ImportRecord {
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@code Optional<String>} representing the value
|
||||
* of the metadata {@code field} found inside the {@code valueList}.
|
||||
* @param field String of the MetadataField to search
|
||||
* @return {@code Optional<String>} non empty if found.
|
||||
*/
|
||||
public Optional<String> getSingleValue(String field) {
|
||||
MetadataFieldName metadataFieldName = new MetadataFieldName(field);
|
||||
return getSingleValue(metadataFieldName.schema, metadataFieldName.element, metadataFieldName.qualifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a single value for the given schema, element, and qualifier.
|
||||
*
|
||||
* @param schema the schema for the value
|
||||
* @param element the element for the value
|
||||
* @param qualifier the qualifier for the value
|
||||
* @return an optional containing the single value, if present
|
||||
*/
|
||||
public Optional<String> getSingleValue(String schema, String element, String qualifier) {
|
||||
return getValue(schema, element, qualifier).stream()
|
||||
.map(MetadatumDTO::getValue)
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a value to the valueList
|
||||
*
|
||||
|
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* 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.dspace.importer.external.metadatamapping.MetadatumDTO;
|
||||
|
||||
/**
|
||||
* A ROR JsonPath Metadata processor that should be configured inside the {@code ror-integration.xml} file.
|
||||
* This allows the extraction of a given contributor with a specific mappings from the ROR JSON response.
|
||||
*
|
||||
* @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com)
|
||||
*/
|
||||
public class RorParentOrgUnitMetadataContributor extends SimpleJsonPathMetadataContributor {
|
||||
|
||||
/**
|
||||
* Determines which field of the JSON detains the {@code type} of this
|
||||
* specific node (that needs to be mapped).
|
||||
*
|
||||
*/
|
||||
private String typeField;
|
||||
|
||||
/**
|
||||
* Determines which is the type of the main parent node that needs to be mapped.
|
||||
* It should match the value of the {@code typeField} of the JSON node.
|
||||
*
|
||||
*/
|
||||
private String parentType;
|
||||
|
||||
/**
|
||||
* Determines which is the field of the JSON that contains the value
|
||||
* that needs to be mapped into a {@code MetadatumDTO}.
|
||||
*/
|
||||
private String labelField;
|
||||
|
||||
/**
|
||||
* Creates a {@code MetadatumDTO} for each correctly mapped JSON node
|
||||
* of the ROR response.
|
||||
* Partial / Unmatched parent-type metadatum will be ignored from this mapping.
|
||||
*
|
||||
* @param fullJson ROR response
|
||||
* @return a collection of read ROR metadata.
|
||||
*/
|
||||
@Override
|
||||
public Collection<MetadatumDTO> contributeMetadata(String fullJson) {
|
||||
|
||||
Collection<MetadatumDTO> metadata = new ArrayList<>();
|
||||
Collection<String> metadataValue = new ArrayList<>();
|
||||
|
||||
JsonNode jsonNode = convertStringJsonToJsonNode(fullJson);
|
||||
JsonNode array = jsonNode.at(getQuery());
|
||||
if (!array.isArray()) {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
Iterator<JsonNode> nodes = array.iterator();
|
||||
while (nodes.hasNext()) {
|
||||
JsonNode node = nodes.next();
|
||||
|
||||
if (!node.has(labelField)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String type = node.has(typeField) ? node.get(typeField).asText() : null;
|
||||
String label = node.get(labelField).asText();
|
||||
|
||||
if (parentType.equalsIgnoreCase(type)) {
|
||||
metadataValue.add(label);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (String value : metadataValue) {
|
||||
MetadatumDTO metadatumDto = new MetadatumDTO();
|
||||
metadatumDto.setValue(value);
|
||||
metadatumDto.setElement(getField().getElement());
|
||||
metadatumDto.setQualifier(getField().getQualifier());
|
||||
metadatumDto.setSchema(getField().getSchema());
|
||||
metadata.add(metadatumDto);
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private JsonNode convertStringJsonToJsonNode(String json) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode body = null;
|
||||
try {
|
||||
body = mapper.readTree(json);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
public String getTypeField() {
|
||||
return typeField;
|
||||
}
|
||||
|
||||
public void setTypeField(String typeField) {
|
||||
this.typeField = typeField;
|
||||
}
|
||||
|
||||
public String getLabelField() {
|
||||
return labelField;
|
||||
}
|
||||
|
||||
public void setLabelField(String labelField) {
|
||||
this.labelField = labelField;
|
||||
}
|
||||
|
||||
public String getParentType() {
|
||||
return parentType;
|
||||
}
|
||||
|
||||
public void setParentType(String parentType) {
|
||||
this.parentType = parentType;
|
||||
}
|
||||
|
||||
}
|
38
dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorFieldMapping.java
vendored
Normal file
38
dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorFieldMapping.java
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.importer.external.ror.service;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.dspace.importer.external.metadatamapping.AbstractMetadataFieldMapping;
|
||||
|
||||
|
||||
/**
|
||||
* An implementation of {@link AbstractMetadataFieldMapping}
|
||||
* Responsible for defining the mapping of the ROR metadatum fields on the DSpace metadatum fields
|
||||
*
|
||||
* @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com)
|
||||
*/
|
||||
public class RorFieldMapping extends AbstractMetadataFieldMapping {
|
||||
|
||||
/**
|
||||
* Defines which metadatum is mapped on which metadatum. Note that while the key must be unique it
|
||||
* only matters here for postprocessing of the value. The mapped MetadatumContributor has full control over
|
||||
* what metadatafield is generated.
|
||||
*
|
||||
* @param metadataFieldMap The map containing the link between retrieve metadata and metadata that will be set to
|
||||
* the item.
|
||||
*/
|
||||
@Override
|
||||
@Resource(name = "rorMetadataFieldMap")
|
||||
public void setMetadataFieldMap(Map metadataFieldMap) {
|
||||
super.setMetadataFieldMap(metadataFieldMap);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,278 @@
|
||||
/**
|
||||
* 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.ror.service;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import javax.el.MethodNotFoundException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.importer.external.datamodel.ImportRecord;
|
||||
import org.dspace.importer.external.datamodel.Query;
|
||||
import org.dspace.importer.external.exception.MetadataSourceException;
|
||||
import org.dspace.importer.external.liveimportclient.service.LiveImportClient;
|
||||
import org.dspace.importer.external.service.AbstractImportMetadataSourceService;
|
||||
import org.dspace.importer.external.service.components.QuerySource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Implements a {@code AbstractImportMetadataSourceService} for querying ROR services.
|
||||
*
|
||||
* @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com)
|
||||
*/
|
||||
public class RorImportMetadataSourceServiceImpl extends AbstractImportMetadataSourceService<String>
|
||||
implements QuerySource {
|
||||
|
||||
private final static Logger log = LogManager.getLogger();
|
||||
protected static final String ROR_IDENTIFIER_PREFIX = "https://ror.org/";
|
||||
|
||||
private String url;
|
||||
|
||||
private int timeout = 1000;
|
||||
|
||||
@Autowired
|
||||
private LiveImportClient liveImportClient;
|
||||
|
||||
@Override
|
||||
public String getImportSource() {
|
||||
return "ror";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportRecord getRecord(String id) throws MetadataSourceException {
|
||||
List<ImportRecord> records = retry(new SearchByIdCallable(id));
|
||||
return CollectionUtils.isEmpty(records) ? null : records.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordsCount(String query) throws MetadataSourceException {
|
||||
return retry(new CountByQueryCallable(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordsCount(Query query) throws MetadataSourceException {
|
||||
return retry(new CountByQueryCallable(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> getRecords(String query, int start, int count) throws MetadataSourceException {
|
||||
return retry(new SearchByQueryCallable(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> getRecords(Query query) throws MetadataSourceException {
|
||||
return retry(new SearchByQueryCallable(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportRecord getRecord(Query query) throws MetadataSourceException {
|
||||
List<ImportRecord> records = retry(new SearchByIdCallable(query));
|
||||
return CollectionUtils.isEmpty(records) ? null : records.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> findMatchingRecords(Query query) throws MetadataSourceException {
|
||||
throw new MethodNotFoundException("This method is not implemented for ROR");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> findMatchingRecords(Item item) throws MetadataSourceException {
|
||||
throw new MethodNotFoundException("This method is not implemented for ROR");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to get ROR entries based on query
|
||||
* object. This Callable use as query value the string queryString passed to
|
||||
* constructor. If the object will be construct through Query.class instance, a
|
||||
* Query's map entry with key "query" will be used. Pagination is supported too,
|
||||
* using the value of the Query's map with keys "start" and "count".
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
private class SearchByQueryCallable implements Callable<List<ImportRecord>> {
|
||||
|
||||
private Query query;
|
||||
|
||||
private SearchByQueryCallable(String queryString) {
|
||||
query = new Query();
|
||||
query.addParameter("query", queryString);
|
||||
}
|
||||
|
||||
private SearchByQueryCallable(Query query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImportRecord> call() throws Exception {
|
||||
return search(query.getParameterAsClass("query", String.class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to get an ROR entry using bibcode The
|
||||
* bibcode to use can be passed through the constructor as a String or as
|
||||
* Query's map entry, with the key "id".
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
private class SearchByIdCallable implements Callable<List<ImportRecord>> {
|
||||
private Query query;
|
||||
|
||||
private SearchByIdCallable(Query query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
private SearchByIdCallable(String id) {
|
||||
this.query = new Query();
|
||||
query.addParameter("id", id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImportRecord> call() throws Exception {
|
||||
return searchById(query.getParameterAsClass("id", String.class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to count the number of entries for a
|
||||
* ROR query. This Callable uses as query value to ROR the string queryString
|
||||
* passed to constructor. If the object will be construct through {@code Query}
|
||||
* instance, the value of the Query's map with the key "query" will be used.
|
||||
*
|
||||
* @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com)
|
||||
*/
|
||||
private class CountByQueryCallable implements Callable<Integer> {
|
||||
private Query query;
|
||||
|
||||
private CountByQueryCallable(String queryString) {
|
||||
query = new Query();
|
||||
query.addParameter("query", queryString);
|
||||
}
|
||||
|
||||
private CountByQueryCallable(Query query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
return count(query.getParameterAsClass("query", String.class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of results for the given query.
|
||||
*
|
||||
* @param query the query string to count results for
|
||||
* @return the number of results for the given query
|
||||
*/
|
||||
public Integer count(String query) {
|
||||
try {
|
||||
Map<String, Map<String, String>> params = new HashMap<String, Map<String, String>>();
|
||||
|
||||
URIBuilder uriBuilder = new URIBuilder(this.url);
|
||||
uriBuilder.addParameter("query", query);
|
||||
|
||||
String resp = liveImportClient.executeHttpGetRequest(timeout, uriBuilder.toString(), params);
|
||||
if (StringUtils.isEmpty(resp)) {
|
||||
return 0;
|
||||
}
|
||||
JsonNode jsonNode = convertStringJsonToJsonNode(resp);
|
||||
return jsonNode.at("/number_of_results").asInt();
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private List<ImportRecord> searchById(String id) {
|
||||
|
||||
List<ImportRecord> importResults = new ArrayList<>();
|
||||
|
||||
id = StringUtils.removeStart(id, ROR_IDENTIFIER_PREFIX);
|
||||
|
||||
try {
|
||||
Map<String, Map<String, String>> params = new HashMap<String, Map<String, String>>();
|
||||
|
||||
URIBuilder uriBuilder = new URIBuilder(this.url + "/" + id);
|
||||
|
||||
String resp = liveImportClient.executeHttpGetRequest(timeout, uriBuilder.toString(), params);
|
||||
if (StringUtils.isEmpty(resp)) {
|
||||
return importResults;
|
||||
}
|
||||
|
||||
JsonNode jsonNode = convertStringJsonToJsonNode(resp);
|
||||
importResults.add(transformSourceRecords(jsonNode.toString()));
|
||||
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return importResults;
|
||||
}
|
||||
|
||||
private List<ImportRecord> search(String query) {
|
||||
List<ImportRecord> importResults = new ArrayList<>();
|
||||
try {
|
||||
Map<String, Map<String, String>> params = new HashMap<String, Map<String, String>>();
|
||||
|
||||
URIBuilder uriBuilder = new URIBuilder(this.url);
|
||||
uriBuilder.addParameter("query", query);
|
||||
|
||||
String resp = liveImportClient.executeHttpGetRequest(timeout, uriBuilder.toString(), params);
|
||||
if (StringUtils.isEmpty(resp)) {
|
||||
return importResults;
|
||||
}
|
||||
|
||||
JsonNode jsonNode = convertStringJsonToJsonNode(resp);
|
||||
JsonNode docs = jsonNode.at("/items");
|
||||
if (docs.isArray()) {
|
||||
Iterator<JsonNode> nodes = docs.elements();
|
||||
while (nodes.hasNext()) {
|
||||
JsonNode node = nodes.next();
|
||||
importResults.add(transformSourceRecords(node.toString()));
|
||||
}
|
||||
} else {
|
||||
importResults.add(transformSourceRecords(docs.toString()));
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return importResults;
|
||||
}
|
||||
|
||||
private JsonNode convertStringJsonToJsonNode(String json) {
|
||||
try {
|
||||
return new ObjectMapper().readTree(json);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Unable to process json response.", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
}
|
@@ -150,6 +150,12 @@
|
||||
<property name="viewMode" value="${scopus.search-api.viewMode}"/>
|
||||
</bean>
|
||||
<bean id="scopusMetadataFieldMapping" class="org.dspace.importer.external.scopus.service.ScopusFieldMapping"/>
|
||||
|
||||
<bean id="rorImportService" class="org.dspace.importer.external.ror.service.RorImportMetadataSourceServiceImpl">
|
||||
<property name="metadataFieldMapping" ref="rorMetadataFieldMapping"/>
|
||||
<property name="url" value="${ror.orgunit-import.api-url}"/>
|
||||
</bean>
|
||||
<bean id="rorMetadataFieldMapping" class="org.dspace.importer.external.ror.service.RorFieldMapping"/>
|
||||
|
||||
<bean id="vufindImportService" class="org.dspace.importer.external.vufind.VuFindImportMetadataSourceServiceImpl" scope="singleton">
|
||||
<!-- Set to empty to use the default set of fields -->
|
||||
|
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.rest;
|
||||
|
||||
import static org.dspace.app.matcher.LambdaMatcher.matches;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.dspace.importer.external.datamodel.ImportRecord;
|
||||
import org.dspace.importer.external.liveimportclient.service.LiveImportClientImpl;
|
||||
import org.dspace.importer.external.ror.service.RorImportMetadataSourceServiceImpl;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
public class RorImportMetadataSourceServiceIT extends AbstractLiveImportIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private LiveImportClientImpl liveImportClient;
|
||||
|
||||
@Autowired
|
||||
private RorImportMetadataSourceServiceImpl rorServiceImpl;
|
||||
|
||||
@Test
|
||||
public void tesGetRecords() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
CloseableHttpClient originalHttpClient = liveImportClient.getHttpClient();
|
||||
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
|
||||
|
||||
try (InputStream file = getClass().getResourceAsStream("ror-records.json")) {
|
||||
|
||||
String jsonResponse = IOUtils.toString(file, Charset.defaultCharset());
|
||||
|
||||
liveImportClient.setHttpClient(httpClient);
|
||||
CloseableHttpResponse response = mockResponse(jsonResponse, 200, "OK");
|
||||
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
Collection<ImportRecord> recordsImported = rorServiceImpl.getRecords("test query", 0, 2);
|
||||
assertThat(recordsImported, hasSize(20));
|
||||
|
||||
ImportRecord record = recordsImported.iterator().next();
|
||||
|
||||
assertThat(record.getValueList(), hasSize(9));
|
||||
|
||||
assertThat(
|
||||
record.getSingleValue("organization.legalName"),
|
||||
is("The University of Texas")
|
||||
);
|
||||
assertThat(record.getSingleValue("organization.identifier.ror"), is("https://ror.org/02f6dcw23"));
|
||||
assertThat(record.getSingleValue("dc.type"), is("Education"));
|
||||
assertThat(record.getSingleValue("organization.address.addressCountry"), is("US"));
|
||||
assertThat(record.getSingleValue("organization.foundingDate"), is("1959"));
|
||||
assertThat(record.getValue("organization", "identifier", "crossrefid"), hasSize(2));
|
||||
assertThat(record.getSingleValue("organization.identifier.isni"), is("0000 0001 0629 5880"));
|
||||
assertThat(record.getSingleValue("organization.parentOrganization"), is("The University of Texas System"));
|
||||
|
||||
} finally {
|
||||
liveImportClient.setHttpClient(originalHttpClient);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tesCount() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
CloseableHttpClient originalHttpClient = liveImportClient.getHttpClient();
|
||||
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
|
||||
|
||||
try (InputStream file = getClass().getResourceAsStream("ror-records.json")) {
|
||||
|
||||
String jsonResponse = IOUtils.toString(file, Charset.defaultCharset());
|
||||
|
||||
liveImportClient.setHttpClient(httpClient);
|
||||
CloseableHttpResponse response = mockResponse(jsonResponse, 200, "OK");
|
||||
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
Integer count = rorServiceImpl.count("test");
|
||||
assertThat(count, equalTo(200));
|
||||
} finally {
|
||||
liveImportClient.setHttpClient(originalHttpClient);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tesGetRecord() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
CloseableHttpClient originalHttpClient = liveImportClient.getHttpClient();
|
||||
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
|
||||
|
||||
try (InputStream file = getClass().getResourceAsStream("ror-record.json")) {
|
||||
|
||||
String jsonResponse = IOUtils.toString(file, Charset.defaultCharset());
|
||||
|
||||
liveImportClient.setHttpClient(httpClient);
|
||||
CloseableHttpResponse response = mockResponse(jsonResponse, 200, "OK");
|
||||
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
ImportRecord record = rorServiceImpl.getRecord("https://ror.org/01sps7q28");
|
||||
assertThat(record.getValueList(), hasSize(7));
|
||||
assertThat(
|
||||
record.getSingleValue("organization.legalName"),
|
||||
is("The University of Texas Health Science Center at Tyler")
|
||||
);
|
||||
assertThat(record.getSingleValue("organization.identifier.ror"), is("https://ror.org/01sps7q28"));
|
||||
assertThat(record.getSingleValue("dc.type"), is("Healthcare"));
|
||||
assertThat(record.getSingleValue("organization.address.addressCountry"), is("US"));
|
||||
assertThat(record.getSingleValue("organization.foundingDate"), is("1947"));
|
||||
assertThat(record.getSingleValue("organization.identifier.isni"), is("0000 0000 9704 5790"));
|
||||
assertThat(record.getSingleValue("organization.parentOrganization"), is("The University of Texas System"));
|
||||
|
||||
} finally {
|
||||
liveImportClient.setHttpClient(originalHttpClient);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tesGetRecordsCount() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
CloseableHttpClient originalHttpClient = liveImportClient.getHttpClient();
|
||||
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
|
||||
try (InputStream rorResponse = getClass().getResourceAsStream("ror-records.json")) {
|
||||
String rorJsonResponse = IOUtils.toString(rorResponse, Charset.defaultCharset());
|
||||
|
||||
liveImportClient.setHttpClient(httpClient);
|
||||
CloseableHttpResponse response = mockResponse(rorJsonResponse, 200, "OK");
|
||||
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
int tot = rorServiceImpl.getRecordsCount("test query");
|
||||
assertEquals(200, tot);
|
||||
} finally {
|
||||
liveImportClient.setHttpClient(originalHttpClient);
|
||||
}
|
||||
}
|
||||
|
||||
private Matcher<Optional<String>> is(String value) {
|
||||
return matches(optionalValue -> optionalValue.isPresent() && optionalValue.get().equals(value));
|
||||
}
|
||||
}
|
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"id": "https://ror.org/01sps7q28",
|
||||
"name": "The University of Texas Health Science Center at Tyler",
|
||||
"email_address": null,
|
||||
"ip_addresses": [
|
||||
|
||||
],
|
||||
"established": 1947,
|
||||
"types": [
|
||||
"Healthcare"
|
||||
],
|
||||
"relationships": [
|
||||
{
|
||||
"label": "The University of Texas System",
|
||||
"type": "Parent",
|
||||
"id": "https://ror.org/01gek1696"
|
||||
}
|
||||
],
|
||||
"addresses": [
|
||||
{
|
||||
"lat": 32.426014,
|
||||
"lng": -95.212728,
|
||||
"state": "Texas",
|
||||
"state_code": "US-TX",
|
||||
"city": "Tyler",
|
||||
"geonames_city": {
|
||||
"id": 4738214,
|
||||
"city": "Tyler",
|
||||
"geonames_admin1": {
|
||||
"name": "Texas",
|
||||
"id": 4736286,
|
||||
"ascii_name": "Texas",
|
||||
"code": "US.TX"
|
||||
},
|
||||
"geonames_admin2": {
|
||||
"name": "Smith County",
|
||||
"id": 4729130,
|
||||
"ascii_name": "Smith County",
|
||||
"code": "US.TX.423"
|
||||
},
|
||||
"license": {
|
||||
"attribution": "Data from geonames.org under a CC-BY 3.0 license",
|
||||
"license": "http://creativecommons.org/licenses/by/3.0/"
|
||||
},
|
||||
"nuts_level1": {
|
||||
"name": null,
|
||||
"code": null
|
||||
},
|
||||
"nuts_level2": {
|
||||
"name": null,
|
||||
"code": null
|
||||
},
|
||||
"nuts_level3": {
|
||||
"name": null,
|
||||
"code": null
|
||||
}
|
||||
},
|
||||
"postcode": null,
|
||||
"primary": false,
|
||||
"line": null,
|
||||
"country_geonames_id": 6252001
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
"https://www.utsystem.edu/institutions/university-texas-health-science-center-tyler"
|
||||
],
|
||||
"aliases": [
|
||||
"East Texas Tuberculosis Sanitarium",
|
||||
"UT Health Northeast"
|
||||
],
|
||||
"acronyms": [
|
||||
"UTHSCT"
|
||||
],
|
||||
"status": "active",
|
||||
"wikipedia_url": "https://en.wikipedia.org/wiki/University_of_Texas_Health_Science_Center_at_Tyler",
|
||||
"labels": [
|
||||
|
||||
],
|
||||
"country": {
|
||||
"country_name": "United States",
|
||||
"country_code": "US"
|
||||
},
|
||||
"external_ids": {
|
||||
"ISNI": {
|
||||
"preferred": null,
|
||||
"all": [
|
||||
"0000 0000 9704 5790"
|
||||
]
|
||||
},
|
||||
"OrgRef": {
|
||||
"preferred": null,
|
||||
"all": [
|
||||
"3446655"
|
||||
]
|
||||
},
|
||||
"Wikidata": {
|
||||
"preferred": null,
|
||||
"all": [
|
||||
"Q7896437"
|
||||
]
|
||||
},
|
||||
"GRID": {
|
||||
"preferred": "grid.267310.1",
|
||||
"all": "grid.267310.1"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -92,3 +92,8 @@ wos.url.search = https://wos-api.clarivate.com/api/wos/?databaseId=WOS&lang=en&u
|
||||
datacite.url = https://api.datacite.org/dois/
|
||||
datacite.timeout = 180000
|
||||
#################################################################
|
||||
#--------------------------- ROR -------------------------------#
|
||||
#---------------------------------------------------------------#
|
||||
|
||||
ror.orgunit-import.api-url = https://api.ror.org/organizations
|
||||
#################################################################
|
@@ -80,6 +80,12 @@
|
||||
<qualifier>crossrefid</qualifier>
|
||||
<scope_note>Crossref identifier</scope_note>
|
||||
</dc-type>
|
||||
|
||||
<dc-type>
|
||||
<schema>organization</schema>
|
||||
<element>parentOrganization</element>
|
||||
<scope_note>The larger organization that this organization is a subOrganization of, if any.</scope_note>
|
||||
</dc-type>
|
||||
|
||||
|
||||
</dspace-dc-types>
|
||||
|
@@ -93,7 +93,7 @@
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
||||
<bean id="orcidPublicationDataProvider" class="org.dspace.external.provider.impl.OrcidPublicationDataProvider">
|
||||
<property name="sourceIdentifier" value="orcidWorks"/>
|
||||
<property name="fieldMapping" ref="orcidPublicationDataProviderFieldMapping"/>
|
||||
@@ -199,6 +199,18 @@
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="rorDataProvider" class="org.dspace.external.provider.impl.LiveImportDataProvider" >
|
||||
<property name="metadataSource" ref="rorImportService"/>
|
||||
<property name="sourceIdentifier" value="ror"/>
|
||||
<property name="recordIdMetadata" value="organization.identifier.ror"/>
|
||||
<property name="displayMetadata" value="organization.legalName"/>
|
||||
<property name="supportedEntityTypes">
|
||||
<list>
|
||||
<value>OrgUnit</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="wosLiveImportDataProvider" class="org.dspace.external.provider.impl.LiveImportDataProvider">
|
||||
<property name="metadataSource" ref="WosImportService"/>
|
||||
|
93
dspace/config/spring/api/ror-integration.xml
Normal file
93
dspace/config/spring/api/ror-integration.xml
Normal file
@@ -0,0 +1,93 @@
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
|
||||
http://www.springframework.org/schema/context
|
||||
http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
|
||||
default-autowire-candidates="*Service,*DAO,javax.sql.DataSource">
|
||||
|
||||
<context:annotation-config/>
|
||||
<!-- allows us to use spring annotations in beans -->
|
||||
|
||||
<util:map id="rorMetadataFieldMap" key-type="org.dspace.importer.external.metadatamapping.MetadataFieldConfig" value-type="org.dspace.importer.external.metadatamapping.contributor.MetadataContributor">
|
||||
<entry key-ref="ror.title" value-ref="rorTitleContrib"/>
|
||||
<entry key-ref="ror.identifier" value-ref="rorIdentifierContrib"/>
|
||||
<entry key-ref="ror.types" value-ref="rorTypesContrib"/>
|
||||
<entry key-ref="ror.country" value-ref="rorCountryContrib"/>
|
||||
<entry key-ref="ror.established" value-ref="rorEstablishedContrib"/>
|
||||
<entry key-ref="ror.fundref" value-ref="rorFundRefContrib"/>
|
||||
<entry key-ref="ror.isni" value-ref="rorISNIContrib"/>
|
||||
<entry key-ref="ror.parent" value-ref="rorParentContrib"/>
|
||||
</util:map>
|
||||
|
||||
<bean id="rorTitleContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="ror.title"/>
|
||||
<property name="query" value="/name"/>
|
||||
</bean>
|
||||
<bean id="ror.title" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="organization.legalName"/>
|
||||
</bean>
|
||||
|
||||
<bean id="rorIdentifierContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="ror.identifier"/>
|
||||
<property name="query" value="/id"/>
|
||||
</bean>
|
||||
<bean id="ror.identifier" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="organization.identifier.ror"/>
|
||||
</bean>
|
||||
|
||||
<bean id="rorTypesContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="ror.types"/>
|
||||
<property name="query" value="/types"/>
|
||||
</bean>
|
||||
<bean id="ror.types" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.type"/>
|
||||
</bean>
|
||||
|
||||
<bean id="rorCountryContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="ror.country"/>
|
||||
<property name="query" value="/country/country_code"/>
|
||||
</bean>
|
||||
<bean id="ror.country" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="organization.address.addressCountry"/>
|
||||
</bean>
|
||||
|
||||
<bean id="rorEstablishedContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="ror.established"/>
|
||||
<property name="query" value="/established"/>
|
||||
</bean>
|
||||
<bean id="ror.established" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="organization.foundingDate"/>
|
||||
</bean>
|
||||
|
||||
<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"/>
|
||||
</bean>
|
||||
<bean id="ror.fundref" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="organization.identifier.crossrefid"/>
|
||||
</bean>
|
||||
|
||||
<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"/>
|
||||
</bean>
|
||||
<bean id="ror.isni" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="organization.identifier.isni"/>
|
||||
</bean>
|
||||
|
||||
<bean id="rorParentContrib" class="org.dspace.importer.external.metadatamapping.contributor.RorParentOrgUnitMetadataContributor">
|
||||
<property name="field" ref="ror.parent"/>
|
||||
<property name="query" value="/relationships"/>
|
||||
<property name="typeField" value="type"/>
|
||||
<property name="labelField" value="label"/>
|
||||
<property name="parentType" value="Parent"/>
|
||||
</bean>
|
||||
|
||||
<bean id="ror.parent" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="organization.parentOrganization"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
@@ -531,18 +531,18 @@
|
||||
</row>
|
||||
<row>
|
||||
<relation-field>
|
||||
<relationship-type>isProjectOfOrgUnit</relationship-type>
|
||||
<relationship-type>isOrgUnitOfProject</relationship-type>
|
||||
<search-configuration>orgunit</search-configuration>
|
||||
<repeatable>false</repeatable>
|
||||
<label>Organization</label>
|
||||
<hint>Enter the organization's name</hint>
|
||||
<linked-metadata-field>
|
||||
<dc-schema>project</dc-schema>
|
||||
<dc-element>funder</dc-element>
|
||||
<dc-qualifier>name</dc-qualifier>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>contributor</dc-element>
|
||||
<dc-qualifier>other</dc-qualifier>
|
||||
<input-type>onebox</input-type>
|
||||
</linked-metadata-field>
|
||||
<externalsources/>
|
||||
<externalsources>ror</externalsources>
|
||||
</relation-field>
|
||||
</row>
|
||||
<row>
|
||||
|
Reference in New Issue
Block a user