289 lines
16 KiB
Java
289 lines
16 KiB
Java
package eu.eudat.depositinterface.zenodorepository.interfaces;
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import eu.eudat.depositinterface.models.DMPDepositModel;
|
|
import eu.eudat.depositinterface.models.FileEnvelope;
|
|
import eu.eudat.depositinterface.repository.RepositoryDeposit;
|
|
import eu.eudat.depositinterface.repository.RepositoryDepositConfiguration;
|
|
import eu.eudat.depositinterface.zenodorepository.config.ConfigLoader;
|
|
import eu.eudat.depositinterface.zenodorepository.config.ZenodoConfig;
|
|
import eu.eudat.depositinterface.zenodorepository.mapper.DMPToZenodoMapper;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.core.env.Environment;
|
|
import org.springframework.core.io.FileSystemResource;
|
|
import org.springframework.http.*;
|
|
import org.springframework.stereotype.Component;
|
|
import org.springframework.util.LinkedMultiValueMap;
|
|
import org.springframework.util.MultiValueMap;
|
|
import org.springframework.web.client.HttpClientErrorException;
|
|
import org.springframework.web.client.HttpServerErrorException;
|
|
import org.springframework.web.client.RestTemplate;
|
|
|
|
import java.io.*;
|
|
import java.nio.file.Files;
|
|
import java.util.*;
|
|
import java.util.stream.Collectors;
|
|
|
|
@Component
|
|
public class ZenodoDeposit implements RepositoryDeposit {
|
|
private static final Logger logger = LoggerFactory.getLogger(ZenodoDeposit.class);
|
|
private static final ObjectMapper objectMapper = new ObjectMapper();
|
|
|
|
private final ConfigLoader configLoader;
|
|
private final Environment environment;
|
|
|
|
@Autowired
|
|
public ZenodoDeposit(ConfigLoader configLoader, Environment environment){
|
|
this.configLoader = configLoader;
|
|
this.environment = environment;
|
|
}
|
|
|
|
@Override
|
|
public String deposit(String repositoryId, DMPDepositModel dmpDepositModel, String zenodoToken) throws Exception {
|
|
|
|
RepositoryDepositConfiguration conf = this.getConfiguration().stream().filter(x -> x.getRepositoryId().equals(repositoryId)).findFirst().orElse(null);
|
|
|
|
if(conf != null) {
|
|
|
|
if (zenodoToken == null) {
|
|
zenodoToken = conf.getAccessToken();
|
|
}
|
|
|
|
String zenodoUrl = conf.getRepositoryUrl();
|
|
|
|
// First step, post call to Zenodo, to create the entry.
|
|
RestTemplate restTemplate = new RestTemplate();
|
|
HttpHeaders headers = new HttpHeaders();
|
|
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
|
|
|
ZenodoConfig zenodoConfig = this.configLoader.getZenodoConfig().stream().filter(x -> x.getRepositoryId().equals(repositoryId)).findFirst().orElse(null);
|
|
eu.eudat.depositinterface.zenodorepository.models.ZenodoDeposit deposit = DMPToZenodoMapper.fromDMP(dmpDepositModel, zenodoConfig);
|
|
|
|
HttpEntity<eu.eudat.depositinterface.zenodorepository.models.ZenodoDeposit> request = new HttpEntity<>(deposit, headers);
|
|
Map createResponse;
|
|
LinkedHashMap<String, String> links;
|
|
String previousDOI = dmpDepositModel.getPreviousDOI();
|
|
String unpublishedUrl = null;
|
|
String publishUrl;
|
|
String finalDoi;
|
|
try {
|
|
|
|
if (previousDOI == null) {
|
|
String createUrl = zenodoUrl + "deposit/depositions" + "?access_token=" + zenodoToken;
|
|
createResponse = restTemplate.postForEntity(createUrl, request, Map.class).getBody();
|
|
links = (LinkedHashMap<String, String>) createResponse.get("links");
|
|
} else {
|
|
unpublishedUrl = this.getUnpublishedDOI(zenodoUrl, previousDOI, zenodoToken, dmpDepositModel.getVersion());
|
|
if (unpublishedUrl == null) {
|
|
//It requires more than one step to create a new version
|
|
//First, get the deposit related to the concept DOI
|
|
String listUrl = zenodoUrl + "deposit/depositions" + "?q=conceptdoi:\"" + previousDOI + "\"&access_token=" + zenodoToken;
|
|
logger.debug("listUrl = " + listUrl);
|
|
ResponseEntity<Map[]> listResponses = restTemplate.getForEntity(listUrl, Map[].class);
|
|
createResponse = listResponses.getBody()[0];
|
|
logger.debug("createResponse-previousDoi:");
|
|
logger.debug(objectMapper.writeValueAsString(createResponse));
|
|
links = (LinkedHashMap<String, String>) createResponse.get("links");
|
|
//Second, make the new version (not in the links?)
|
|
String newVersionUrl = links.get("latest_draft") + "/actions/newversion" + "?access_token=" + zenodoToken;
|
|
logger.debug("new version url: " + newVersionUrl);
|
|
createResponse = restTemplate.postForObject(newVersionUrl, null, Map.class);
|
|
logger.debug("createResponse-newVersion:");
|
|
logger.debug(objectMapper.writeValueAsString(createResponse));
|
|
links = (LinkedHashMap<String, String>) createResponse.get("links");
|
|
//Third, get the new deposit
|
|
String latestDraftUrl = links.get("latest_draft") + "?access_token=" + zenodoToken;
|
|
createResponse = restTemplate.getForObject(latestDraftUrl, Map.class);
|
|
logger.debug("createResponse-latestDraft:");
|
|
logger.debug(objectMapper.writeValueAsString(createResponse));
|
|
links = (LinkedHashMap<String, String>) createResponse.get("links");
|
|
//At this point it might fail to perform the next requests so enclose them with try catch
|
|
try {
|
|
//Forth, update the new deposit's metadata
|
|
String updateUrl = links.get("self") + "?access_token=" + zenodoToken;
|
|
restTemplate.put(updateUrl, request);
|
|
//And finally remove pre-existing files from it
|
|
String fileListUrl = links.get("self") + "/files" + "?access_token=" + zenodoToken;
|
|
ResponseEntity<Map[]> fileListResponse = restTemplate.getForEntity(fileListUrl, Map[].class);
|
|
for (Map file : fileListResponse.getBody()) {
|
|
String fileDeleteUrl = links.get("self") + "/files/" + file.get("id") + "?access_token=" + zenodoToken;
|
|
restTemplate.delete(fileDeleteUrl);
|
|
}
|
|
} catch (Exception e) {
|
|
//In case the last two steps fail delete the latest Deposit it in order to create a new one (only one at a time is allowed)
|
|
restTemplate.delete(latestDraftUrl);
|
|
throw e;
|
|
}
|
|
} else {
|
|
String listUrl = zenodoUrl + "deposit/depositions" + "?q=conceptdoi:\"" + previousDOI + "\"&access_token=" + zenodoToken;
|
|
ResponseEntity<Map[]> listResponses = restTemplate.getForEntity(listUrl, Map[].class);
|
|
createResponse = listResponses.getBody()[0];
|
|
links = (LinkedHashMap<String, String>) createResponse.get("links");
|
|
}
|
|
}
|
|
|
|
if (unpublishedUrl == null) {
|
|
// Second step, add the file to the entry.
|
|
FileEnvelope pdfEnvelope = dmpDepositModel.getPdfFile();
|
|
FileSystemResource fileSystemResource = new FileSystemResource(pdfEnvelope.getFile());
|
|
HttpHeaders fileHeaders = new HttpHeaders();
|
|
fileHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
|
|
HttpEntity<FileSystemResource> addFileMapRequest = new HttpEntity<>(fileSystemResource, fileHeaders);
|
|
|
|
String addFileUrl = links.get("bucket") + "/" + pdfEnvelope.getFilename() + "?access_token=" + zenodoToken;
|
|
restTemplate.put(addFileUrl, addFileMapRequest);
|
|
|
|
FileEnvelope rdaJsonEnvelope = dmpDepositModel.getRdaJsonFile();
|
|
HttpHeaders responseHeaders = new HttpHeaders();
|
|
responseHeaders.setContentLength(rdaJsonEnvelope.getFile().length());
|
|
responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
|
|
responseHeaders.set("Content-Disposition", "attachment;filename=" + rdaJsonEnvelope.getFilename());
|
|
responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition");
|
|
responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type");
|
|
|
|
byte[] content = Files.readAllBytes(rdaJsonEnvelope.getFile().toPath());
|
|
|
|
ResponseEntity<byte[]> jsonFile = new ResponseEntity<>(content, responseHeaders, HttpStatus.OK);
|
|
|
|
UUID jsonFileUUID = UUID.randomUUID();
|
|
File tempJsonFile = new File(this.environment.getProperty("zenodo_plugin.storage.temp") + jsonFileUUID + ".json");
|
|
try (FileOutputStream jsonFos = new FileOutputStream(tempJsonFile)) {
|
|
jsonFos.write(jsonFile.getBody());
|
|
jsonFos.flush();
|
|
}
|
|
fileSystemResource = new FileSystemResource(tempJsonFile);
|
|
HttpHeaders jsonHeaders = new HttpHeaders();
|
|
jsonHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
|
addFileMapRequest = new HttpEntity<>(fileSystemResource, jsonHeaders);
|
|
String jsonFileName = jsonFile.getHeaders().get("Content-Disposition").get(0).substring(jsonFile.getHeaders().get("Content-Disposition").get(0).lastIndexOf('=') + 1);
|
|
addFileUrl = links.get("bucket") + "/" + jsonFileName + "?access_token=" + zenodoToken;
|
|
restTemplate.put(addFileUrl, addFileMapRequest);
|
|
Files.deleteIfExists(tempJsonFile.toPath());
|
|
|
|
if (dmpDepositModel.getSupportingFilesZip() != null) {
|
|
File supportinFilesZip = dmpDepositModel.getSupportingFilesZip();
|
|
String supportinFilesZipName = dmpDepositModel.getSupportingFilesZip().getName();
|
|
fileSystemResource = new FileSystemResource(supportinFilesZip);
|
|
addFileMapRequest = new HttpEntity<>(fileSystemResource, null);
|
|
|
|
addFileUrl = links.get("bucket") + "/" + supportinFilesZipName + "?access_token=" + zenodoToken;
|
|
restTemplate.put(addFileUrl, addFileMapRequest);
|
|
}
|
|
|
|
// Third post call to Zenodo to publish the entry and return the DOI.
|
|
publishUrl = links.get("publish") + "?access_token=" + zenodoToken;
|
|
} else {
|
|
publishUrl = unpublishedUrl + "?access_token=" + zenodoToken;
|
|
}
|
|
|
|
return this.publish(publishUrl);
|
|
|
|
} catch (HttpClientErrorException | HttpServerErrorException ex) {
|
|
Map<String, String> parsedException = objectMapper.readValue(ex.getResponseBodyAsString(), HashMap.class);
|
|
throw new IOException(parsedException.get("message"), ex);
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
private String publish(String publishUrl){
|
|
RestTemplate restTemplate = new RestTemplate();
|
|
Map<String, Object> publishResponce = restTemplate.postForObject(publishUrl, "", Map.class);
|
|
return (String) publishResponce.get("conceptdoi");
|
|
}
|
|
|
|
|
|
@Override
|
|
public List<RepositoryDepositConfiguration> getConfiguration() {
|
|
List<ZenodoConfig> zenodoConfigs = this.configLoader.getZenodoConfig();
|
|
return (zenodoConfigs != null) ? zenodoConfigs.stream().map(ZenodoConfig::toRepoConfig).collect(Collectors.toList()) : null;
|
|
}
|
|
|
|
@Override
|
|
public String authenticate(String repositoryId, String code){
|
|
|
|
RepositoryDepositConfiguration conf = this.getConfiguration().stream().filter(x -> x.getRepositoryId().equals(repositoryId)).findFirst().orElse(null);
|
|
|
|
if(conf != null) {
|
|
|
|
RestTemplate restTemplate = new RestTemplate();
|
|
HttpHeaders headers = new HttpHeaders();
|
|
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
|
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
|
|
|
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
|
|
map.add("client_id", conf.getRepositoryClientId());
|
|
map.add("client_secret", conf.getRepositoryClientSecret());
|
|
map.add("grant_type", "authorization_code");
|
|
map.add("code", code);
|
|
map.add("redirect_uri", conf.getRedirectUri());
|
|
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
|
|
|
|
try {
|
|
Map<String, Object> values = restTemplate.postForObject(conf.getRepositoryAccessTokenUrl(), request, Map.class);
|
|
//ZenodoResponseToken zenodoResponseToken = new ZenodoResponseToken();
|
|
Map<String, Object> user = (Map<String, Object>) values.get("user");
|
|
// zenodoResponseToken.setUserId((String) user.get("id"));
|
|
// zenodoResponseToken.setEmail((String) user.get("email"));
|
|
// zenodoResponseToken.setExpiresIn((Integer) values.get("expires_in"));
|
|
// zenodoResponseToken.setAccessToken((String) values.get("access_token"));
|
|
// zenodoResponseToken.setRefreshToken((String) values.get("refresh_token"));
|
|
|
|
//return zenodoResponseToken;
|
|
return (String) values.get("access_token");
|
|
} catch (HttpClientErrorException ex) {
|
|
logger.error(ex.getResponseBodyAsString(), ex);
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public String getLogo(String repositoryId) {
|
|
RepositoryDepositConfiguration conf = this.getConfiguration().stream().filter(x -> x.getRepositoryId().equals(repositoryId)).findFirst().orElse(null);
|
|
if(conf != null) {
|
|
if(conf.isHasLogo()){
|
|
byte[] logo = this.configLoader.getLogo(repositoryId);
|
|
return (logo != null && logo.length != 0) ? Base64.getEncoder().encodeToString(logo) : null;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private String getUnpublishedDOI(String zenodoUrl, String DOI, String token, Integer version) {
|
|
try {
|
|
RestTemplate restTemplate = new RestTemplate();
|
|
HttpHeaders headers = new HttpHeaders();
|
|
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
|
Map createResponse = null;
|
|
LinkedHashMap<String, String> links = null;
|
|
LinkedHashMap<String, String> metadata = null;
|
|
String listUrl = zenodoUrl + "deposit/depositions" + "?q=conceptdoi:\"" + DOI + "\"&access_token=" + token;
|
|
ResponseEntity<Map[]> listResponses = restTemplate.getForEntity(listUrl, Map[].class);
|
|
createResponse = listResponses.getBody()[0];
|
|
metadata = (LinkedHashMap<String, String>) createResponse.get("metadata");
|
|
links = (LinkedHashMap<String, String>) createResponse.get("links");
|
|
|
|
if (metadata.get("version").equals(version.toString())) {
|
|
return links.get("publish");
|
|
} else {
|
|
return null;
|
|
}
|
|
}catch (Exception e) {
|
|
logger.warn(e.getMessage(), e);
|
|
return null;
|
|
}
|
|
}
|
|
}
|