mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 01:54:22 +00:00
Merge branch 'main' into CST-5303-LiveImport-3PR
This commit is contained in:
@@ -193,13 +193,11 @@ public class SyndicationFeed {
|
||||
String defaultTitle = null;
|
||||
boolean podcastFeed = false;
|
||||
this.request = request;
|
||||
|
||||
// dso is null for the whole site, or a search without scope
|
||||
if (dso == null) {
|
||||
defaultTitle = configurationService.getProperty("dspace.name");
|
||||
feed.setDescription(localize(labels, MSG_FEED_DESCRIPTION));
|
||||
objectURL = resolveURL(request, null);
|
||||
logoURL = configurationService.getProperty("webui.feed.logo.url");
|
||||
} else {
|
||||
Bitstream logo = null;
|
||||
if (dso instanceof IndexableCollection) {
|
||||
@@ -329,7 +327,8 @@ public class SyndicationFeed {
|
||||
dcDescriptionField != null) {
|
||||
DCModule dc = new DCModuleImpl();
|
||||
if (dcCreatorField != null) {
|
||||
List<MetadataValue> dcAuthors = itemService.getMetadataByMetadataString(item, dcCreatorField);
|
||||
List<MetadataValue> dcAuthors = itemService
|
||||
.getMetadataByMetadataString(item, dcCreatorField);
|
||||
if (dcAuthors.size() > 0) {
|
||||
List<String> creators = new ArrayList<>();
|
||||
for (MetadataValue author : dcAuthors) {
|
||||
@@ -345,7 +344,8 @@ public class SyndicationFeed {
|
||||
}
|
||||
}
|
||||
if (dcDescriptionField != null) {
|
||||
List<MetadataValue> v = itemService.getMetadataByMetadataString(item, dcDescriptionField);
|
||||
List<MetadataValue> v = itemService
|
||||
.getMetadataByMetadataString(item, dcDescriptionField);
|
||||
if (v.size() > 0) {
|
||||
StringBuilder descs = new StringBuilder();
|
||||
for (MetadataValue d : v) {
|
||||
@@ -376,6 +376,7 @@ public class SyndicationFeed {
|
||||
enc.setLength(bit.getSizeBytes());
|
||||
enc.setUrl(urlOfBitstream(request, bit));
|
||||
enclosures.add(enc);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ package org.dspace.core;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -91,12 +92,12 @@ public class Context implements AutoCloseable {
|
||||
/**
|
||||
* Group IDs of special groups user is a member of
|
||||
*/
|
||||
private List<UUID> specialGroups;
|
||||
private Set<UUID> specialGroups;
|
||||
|
||||
/**
|
||||
* Temporary store for the specialGroups when the current user is temporary switched
|
||||
*/
|
||||
private List<UUID> specialGroupsPreviousState;
|
||||
private Set<UUID> specialGroupsPreviousState;
|
||||
|
||||
/**
|
||||
* The currently used authentication method
|
||||
@@ -183,7 +184,7 @@ public class Context implements AutoCloseable {
|
||||
extraLogInfo = "";
|
||||
ignoreAuth = false;
|
||||
|
||||
specialGroups = new ArrayList<>();
|
||||
specialGroups = new HashSet<>();
|
||||
|
||||
authStateChangeHistory = new ConcurrentLinkedDeque<>();
|
||||
authStateClassCallHistory = new ConcurrentLinkedDeque<>();
|
||||
@@ -673,7 +674,7 @@ public class Context implements AutoCloseable {
|
||||
|
||||
currentUserPreviousState = currentUser;
|
||||
specialGroupsPreviousState = specialGroups;
|
||||
specialGroups = new ArrayList<>();
|
||||
specialGroups = new HashSet<>();
|
||||
currentUser = newUser;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 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.crossref;
|
||||
|
||||
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;
|
||||
import org.dspace.importer.external.metadatamapping.contributor.JsonPathMetadataProcessor;
|
||||
|
||||
/**
|
||||
* This class is used for CrossRef's Live-Import to extract
|
||||
* attributes such as "given" and "family" from the array of authors/editors
|
||||
* and return them concatenated.
|
||||
* Beans are configured in the crossref-integration.xml file.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
public class CrossRefAuthorMetadataProcessor implements JsonPathMetadataProcessor {
|
||||
|
||||
private final static Logger log = LogManager.getLogger();
|
||||
|
||||
private String pathToArray;
|
||||
|
||||
@Override
|
||||
public Collection<String> processMetadata(String json) {
|
||||
JsonNode rootNode = convertStringJsonToJsonNode(json);
|
||||
Iterator<JsonNode> authors = rootNode.at(pathToArray).iterator();
|
||||
Collection<String> values = new ArrayList<>();
|
||||
while (authors.hasNext()) {
|
||||
JsonNode author = authors.next();
|
||||
String givenName = author.at("/given").textValue();
|
||||
String familyName = author.at("/family").textValue();
|
||||
if (StringUtils.isNoneBlank(givenName) && StringUtils.isNoneBlank(familyName)) {
|
||||
values.add(givenName + " " + familyName);
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private JsonNode convertStringJsonToJsonNode(String json) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode body = null;
|
||||
try {
|
||||
body = mapper.readTree(json);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Unable to process json response.", e);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
public void setPathToArray(String pathToArray) {
|
||||
this.pathToArray = pathToArray;
|
||||
}
|
||||
|
||||
}
|
39
dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefFieldMapping.java
vendored
Normal file
39
dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefFieldMapping.java
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.importer.external.crossref;
|
||||
|
||||
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 CrossRef metadatum fields on the DSpace metadatum fields
|
||||
*
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class CrossRefFieldMapping 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
|
||||
@SuppressWarnings("unchecked")
|
||||
@Resource(name = "crossrefMetadataFieldMap")
|
||||
public void setMetadataFieldMap(Map metadataFieldMap) {
|
||||
super.setMetadataFieldMap(metadataFieldMap);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,337 @@
|
||||
/**
|
||||
* 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.crossref;
|
||||
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
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.collections4.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.DoiCheck;
|
||||
import org.dspace.importer.external.service.components.QuerySource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Implements a data source for querying CrossRef
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.com)
|
||||
*/
|
||||
public class CrossRefImportMetadataSourceServiceImpl extends AbstractImportMetadataSourceService<String>
|
||||
implements QuerySource {
|
||||
|
||||
private final static Logger log = LogManager.getLogger();
|
||||
|
||||
private String url;
|
||||
|
||||
@Autowired
|
||||
private LiveImportClient liveImportClient;
|
||||
|
||||
@Override
|
||||
public String getImportSource() {
|
||||
return "crossref";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws Exception {}
|
||||
|
||||
@Override
|
||||
public ImportRecord getRecord(String recordId) throws MetadataSourceException {
|
||||
String id = getID(recordId);
|
||||
List<ImportRecord> records = StringUtils.isNotBlank(id) ? retry(new SearchByIdCallable(id))
|
||||
: retry(new SearchByIdCallable(recordId));
|
||||
return CollectionUtils.isEmpty(records) ? null : records.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordsCount(String query) throws MetadataSourceException {
|
||||
String id = getID(query);
|
||||
return StringUtils.isNotBlank(id) ? retry(new DoiCheckCallable(id)) : retry(new CountByQueryCallable(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordsCount(Query query) throws MetadataSourceException {
|
||||
String id = getID(query.toString());
|
||||
return StringUtils.isNotBlank(id) ? retry(new DoiCheckCallable(id)) : retry(new CountByQueryCallable(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> getRecords(String query, int start, int count) throws MetadataSourceException {
|
||||
String id = getID(query.toString());
|
||||
return StringUtils.isNotBlank(id) ? retry(new SearchByIdCallable(id))
|
||||
: retry(new SearchByQueryCallable(query, count, start));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> getRecords(Query query) throws MetadataSourceException {
|
||||
String id = getID(query.toString());
|
||||
if (StringUtils.isNotBlank(id)) {
|
||||
return retry(new SearchByIdCallable(id));
|
||||
}
|
||||
return retry(new SearchByQueryCallable(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportRecord getRecord(Query query) throws MetadataSourceException {
|
||||
String id = getID(query.toString());
|
||||
List<ImportRecord> records = StringUtils.isNotBlank(id) ? retry(new SearchByIdCallable(id))
|
||||
: retry(new SearchByIdCallable(query));
|
||||
return CollectionUtils.isEmpty(records) ? null : records.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> findMatchingRecords(Query query) throws MetadataSourceException {
|
||||
String id = getID(query.toString());
|
||||
return StringUtils.isNotBlank(id) ? retry(new SearchByIdCallable(id))
|
||||
: retry(new FindMatchingRecordCallable(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> findMatchingRecords(Item item) throws MetadataSourceException {
|
||||
throw new MethodNotFoundException("This method is not implemented for CrossRef");
|
||||
}
|
||||
|
||||
public String getID(String id) {
|
||||
return DoiCheck.isDoi(id) ? "filter=doi:" + id : StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to get CrossRef 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, Integer maxResult, Integer start) {
|
||||
query = new Query();
|
||||
query.addParameter("query", queryString);
|
||||
query.addParameter("count", maxResult);
|
||||
query.addParameter("start", start);
|
||||
}
|
||||
|
||||
private SearchByQueryCallable(Query query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImportRecord> call() throws Exception {
|
||||
List<ImportRecord> results = new ArrayList<>();
|
||||
Integer count = query.getParameterAsClass("count", Integer.class);
|
||||
Integer start = query.getParameterAsClass("start", Integer.class);
|
||||
|
||||
URIBuilder uriBuilder = new URIBuilder(url);
|
||||
uriBuilder.addParameter("query", query.getParameterAsClass("query", String.class));
|
||||
if (Objects.nonNull(count)) {
|
||||
uriBuilder.addParameter("rows", count.toString());
|
||||
}
|
||||
if (Objects.nonNull(start)) {
|
||||
uriBuilder.addParameter("offset", start.toString());
|
||||
}
|
||||
|
||||
String response = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(),
|
||||
new HashMap<String, String>());
|
||||
JsonNode jsonNode = convertStringJsonToJsonNode(response);
|
||||
Iterator<JsonNode> nodes = jsonNode.at("/message/items").iterator();
|
||||
while (nodes.hasNext()) {
|
||||
JsonNode node = nodes.next();
|
||||
results.add(transformSourceRecords(node.toString()));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to get an CrossRef entry using DOI
|
||||
* The DOI 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 {
|
||||
List<ImportRecord> results = new ArrayList<>();
|
||||
String ID = URLDecoder.decode(query.getParameterAsClass("id", String.class), "UTF-8");
|
||||
URIBuilder uriBuilder = new URIBuilder(url + "/" + ID);
|
||||
String responseString = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(),
|
||||
new HashMap<String, String>());
|
||||
JsonNode jsonNode = convertStringJsonToJsonNode(responseString);
|
||||
JsonNode messageNode = jsonNode.at("/message");
|
||||
results.add(transformSourceRecords(messageNode.toString()));
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to search CrossRef entries using author and title.
|
||||
* There are two field in the Query map to pass, with keys "title" and "author"
|
||||
* (at least one must be used).
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
private class FindMatchingRecordCallable implements Callable<List<ImportRecord>> {
|
||||
|
||||
private Query query;
|
||||
|
||||
private FindMatchingRecordCallable(Query q) {
|
||||
query = q;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImportRecord> call() throws Exception {
|
||||
String queryValue = query.getParameterAsClass("query", String.class);
|
||||
Integer count = query.getParameterAsClass("count", Integer.class);
|
||||
Integer start = query.getParameterAsClass("start", Integer.class);
|
||||
String author = query.getParameterAsClass("author", String.class);
|
||||
String title = query.getParameterAsClass("title", String.class);
|
||||
String bibliographics = query.getParameterAsClass("bibliographics", String.class);
|
||||
List<ImportRecord> results = new ArrayList<>();
|
||||
URIBuilder uriBuilder = new URIBuilder(url);
|
||||
if (Objects.nonNull(queryValue)) {
|
||||
uriBuilder.addParameter("query", queryValue);
|
||||
}
|
||||
if (Objects.nonNull(count)) {
|
||||
uriBuilder.addParameter("rows", count.toString());
|
||||
}
|
||||
if (Objects.nonNull(start)) {
|
||||
uriBuilder.addParameter("offset", start.toString());
|
||||
}
|
||||
if (Objects.nonNull(author)) {
|
||||
uriBuilder.addParameter("query.author", author);
|
||||
}
|
||||
if (Objects.nonNull(title )) {
|
||||
uriBuilder.addParameter("query.container-title", title);
|
||||
}
|
||||
if (Objects.nonNull(bibliographics)) {
|
||||
uriBuilder.addParameter("query.bibliographic", bibliographics);
|
||||
}
|
||||
|
||||
String resp = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(),
|
||||
new HashMap<String, String>());
|
||||
JsonNode jsonNode = convertStringJsonToJsonNode(resp);
|
||||
Iterator<JsonNode> nodes = jsonNode.at("/message/items").iterator();
|
||||
while (nodes.hasNext()) {
|
||||
JsonNode node = nodes.next();
|
||||
results.add(transformSourceRecords(node.toString()));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to count the number of entries for an CrossRef query.
|
||||
* This Callable use as query value to CrossRef the string queryString passed to constructor.
|
||||
* If the object will be construct through Query.class instance, the value of the Query's
|
||||
* map with the key "query" will be used.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@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 {
|
||||
URIBuilder uriBuilder = new URIBuilder(url);
|
||||
uriBuilder.addParameter("query", query.getParameterAsClass("query", String.class));
|
||||
String responseString = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(),
|
||||
new HashMap<String, String>());
|
||||
JsonNode jsonNode = convertStringJsonToJsonNode(responseString);
|
||||
return jsonNode.at("/message/total-results").asInt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to check if exist an CrossRef entry using DOI.
|
||||
* The DOI to use can be passed through the constructor as a String or as Query's map entry, with the key "id".
|
||||
* return 1 if CrossRef entry exists otherwise 0
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
private class DoiCheckCallable implements Callable<Integer> {
|
||||
|
||||
private final Query query;
|
||||
|
||||
private DoiCheckCallable(final String id) {
|
||||
final Query query = new Query();
|
||||
query.addParameter("id", id);
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
private DoiCheckCallable(final Query query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
URIBuilder uriBuilder = new URIBuilder(url + "/" + query.getParameterAsClass("id", String.class));
|
||||
String responseString = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(),
|
||||
new HashMap<String, String>());
|
||||
JsonNode jsonNode = convertStringJsonToJsonNode(responseString);
|
||||
return StringUtils.equals(jsonNode.at("/status").toString(), "ok") ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@@ -60,7 +60,7 @@ public class LiveImportClientImpl implements LiveImportClient {
|
||||
|
||||
HttpResponse httpResponse = httpClient.execute(method);
|
||||
if (isNotSuccessfull(httpResponse)) {
|
||||
throw new RuntimeException();
|
||||
throw new RuntimeException("The request failed with: " + getStatusCode(httpResponse) + " code");
|
||||
}
|
||||
InputStream inputStream = httpResponse.getEntity().getContent();
|
||||
return IOUtils.toString(inputStream, Charset.defaultCharset());
|
||||
|
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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 allows to extract attribute values of an array.
|
||||
* For exaple to extract all values of secondAttribute,
|
||||
* "array":[
|
||||
* {
|
||||
* "firstAttribute":"first value",
|
||||
* "secondAttribute":"second value"
|
||||
* },
|
||||
* {
|
||||
* "firstAttribute":"first value",
|
||||
* "secondAttribute":"second value"
|
||||
* }
|
||||
* ]
|
||||
*
|
||||
* it's possible configure a bean with
|
||||
* pathToArray=/array and elementAttribute=/secondAttribute
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
public class ArrayElementAttributeProcessor implements JsonPathMetadataProcessor {
|
||||
|
||||
private final static Logger log = LogManager.getLogger();
|
||||
|
||||
private String pathToArray;
|
||||
|
||||
private String elementAttribute;
|
||||
|
||||
@Override
|
||||
public Collection<String> processMetadata(String json) {
|
||||
JsonNode rootNode = convertStringJsonToJsonNode(json);
|
||||
Iterator<JsonNode> array = rootNode.at(pathToArray).iterator();
|
||||
Collection<String> values = new ArrayList<>();
|
||||
while (array.hasNext()) {
|
||||
JsonNode element = array.next();
|
||||
String value = element.at(elementAttribute).textValue();
|
||||
if (StringUtils.isNoneBlank(value)) {
|
||||
values.add(value);
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private JsonNode convertStringJsonToJsonNode(String json) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode body = null;
|
||||
try {
|
||||
body = mapper.readTree(json);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Unable to process json response.", e);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
public void setPathToArray(String pathToArray) {
|
||||
this.pathToArray = pathToArray;
|
||||
}
|
||||
|
||||
public void setElementAttribute(String elementAttribute) {
|
||||
this.elementAttribute = elementAttribute;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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.Collection;
|
||||
|
||||
/**
|
||||
* Service interface class for processing json object.
|
||||
* The implementation of this class is responsible for all business logic calls
|
||||
* for extracting of values from json object.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
public interface JsonPathMetadataProcessor {
|
||||
|
||||
public Collection<String> processMetadata(String json);
|
||||
|
||||
}
|
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* 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 allows to extract all values of a matrix.
|
||||
* Only need to configure the path to the matrix in "pathToMatrix"
|
||||
* For exaple to extract all values
|
||||
* "matrix": [
|
||||
* [
|
||||
* "first",
|
||||
* "second"
|
||||
* ],
|
||||
* [
|
||||
* "third"
|
||||
* ],
|
||||
* [
|
||||
* "fourth",
|
||||
* "fifth"
|
||||
* ]
|
||||
* ],
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
public class MatrixElementProcessor implements JsonPathMetadataProcessor {
|
||||
|
||||
private final static Logger log = LogManager.getLogger();
|
||||
|
||||
private String pathToMatrix;
|
||||
|
||||
@Override
|
||||
public Collection<String> processMetadata(String json) {
|
||||
JsonNode rootNode = convertStringJsonToJsonNode(json);
|
||||
Iterator<JsonNode> array = rootNode.at(pathToMatrix).elements();
|
||||
Collection<String> values = new ArrayList<>();
|
||||
while (array.hasNext()) {
|
||||
JsonNode element = array.next();
|
||||
if (element.isArray()) {
|
||||
Iterator<JsonNode> nodes = element.iterator();
|
||||
while (nodes.hasNext()) {
|
||||
String nodeValue = nodes.next().textValue();
|
||||
if (StringUtils.isNotBlank(nodeValue)) {
|
||||
values.add(nodeValue);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String nodeValue = element.textValue();
|
||||
if (StringUtils.isNotBlank(nodeValue)) {
|
||||
values.add(nodeValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private JsonNode convertStringJsonToJsonNode(String json) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode body = null;
|
||||
try {
|
||||
body = mapper.readTree(json);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Unable to process json response.", e);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
public void setPathToMatrix(String pathToMatrix) {
|
||||
this.pathToMatrix = pathToMatrix;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* 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 java.util.Objects;
|
||||
|
||||
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;
|
||||
import org.dspace.importer.external.metadatamapping.MetadataFieldConfig;
|
||||
import org.dspace.importer.external.metadatamapping.MetadataFieldMapping;
|
||||
import org.dspace.importer.external.metadatamapping.MetadatumDTO;
|
||||
|
||||
/**
|
||||
* A simple JsonPath Metadata processor
|
||||
* that allow extract value from json object
|
||||
* by configuring the path in the query variable via the bean.
|
||||
* moreover this can also perform more compact extractions
|
||||
* by configuring specific json processor in "metadataProcessor"
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
public class SimpleJsonPathMetadataContributor implements MetadataContributor<String> {
|
||||
|
||||
private final static Logger log = LogManager.getLogger();
|
||||
|
||||
private String query;
|
||||
|
||||
private MetadataFieldConfig field;
|
||||
|
||||
protected JsonPathMetadataProcessor metadataProcessor;
|
||||
|
||||
/**
|
||||
* Initialize SimpleJsonPathMetadataContributor with a query, prefixToNamespaceMapping and MetadataFieldConfig
|
||||
*
|
||||
* @param query The JSonPath query
|
||||
* @param field the matadata field to map the result of the Json path query
|
||||
* <a href="https://github.com/DSpace/DSpace/tree/master/dspace-api/src/main/java/org/dspace/importer/external#metadata-mapping-">MetadataFieldConfig</a>
|
||||
*/
|
||||
public SimpleJsonPathMetadataContributor(String query, MetadataFieldConfig field) {
|
||||
this.query = query;
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unused by this implementation
|
||||
*/
|
||||
@Override
|
||||
public void setMetadataFieldMapping(MetadataFieldMapping<String, MetadataContributor<String>> rt) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty constructor for SimpleJsonPathMetadataContributor
|
||||
*/
|
||||
public SimpleJsonPathMetadataContributor() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the MetadataFieldConfig used while retrieving MetadatumDTO
|
||||
*
|
||||
* @return MetadataFieldConfig
|
||||
*/
|
||||
public MetadataFieldConfig getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting the MetadataFieldConfig
|
||||
*
|
||||
* @param field MetadataFieldConfig used while retrieving MetadatumDTO
|
||||
*/
|
||||
public void setField(MetadataFieldConfig field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return query used to create the JSonPath
|
||||
*
|
||||
* @return the query this instance is based on
|
||||
*/
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return query used to create the JSonPath
|
||||
*
|
||||
*/
|
||||
public void setQuery(String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to process data got by jsonpath expression, like arrays to stringify, change date format or else
|
||||
* If it is null, toString will be used.
|
||||
*
|
||||
* @param metadataProcessor
|
||||
*/
|
||||
public void setMetadataProcessor(JsonPathMetadataProcessor metadataProcessor) {
|
||||
this.metadataProcessor = metadataProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the metadata associated with the given object.
|
||||
* The toString() of the resulting object will be used.
|
||||
*
|
||||
* @param t A class to retrieve metadata from.
|
||||
* @return a collection of import records. Only the identifier of the found records may be put in the record.
|
||||
*/
|
||||
@Override
|
||||
public Collection<MetadatumDTO> contributeMetadata(String fullJson) {
|
||||
Collection<MetadatumDTO> metadata = new ArrayList<>();
|
||||
Collection<String> metadataValue = new ArrayList<>();
|
||||
if (Objects.nonNull(metadataProcessor)) {
|
||||
metadataValue = metadataProcessor.processMetadata(fullJson);
|
||||
} else {
|
||||
JsonNode jsonNode = convertStringJsonToJsonNode(fullJson);
|
||||
JsonNode node = jsonNode.at(query);
|
||||
if (node.isArray()) {
|
||||
Iterator<JsonNode> nodes = node.iterator();
|
||||
while (nodes.hasNext()) {
|
||||
String nodeValue = getStringValue(nodes.next());
|
||||
if (StringUtils.isNotBlank(nodeValue)) {
|
||||
metadataValue.add(nodeValue);
|
||||
}
|
||||
}
|
||||
} else if (!node.isNull() && StringUtils.isNotBlank(node.toString())) {
|
||||
String nodeValue = getStringValue(node);
|
||||
if (StringUtils.isNotBlank(nodeValue)) {
|
||||
metadataValue.add(nodeValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String value : metadataValue) {
|
||||
MetadatumDTO metadatumDto = new MetadatumDTO();
|
||||
metadatumDto.setValue(value);
|
||||
metadatumDto.setElement(field.getElement());
|
||||
metadatumDto.setQualifier(field.getQualifier());
|
||||
metadatumDto.setSchema(field.getSchema());
|
||||
metadata.add(metadatumDto);
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private String getStringValue(JsonNode node) {
|
||||
if (node.isTextual()) {
|
||||
return node.textValue();
|
||||
}
|
||||
if (node.isNumber()) {
|
||||
return node.numberValue().toString();
|
||||
}
|
||||
log.error("It wasn't possible to convert the value of the following JsonNode:" + node.asText());
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
private JsonNode convertStringJsonToJsonNode(String json) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode body = null;
|
||||
try {
|
||||
body = mapper.readTree(json);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Unable to process json response.", e);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
}
|
@@ -29,7 +29,6 @@ public class DoiCheck {
|
||||
"|10.1207/[\\w\\d]+\\&\\d+_\\d+",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
|
||||
private DoiCheck() {}
|
||||
|
||||
public static boolean isDoi(final String value) {
|
||||
|
@@ -0,0 +1,336 @@
|
||||
/**
|
||||
* 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.vufind;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
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.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 data source for querying VuFind
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.com)
|
||||
*/
|
||||
public class VuFindImportMetadataSourceServiceImpl extends AbstractImportMetadataSourceService<String>
|
||||
implements QuerySource {
|
||||
|
||||
private final static Logger log = LogManager.getLogger();
|
||||
|
||||
private String url;
|
||||
private String urlSearch;
|
||||
|
||||
private String fields;
|
||||
|
||||
@Autowired
|
||||
private LiveImportClient liveImportClient;
|
||||
|
||||
public VuFindImportMetadataSourceServiceImpl(String fields) {
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImportSource() {
|
||||
return "VuFind";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportRecord getRecord(String id) throws MetadataSourceException {
|
||||
String records = retry(new GetByVuFindIdCallable(id, fields));
|
||||
List<ImportRecord> importRecords = extractMetadataFromRecordList(records);
|
||||
return importRecords != null && !importRecords.isEmpty() ? importRecords.get(0) : null;
|
||||
}
|
||||
|
||||
@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 {
|
||||
String records = retry(new SearchByQueryCallable(query, count, start, fields));
|
||||
return extractMetadataFromRecordList(records);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> getRecords(Query query) throws MetadataSourceException {
|
||||
String records = retry(new SearchByQueryCallable(query, fields));
|
||||
return extractMetadataFromRecordList(records);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportRecord getRecord(Query query) throws MetadataSourceException {
|
||||
String records = retry(new SearchByQueryCallable(query, fields));
|
||||
List<ImportRecord> importRecords = extractMetadataFromRecordList(records);
|
||||
return importRecords != null && !importRecords.isEmpty() ? importRecords.get(0) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> findMatchingRecords(Query query) throws MetadataSourceException {
|
||||
String records = retry(new FindMatchingRecordsCallable(query));
|
||||
return extractMetadataFromRecordList(records);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> findMatchingRecords(Item item) throws MetadataSourceException {
|
||||
throw new MethodNotFoundException("This method is not implemented for VuFind");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws Exception {}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to count the number of entries for an VuFind query.
|
||||
* This Callable use as query value to CrossRef the string queryString passed to constructor.
|
||||
* If the object will be construct through Query.class instance, the value of the Query's
|
||||
* map with the key "query" will be used.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
private class CountByQueryCallable implements Callable<Integer> {
|
||||
|
||||
private Query query;
|
||||
|
||||
public CountByQueryCallable(String queryString) {
|
||||
query = new Query();
|
||||
query.addParameter("query", queryString);
|
||||
}
|
||||
|
||||
public CountByQueryCallable(Query query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
Integer start = 0;
|
||||
Integer count = 1;
|
||||
int page = start / count + 1;
|
||||
URIBuilder uriBuilder = new URIBuilder(urlSearch);
|
||||
uriBuilder.addParameter("type", "AllField");
|
||||
uriBuilder.addParameter("page", String.valueOf(page));
|
||||
uriBuilder.addParameter("limit", count.toString());
|
||||
uriBuilder.addParameter("prettyPrint", String.valueOf(true));
|
||||
uriBuilder.addParameter("lookfor", query.getParameterAsClass("query", String.class));
|
||||
String responseString = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(),
|
||||
new HashMap<String, String>());
|
||||
JsonNode node = convertStringJsonToJsonNode(responseString);
|
||||
JsonNode resultCountNode = node.get("resultCount");
|
||||
return resultCountNode.intValue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to get an VuFind entry using VuFind id
|
||||
* The id 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 GetByVuFindIdCallable implements Callable<String> {
|
||||
|
||||
private String id;
|
||||
|
||||
private String fields;
|
||||
|
||||
public GetByVuFindIdCallable(String id, String fields) {
|
||||
this.id = id;
|
||||
if (fields != null && fields.length() > 0) {
|
||||
this.fields = fields;
|
||||
} else {
|
||||
this.fields = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
URIBuilder uriBuilder = new URIBuilder(url);
|
||||
uriBuilder.addParameter("id", id);
|
||||
uriBuilder.addParameter("prettyPrint", "false");
|
||||
if (StringUtils.isNotBlank(fields)) {
|
||||
for (String field : fields.split(",")) {
|
||||
uriBuilder.addParameter("field[]", field);
|
||||
}
|
||||
}
|
||||
String response = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(),
|
||||
new HashMap<String, String>());
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to get VuFind 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<String> {
|
||||
|
||||
private Query query;
|
||||
|
||||
private String fields;
|
||||
|
||||
public SearchByQueryCallable(String queryString, Integer maxResult, Integer start, String fields) {
|
||||
query = new Query();
|
||||
query.addParameter("query", queryString);
|
||||
query.addParameter("count", maxResult);
|
||||
query.addParameter("start", start);
|
||||
if (StringUtils.isNotBlank(fields)) {
|
||||
this.fields = fields;
|
||||
} else {
|
||||
this.fields = null;
|
||||
}
|
||||
}
|
||||
|
||||
public SearchByQueryCallable(Query query, String fields) {
|
||||
this.query = query;
|
||||
if (StringUtils.isNotBlank(fields)) {
|
||||
this.fields = fields;
|
||||
} else {
|
||||
this.fields = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
Integer start = query.getParameterAsClass("start", Integer.class);
|
||||
Integer count = query.getParameterAsClass("count", Integer.class);
|
||||
int page = count != 0 ? start / count : 0;
|
||||
URIBuilder uriBuilder = new URIBuilder(urlSearch);
|
||||
uriBuilder.addParameter("type", "AllField");
|
||||
//page looks 1 based (start = 0, count = 20 -> page = 0)
|
||||
uriBuilder.addParameter("page", String.valueOf(page + 1));
|
||||
uriBuilder.addParameter("limit", count.toString());
|
||||
uriBuilder.addParameter("prettyPrint", String.valueOf(true));
|
||||
uriBuilder.addParameter("lookfor", query.getParameterAsClass("query", String.class));
|
||||
if (StringUtils.isNotBlank(fields)) {
|
||||
for (String field : fields.split(",")) {
|
||||
uriBuilder.addParameter("field[]", field);
|
||||
}
|
||||
}
|
||||
return liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), new HashMap<String, String>());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to search VuFind entries using author and title.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
public class FindMatchingRecordsCallable implements Callable<String> {
|
||||
|
||||
private Query query;
|
||||
|
||||
private String fields;
|
||||
|
||||
public FindMatchingRecordsCallable(Query query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
String author = query.getParameterAsClass("author", String.class);
|
||||
String title = query.getParameterAsClass("title", String.class);
|
||||
Integer start = query.getParameterAsClass("start", Integer.class);
|
||||
Integer count = query.getParameterAsClass("count", Integer.class);
|
||||
int page = count != 0 ? start / count : 0;
|
||||
URIBuilder uriBuilder = new URIBuilder(url);
|
||||
uriBuilder.addParameter("type", "AllField");
|
||||
//pagination is 1 based (first page: start = 0, count = 20 -> page = 0 -> +1 = 1)
|
||||
uriBuilder.addParameter("page", String.valueOf(page ++));
|
||||
uriBuilder.addParameter("limit", count.toString());
|
||||
uriBuilder.addParameter("prettyPrint", "true");
|
||||
if (fields != null && !fields.isEmpty()) {
|
||||
for (String field : fields.split(",")) {
|
||||
uriBuilder.addParameter("field[]", field);
|
||||
}
|
||||
}
|
||||
String filter = StringUtils.EMPTY;
|
||||
if (StringUtils.isNotBlank(author)) {
|
||||
filter = "author:" + author;
|
||||
}
|
||||
if (StringUtils.isNotBlank(title)) {
|
||||
if (StringUtils.isNotBlank(filter)) {
|
||||
filter = filter + " AND title:" + title;
|
||||
} else {
|
||||
filter = "title:" + title;
|
||||
}
|
||||
}
|
||||
uriBuilder.addParameter("lookfor", filter);
|
||||
return liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), new HashMap<String, String>());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private JsonNode convertStringJsonToJsonNode(String json) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode body = null;
|
||||
try {
|
||||
body = mapper.readTree(json);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Unable to process json response.", e);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
private List<ImportRecord> extractMetadataFromRecordList(String records) {
|
||||
List<ImportRecord> recordsResult = new ArrayList<>();
|
||||
JsonNode jsonNode = convertStringJsonToJsonNode(records);
|
||||
JsonNode node = jsonNode.get("records");
|
||||
if (Objects.nonNull(node) && node.isArray()) {
|
||||
Iterator<JsonNode> nodes = node.iterator();
|
||||
while (nodes.hasNext()) {
|
||||
recordsResult.add(transformSourceRecords(nodes.next().toString()));
|
||||
}
|
||||
}
|
||||
return recordsResult;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getUrlSearch() {
|
||||
return urlSearch;
|
||||
}
|
||||
|
||||
public void setUrlSearch(String urlSearch) {
|
||||
this.urlSearch = urlSearch;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.importer.external.vufind.metadatamapping;
|
||||
|
||||
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 VuFind metadatum fields on the DSpace metadatum fields
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class VuFindFieldMapping 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
|
||||
@SuppressWarnings("unchecked")
|
||||
@Resource(name = "vufindMetadataFieldMap")
|
||||
public void setMetadataFieldMap(Map metadataFieldMap) {
|
||||
super.setMetadataFieldMap(metadataFieldMap);
|
||||
}
|
||||
|
||||
}
|
@@ -115,6 +115,20 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="CrossRefImportService" class="org.dspace.importer.external.crossref.CrossRefImportMetadataSourceServiceImpl" scope="singleton">
|
||||
<property name="metadataFieldMapping" ref="CrossRefMetadataFieldMapping"/>
|
||||
<property name="url" value="${crossref.url}"/>
|
||||
</bean>
|
||||
<bean id="CrossRefMetadataFieldMapping" class="org.dspace.importer.external.crossref.CrossRefFieldMapping"/>
|
||||
|
||||
<bean id="vufindImportService" class="org.dspace.importer.external.vufind.VuFindImportMetadataSourceServiceImpl" scope="singleton">
|
||||
<!-- Set to empty to use the default set of fields -->
|
||||
<constructor-arg type="java.lang.String" value=""/>
|
||||
<property name="metadataFieldMapping" ref="vufindMetadataFieldMapping"/>
|
||||
<property name="url" value="${vufind.url}"/>
|
||||
<property name="urlSearch" value="${vufind.url.search}"/>
|
||||
</bean>
|
||||
<bean id="vufindMetadataFieldMapping" class="org.dspace.importer.external.vufind.metadatamapping.VuFindFieldMapping"/>
|
||||
|
||||
<bean id="ScieloImportService" class="org.dspace.importer.external.scielo.service.ScieloImportMetadataSourceServiceImpl" scope="singleton">
|
||||
<property name="metadataFieldMapping" ref="scieloMetadataFieldMapping"/>
|
||||
|
@@ -8,6 +8,7 @@
|
||||
package org.dspace.core;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.hasItems;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
@@ -511,9 +512,8 @@ public class ContextTest extends AbstractUnitTest {
|
||||
|
||||
// Now get our special groups
|
||||
List<Group> specialGroups = instance.getSpecialGroups();
|
||||
assertThat("testGetSpecialGroup 0", specialGroups.size(), equalTo(2));
|
||||
assertThat("testGetSpecialGroup 1", specialGroups.get(0), equalTo(group));
|
||||
assertThat("testGetSpecialGroup 1", specialGroups.get(1), equalTo(adminGroup));
|
||||
assertThat("testGetSpecialGroup size", specialGroups.size(), equalTo(2));
|
||||
assertThat("testGetSpecialGroup content", specialGroups, hasItems(group, adminGroup));
|
||||
|
||||
// Cleanup our context & group
|
||||
groupService.delete(instance, group);
|
||||
|
65
dspace-api/src/test/java/org/dspace/util/DoiCheckTest.java
Normal file
65
dspace-api/src/test/java/org/dspace/util/DoiCheckTest.java
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 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.util;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.dspace.importer.external.service.DoiCheck;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test class for the DoiCheck
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
public class DoiCheckTest {
|
||||
|
||||
@Test
|
||||
public void checkDOIsTest() throws ParseException {
|
||||
for (String doi : DOIsToTest()) {
|
||||
assertTrue("The: " + doi + " is a doi!", DoiCheck.isDoi(doi));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkWrongDOIsTest() throws ParseException {
|
||||
for (String key : wrongDOIsToTest()) {
|
||||
assertFalse("This : " + key + " isn't a doi!", DoiCheck.isDoi(key));
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> DOIsToTest() {
|
||||
return Arrays.asList(
|
||||
"10.1430/8105",
|
||||
"10.1038/nphys1170",
|
||||
"10.1002/0470841559.ch1",
|
||||
"10.1594/PANGAEA.726855",
|
||||
"10.1594/GFZ.GEOFON.gfz2009kciu",
|
||||
"10.3866/PKU.WHXB201112303",
|
||||
"10.11467/isss2003.7.1_11",
|
||||
"10.3972/water973.0145.db"
|
||||
);
|
||||
}
|
||||
|
||||
private List<String> wrongDOIsToTest() {
|
||||
return Arrays.asList(
|
||||
StringUtils.EMPTY,
|
||||
"123456789",
|
||||
"nphys1170/10.1038",
|
||||
"10.", "10",
|
||||
"10.1038/"
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@@ -7,8 +7,13 @@
|
||||
*/
|
||||
package org.dspace.app.rest;
|
||||
|
||||
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
|
||||
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@@ -19,9 +24,11 @@ import org.dspace.app.rest.model.AuthenticationStatusRest;
|
||||
import org.dspace.app.rest.model.AuthenticationTokenRest;
|
||||
import org.dspace.app.rest.model.AuthnRest;
|
||||
import org.dspace.app.rest.model.EPersonRest;
|
||||
import org.dspace.app.rest.model.GroupRest;
|
||||
import org.dspace.app.rest.model.hateoas.AuthenticationStatusResource;
|
||||
import org.dspace.app.rest.model.hateoas.AuthenticationTokenResource;
|
||||
import org.dspace.app.rest.model.hateoas.AuthnResource;
|
||||
import org.dspace.app.rest.model.hateoas.EmbeddedPage;
|
||||
import org.dspace.app.rest.model.wrapper.AuthenticationToken;
|
||||
import org.dspace.app.rest.projection.Projection;
|
||||
import org.dspace.app.rest.security.RestAuthenticationService;
|
||||
@@ -34,6 +41,10 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.web.PagedResourcesAssembler;
|
||||
import org.springframework.hateoas.EntityModel;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -109,6 +120,8 @@ public class AuthenticationRestController implements InitializingBean {
|
||||
if (context.getCurrentUser() != null) {
|
||||
ePersonRest = converter.toRest(context.getCurrentUser(), projection);
|
||||
}
|
||||
List<GroupRest> groupList = context.getSpecialGroups().stream()
|
||||
.map(g -> (GroupRest) converter.toRest(g, projection)).collect(Collectors.toList());
|
||||
|
||||
AuthenticationStatusRest authenticationStatusRest = new AuthenticationStatusRest(ePersonRest);
|
||||
// When not authenticated add WWW-Authenticate so client can retrieve all available authentication methods
|
||||
@@ -120,11 +133,41 @@ public class AuthenticationRestController implements InitializingBean {
|
||||
}
|
||||
authenticationStatusRest.setAuthenticationMethod(context.getAuthenticationMethod());
|
||||
authenticationStatusRest.setProjection(projection);
|
||||
authenticationStatusRest.setSpecialGroups(groupList);
|
||||
|
||||
AuthenticationStatusResource authenticationStatusResource = converter.toResource(authenticationStatusRest);
|
||||
|
||||
return authenticationStatusResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the current user's authentication status (i.e. whether they are authenticated or not) and,
|
||||
* if authenticated, retrieves the current context's special groups.
|
||||
* @param page
|
||||
* @param assembler
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
@RequestMapping(value = "/status/specialGroups", method = RequestMethod.GET)
|
||||
public EntityModel retrieveSpecialGroups(Pageable page, PagedResourcesAssembler assembler,
|
||||
HttpServletRequest request, HttpServletResponse response)
|
||||
throws SQLException {
|
||||
Context context = ContextUtil.obtainContext(request);
|
||||
Projection projection = utils.obtainProjection();
|
||||
|
||||
List<GroupRest> groupList = context.getSpecialGroups().stream()
|
||||
.map(g -> (GroupRest) converter.toRest(g, projection)).collect(Collectors.toList());
|
||||
Page<GroupRest> groupPage = (Page<GroupRest>) utils.getPage(groupList, page);
|
||||
Link link = linkTo(
|
||||
methodOn(AuthenticationRestController.class).retrieveSpecialGroups(page, assembler, request, response))
|
||||
.withSelfRel();
|
||||
|
||||
return EntityModel.of(new EmbeddedPage(link.getHref(),
|
||||
groupPage.map(converter::toResource), null, "specialGroups"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the login has succeeded or not. The actual login is performed by one of the enabled login filters
|
||||
* (e.g. {@link org.dspace.app.rest.security.StatelessLoginFilter}).
|
||||
|
@@ -23,6 +23,7 @@ import javax.xml.transform.stream.StreamResult;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.rest.utils.ContextUtil;
|
||||
import org.dspace.app.rest.utils.ScopeResolver;
|
||||
import org.dspace.app.util.SyndicationFeed;
|
||||
import org.dspace.app.util.factory.UtilServiceFactory;
|
||||
import org.dspace.app.util.service.OpenSearchService;
|
||||
@@ -35,12 +36,17 @@ import org.dspace.core.Context;
|
||||
import org.dspace.core.LogHelper;
|
||||
import org.dspace.core.Utils;
|
||||
import org.dspace.discovery.DiscoverQuery;
|
||||
import org.dspace.discovery.DiscoverQuery.SORT_ORDER;
|
||||
import org.dspace.discovery.DiscoverResult;
|
||||
import org.dspace.discovery.IndexableObject;
|
||||
import org.dspace.discovery.SearchService;
|
||||
import org.dspace.discovery.SearchServiceException;
|
||||
import org.dspace.discovery.SearchUtils;
|
||||
import org.dspace.discovery.configuration.DiscoveryConfiguration;
|
||||
import org.dspace.discovery.configuration.DiscoveryConfigurationService;
|
||||
import org.dspace.discovery.configuration.DiscoverySearchFilter;
|
||||
import org.dspace.discovery.indexobject.IndexableItem;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -67,8 +73,17 @@ public class OpenSearchController {
|
||||
private AuthorizeService authorizeService;
|
||||
private OpenSearchService openSearchService;
|
||||
|
||||
@Autowired
|
||||
private SearchService searchService;
|
||||
|
||||
@Autowired
|
||||
private DiscoveryConfigurationService searchConfigurationService;
|
||||
|
||||
private Context context;
|
||||
|
||||
@Autowired
|
||||
private ScopeResolver scopeResolver;
|
||||
|
||||
/**
|
||||
* This method provides the OpenSearch query on the path /search
|
||||
* It will pass the result as a OpenSearchDocument directly to the client
|
||||
@@ -80,6 +95,9 @@ public class OpenSearchController {
|
||||
@RequestParam(name = "start", required = false) Integer start,
|
||||
@RequestParam(name = "rpp", required = false) Integer count,
|
||||
@RequestParam(name = "format", required = false) String format,
|
||||
@RequestParam(name = "sort", required = false) String sort,
|
||||
@RequestParam(name = "sort_direction", required = false) String sortDirection,
|
||||
@RequestParam(name = "scope", required = false) String dsoObject,
|
||||
Model model) throws IOException, ServletException {
|
||||
context = ContextUtil.obtainContext(request);
|
||||
if (start == null) {
|
||||
@@ -115,9 +133,34 @@ public class OpenSearchController {
|
||||
|
||||
// support pagination parameters
|
||||
DiscoverQuery queryArgs = new DiscoverQuery();
|
||||
queryArgs.setQuery(query);
|
||||
if (query == null) {
|
||||
query = "";
|
||||
} else {
|
||||
queryArgs.setQuery(query);
|
||||
}
|
||||
queryArgs.setStart(start);
|
||||
queryArgs.setMaxResults(count);
|
||||
queryArgs.setDSpaceObjectFilter(IndexableItem.TYPE);
|
||||
if (sort != null) {
|
||||
//this is the default sort so we want to switch this to date accessioned
|
||||
if (sortDirection != null && sortDirection.equals("DESC")) {
|
||||
queryArgs.setSortField(sort + "_sort", SORT_ORDER.desc);
|
||||
} else {
|
||||
queryArgs.setSortField(sort + "_sort", SORT_ORDER.asc);
|
||||
}
|
||||
} else {
|
||||
queryArgs.setSortField("dc.date.accessioned_dt", SORT_ORDER.desc);
|
||||
}
|
||||
if (dsoObject != null) {
|
||||
container = scopeResolver.resolveScope(context, dsoObject);
|
||||
DiscoveryConfiguration discoveryConfiguration = searchConfigurationService
|
||||
.getDiscoveryConfigurationByNameOrDso("site", container);
|
||||
queryArgs.setDiscoveryConfigurationName(discoveryConfiguration.getId());
|
||||
queryArgs.addFilterQueries(discoveryConfiguration.getDefaultFilterQueries()
|
||||
.toArray(
|
||||
new String[discoveryConfiguration.getDefaultFilterQueries()
|
||||
.size()]));
|
||||
}
|
||||
|
||||
// Perform the search
|
||||
DiscoverResult qResults = null;
|
||||
|
@@ -7,9 +7,12 @@
|
||||
*/
|
||||
package org.dspace.app.rest.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.dspace.app.rest.RestResourceController;
|
||||
|
||||
|
||||
/**
|
||||
* Find out your authentication status.
|
||||
*/
|
||||
@@ -18,7 +21,11 @@ public class AuthenticationStatusRest extends BaseObjectRest<Integer> {
|
||||
private boolean authenticated;
|
||||
private String authenticationMethod;
|
||||
|
||||
private EPersonRest ePersonRest;
|
||||
private List<GroupRest> specialGroups;
|
||||
|
||||
public static final String NAME = "status";
|
||||
public static final String SPECIALGROUPS = "specialGroups";
|
||||
public static final String CATEGORY = RestAddressableModel.AUTHENTICATION;
|
||||
|
||||
@Override
|
||||
@@ -41,9 +48,6 @@ public class AuthenticationStatusRest extends BaseObjectRest<Integer> {
|
||||
return RestResourceController.class;
|
||||
}
|
||||
|
||||
|
||||
private EPersonRest ePersonRest;
|
||||
|
||||
public AuthenticationStatusRest() {
|
||||
setOkay(true);
|
||||
setAuthenticated(false);
|
||||
@@ -90,4 +94,14 @@ public class AuthenticationStatusRest extends BaseObjectRest<Integer> {
|
||||
public void setAuthenticationMethod(final String authenticationMethod) {
|
||||
this.authenticationMethod = authenticationMethod;
|
||||
}
|
||||
|
||||
public void setSpecialGroups(List<GroupRest> groupList) {
|
||||
this.specialGroups = groupList;
|
||||
}
|
||||
|
||||
@LinkRest(name = "specialGroups")
|
||||
@JsonIgnore
|
||||
public List<GroupRest> getSpecialGroups() {
|
||||
return specialGroups;
|
||||
}
|
||||
}
|
||||
|
@@ -68,6 +68,11 @@ public class GroupRestPermissionEvaluatorPlugin extends RestObjectPermissionEval
|
||||
|
||||
Group group = groupService.find(context, dsoId);
|
||||
|
||||
// if the group is one of the special groups of the context it is readable
|
||||
if (context.getSpecialGroups().contains(group)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// anonymous user
|
||||
if (ePerson == null) {
|
||||
return false;
|
||||
|
@@ -7,12 +7,16 @@
|
||||
*/
|
||||
package org.dspace.app.rest;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Collection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.codec.binary.StringUtils;
|
||||
import org.apache.http.ProtocolVersion;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
@@ -27,14 +31,30 @@ import org.dspace.importer.external.metadatamapping.MetadatumDTO;
|
||||
*/
|
||||
public class AbstractLiveImportIntegrationTest extends AbstractControllerIntegrationTest {
|
||||
|
||||
protected boolean matchRecords(Collection<ImportRecord> recordsImported, Collection<ImportRecord> records2match) {
|
||||
ImportRecord firstImported = recordsImported.iterator().next();
|
||||
ImportRecord secondImported = recordsImported.iterator().next();
|
||||
ImportRecord first2match = recordsImported.iterator().next();
|
||||
ImportRecord second2match = recordsImported.iterator().next();
|
||||
boolean checkFirstRecord = firstImported.getValueList().containsAll(first2match.getValueList());
|
||||
boolean checkSecondRecord = secondImported.getValueList().containsAll(second2match.getValueList());
|
||||
return checkFirstRecord && checkSecondRecord;
|
||||
protected void matchRecords(ArrayList<ImportRecord> recordsImported, ArrayList<ImportRecord> records2match) {
|
||||
assertEquals(records2match.size(), recordsImported.size());
|
||||
for (int i = 0; i < recordsImported.size(); i++) {
|
||||
ImportRecord firstImported = recordsImported.get(i);
|
||||
ImportRecord first2match = records2match.get(i);
|
||||
checkMetadataValue(firstImported.getValueList(), first2match.getValueList());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkMetadataValue(List<MetadatumDTO> list, List<MetadatumDTO> list2) {
|
||||
assertEquals(list.size(), list2.size());
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
assertTrue(sameMetadatum(list.get(i), list2.get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean sameMetadatum(MetadatumDTO metadatum, MetadatumDTO metadatum2) {
|
||||
if (StringUtils.equals(metadatum.getSchema(), metadatum2.getSchema()) &&
|
||||
StringUtils.equals(metadatum.getElement(), metadatum2.getElement()) &&
|
||||
StringUtils.equals(metadatum.getQualifier(), metadatum2.getQualifier()) &&
|
||||
StringUtils.equals(metadatum.getValue(), metadatum2.getValue())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected MetadatumDTO createMetadatumDTO(String schema, String element, String qualifier, String value) {
|
||||
|
@@ -48,6 +48,7 @@ import org.dspace.app.rest.converter.EPersonConverter;
|
||||
import org.dspace.app.rest.matcher.AuthenticationStatusMatcher;
|
||||
import org.dspace.app.rest.matcher.AuthorizationMatcher;
|
||||
import org.dspace.app.rest.matcher.EPersonMatcher;
|
||||
import org.dspace.app.rest.matcher.GroupMatcher;
|
||||
import org.dspace.app.rest.matcher.HalMatcher;
|
||||
import org.dspace.app.rest.model.EPersonRest;
|
||||
import org.dspace.app.rest.projection.DefaultProjection;
|
||||
@@ -106,6 +107,10 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
|
||||
"org.dspace.authenticate.IPAuthentication",
|
||||
"org.dspace.authenticate.ShibAuthentication"
|
||||
};
|
||||
public static final String[] PASS_AND_IP = {
|
||||
"org.dspace.authenticate.PasswordAuthentication",
|
||||
"org.dspace.authenticate.IPAuthentication"
|
||||
};
|
||||
|
||||
// see proxies.trusted.ipranges in local.cfg
|
||||
public static final String TRUSTED_IP = "7.7.7.7";
|
||||
@@ -160,6 +165,101 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
|
||||
.andExpect(status().isNoContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* This test verifies:
|
||||
* - that a logged in via password user finds the expected specialGroupPwd in _embedded.specialGroups;
|
||||
* - that a logged in via password and specific IP user finds the expected specialGroupPwd and specialGroupIP
|
||||
* in _embedded.specialGroups;
|
||||
* - that a not logged in user with a specific IP finds the expected specialGroupIP in _embedded.specialGroups;
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testStatusGetSpecialGroups() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
Group specialGroupPwd = GroupBuilder.createGroup(context)
|
||||
.withName("specialGroupPwd")
|
||||
.build();
|
||||
Group specialGroupIP = GroupBuilder.createGroup(context)
|
||||
.withName("specialGroupIP")
|
||||
.build();
|
||||
|
||||
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", PASS_AND_IP);
|
||||
configurationService.setProperty("authentication-password.login.specialgroup","specialGroupPwd");
|
||||
configurationService.setProperty("authentication-ip.specialGroupIP", "123.123.123.123");
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String token = getAuthToken(eperson.getEmail(), password);
|
||||
|
||||
getClient(token).perform(get("/api/authn/status").param("projection", "full"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", AuthenticationStatusMatcher.matchFullEmbeds()))
|
||||
.andExpect(jsonPath("$", AuthenticationStatusMatcher.matchLinks()))
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$.okay", is(true)))
|
||||
.andExpect(jsonPath("$.authenticated", is(true)))
|
||||
.andExpect(jsonPath("$.authenticationMethod", is("password")))
|
||||
.andExpect(jsonPath("$.type", is("status")))
|
||||
.andExpect(jsonPath("$._links.specialGroups.href", startsWith(REST_SERVER_URL)))
|
||||
.andExpect(jsonPath("$._embedded.specialGroups._embedded.specialGroups",
|
||||
Matchers.containsInAnyOrder(
|
||||
GroupMatcher.matchGroupWithName("specialGroupPwd"))));
|
||||
|
||||
// try the special groups link endpoint in the same scenario than above
|
||||
getClient(token).perform(get("/api/authn/status/specialGroups").param("projection", "full"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.specialGroups",
|
||||
Matchers.containsInAnyOrder(
|
||||
GroupMatcher.matchGroupWithName("specialGroupPwd"))));
|
||||
|
||||
getClient(token).perform(get("/api/authn/status").param("projection", "full")
|
||||
.with(ip("123.123.123.123")))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", AuthenticationStatusMatcher.matchFullEmbeds()))
|
||||
.andExpect(jsonPath("$", AuthenticationStatusMatcher.matchLinks()))
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$.okay", is(true)))
|
||||
.andExpect(jsonPath("$.authenticated", is(true)))
|
||||
.andExpect(jsonPath("$.authenticationMethod", is("password")))
|
||||
.andExpect(jsonPath("$.type", is("status")))
|
||||
.andExpect(jsonPath("$._links.specialGroups.href", startsWith(REST_SERVER_URL)))
|
||||
.andExpect(jsonPath("$._embedded.specialGroups._embedded.specialGroups",
|
||||
Matchers.containsInAnyOrder(
|
||||
GroupMatcher.matchGroupWithName("specialGroupPwd"),
|
||||
GroupMatcher.matchGroupWithName("specialGroupIP"))));
|
||||
|
||||
// try the special groups link endpoint in the same scenario than above
|
||||
getClient(token).perform(get("/api/authn/status/specialGroups").param("projection", "full")
|
||||
.with(ip("123.123.123.123")))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.specialGroups",
|
||||
Matchers.containsInAnyOrder(
|
||||
GroupMatcher.matchGroupWithName("specialGroupPwd"),
|
||||
GroupMatcher.matchGroupWithName("specialGroupIP"))));
|
||||
|
||||
getClient().perform(get("/api/authn/status").param("projection", "full").with(ip("123.123.123.123")))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", AuthenticationStatusMatcher.matchFullEmbeds()))
|
||||
// fails due to bug https://github.com/DSpace/DSpace/issues/8274
|
||||
//.andExpect(jsonPath("$", AuthenticationStatusMatcher.matchLinks()))
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$.okay", is(true)))
|
||||
.andExpect(jsonPath("$.authenticated", is(false)))
|
||||
.andExpect(jsonPath("$._embedded.specialGroups._embedded.specialGroups",
|
||||
Matchers.containsInAnyOrder(GroupMatcher.matchGroupWithName("specialGroupIP"))));
|
||||
|
||||
// try the special groups link endpoint in the same scenario than above
|
||||
getClient().perform(get("/api/authn/status/specialGroups").param("projection", "full")
|
||||
.with(ip("123.123.123.123")))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.specialGroups",
|
||||
Matchers.containsInAnyOrder(
|
||||
GroupMatcher.matchGroupWithName("specialGroupIP"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
// Ignored until an endpoint is added to return all groups. Anonymous is not considered a direct group.
|
||||
|
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* 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.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import javax.el.MethodNotFoundException;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.dspace.builder.CollectionBuilder;
|
||||
import org.dspace.builder.CommunityBuilder;
|
||||
import org.dspace.builder.ItemBuilder;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.importer.external.crossref.CrossRefImportMetadataSourceServiceImpl;
|
||||
import org.dspace.importer.external.datamodel.ImportRecord;
|
||||
import org.dspace.importer.external.liveimportclient.service.LiveImportClientImpl;
|
||||
import org.dspace.importer.external.metadatamapping.MetadatumDTO;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link CrossRefImportMetadataSourceServiceImpl}
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.com)
|
||||
*/
|
||||
public class CrossRefImportMetadataSourceServiceIT extends AbstractLiveImportIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private LiveImportClientImpl liveImportClientImpl;
|
||||
|
||||
@Autowired
|
||||
private CrossRefImportMetadataSourceServiceImpl crossRefServiceImpl;
|
||||
|
||||
@Test
|
||||
public void crossRefImportMetadataGetRecordsTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
CloseableHttpClient originalHttpClient = liveImportClientImpl.getHttpClient();
|
||||
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
|
||||
try (InputStream crossRefResp = getClass().getResourceAsStream("crossRef-test.json")) {
|
||||
|
||||
String crossRefRespXmlResp = IOUtils.toString(crossRefResp, Charset.defaultCharset());
|
||||
|
||||
liveImportClientImpl.setHttpClient(httpClient);
|
||||
CloseableHttpResponse response = mockResponse(crossRefRespXmlResp, 200, "OK");
|
||||
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
ArrayList<ImportRecord> collection2match = getRecords();
|
||||
Collection<ImportRecord> recordsImported = crossRefServiceImpl.getRecords("test query", 0, 2);
|
||||
assertEquals(2, recordsImported.size());
|
||||
matchRecords(new ArrayList<ImportRecord>(recordsImported), collection2match);
|
||||
} finally {
|
||||
liveImportClientImpl.setHttpClient(originalHttpClient);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void crossRefImportMetadataGetRecordsCountTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
CloseableHttpClient originalHttpClient = liveImportClientImpl.getHttpClient();
|
||||
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
|
||||
try (InputStream crossRefResp = getClass().getResourceAsStream("crossRef-test.json")) {
|
||||
String crossRefRespXmlResp = IOUtils.toString(crossRefResp, Charset.defaultCharset());
|
||||
|
||||
liveImportClientImpl.setHttpClient(httpClient);
|
||||
CloseableHttpResponse response = mockResponse(crossRefRespXmlResp, 200, "OK");
|
||||
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
int tot = crossRefServiceImpl.getRecordsCount("test query");
|
||||
assertEquals(10, tot);
|
||||
} finally {
|
||||
liveImportClientImpl.setHttpClient(originalHttpClient);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void crossRefImportMetadataGetRecordByIdTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
CloseableHttpClient originalHttpClient = liveImportClientImpl.getHttpClient();
|
||||
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
|
||||
|
||||
try (InputStream crossRefResp = getClass().getResourceAsStream("crossRef-by-id.json")) {
|
||||
|
||||
String crossRefRespXmlResp = IOUtils.toString(crossRefResp, Charset.defaultCharset());
|
||||
|
||||
liveImportClientImpl.setHttpClient(httpClient);
|
||||
CloseableHttpResponse response = mockResponse(crossRefRespXmlResp, 200, "OK");
|
||||
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
ArrayList<ImportRecord> collection2match = getRecords();
|
||||
collection2match.remove(1);
|
||||
ImportRecord recordImported = crossRefServiceImpl.getRecord("10.26693/jmbs01.02.184");
|
||||
assertNotNull(recordImported);
|
||||
Collection<ImportRecord> recordsImported = Arrays.asList(recordImported);
|
||||
matchRecords(new ArrayList<ImportRecord>(recordsImported), collection2match);
|
||||
} finally {
|
||||
liveImportClientImpl.setHttpClient(originalHttpClient);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = MethodNotFoundException.class)
|
||||
public void crossRefImportMetadataFindMatchingRecordsTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
|
||||
org.dspace.content.Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
|
||||
.withName("Collection 1")
|
||||
.build();
|
||||
|
||||
Item testItem = ItemBuilder.createItem(context, col1)
|
||||
.withTitle("test item")
|
||||
.withIssueDate("2021")
|
||||
.build();
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
crossRefServiceImpl.findMatchingRecords(testItem);
|
||||
}
|
||||
|
||||
private ArrayList<ImportRecord> getRecords() {
|
||||
ArrayList<ImportRecord> records = new ArrayList<>();
|
||||
//define first record
|
||||
List<MetadatumDTO> metadatums = new ArrayList<MetadatumDTO>();
|
||||
MetadatumDTO title = createMetadatumDTO("dc", "title", null,
|
||||
"State of Awareness of Freshers’ Groups Chortkiv State"
|
||||
+ " Medical College of Prevention of Iodine Deficiency Diseases");
|
||||
MetadatumDTO author = createMetadatumDTO("dc", "contributor", "author", "L.V. Senyuk");
|
||||
MetadatumDTO type = createMetadatumDTO("dc", "type", null, "journal-article");
|
||||
MetadatumDTO date = createMetadatumDTO("dc", "date", "issued", "2016");
|
||||
MetadatumDTO ispartof = createMetadatumDTO("dc", "relation", "ispartof",
|
||||
"Ukraïnsʹkij žurnal medicini, bìologìï ta sportu");
|
||||
MetadatumDTO doi = createMetadatumDTO("dc", "identifier", "doi", "10.26693/jmbs01.02.184");
|
||||
MetadatumDTO issn = createMetadatumDTO("dc", "identifier", "issn", "2415-3060");
|
||||
MetadatumDTO volume = createMetadatumDTO("oaire", "citation", "volume", "1");
|
||||
MetadatumDTO issue = createMetadatumDTO("oaire", "citation", "issue", "2");
|
||||
|
||||
metadatums.add(title);
|
||||
metadatums.add(author);
|
||||
metadatums.add(date);
|
||||
metadatums.add(type);
|
||||
metadatums.add(ispartof);
|
||||
metadatums.add(doi);
|
||||
metadatums.add(issn);
|
||||
metadatums.add(volume);
|
||||
metadatums.add(issue);
|
||||
|
||||
ImportRecord firstrRecord = new ImportRecord(metadatums);
|
||||
|
||||
//define second record
|
||||
List<MetadatumDTO> metadatums2 = new ArrayList<MetadatumDTO>();
|
||||
MetadatumDTO title2 = createMetadatumDTO("dc", "title", null,
|
||||
"Ischemic Heart Disease and Role of Nurse of Cardiology Department");
|
||||
MetadatumDTO author2 = createMetadatumDTO("dc", "contributor", "author", "K. І. Kozak");
|
||||
MetadatumDTO type2 = createMetadatumDTO("dc", "type", null, "journal-article");
|
||||
MetadatumDTO date2 = createMetadatumDTO("dc", "date", "issued", "2016");
|
||||
MetadatumDTO ispartof2 = createMetadatumDTO("dc", "relation", "ispartof",
|
||||
"Ukraïnsʹkij žurnal medicini, bìologìï ta sportu");
|
||||
MetadatumDTO doi2 = createMetadatumDTO("dc", "identifier", "doi", "10.26693/jmbs01.02.105");
|
||||
MetadatumDTO issn2 = createMetadatumDTO("dc", "identifier", "issn", "2415-3060");
|
||||
MetadatumDTO volume2 = createMetadatumDTO("oaire", "citation", "volume", "1");
|
||||
MetadatumDTO issue2 = createMetadatumDTO("oaire", "citation", "issue", "2");
|
||||
|
||||
metadatums2.add(title2);
|
||||
metadatums2.add(author2);
|
||||
metadatums2.add(date2);
|
||||
metadatums2.add(type2);
|
||||
metadatums2.add(ispartof2);
|
||||
metadatums2.add(doi2);
|
||||
metadatums2.add(issn2);
|
||||
metadatums2.add(volume2);
|
||||
metadatums2.add(issue2);
|
||||
|
||||
ImportRecord secondRecord = new ImportRecord(metadatums2);
|
||||
records.add(firstrRecord);
|
||||
records.add(secondRecord);
|
||||
return records;
|
||||
}
|
||||
|
||||
}
|
@@ -9,7 +9,6 @@ package org.dspace.app.rest;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.InputStream;
|
||||
@@ -17,7 +16,6 @@ import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import javax.el.MethodNotFoundException;
|
||||
|
||||
@@ -65,10 +63,10 @@ public class ScieloImportMetadataSourceServiceIT extends AbstractLiveImportInteg
|
||||
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
Collection<ImportRecord> collection2match = getRecords();
|
||||
ArrayList<ImportRecord> collection2match = getRecords();
|
||||
Collection<ImportRecord> recordsImported = scieloServiceImpl.getRecords("test query", 0, 2);
|
||||
assertEquals(2, recordsImported.size());
|
||||
assertTrue(matchRecords(recordsImported, collection2match));
|
||||
matchRecords(new ArrayList<ImportRecord>(recordsImported), collection2match);
|
||||
} finally {
|
||||
liveImportClientImpl.setHttpClient(originalHttpClient);
|
||||
}
|
||||
@@ -134,19 +132,19 @@ public class ScieloImportMetadataSourceServiceIT extends AbstractLiveImportInteg
|
||||
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
Collection<ImportRecord> collection2match = getRecords();
|
||||
Collection<ImportRecord> firstRecord = Arrays.asList(collection2match.iterator().next());
|
||||
ArrayList<ImportRecord> collection2match = getRecords();
|
||||
collection2match.remove(1);
|
||||
ImportRecord record = scieloServiceImpl.getRecord("S0185-30582021000200231-mex");
|
||||
assertNotNull(record);
|
||||
Collection<ImportRecord> recordsImported = Arrays.asList(record);
|
||||
assertTrue(matchRecords(recordsImported, firstRecord));
|
||||
matchRecords(new ArrayList<ImportRecord>(recordsImported), collection2match);
|
||||
} finally {
|
||||
liveImportClientImpl.setHttpClient(originalHttpClient);
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<ImportRecord> getRecords() {
|
||||
Collection<ImportRecord> records = new LinkedList<ImportRecord>();
|
||||
private ArrayList<ImportRecord> getRecords() {
|
||||
ArrayList<ImportRecord> records = new ArrayList<>();
|
||||
//define first record
|
||||
List<MetadatumDTO> metadatums = new ArrayList<MetadatumDTO>();
|
||||
MetadatumDTO ispartof = createMetadatumDTO("dc", "relation", "ispartof", "Nova tellus");
|
||||
|
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* 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.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import javax.el.MethodNotFoundException;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.dspace.builder.CollectionBuilder;
|
||||
import org.dspace.builder.CommunityBuilder;
|
||||
import org.dspace.builder.ItemBuilder;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.importer.external.datamodel.ImportRecord;
|
||||
import org.dspace.importer.external.liveimportclient.service.LiveImportClientImpl;
|
||||
import org.dspace.importer.external.metadatamapping.MetadatumDTO;
|
||||
import org.dspace.importer.external.vufind.VuFindImportMetadataSourceServiceImpl;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VuFindImportMetadataSourceServiceImpl}
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.com)
|
||||
*/
|
||||
public class VuFindImportMetadataSourceServiceIT extends AbstractLiveImportIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private LiveImportClientImpl liveImportClientImpl;
|
||||
|
||||
@Autowired
|
||||
private VuFindImportMetadataSourceServiceImpl vuFindService;
|
||||
|
||||
@Test
|
||||
public void vuFindImportMetadataGetRecordsTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
CloseableHttpClient originalHttpClient = liveImportClientImpl.getHttpClient();
|
||||
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
|
||||
|
||||
try (InputStream vuFindRespIS = getClass().getResourceAsStream("vuFind-generic.json")) {
|
||||
|
||||
String vuFindResp = IOUtils.toString(vuFindRespIS, Charset.defaultCharset());
|
||||
|
||||
liveImportClientImpl.setHttpClient(httpClient);
|
||||
CloseableHttpResponse response = mockResponse(vuFindResp, 200, "OK");
|
||||
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
ArrayList<ImportRecord> collection2match = getRecords();
|
||||
Collection<ImportRecord> recordsImported = vuFindService.getRecords("test query", 0, 2);
|
||||
assertEquals(2, recordsImported.size());
|
||||
matchRecords(new ArrayList<>(recordsImported), collection2match);
|
||||
} finally {
|
||||
liveImportClientImpl.setHttpClient(originalHttpClient);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vuFindImportMetadataGetRecordsCountTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
CloseableHttpClient originalHttpClient = liveImportClientImpl.getHttpClient();
|
||||
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
|
||||
|
||||
try (InputStream vuFindRespIS = getClass().getResourceAsStream("vuFind-generic.json")) {
|
||||
String vuFindResp = IOUtils.toString(vuFindRespIS, Charset.defaultCharset());
|
||||
|
||||
liveImportClientImpl.setHttpClient(httpClient);
|
||||
CloseableHttpResponse response = mockResponse(vuFindResp, 200, "OK");
|
||||
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
int tot = vuFindService.getRecordsCount("test query");
|
||||
assertEquals(1994, tot);
|
||||
} finally {
|
||||
liveImportClientImpl.setHttpClient(originalHttpClient);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vuFindImportMetadataGetRecordByIdTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
CloseableHttpClient originalHttpClient = liveImportClientImpl.getHttpClient();
|
||||
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
|
||||
|
||||
try (InputStream vuFindByIdResp = getClass().getResourceAsStream("vuFind-by-id.json")) {
|
||||
|
||||
String vuFindResp = IOUtils.toString(vuFindByIdResp, Charset.defaultCharset());
|
||||
|
||||
liveImportClientImpl.setHttpClient(httpClient);
|
||||
CloseableHttpResponse response = mockResponse(vuFindResp, 200, "OK");
|
||||
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
ArrayList<ImportRecord> collection2match = getRecords();
|
||||
collection2match.remove(1);
|
||||
ImportRecord recordImported = vuFindService.getRecord("653510");
|
||||
assertNotNull(recordImported);
|
||||
Collection<ImportRecord> recordsImported = Arrays.asList(recordImported);
|
||||
matchRecords(new ArrayList<>(recordsImported), collection2match);
|
||||
} finally {
|
||||
liveImportClientImpl.setHttpClient(originalHttpClient);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = MethodNotFoundException.class)
|
||||
public void vuFindImportMetadataFindMatchingRecordsTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
|
||||
org.dspace.content.Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
|
||||
.withName("Collection 1")
|
||||
.build();
|
||||
|
||||
Item testItem = ItemBuilder.createItem(context, col1)
|
||||
.withTitle("test item")
|
||||
.withIssueDate("2021")
|
||||
.build();
|
||||
context.restoreAuthSystemState();
|
||||
vuFindService.findMatchingRecords(testItem);
|
||||
}
|
||||
|
||||
private ArrayList<ImportRecord> getRecords() {
|
||||
ArrayList<ImportRecord> records = new ArrayList<>();
|
||||
//define first record
|
||||
List<MetadatumDTO> metadatums = new ArrayList<MetadatumDTO>();
|
||||
MetadatumDTO identifierOther = createMetadatumDTO("dc", "identifier", "other", "653510");
|
||||
MetadatumDTO language = createMetadatumDTO("dc", "language", "iso", "Italian");
|
||||
MetadatumDTO title = createMetadatumDTO("dc", "title", null,
|
||||
"La pianta marmorea di Roma antica: Forma urbis Romae /");
|
||||
MetadatumDTO subject = createMetadatumDTO("dc", "subject", null, "Rome (Italy)");
|
||||
MetadatumDTO subject2 = createMetadatumDTO("dc", "subject", null, "Maps");
|
||||
MetadatumDTO subject3 = createMetadatumDTO("dc", "subject", null, "Early works to 1800.");
|
||||
MetadatumDTO subject4 = createMetadatumDTO("dc", "subject", null, "Rome (Italy)");
|
||||
MetadatumDTO subject5 = createMetadatumDTO("dc", "subject", null, "Antiquities");
|
||||
MetadatumDTO subject6 = createMetadatumDTO("dc", "subject", null, "Maps.");
|
||||
MetadatumDTO identifier = createMetadatumDTO("dc", "identifier", null,
|
||||
"http://hdl.handle.net/20.500.12390/231");
|
||||
metadatums.add(identifierOther);
|
||||
metadatums.add(language);
|
||||
metadatums.add(title);
|
||||
metadatums.add(identifier);
|
||||
metadatums.add(subject);
|
||||
metadatums.add(subject2);
|
||||
metadatums.add(subject3);
|
||||
metadatums.add(subject4);
|
||||
metadatums.add(subject5);
|
||||
metadatums.add(subject6);
|
||||
|
||||
ImportRecord firstrRecord = new ImportRecord(metadatums);
|
||||
|
||||
//define second record
|
||||
List<MetadatumDTO> metadatums2 = new ArrayList<MetadatumDTO>();
|
||||
MetadatumDTO identifierOther2 = createMetadatumDTO("dc", "identifier", "other", "1665326");
|
||||
MetadatumDTO language2 = createMetadatumDTO("dc", "language", "iso", "English");
|
||||
MetadatumDTO title2 = createMetadatumDTO("dc", "title", null,
|
||||
"Expert frames : scientific and policy practices of Roma classification /");
|
||||
MetadatumDTO subject7 = createMetadatumDTO("dc", "subject", null, "Public opinion");
|
||||
MetadatumDTO subject8 = createMetadatumDTO("dc", "subject", null, "Europe.");
|
||||
MetadatumDTO subject9 = createMetadatumDTO("dc", "subject", null, "Stereotypes (Social psychology)");
|
||||
MetadatumDTO subject10 = createMetadatumDTO("dc", "subject", null, "Romanies");
|
||||
MetadatumDTO subject11 = createMetadatumDTO("dc", "subject", null, "Public opinion.");
|
||||
MetadatumDTO identifier2 = createMetadatumDTO("dc", "identifier", null,
|
||||
"http://ezproxy.villanova.edu/login?URL=http://www.jstor.org/stable/10.7829/j.ctt1ggjj08");
|
||||
metadatums2.add(identifierOther2);
|
||||
metadatums2.add(language2);
|
||||
metadatums2.add(title2);
|
||||
metadatums2.add(identifier2);
|
||||
metadatums2.add(subject7);
|
||||
metadatums2.add(subject8);
|
||||
metadatums2.add(subject9);
|
||||
metadatums2.add(subject10);
|
||||
metadatums2.add(subject11);
|
||||
|
||||
ImportRecord secondRecord = new ImportRecord(metadatums2);
|
||||
records.add(firstrRecord);
|
||||
records.add(secondRecord);
|
||||
return records;
|
||||
}
|
||||
|
||||
}
|
@@ -27,7 +27,8 @@ public class AuthenticationStatusMatcher {
|
||||
*/
|
||||
public static Matcher<? super Object> matchFullEmbeds() {
|
||||
return matchEmbeds(
|
||||
"eperson"
|
||||
"eperson",
|
||||
"specialGroups"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,7 +37,9 @@ public class AuthenticationStatusMatcher {
|
||||
*/
|
||||
public static Matcher<? super Object> matchLinks() {
|
||||
return allOf(
|
||||
//FIXME https://github.com/DSpace/DSpace/issues/8274
|
||||
hasJsonPath("$._links.eperson.href", containsString("api/eperson/epersons")),
|
||||
hasJsonPath("$._links.self.href", containsString("api/authn/status")));
|
||||
hasJsonPath("$._links.self.href", containsString("api/authn/status")),
|
||||
hasJsonPath("$._links.specialGroups.href", containsString("api/authn/status/specialGroups")));
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,169 @@
|
||||
{
|
||||
"status":"ok",
|
||||
"message-type":"work",
|
||||
"message-version":"1.0.0",
|
||||
"message":{
|
||||
"indexed":{
|
||||
"date-parts":[
|
||||
[
|
||||
2022,
|
||||
4,
|
||||
5
|
||||
]
|
||||
],
|
||||
"date-time":"2022-04-05T22:05:30Z",
|
||||
"timestamp":1649196330913
|
||||
},
|
||||
"reference-count":0,
|
||||
"publisher":"Petro Mohyla Black Sea National University",
|
||||
"issue":"2",
|
||||
"content-domain":{
|
||||
"domain":[
|
||||
|
||||
],
|
||||
"crossmark-restriction":false
|
||||
},
|
||||
"short-container-title":[
|
||||
"Ukr. \u017e. med. b\u00ecol. sportu"
|
||||
],
|
||||
"published-print":{
|
||||
"date-parts":[
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
},
|
||||
"DOI":"10.26693\/jmbs01.02.184",
|
||||
"type":"journal-article",
|
||||
"created":{
|
||||
"date-parts":[
|
||||
[
|
||||
2017,
|
||||
9,
|
||||
7
|
||||
]
|
||||
],
|
||||
"date-time":"2017-09-07T13:30:46Z",
|
||||
"timestamp":1504791046000
|
||||
},
|
||||
"page":"184-187",
|
||||
"source":"Crossref",
|
||||
"is-referenced-by-count":0,
|
||||
"title":[
|
||||
"State of Awareness of Freshers\u2019 Groups Chortkiv State Medical College of Prevention of Iodine Deficiency Diseases"
|
||||
],
|
||||
"prefix":"10.26693",
|
||||
"volume":"1",
|
||||
"author":[
|
||||
{
|
||||
"given":"L.V.",
|
||||
"family":"Senyuk",
|
||||
"sequence":"first",
|
||||
"affiliation":[
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"Chortkiv State Medical College 7, Gogola St., Chortkiv, Ternopil region 48500, Ukraine",
|
||||
"sequence":"first",
|
||||
"affiliation":[
|
||||
|
||||
]
|
||||
}
|
||||
],
|
||||
"member":"11225",
|
||||
"published-online":{
|
||||
"date-parts":[
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
},
|
||||
"container-title":[
|
||||
"Ukra\u00efns\u02b9kij \u017eurnal medicini, b\u00ecolog\u00ec\u00ef ta sportu"
|
||||
],
|
||||
"original-title":[
|
||||
"\u0421\u0422\u0410\u041d \u041e\u0411\u0406\u0417\u041d\u0410\u041d\u041e\u0421\u0422\u0406 \u0421\u0422\u0423\u0414\u0415\u041d\u0422\u0406\u0412 \u041d\u041e\u0412\u041e\u041d\u0410\u0411\u0420\u0410\u041d\u0418\u0425 \u0413\u0420\u0423\u041f \u0427\u041e\u0420\u0422\u041a\u0406\u0412\u0421\u042c\u041a\u041e\u0413\u041e \u0414\u0415\u0420\u0416\u0410\u0412\u041d\u041e\u0413\u041e \u041c\u0415\u0414\u0418\u0427\u041d\u041e\u0413\u041e \u041a\u041e\u041b\u0415\u0414\u0416\u0423 \u0417 \u041f\u0418\u0422\u0410\u041d\u042c \u041f\u0420\u041e\u0424\u0406\u041b\u0410\u041a\u0422\u0418\u041a\u0418 \u0419\u041e\u0414\u041e\u0414\u0415\u0424\u0406\u0426\u0418\u0422\u041d\u0418\u0425 \u0417\u0410\u0425\u0412\u041e\u0420\u042e\u0412\u0410\u041d\u042c"
|
||||
],
|
||||
"deposited":{
|
||||
"date-parts":[
|
||||
[
|
||||
2017,
|
||||
9,
|
||||
8
|
||||
]
|
||||
],
|
||||
"date-time":"2017-09-08T10:14:53Z",
|
||||
"timestamp":1504865693000
|
||||
},
|
||||
"score":1,
|
||||
"resource":{
|
||||
"primary":{
|
||||
"URL":"http:\/\/en.jmbs.com.ua\/archive\/1\/2\/184"
|
||||
}
|
||||
},
|
||||
"subtitle":[
|
||||
|
||||
],
|
||||
"short-title":[
|
||||
|
||||
],
|
||||
"issued":{
|
||||
"date-parts":[
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
},
|
||||
"references-count":0,
|
||||
"journal-issue":{
|
||||
"issue":"2",
|
||||
"published-online":{
|
||||
"date-parts":[
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
},
|
||||
"published-print":{
|
||||
"date-parts":[
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"URL":"http:\/\/dx.doi.org\/10.26693\/jmbs01.02.184",
|
||||
"relation":{
|
||||
|
||||
},
|
||||
"ISSN":[
|
||||
"2415-3060"
|
||||
],
|
||||
"issn-type":[
|
||||
{
|
||||
"value":"2415-3060",
|
||||
"type":"print"
|
||||
}
|
||||
],
|
||||
"published":{
|
||||
"date-parts":[
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,309 @@
|
||||
{
|
||||
"status": "ok",
|
||||
"message-type": "work-list",
|
||||
"message-version": "1.0.0",
|
||||
"message": {
|
||||
"facets": {},
|
||||
"total-results": 10,
|
||||
"items": [
|
||||
{
|
||||
"indexed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2021,
|
||||
12,
|
||||
22
|
||||
]
|
||||
],
|
||||
"date-time": "2021-12-22T10:58:16Z",
|
||||
"timestamp": 1640170696598
|
||||
},
|
||||
"reference-count": 0,
|
||||
"publisher": "Petro Mohyla Black Sea National University",
|
||||
"issue": "2",
|
||||
"content-domain": {
|
||||
"domain": [],
|
||||
"crossmark-restriction": false
|
||||
},
|
||||
"short-container-title": [
|
||||
"Ukr. ž. med. bìol. sportu"
|
||||
],
|
||||
"published-print": {
|
||||
"date-parts": [
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
},
|
||||
"DOI": "10.26693/jmbs01.02.184",
|
||||
"type": "journal-article",
|
||||
"created": {
|
||||
"date-parts": [
|
||||
[
|
||||
2017,
|
||||
9,
|
||||
7
|
||||
]
|
||||
],
|
||||
"date-time": "2017-09-07T13:30:46Z",
|
||||
"timestamp": 1504791046000
|
||||
},
|
||||
"page": "184-187",
|
||||
"source": "Crossref",
|
||||
"is-referenced-by-count": 0,
|
||||
"title": [
|
||||
"State of Awareness of Freshers’ Groups Chortkiv State Medical College of Prevention of Iodine Deficiency Diseases"
|
||||
],
|
||||
"prefix": "10.26693",
|
||||
"volume": "1",
|
||||
"author": [
|
||||
{
|
||||
"given": "L.V.",
|
||||
"family": "Senyuk",
|
||||
"sequence": "first",
|
||||
"affiliation": []
|
||||
},
|
||||
{
|
||||
"name": "Chortkiv State Medical College 7, Gogola St., Chortkiv, Ternopil region 48500, Ukraine",
|
||||
"sequence": "first",
|
||||
"affiliation": []
|
||||
}
|
||||
],
|
||||
"member": "11225",
|
||||
"published-online": {
|
||||
"date-parts": [
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
},
|
||||
"container-title": [
|
||||
"Ukraïnsʹkij žurnal medicini, bìologìï ta sportu"
|
||||
],
|
||||
"original-title": [
|
||||
"СТАН ОБІЗНАНОСТІ СТУДЕНТІВ НОВОНАБРАНИХ ГРУП ЧОРТКІВСЬКОГО ДЕРЖАВНОГО МЕДИЧНОГО КОЛЕДЖУ З ПИТАНЬ ПРОФІЛАКТИКИ ЙОДОДЕФІЦИТНИХ ЗАХВОРЮВАНЬ"
|
||||
],
|
||||
"deposited": {
|
||||
"date-parts": [
|
||||
[
|
||||
2017,
|
||||
9,
|
||||
8
|
||||
]
|
||||
],
|
||||
"date-time": "2017-09-08T10:14:53Z",
|
||||
"timestamp": 1504865693000
|
||||
},
|
||||
"score": 22.728952,
|
||||
"issued": {
|
||||
"date-parts": [
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
},
|
||||
"references-count": 0,
|
||||
"journal-issue": {
|
||||
"issue": "2",
|
||||
"published-online": {
|
||||
"date-parts": [
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
},
|
||||
"published-print": {
|
||||
"date-parts": [
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"URL": "http://dx.doi.org/10.26693/jmbs01.02.184",
|
||||
"ISSN": [
|
||||
"2415-3060"
|
||||
],
|
||||
"issn-type": [
|
||||
{
|
||||
"value": "2415-3060",
|
||||
"type": "print"
|
||||
}
|
||||
],
|
||||
"published": {
|
||||
"date-parts": [
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"indexed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
3,
|
||||
29
|
||||
]
|
||||
],
|
||||
"date-time": "2022-03-29T13:04:48Z",
|
||||
"timestamp": 1648559088439
|
||||
},
|
||||
"reference-count": 0,
|
||||
"publisher": "Petro Mohyla Black Sea National University",
|
||||
"issue": "2",
|
||||
"content-domain": {
|
||||
"domain": [],
|
||||
"crossmark-restriction": false
|
||||
},
|
||||
"short-container-title": [
|
||||
"Ukr. ž. med. bìol. sportu"
|
||||
],
|
||||
"published-print": {
|
||||
"date-parts": [
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
},
|
||||
"DOI": "10.26693/jmbs01.02.105",
|
||||
"type": "journal-article",
|
||||
"created": {
|
||||
"date-parts": [
|
||||
[
|
||||
2017,
|
||||
9,
|
||||
1
|
||||
]
|
||||
],
|
||||
"date-time": "2017-09-01T10:04:04Z",
|
||||
"timestamp": 1504260244000
|
||||
},
|
||||
"page": "105-108",
|
||||
"source": "Crossref",
|
||||
"is-referenced-by-count": 0,
|
||||
"title": [
|
||||
"Ischemic Heart Disease and Role of Nurse of Cardiology Department"
|
||||
],
|
||||
"prefix": "10.26693",
|
||||
"volume": "1",
|
||||
"author": [
|
||||
{
|
||||
"given": "K. І.",
|
||||
"family": "Kozak",
|
||||
"sequence": "first",
|
||||
"affiliation": []
|
||||
},
|
||||
{
|
||||
"name": "Chortkiv State Medical College 7, Gogola St., Chortkiv, Ternopil region 48500, Ukraine",
|
||||
"sequence": "first",
|
||||
"affiliation": []
|
||||
}
|
||||
],
|
||||
"member": "11225",
|
||||
"published-online": {
|
||||
"date-parts": [
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
},
|
||||
"container-title": [
|
||||
"Ukraïnsʹkij žurnal medicini, bìologìï ta sportu"
|
||||
],
|
||||
"original-title": [
|
||||
"ІШЕМІЧНА ХВОРОБА СЕРЦЯ ТА РОЛЬ МЕДИЧНОЇ СЕСТРИ КАРДІОЛОГІЧНОГО ВІДДІЛЕННЯ"
|
||||
],
|
||||
"deposited": {
|
||||
"date-parts": [
|
||||
[
|
||||
2017,
|
||||
9,
|
||||
2
|
||||
]
|
||||
],
|
||||
"date-time": "2017-09-02T12:36:15Z",
|
||||
"timestamp": 1504355775000
|
||||
},
|
||||
"score": 18.263277,
|
||||
"resource": {
|
||||
"primary": {
|
||||
"URL": "http://en.jmbs.com.ua/archive/1/2/105"
|
||||
}
|
||||
},
|
||||
"issued": {
|
||||
"date-parts": [
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
},
|
||||
"references-count": 0,
|
||||
"journal-issue": {
|
||||
"issue": "2",
|
||||
"published-online": {
|
||||
"date-parts": [
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
},
|
||||
"published-print": {
|
||||
"date-parts": [
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"URL": "http://dx.doi.org/10.26693/jmbs01.02.105",
|
||||
"ISSN": [
|
||||
"2415-3060"
|
||||
],
|
||||
"issn-type": [
|
||||
{
|
||||
"value": "2415-3060",
|
||||
"type": "print"
|
||||
}
|
||||
],
|
||||
"published": {
|
||||
"date-parts": [
|
||||
[
|
||||
2016,
|
||||
5,
|
||||
19
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"items-per-page": 2,
|
||||
"query": {
|
||||
"start-index": 0,
|
||||
"search-terms": "chortkiv"
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"resultCount": 1,
|
||||
"records": [
|
||||
{
|
||||
"authors": {
|
||||
"primary": [],
|
||||
"secondary": {
|
||||
"Carettoni, Gianfilippo.": []
|
||||
},
|
||||
"corporate": []
|
||||
},
|
||||
"formats": [
|
||||
"Map",
|
||||
"Map",
|
||||
"Book"
|
||||
],
|
||||
"id": "653510",
|
||||
"languages": [
|
||||
"Italian"
|
||||
],
|
||||
"series": [],
|
||||
"subjects": [
|
||||
[
|
||||
"Rome (Italy)",
|
||||
"Maps",
|
||||
"Early works to 1800."
|
||||
],
|
||||
[
|
||||
"Rome (Italy)",
|
||||
"Antiquities",
|
||||
"Maps."
|
||||
]
|
||||
],
|
||||
"title": "La pianta marmorea di Roma antica: Forma urbis Romae /",
|
||||
"urls": [
|
||||
{
|
||||
"url": "http://hdl.handle.net/20.500.12390/231",
|
||||
"desc": "http://hdl.handle.net/20.500.12390/231"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"status": "OK"
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"resultCount": 1994,
|
||||
"records": [
|
||||
{
|
||||
"authors": {
|
||||
"primary": [],
|
||||
"secondary": {
|
||||
"Carettoni, Gianfilippo.": []
|
||||
},
|
||||
"corporate": []
|
||||
},
|
||||
"formats": [
|
||||
"Map",
|
||||
"Map",
|
||||
"Book"
|
||||
],
|
||||
"id": "653510",
|
||||
"languages": [
|
||||
"Italian"
|
||||
],
|
||||
"series": [],
|
||||
"subjects": [
|
||||
[
|
||||
"Rome (Italy)",
|
||||
"Maps",
|
||||
"Early works to 1800."
|
||||
],
|
||||
[
|
||||
"Rome (Italy)",
|
||||
"Antiquities",
|
||||
"Maps."
|
||||
]
|
||||
],
|
||||
"title": "La pianta marmorea di Roma antica: Forma urbis Romae /",
|
||||
"urls": [
|
||||
{
|
||||
"url": "http://hdl.handle.net/20.500.12390/231",
|
||||
"desc": "http://hdl.handle.net/20.500.12390/231"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": {
|
||||
"primary": {
|
||||
"Surdu, Mihai.": []
|
||||
},
|
||||
"secondary": [],
|
||||
"corporate": []
|
||||
},
|
||||
"formats": [
|
||||
"Online",
|
||||
"Book"
|
||||
],
|
||||
"id": "1665326",
|
||||
"languages": [
|
||||
"English"
|
||||
],
|
||||
"series": [],
|
||||
"subjects": [
|
||||
[
|
||||
"Public opinion",
|
||||
"Europe."
|
||||
],
|
||||
[
|
||||
"Stereotypes (Social psychology)"
|
||||
],
|
||||
[
|
||||
"Romanies",
|
||||
"Public opinion."
|
||||
]
|
||||
],
|
||||
"title": "Expert frames : scientific and policy practices of Roma classification /",
|
||||
"urls": [
|
||||
{
|
||||
"url": "http://ezproxy.villanova.edu/login?URL=http://www.jstor.org/stable/10.7829/j.ctt1ggjj08",
|
||||
"desc": "http://ezproxy.villanova.edu/login?URL=http://www.jstor.org/stable/10.7829/j.ctt1ggjj08"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"status": "OK"
|
||||
}
|
@@ -1228,20 +1228,6 @@ plugin.named.org.dspace.content.license.LicenseArgumentFormatter = \
|
||||
#### Syndication Feed (RSS) Settings ######
|
||||
# TODO: UNSUPPORTED in DSpace 7.0. Will be added in a later release
|
||||
|
||||
# enable syndication feeds - links display on community and collection home pages
|
||||
webui.feed.enable = true
|
||||
# number of DSpace items per feed (the most recent submissions)
|
||||
webui.feed.items = 4
|
||||
# maximum number of feeds in memory cache
|
||||
# value of 0 will disable caching
|
||||
webui.feed.cache.size = 100
|
||||
# number of hours to keep cached feeds before checking currency
|
||||
# value of 0 will force a check with each request
|
||||
webui.feed.cache.age = 48
|
||||
# which syndication formats to offer
|
||||
# use one or more (comma-separated) values from list:
|
||||
# rss_0.90, rss_0.91, rss_0.92, rss_0.93, rss_0.94, rss_1.0, rss_2.0
|
||||
webui.feed.formats = rss_1.0,rss_2.0,atom_1.0
|
||||
# URLs returned by the feed will point at the global handle server
|
||||
# (e.g. https://hdl.handle.net/123456789/1). Set to true to use local server
|
||||
# URLs (i.e. https://myserver.myorg/handle/123456789/1)
|
||||
@@ -1311,7 +1297,9 @@ webui.feed.item.author = dc.contributor.author
|
||||
# NB: for result data formatting, OpenSearch uses Syndication Feed Settings
|
||||
# so even if Syndication Feeds are not enabled, they must be configured
|
||||
# enable open search
|
||||
websvc.opensearch.enable = false
|
||||
websvc.opensearch.enable = true
|
||||
# url used in service document
|
||||
websvc.opensearch.svccontext = opensearch
|
||||
# context for html request URLs - change only for non-standard servlet mapping
|
||||
websvc.opensearch.uicontext = simple-search
|
||||
# present autodiscovery link in every page head
|
||||
@@ -1667,3 +1655,4 @@ include = ${module_dir}/translator.cfg
|
||||
include = ${module_dir}/usage-statistics.cfg
|
||||
include = ${module_dir}/versioning.cfg
|
||||
include = ${module_dir}/workflow.cfg
|
||||
include = ${module_dir}/external-providers.cfg
|
||||
|
@@ -6,6 +6,19 @@
|
||||
#---------------------------------------------------------------#
|
||||
|
||||
|
||||
#################################################################
|
||||
#---------------------- CrossRef ---------------------------#
|
||||
#---------------------------------------------------------------#
|
||||
|
||||
crossref.url = https://api.crossref.org/works
|
||||
|
||||
#################################################################
|
||||
#---------------------- VuFind -----------------------------#
|
||||
#---------------------------------------------------------------#
|
||||
|
||||
vufind.url = https://vufind.org/advanced_demo/api/v1/record
|
||||
vufind.url.search = https://vufind.org/advanced_demo/api/v1/search
|
||||
|
||||
#################################################################
|
||||
#---------------------- Scielo -----------------------------#
|
||||
#---------------------------------------------------------------#
|
||||
|
@@ -32,7 +32,9 @@ rest.projection.specificLevel.maxEmbed = 5
|
||||
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
|
||||
rest.properties.exposed = plugin.named.org.dspace.curate.CurationTask
|
||||
rest.properties.exposed = google.analytics.key
|
||||
rest.properties.exposed = websvc.opensearch.enable
|
||||
rest.properties.exposed = versioning.item.history.include.submitter
|
||||
rest.properties.exposed = websvc.opensearch.svccontext
|
||||
rest.properties.exposed = submit.type-bind.field
|
||||
|
||||
#---------------------------------------------------------------#
|
||||
|
141
dspace/config/spring/api/crossref-integration.xml
Normal file
141
dspace/config/spring/api/crossref-integration.xml
Normal file
@@ -0,0 +1,141 @@
|
||||
<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="crossrefMetadataFieldMap" key-type="org.dspace.importer.external.metadatamapping.MetadataFieldConfig"
|
||||
value-type="org.dspace.importer.external.metadatamapping.contributor.MetadataContributor">
|
||||
<description>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.
|
||||
</description>
|
||||
<entry key-ref="crossref.title" value-ref="crossrefTitleContrib"/>
|
||||
<entry key-ref="crossref.authors" value-ref="crossrefAuthorContrib"/>
|
||||
<entry key-ref="crossref.isbn" value-ref="crossrefISBNContrib"/>
|
||||
<entry key-ref="crossref.year" value-ref="crossrefYearContrib"/>
|
||||
<entry key-ref="crossref.editors" value-ref="crossrefEditorsContrib"/>
|
||||
<entry key-ref="crossref.type" value-ref="crossrefDoiTypeContrib"/>
|
||||
<entry key-ref="crossref.journal" value-ref="crossrefJournalContrib"/>
|
||||
<entry key-ref="crossref.id" value-ref="crossrefIDContrib"/>
|
||||
<entry key-ref="crossref.issn" value-ref="crossrefIdentifierISSN" />
|
||||
<entry key-ref="crossref.volume" value-ref="crossrefVolume" />
|
||||
<entry key-ref="crossref.issue" value-ref="crossrefIssue" />
|
||||
<entry key-ref="crossref.abstract" value-ref="crossrefAbstract" />
|
||||
</util:map>
|
||||
|
||||
<bean id="crossrefIDContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="crossref.id"/>
|
||||
<property name="query" value="/DOI"/>
|
||||
</bean>
|
||||
<bean id="crossref.id" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.identifier.doi"/>
|
||||
</bean>
|
||||
|
||||
<bean id="crossrefJournalContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="crossref.journal"/>
|
||||
<property name="query" value="/container-title"/>
|
||||
</bean>
|
||||
<bean id="crossref.journal" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.relation.ispartof"/>
|
||||
</bean>
|
||||
|
||||
<bean id="crossrefDoiTypeContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="crossref.type"/>
|
||||
<property name="query" value="/type"/>
|
||||
</bean>
|
||||
<bean id="crossref.type" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.type"/>
|
||||
</bean>
|
||||
|
||||
<bean id="crossrefEditorsContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="crossref.editors"/>
|
||||
<property name="metadataProcessor" ref="crossrefEditorMetadataProcessor"></property>
|
||||
</bean>
|
||||
<bean name="crossrefEditorMetadataProcessor" class="org.dspace.importer.external.crossref.CrossRefAuthorMetadataProcessor">
|
||||
<property name="pathToArray" value="/editor"></property>
|
||||
</bean>
|
||||
<bean id="crossref.editors" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.contributor.editor"/>
|
||||
</bean>
|
||||
|
||||
<bean id="crossrefYearContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="crossref.year"/>
|
||||
<property name="query" value="/issued/date-parts/0/0"/>
|
||||
</bean>
|
||||
<bean id="crossref.year" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.date.issued"/>
|
||||
</bean>
|
||||
|
||||
<bean id="crossrefISBNContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="crossref.isbn"/>
|
||||
<property name="query" value="/ISBN"/>
|
||||
</bean>
|
||||
<bean id="crossref.isbn" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.identifier.isbn"/>
|
||||
</bean>
|
||||
|
||||
<bean id="crossrefAuthorContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="crossref.authors"/>
|
||||
<property name="metadataProcessor" ref="crossrefAuthorMetadataProcessor"></property>
|
||||
</bean>
|
||||
<bean name="crossrefAuthorMetadataProcessor" class="org.dspace.importer.external.crossref.CrossRefAuthorMetadataProcessor">
|
||||
<property name="pathToArray" value="/author"></property>
|
||||
</bean>
|
||||
<bean id="crossref.authors" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.contributor.author"/>
|
||||
</bean>
|
||||
|
||||
<bean id="crossrefTitleContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="crossref.title"/>
|
||||
<property name="query" value="/title"/>
|
||||
</bean>
|
||||
<bean id="crossref.title" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.title"/>
|
||||
</bean>
|
||||
|
||||
<bean id="crossrefIdentifierISSN" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="crossref.issn"/>
|
||||
<property name="query" value="/ISSN"/>
|
||||
</bean>
|
||||
<bean id="crossref.issn" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.identifier.issn"/>
|
||||
</bean>
|
||||
|
||||
<bean id="crossrefVolume" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="crossref.volume"/>
|
||||
<property name="query" value="/volume"/>
|
||||
</bean>
|
||||
<bean id="crossref.volume" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="oaire.citation.volume"/>
|
||||
</bean>
|
||||
|
||||
<bean id="crossrefIssue" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="crossref.issue"/>
|
||||
<property name="query" value="/journal-issue/issue"/>
|
||||
</bean>
|
||||
<bean id="crossref.issue" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="oaire.citation.issue"/>
|
||||
</bean>
|
||||
|
||||
<bean id="crossrefAbstract" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="crossref.abstract"/>
|
||||
<property name="query" value="/abstract"/>
|
||||
</bean>
|
||||
<bean id="crossref.abstract" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.description.abstract"/>
|
||||
</bean>
|
||||
|
||||
<bean class="java.lang.Integer" id="maxRetry">
|
||||
<constructor-arg value="3"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
@@ -94,6 +94,30 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="crossRefLiveImportDataProvider" class="org.dspace.external.provider.impl.LiveImportDataProvider">
|
||||
<property name="metadataSource" ref="CrossRefImportService"/>
|
||||
<property name="sourceIdentifier" value="crossref"/>
|
||||
<property name="recordIdMetadata" value="dc.identifier.doi"/>
|
||||
<property name="supportedEntityTypes">
|
||||
<list>
|
||||
<value>Publication</value>
|
||||
<value>none</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="vufindLiveImportDataProvider" class="org.dspace.external.provider.impl.LiveImportDataProvider">
|
||||
<property name="metadataSource" ref="vufindImportService"/>
|
||||
<property name="sourceIdentifier" value="vufind"/>
|
||||
<property name="recordIdMetadata" value="dc.identifier.other"/>
|
||||
<property name="supportedEntityTypes">
|
||||
<list>
|
||||
<value>Publication</value>
|
||||
<value>none</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="scieloLiveImportDataProvider" class="org.dspace.external.provider.impl.LiveImportDataProvider">
|
||||
<property name="metadataSource" ref="ScieloImportService"/>
|
||||
<property name="sourceIdentifier" value="scielo"/>
|
||||
|
165
dspace/config/spring/api/vufind-integration.xml
Normal file
165
dspace/config/spring/api/vufind-integration.xml
Normal file
@@ -0,0 +1,165 @@
|
||||
<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="vufindMetadataFieldMap" key-type="org.dspace.importer.external.metadatamapping.MetadataFieldConfig"
|
||||
value-type="org.dspace.importer.external.metadatamapping.contributor.MetadataContributor">
|
||||
<description>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.
|
||||
</description>
|
||||
<entry key-ref="vufind.id" value-ref="vufindIDContrib"/>
|
||||
<entry key-ref="vufind.dc.language.iso" value-ref="vufindLanguages"/>
|
||||
<entry key-ref="vufind.dc.publisher" value-ref="vufindPublisher"/>
|
||||
<entry key-ref="vufind.dc.title" value-ref="vufindTitle"/>
|
||||
<entry key-ref="vufind.dc.identifier" value-ref="vufindIdentifier"/>
|
||||
<entry key-ref="vufind.dc.contributor.author" value-ref="vufindAuthors"/>
|
||||
<entry key-ref="vufind.dc.subject" value-ref="vufindSubject"/>
|
||||
<entry key-ref="vufind.dc.description.abstract" value-ref="vufindAbstract"/>
|
||||
<entry key-ref="vufind.dc.titlealternative" value-ref="vufindTitleAlternative"/>
|
||||
<entry key-ref="vufind.dc.contributor.editor" value-ref="vufindContributorEditor"/>
|
||||
<entry key-ref="vufind.dc.date.issued" value-ref="vufindDateIssued"/>
|
||||
<entry key-ref="vufind.dc.identifier.doi" value-ref="vufindIdentifierDoi"/>
|
||||
<entry key-ref="vufind.dc.identifier.isbn" value-ref="vufindIdentifierIsbn"/>
|
||||
</util:map>
|
||||
|
||||
<bean id="vufindIDContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="vufind.id"/>
|
||||
<property name="query" value="/id"/>
|
||||
</bean>
|
||||
<bean id="vufind.id" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.identifier.other"/>
|
||||
</bean>
|
||||
|
||||
<bean id="vufindLanguages" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="vufind.dc.language.iso"/>
|
||||
<property name="query" value="/languages"/>
|
||||
</bean>
|
||||
<bean id="vufind.dc.language.iso" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.language.iso"/>
|
||||
</bean>
|
||||
|
||||
<bean id="vufindTitle" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="vufind.dc.title"/>
|
||||
<property name="query" value="/title"/>
|
||||
</bean>
|
||||
<bean id="vufind.dc.title" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.title"/>
|
||||
</bean>
|
||||
|
||||
<bean id="vufindIdentifier" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="vufind.dc.identifier"/>
|
||||
<property name="metadataProcessor" ref="vufindUriProcessor"></property>
|
||||
</bean>
|
||||
<bean name="vufindUriProcessor" class="org.dspace.importer.external.metadatamapping.contributor.ArrayElementAttributeProcessor">
|
||||
<property name="pathToArray" value="/urls"></property>
|
||||
<property name="elementAttribute" value="/url"></property>
|
||||
</bean>
|
||||
<bean id="vufind.dc.identifier" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.identifier"/>
|
||||
</bean>
|
||||
|
||||
<bean id="vufindSubject" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="vufind.dc.subject"/>
|
||||
<property name="metadataProcessor" ref="vufindSubjectsProcessor"></property>
|
||||
</bean>
|
||||
<bean name="vufindSubjectsProcessor" class="org.dspace.importer.external.metadatamapping.contributor.MatrixElementProcessor">
|
||||
<property name="pathToMatrix" value="/subjects"></property>
|
||||
</bean>
|
||||
<bean id="vufind.dc.subject" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.subject"/>
|
||||
</bean>
|
||||
|
||||
<bean id="vufindAuthors" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="vufind.dc.contributor.author"/>
|
||||
<property name="query" value="/dcContributorAuthor"/>
|
||||
</bean>
|
||||
|
||||
<util:list id="vufindAuthorsList">
|
||||
<bean id="vufindPrimaryAuthors" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="vufind.dc.contributor.author"/>
|
||||
<property name="query" value="/authors/primary" />
|
||||
</bean>
|
||||
<bean id="vufindSecondaryAuthors" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="vufind.dc.contributor.author"/>
|
||||
<property name="query" value="/authors/secondary" />
|
||||
</bean>
|
||||
</util:list>
|
||||
|
||||
<bean id="vufind.dc.contributor.author" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.contributor.author"/>
|
||||
</bean>
|
||||
|
||||
<bean class="java.lang.Integer" id="maxRetry">
|
||||
<constructor-arg value="3"/>
|
||||
</bean>
|
||||
|
||||
<!-- Below there are other mapping that apply to an extended configuration in use for instance at the vuFind installation
|
||||
of ALICIA https://alicia.concytec.gob.pe/vufind/Search/Results?type=AllFields -->
|
||||
|
||||
<bean id="vufindIdentifierIsbn" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="vufind.dc.identifier.isbn"/>
|
||||
<property name="query" value="/dcIdentifierIsbn"/>
|
||||
</bean>
|
||||
<bean id="vufind.dc.identifier.isbn" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.identifier.isbn"/>
|
||||
</bean>
|
||||
|
||||
<bean id="vufindIdentifierDoi" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="vufind.dc.identifier.doi"/>
|
||||
<property name="query" value="/dcIdentifierDoi"/>
|
||||
</bean>
|
||||
<bean id="vufind.dc.identifier.doi" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.identifier.doi"/>
|
||||
</bean>
|
||||
|
||||
<bean id="vufindDateIssued" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="vufind.dc.date.issued"/>
|
||||
<property name="query" value="/dcDateIssued"/>
|
||||
</bean>
|
||||
<bean id="vufind.dc.date.issued" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.date.issued"/>
|
||||
</bean>
|
||||
|
||||
<bean id="vufindContributorEditor" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="vufind.dc.contributor.editor"/>
|
||||
<property name="query" value="/dcContributorEditor"/>
|
||||
</bean>
|
||||
<bean id="vufind.dc.contributor.editor" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.contributor.editor"/>
|
||||
</bean>
|
||||
|
||||
<bean id="vufindTitleAlternative" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="vufind.dc.titlealternative"/>
|
||||
<property name="query" value="/dcTitleAlternative"/>
|
||||
</bean>
|
||||
<bean id="vufind.dc.titlealternative" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.title.alternative"/>
|
||||
</bean>
|
||||
|
||||
<bean id="vufindAbstract" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="vufind.dc.description.abstract"/>
|
||||
<property name="query" value="/dcDescriptionAbstract"/>
|
||||
</bean>
|
||||
<bean id="vufind.dc.description.abstract" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.description.abstract"/>
|
||||
</bean>
|
||||
|
||||
<bean id="vufindPublisher" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
|
||||
<property name="field" ref="vufind.dc.publisher"/>
|
||||
<property name="query" value="/dcPublisher"/>
|
||||
</bean>
|
||||
<bean id="vufind.dc.publisher" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
|
||||
<constructor-arg value="dc.publisher"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
Reference in New Issue
Block a user