70337: Search CC License

This commit is contained in:
Yana De Pauw
2020-04-14 15:59:21 +02:00
parent f51a12d010
commit 794ee9fb9f
10 changed files with 501 additions and 17 deletions

View File

@@ -24,4 +24,16 @@ public interface CCLicenseConnectorService {
*/
public Map<String, CCLicense> retrieveLicenses(String language);
/**
* Retrieve the CC License URI based on the provided license id, language and answers to the field questions from
* the CC License API
* @param licenseId - the ID of the license
* @param language - the language for which to retrieve the full answerMap
* @param answerMap - the answers to the different field questions
* @return the CC License URI
*/
public String retrieveRightsByQuestion(String licenseId,
String language,
Map<String, String> answerMap);
}

View File

@@ -9,6 +9,7 @@ package org.dspace.license;
import java.io.IOException;
import java.io.StringReader;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
@@ -17,8 +18,11 @@ import java.util.Map;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
@@ -45,6 +49,15 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService,
private CloseableHttpClient client;
private SAXBuilder parser = new SAXBuilder();
private String postArgument = "answers";
private String postAnswerFormat =
"<answers> " +
"<locale>{1}</locale>" +
"<license-{0}>" +
"{2}" +
"</license-{0}>" +
"</answers>";
@Autowired
private ConfigurationService configurationService;
@@ -221,4 +234,85 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService,
return getNodeValue(singleNode);
}
/**
* Retrieve the CC License URI based on the provided license id, language and answers to the field questions from
* the CC License API
* @param licenseId - the ID of the license
* @param language - the language for which to retrieve the full answerMap
* @param answerMap - the answers to the different field questions
* @return the CC License URI
*/
public String retrieveRightsByQuestion(String licenseId,
String language,
Map<String, String> answerMap) {
String ccLicenseUrl = configurationService.getProperty("cc.api.rooturl");
HttpPost httpPost = new HttpPost(ccLicenseUrl + "/license/" + licenseId + "/issue");
String answers = createAnswerString(answerMap);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
String text = MessageFormat.format(postAnswerFormat, licenseId, language, answers);
builder.addTextBody(postArgument, text);
HttpEntity multipart = builder.build();
httpPost.setEntity(multipart);
try (CloseableHttpResponse response = client.execute(httpPost)) {
return retrieveLicenseUri(response);
} catch (JDOMException | JaxenException | IOException e) {
log.error("Error while retrieving the license uri for license : " + licenseId + " with answers "
+ answerMap.toString(), e);
}
return null;
}
/**
* Parse the response for the CC License URI request and return the corresponding CC License URI
*
* @param response for a specific CC License URI response
* @return the corresponding CC License URI as a string
* @throws IOException
* @throws JaxenException
* @throws JDOMException
*/
private String retrieveLicenseUri(final CloseableHttpResponse response)
throws IOException, JaxenException, JDOMException {
String responseString = EntityUtils.toString(response.getEntity());
JDOMXPath licenseClassXpath = new JDOMXPath("//result/license-uri");
try (StringReader stringReader = new StringReader(responseString)) {
InputSource is = new InputSource(stringReader);
org.jdom.Document classDoc = this.parser.build(is);
Object node = licenseClassXpath.selectSingleNode(classDoc);
String nodeValue = getNodeValue(node);
if (StringUtils.isNotBlank(nodeValue)) {
return nodeValue;
}
}
return null;
}
private String createAnswerString(final Map<String, String> parameterMap) {
StringBuilder sb = new StringBuilder();
for (String key : parameterMap.keySet()) {
sb.append("<");
sb.append(key);
sb.append(">");
sb.append(parameterMap.get(key));
sb.append("</");
sb.append(key);
sb.append(">");
}
return sb.toString();
}
}

View File

@@ -90,6 +90,9 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
private String defaultLanguage;
private Map<String, Map<String, CCLicense>> ccLicenses;
protected CreativeCommonsServiceImpl() {
@@ -109,7 +112,7 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
}
ccLicenses = new HashMap<>();
defaultLanguage = configurationService.getProperty("cc.license.locale", "en");
try {
templates = TransformerFactory.newInstance().newTemplates(
@@ -399,8 +402,7 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
* @return A list of available CC Licenses
*/
public List<CCLicense> findAllCCLicenses() {
String language = configurationService.getProperty("cc.license.locale", "en");
return findAllCCLicenses(language);
return findAllCCLicenses(defaultLanguage);
}
/**
@@ -424,8 +426,7 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
* @return the corresponding license if found or null when not found
*/
public CCLicense findOne(String id) {
String language = configurationService.getProperty("cc.license.locale", "en");
return findOne(id, language);
return findOne(id, defaultLanguage);
}
/**
@@ -456,4 +457,124 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi
ccLicenses.put(language, licenseMap);
}
/**
* Retrieve the CC License URI for the provided license ID, based on the provided answers, using the default
* language found in the configuration
*
* @param licenseId - the ID of the license
* @param answerMap - the answers to the different field questions
* @return the corresponding license URI
*/
public String retrieveLicenseUri(String licenseId, Map<String, String> answerMap) {
return retrieveLicenseUri(licenseId, defaultLanguage, answerMap);
}
/**
* Retrieve the CC License URI for the provided license ID and language based on the provided answers
*
* @param licenseId - the ID of the license
* @param language - the language for which to find the CC License URI
* @param answerMap - the answers to the different field questions
* @return the corresponding license URI
*/
public String retrieveLicenseUri(String licenseId, String language, Map<String, String> answerMap) {
return ccLicenseConnectorService.retrieveRightsByQuestion(licenseId, language, answerMap);
}
/**
* Verify whether the answer map contains a valid response to all field questions and no answers that don't have a
* corresponding question in the license, using the default language found in the config to check the license
*
* @param licenseId - the ID of the license
* @param fullAnswerMap - the answers to the different field questions
* @return whether the information is valid
*/
public boolean verifyLicenseInformation(String licenseId, Map<String, String> fullAnswerMap) {
return verifyLicenseInformation(licenseId, defaultLanguage, fullAnswerMap);
}
/**
* Verify whether the answer map contains a valid response to all field questions and no answers that don't have a
* corresponding question in the license, using the provided language to check the license
*
* @param licenseId - the ID of the license
* @param language - the language for which to retrieve the full answerMap
* @param fullAnswerMap - the answers to the different field questions
* @return whether the information is valid
*/
public boolean verifyLicenseInformation(String licenseId, String language, Map<String, String> fullAnswerMap) {
CCLicense ccLicense = findOne(licenseId, language);
List<CCLicenseField> ccLicenseFieldList = ccLicense.getCcLicenseFieldList();
for (String field : fullAnswerMap.keySet()) {
CCLicenseField ccLicenseField = findCCLicenseField(field, ccLicenseFieldList);
if (ccLicenseField == null) {
return false;
}
if (!containsAnswerEnum(fullAnswerMap.get(field), ccLicenseField)) {
return false;
}
}
return true;
}
/**
* Retrieve the full answer map containing empty values when an answer for a field was not provided in the
* answerMap, using the default language found in the configuration
*
* @param licenseId - the ID of the license
* @param answerMap - the answers to the different field questions
* @return the answerMap supplemented with all other license fields with a blank answer
*/
public Map<String, String> retrieveFullAnswerMap(String licenseId, Map<String, String> answerMap) {
return retrieveFullAnswerMap(licenseId, defaultLanguage, answerMap);
}
/**
* Retrieve the full answer map for a provided language, containing empty values when an answer for a field was not
* provided in the answerMap.
*
* @param licenseId - the ID of the license
* @param language - the language for which to retrieve the full answerMap
* @param answerMap - the answers to the different field questions
* @return the answerMap supplemented with all other license fields with a blank answer for the provided language
*/
public Map<String, String> retrieveFullAnswerMap(String licenseId, String language, Map<String, String> answerMap) {
CCLicense ccLicense = findOne(licenseId, language);
if (ccLicense == null) {
return null;
}
Map<String, String> fullParamMap = new HashMap<>(answerMap);
List<CCLicenseField> ccLicenseFieldList = ccLicense.getCcLicenseFieldList();
for (CCLicenseField ccLicenseField : ccLicenseFieldList) {
if (!fullParamMap.containsKey(ccLicenseField.getId())) {
fullParamMap.put(ccLicenseField.getId(), "");
}
}
return fullParamMap;
}
private boolean containsAnswerEnum(final String enumAnswer, final CCLicenseField ccLicenseField) {
List<CCLicenseFieldEnum> fieldEnums = ccLicenseField.getFieldEnum();
for (CCLicenseFieldEnum fieldEnum : fieldEnums) {
if (StringUtils.equals(fieldEnum.getId(), enumAnswer)) {
return true;
}
}
return false;
}
private CCLicenseField findCCLicenseField(final String field, final List<CCLicenseField> ccLicenseFieldList) {
for (CCLicenseField ccLicenseField : ccLicenseFieldList) {
if (StringUtils.equals(ccLicenseField.getId(), field)) {
return ccLicenseField;
}
}
return null;
}
}

View File

@@ -11,6 +11,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
@@ -52,7 +53,7 @@ public interface CreativeCommonsService {
* to perform a particular action.
*/
public void setLicenseRDF(Context context, Item item, String licenseRdf)
throws SQLException, IOException, AuthorizeException;
throws SQLException, IOException, AuthorizeException;
/**
@@ -74,19 +75,19 @@ public interface CreativeCommonsService {
*/
public void setLicense(Context context, Item item,
InputStream licenseStm, String mimeType)
throws SQLException, IOException, AuthorizeException;
throws SQLException, IOException, AuthorizeException;
public void removeLicense(Context context, Item item)
throws SQLException, IOException, AuthorizeException;
throws SQLException, IOException, AuthorizeException;
public boolean hasLicense(Context context, Item item)
throws SQLException, IOException;
throws SQLException, IOException;
public String getLicenseURL(Context context, Item item)
throws SQLException, IOException, AuthorizeException;
throws SQLException, IOException, AuthorizeException;
public String getLicenseRDF(Context context, Item item)
throws SQLException, IOException, AuthorizeException;
throws SQLException, IOException, AuthorizeException;
/**
* Get Creative Commons license RDF, returning Bitstream object.
@@ -99,7 +100,7 @@ public interface CreativeCommonsService {
* to perform a particular action.
*/
public Bitstream getLicenseRdfBitstream(Item item)
throws SQLException, IOException, AuthorizeException;
throws SQLException, IOException, AuthorizeException;
/**
* Get Creative Commons license Text, returning Bitstream object.
@@ -114,7 +115,7 @@ public interface CreativeCommonsService {
* is no longer stored (see https://jira.duraspace.org/browse/DS-2604)
*/
public Bitstream getLicenseTextBitstream(Item item)
throws SQLException, IOException, AuthorizeException;
throws SQLException, IOException, AuthorizeException;
/**
* Get a few license-specific properties. We expect these to be cached at
@@ -150,7 +151,7 @@ public interface CreativeCommonsService {
*/
public void removeLicense(Context context, LicenseMetadataValue uriField,
LicenseMetadataValue nameField, Item item)
throws AuthorizeException, IOException, SQLException;
throws AuthorizeException, IOException, SQLException;
/**
* Find all CC Licenses using the default language found in the configuration
@@ -170,7 +171,7 @@ public interface CreativeCommonsService {
/**
* Find the CC License corresponding to the provided ID using the default language found in the configuration
*
* @param id - the ID of the license to be found
* @param id - the ID of the license to be found
* @return the corresponding license if found or null when not found
*/
public CCLicense findOne(String id);
@@ -178,10 +179,72 @@ public interface CreativeCommonsService {
/**
* Find the CC License corresponding to the provided ID and provided language
*
* @param id - the ID of the license to be found
* @param language - the language for which to find the CC License
* @param id - the ID of the license to be found
* @param language - the language for which to find the CC License
* @return the corresponding license if found or null when not found
*/
public CCLicense findOne(String id, String language);
/**
* Retrieve the CC License URI for the provided license ID, based on the provided answers, using the default
* language found in the configuration
*
* @param licenseId - the ID of the license
* @param answerMap - the answers to the different field questions
* @return the corresponding license URI
*/
public String retrieveLicenseUri(String licenseId, Map<String, String> answerMap);
/**
* Retrieve the CC License URI for the provided license ID and language based on the provided answers
*
* @param licenseId - the ID of the license
* @param language - the language for which to find the CC License URI
* @param answerMap - the answers to the different field questions
* @return the corresponding license URI
*/
public String retrieveLicenseUri(String licenseId, String language, Map<String, String> answerMap);
/**
* Retrieve the full answer map containing empty values when an answer for a field was not provided in the
* answerMap, using the default language found in the configuration
*
* @param licenseId - the ID of the license
* @param answerMap - the answers to the different field questions
* @return the answerMap supplemented with all other license fields with a blank answer
*/
public Map<String, String> retrieveFullAnswerMap(String licenseId, Map<String, String> answerMap);
/**
* Retrieve the full answer map for a provided language, containing empty values when an answer for a field was not
* provided in the answerMap.
*
* @param licenseId - the ID of the license
* @param language - the language for which to retrieve the full answerMap
* @param answerMap - the answers to the different field questions
* @return the answerMap supplemented with all other license fields with a blank answer for the provided language
*/
public Map<String, String> retrieveFullAnswerMap(String licenseId, String language, Map<String, String> answerMap);
/**
* Verify whether the answer map contains a valid response to all field questions and no answers that don't have a
* corresponding question in the license, using the default language found in the config to check the license
*
* @param licenseId - the ID of the license
* @param fullAnswerMap - the answers to the different field questions
* @return whether the information is valid
*/
public boolean verifyLicenseInformation(String licenseId, Map<String, String> fullAnswerMap);
/**
* Verify whether the answer map contains a valid response to all field questions and no answers that don't have a
* corresponding question in the license, using the provided language to check the license
*
* @param licenseId - the ID of the license
* @param language - the language for which to retrieve the full answerMap
* @param fullAnswerMap - the answers to the different field questions
* @return whether the information is valid
*/
public boolean verifyLicenseInformation(String licenseId, String language, Map<String, String> fullAnswerMap);
}

View File

@@ -20,6 +20,7 @@ public class MockCCLicenseConnectorServiceImpl extends CCLicenseConnectorService
/**
* Retrieves mock CC Licenses for the provided language
*
* @param language - the language
* @return a map of mocked licenses with the id and the license
*/
@@ -75,4 +76,18 @@ public class MockCCLicenseConnectorServiceImpl extends CCLicenseConnectorService
}
/**
* Retrieve a mock CC License URI
*
* @param licenseId - the ID of the license
* @param language - the language for which to retrieve the full answerMap
* @param answerMap - the answers to the different field questions
* @return the CC License URI
*/
public String retrieveRightsByQuestion(final String licenseId,
final String language,
final Map<String, String> answerMap) {
return "mock-license-uri";
}
}

View File

@@ -0,0 +1,95 @@
/**
* 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 java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.model.SubmissionCCLicenseRest;
import org.dspace.app.rest.utils.Utils;
import org.dspace.license.service.CreativeCommonsService;
import org.dspace.services.RequestService;
import org.dspace.utils.DSpace;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
* This controller is responsible for searching the CC License URI
*/
@RestController
@RequestMapping("/api/" + SubmissionCCLicenseRest.CATEGORY + "/" + SubmissionCCLicenseRest.PLURAL + "/search" +
"/rightsByQuestions")
public class SubmissionCCLicenseSearchController {
private static final Logger log = LogManager.getLogger();
@Autowired
protected Utils utils;
@Autowired
protected CreativeCommonsService creativeCommonsService;
protected RequestService requestService = new DSpace().getRequestService();
/**
* Retrieves the CC License URI based on the license ID and answers in the field questions, provided as parameters
* to this request
*
* @return the CC License URI as a string
*/
@RequestMapping(method = RequestMethod.GET)
@ResponseBody
public String findByRightsByQuestions() {
ServletRequest servletRequest = requestService.getCurrentRequest()
.getServletRequest();
Map<String, String[]> requestParameterMap = servletRequest
.getParameterMap();
Map<String, String> parameterMap = new HashMap<>();
String licenseId = servletRequest.getParameter("license");
if (StringUtils.isBlank(licenseId)) {
throw new DSpaceBadRequestException(
"A \"license\" parameter needs to be provided.");
}
for (String parameter : requestParameterMap.keySet()) {
if (StringUtils.startsWith(parameter, "answer_")) {
String field = StringUtils.substringAfter(parameter, "answer_");
String answer = "";
if (requestParameterMap.get(parameter).length > 0) {
answer = requestParameterMap.get(parameter)[0];
}
parameterMap.put(field, answer);
}
}
Map<String, String> fullParamMap = creativeCommonsService.retrieveFullAnswerMap(licenseId, parameterMap);
if (fullParamMap == null) {
throw new ResourceNotFoundException("No CC License could be matched on the provided ID: " + licenseId);
}
boolean licenseContainsCorrectInfo = creativeCommonsService.verifyLicenseInformation(licenseId, fullParamMap);
if (!licenseContainsCorrectInfo) {
throw new DSpaceBadRequestException(
"The provided answers do not match the required fields for the provided license.");
}
String licenseUri = creativeCommonsService.retrieveLicenseUri(licenseId, fullParamMap);
if (StringUtils.isBlank(licenseUri)) {
throw new ResourceNotFoundException("No CC License URI could be found for ID: " + licenseId);
}
return licenseUri;
}
}

View File

@@ -20,6 +20,7 @@ import org.dspace.app.rest.RestResourceController;
*/
public class SubmissionCCLicenseRest extends BaseObjectRest<String> {
public static final String NAME = "submissioncclicense";
public static final String PLURAL = "submissioncclicenses";
public static final String CATEGORY = RestAddressableModel.CONFIGURATION;

View File

@@ -29,6 +29,7 @@ public class SubmissionCCLicenseRestRepositoryIT extends AbstractControllerInteg
/**
* Test the findAll method form the SubmissionCCLicenseRestRepository
*
* @throws Exception
*/
@Test

View File

@@ -0,0 +1,68 @@
/**
* 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.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.junit.Test;
/**
* Class to the methods from the SubmissionCCLicenseSearchController
* Since the CC Licenses and the corresponding URIs are obtained from the CC License API, a mock service has been
* implemented.
* This mock service will return a fixed set of CC Licenses using a similar structure to the ones obtained from the
* CC License API.
* Refer to {@link org.dspace.license.MockCCLicenseConnectorServiceImpl} for more information
*/
public class SubmissionCCLicenseSearchControllerIT extends AbstractControllerIntegrationTest {
@Test
public void searchRightsByQuestionsTest() throws Exception {
getClient().perform(get(
"/api/config/submissioncclicenses/search/rightsByQuestions?license=license2&answer_license2-field0" +
"=license2-field0-enum1"))
.andExpect(status().isOk())
.andExpect(content().string("mock-license-uri"));
}
@Test
public void searchRightsByQuestionsTestLicenseWithoutFields() throws Exception {
getClient().perform(get("/api/config/submissioncclicenses/search/rightsByQuestions?license=license3"))
.andExpect(status().isOk())
.andExpect(content().string("mock-license-uri"));
}
@Test
public void searchRightsByQuestionsNonExistingLicense() throws Exception {
getClient().perform(get(
"/api/config/submissioncclicenses/search/rightsByQuestions?license=nonexisting-license" +
"&answer_license2-field0=license2-field0-enum1"))
.andExpect(status().isNotFound());
}
@Test
public void searchRightsByQuestionsMissingRequiredAnswer() throws Exception {
getClient().perform(get(
"/api/config/submissioncclicenses/search/rightsByQuestions?license=license1&answer_license1field0" +
"=license1field0enum1"))
.andExpect(status().isBadRequest());
}
@Test
public void searchRightsByQuestionsAdditionalNonExistingAnswer() throws Exception {
getClient().perform(get(
"/api/config/submissioncclicenses/search/rightsByQuestions?license=license2" +
"&answer_license2field0=license2field0enum1&answer_nonexisting=test"))
.andExpect(status().isBadRequest());
}
}

View File

@@ -75,4 +75,18 @@ public class MockCCLicenseConnectorServiceImpl extends CCLicenseConnectorService
}
/**
* Retrieve a mock CC License URI
*
* @param licenseId - the ID of the license
* @param language - the language for which to retrieve the full answerMap
* @param answerMap - the answers to the different field questions
* @return the CC License URI
*/
public String retrieveRightsByQuestion(final String licenseId,
final String language,
final Map<String, String> answerMap) {
return "mock-license-uri";
}
}