diff --git a/pom.xml b/pom.xml index c532746..f9a77d5 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,18 @@ 1.3.2 + + org.apache.httpcomponents + httpclient + 4.3.6 + + + + org.apache.httpcomponents + httpmime + 4.3.6 + + junit diff --git a/src/main/java/org/gcube/common/deposition/ElaborateDepositionVersion.java b/src/main/java/org/gcube/common/deposition/ElaborateDepositionVersion.java index 169b047..4e61ccc 100644 --- a/src/main/java/org/gcube/common/deposition/ElaborateDepositionVersion.java +++ b/src/main/java/org/gcube/common/deposition/ElaborateDepositionVersion.java @@ -178,22 +178,18 @@ public class ElaborateDepositionVersion { logger.trace("DepositionVersion to be managed is:{}", objectMapper.writeValueAsString(depositionVersion)); DepositionVersionExecutor dve = DepositionVersionExecutor.getDefaultExecutor(); + dve.setObjectMapper(objectMapper); dve.setMetadata(getMetadata()); if(depositionVersion.getDOIURL()!=null) { - boolean updateEnabled = false; + Boolean update = depositionVersion.getUpdate(); - try { - String updatedDeposit = Config.getProperties().getProperty("update_deposit"); - updateEnabled = Boolean.valueOf(updatedDeposit); - }catch (Exception e) { - updateEnabled = false; + if(update==null) { + update = depositionVersion.getDeposition().getDefaultUpdate(); } - logger.trace("Deposition update {}", updateEnabled ? "enabled" : "disabled"); - - if(updateEnabled) { + if(update) { logger.info("Going to update deposition {} {}.", name , depositionVersion.getVersion()); dve.setDOIURL(depositionVersion.getDOIURL()); diff --git a/src/main/java/org/gcube/common/deposition/executor/DepositionVersionExecutor.java b/src/main/java/org/gcube/common/deposition/executor/DepositionVersionExecutor.java index 77687e4..c6fe80b 100644 --- a/src/main/java/org/gcube/common/deposition/executor/DepositionVersionExecutor.java +++ b/src/main/java/org/gcube/common/deposition/executor/DepositionVersionExecutor.java @@ -4,6 +4,7 @@ import java.net.URL; import java.util.List; import org.gcube.com.fasterxml.jackson.databind.JsonNode; +import org.gcube.com.fasterxml.jackson.databind.ObjectMapper; import org.gcube.common.deposition.executor.zenodo.ZenodoDepositionVersionExecutor; import org.gcube.common.deposition.model.DepositionFile; @@ -16,10 +17,19 @@ public abstract class DepositionVersionExecutor { return new ZenodoDepositionVersionExecutor(); } + protected ObjectMapper objectMapper; protected JsonNode metadata; protected URL doiURL; protected List depositionFiles; + public ObjectMapper getObjectMapper() { + return objectMapper; + } + + public void setObjectMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + public JsonNode getMetadata() { return metadata; } diff --git a/src/main/java/org/gcube/common/deposition/executor/zenodo/ZenodoDepositionVersionExecutor.java b/src/main/java/org/gcube/common/deposition/executor/zenodo/ZenodoDepositionVersionExecutor.java index b2524af..e72caac 100644 --- a/src/main/java/org/gcube/common/deposition/executor/zenodo/ZenodoDepositionVersionExecutor.java +++ b/src/main/java/org/gcube/common/deposition/executor/zenodo/ZenodoDepositionVersionExecutor.java @@ -1,16 +1,34 @@ package org.gcube.common.deposition.executor.zenodo; +import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.commons.io.FileUtils; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.gcube.com.fasterxml.jackson.databind.JsonNode; +import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode; +import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode; import org.gcube.common.deposition.config.Config; import org.gcube.common.deposition.executor.DepositionVersionExecutor; import org.gcube.common.deposition.model.DepositionFile; +import org.gcube.common.gxhttp.request.GXHTTPStringRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,13 +39,43 @@ public class ZenodoDepositionVersionExecutor extends DepositionVersionExecutor { private static final Logger logger = LoggerFactory.getLogger(ZenodoDepositionVersionExecutor.class); + public static final String GUCBE_ZENODO_SOFTWARE_DEPOSIT = "gCubeSoftwareDeposit"; + + public static final String METADATA_FIELD_NAME = "metadata"; + + public static final String ZENODO_DOI_URL_BASE_PATH = "https://doi.org/10.5072/zenodo."; + + public static final String DEPOSITIONS_COLLECTION_PATH = "/api/deposit/depositions"; + public static final String DEPOSITION_PATH = DEPOSITIONS_COLLECTION_PATH + ":id"; + public static final String DEPOSTION_FILES_PATH = DEPOSITION_PATH + "/files"; + + public static final String DEPOSTION_NEW_VERSION_PATH = DEPOSITION_PATH + "/actions/newversion"; + public static final String DEPOSTION_PUBLISH_PATH = DEPOSITION_PATH + "/actions/publish"; + + protected URL zenodoBaseURL; protected String accessToken; + protected String zenodoID; + protected JsonNode response; + + protected String getZenodoIDFromDOIURL(URL doiURL) { + return doiURL.toString().replace(ZENODO_DOI_URL_BASE_PATH, ""); + } + + protected Map getAccessTokenQueryParamters() { + Map map = new HashMap<>(); + map.put("access_token", accessToken); + return map; + } + public ZenodoDepositionVersionExecutor() { try { this.zenodoBaseURL = new URL(Config.getProperties().getProperty("zenodo_base_url")); this.accessToken = Config.getProperties().getProperty("zenodo_access_token"); + if(doiURL!=null) { + + } }catch (Exception e) { throw new RuntimeException(e); } @@ -52,6 +100,73 @@ public class ZenodoDepositionVersionExecutor extends DepositionVersionExecutor { return file; } + protected void addFilesToDeposition(List files ) throws Exception { + String depositID = getZenodoIDFromDOIURL(doiURL); + String newFilePath = DEPOSTION_FILES_PATH.replace(":id", depositID); + URL url = new URL(zenodoBaseURL, newFilePath); + + for(File file : files) { + + CloseableHttpClient httpClient = HttpClients.createDefault(); + HttpPost uploadFile = new HttpPost(url.toURI()); + uploadFile.addHeader("access_token", accessToken); + uploadFile.addHeader("Accept", "application/json"); + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + builder.addTextBody("name", file.getName(), ContentType.TEXT_PLAIN); + builder.addBinaryBody("file", new FileInputStream(file), + ContentType.APPLICATION_OCTET_STREAM, + file.getName() + ); + + HttpEntity multipart = builder.build(); + uploadFile.setEntity(multipart); + + CloseableHttpResponse response = httpClient.execute(uploadFile); + HttpEntity responseEntity = response.getEntity(); + StringBuilder result = getStringBuilder(responseEntity.getContent()); + String res = result.toString(); + logger.debug("Created file from {} response is {}", file.getAbsolutePath(), res); + + // JsonNode jsonNode = objectMapper.readTree(res); + + } + } + + protected void updateMetadata() throws Exception { + GXHTTPStringRequest gxHTTPStringRequest = GXHTTPStringRequest.newRequest(zenodoBaseURL.toString()); + gxHTTPStringRequest.from(GUCBE_ZENODO_SOFTWARE_DEPOSIT); + gxHTTPStringRequest.queryParams(getAccessTokenQueryParamters()); + gxHTTPStringRequest.header("Content-Type", "application/json"); + gxHTTPStringRequest.header("Accept", "application/json"); + + String conceptID = getZenodoIDFromDOIURL(doiURL); + gxHTTPStringRequest.path(DEPOSITION_PATH.replace(":id", conceptID)); + + + ObjectNode body = objectMapper.createObjectNode(); + body.set(METADATA_FIELD_NAME, metadata); + + HttpURLConnection httpURLConnection = gxHTTPStringRequest.put(objectMapper.writeValueAsString(body)); + JsonNode jsonNode = getResponse(httpURLConnection); + + + } + + protected void publish() throws Exception { + GXHTTPStringRequest gxHTTPStringRequest = GXHTTPStringRequest.newRequest(zenodoBaseURL.toString()); + gxHTTPStringRequest.from(GUCBE_ZENODO_SOFTWARE_DEPOSIT); + gxHTTPStringRequest.queryParams(getAccessTokenQueryParamters()); + gxHTTPStringRequest.header("Content-Type", "application/json"); + gxHTTPStringRequest.header("Accept", "application/json"); + + String id = getZenodoIDFromDOIURL(doiURL); + gxHTTPStringRequest.path(DEPOSTION_PUBLISH_PATH.replace(":id", id)); + + HttpURLConnection httpURLConnection = gxHTTPStringRequest.post(); + JsonNode jsonNode = getResponse(httpURLConnection); + + } + protected void finalize() throws Exception { List files = new ArrayList<>(); for(DepositionFile df : depositionFiles) { @@ -61,10 +176,14 @@ public class ZenodoDepositionVersionExecutor extends DepositionVersionExecutor { Thread.sleep(TimeUnit.SECONDS.toMillis(5)); - // TODO Add depositionFiles - // TODO Update deposit metadata + //Add depositionFiles + addFilesToDeposition(files); - // TODO Publish the version + //Update deposit metadata + updateMetadata(); + + // Publish the version + publish(); for(File file : files) { if(!file.exists()) { @@ -78,9 +197,79 @@ public class ZenodoDepositionVersionExecutor extends DepositionVersionExecutor { } } + protected StringBuilder getStringBuilder(InputStream inputStream) throws IOException { + StringBuilder result = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { + String line; + while ((line = reader.readLine()) != null) { + result.append(line); + } + } + + return result; + } + + public JsonNode getResponse(HttpURLConnection connection) throws Exception { + + try { + + int responseCode = connection.getResponseCode(); + String responseMessage = connection.getResponseMessage(); + + logger.trace("Response {} {}", responseCode, responseMessage); + + if(connection.getRequestMethod().compareTo("HEAD")==0) { + if(responseCode == HttpURLConnection.HTTP_NO_CONTENT) { + return null; + } + + if(responseCode == HttpURLConnection.HTTP_NOT_FOUND) { + throw new RuntimeException(responseCode + " " + responseMessage); + } + if(responseCode == HttpURLConnection.HTTP_FORBIDDEN) { + throw new RuntimeException(responseCode + " " + responseMessage); + } + } + + if(responseCode >= HttpURLConnection.HTTP_BAD_REQUEST) { + + InputStream inputStream = connection.getErrorStream(); + StringBuilder result = getStringBuilder(inputStream); + + String res = result.toString(); + + throw new RuntimeException(res); + + } + + StringBuilder result = getStringBuilder(connection.getInputStream()); + + String res = result.toString(); + logger.trace("Server returned content : {}", res); + + return objectMapper.readTree(res); + } finally { + connection.disconnect(); + } + } + @Override public void create() throws Exception { - // TODO New deposit on Zenodo + GXHTTPStringRequest gxHTTPStringRequest = GXHTTPStringRequest.newRequest(zenodoBaseURL.toString()); + gxHTTPStringRequest.from(GUCBE_ZENODO_SOFTWARE_DEPOSIT); + gxHTTPStringRequest.queryParams(getAccessTokenQueryParamters()); + gxHTTPStringRequest.header("Content-Type", "application/json"); + gxHTTPStringRequest.header("Accept", "application/json"); + gxHTTPStringRequest.path(DEPOSITIONS_COLLECTION_PATH); + + ObjectNode body = objectMapper.createObjectNode(); + body.set(METADATA_FIELD_NAME, metadata); + + HttpURLConnection httpURLConnection = gxHTTPStringRequest.post(objectMapper.writeValueAsString(body)); + response = getResponse(httpURLConnection); + + doiURL = new URL(ZENODO_DOI_URL_BASE_PATH + response.get("conceptrecid").asText()); + finalize(); } @@ -89,10 +278,44 @@ public class ZenodoDepositionVersionExecutor extends DepositionVersionExecutor { // TODO edit a published version } + /** + * Remove previous depositionFiles + * @throws Exception + */ + protected void deletePreviousFiles() throws Exception { + ArrayNode files = (ArrayNode) response.get("files"); + for(int i=0; i defaultFiles; @@ -32,34 +35,22 @@ public class Deposition { return name; } - public void setName(String name) { - this.name = name; + public Boolean getDefaultUpdate() { + return defaultUpdate; } public List getDefaultFiles() { return defaultFiles; } - public void setDefaultFiles(List defaultFiles) { - this.defaultFiles = defaultFiles; - } - public String getDefaultCodeLocation() { return defaultCodeLocation; } - public void setDefaultCodeLocation(String defaultCodeLocation) { - this.defaultCodeLocation= defaultCodeLocation; - } - public String getCodeLocationAdditionalDescription() { return codeLocationAdditionalDescription; } - public void setCodeLocationAdditionalDescription(String codeLocationAdditionalDescription) { - this.codeLocationAdditionalDescription = codeLocationAdditionalDescription; - } - public List getDepositionVersions() { return depositionVersions; } @@ -68,8 +59,4 @@ public class Deposition { return metadata; } - public void setMetadata(JsonNode metadata) { - this.metadata = metadata; - } - } diff --git a/src/main/java/org/gcube/common/deposition/model/DepositionVersion.java b/src/main/java/org/gcube/common/deposition/model/DepositionVersion.java index 10d5488..d7dc255 100644 --- a/src/main/java/org/gcube/common/deposition/model/DepositionVersion.java +++ b/src/main/java/org/gcube/common/deposition/model/DepositionVersion.java @@ -59,6 +59,9 @@ public class DepositionVersion { @JsonProperty(value = "doi_url") protected URL doiURL; + + @JsonProperty(value = "update") + protected Boolean update; public DepositionVersion() { this.noCodeLocation = false; @@ -183,6 +186,14 @@ public class DepositionVersion { this.doiURL = null; } } + + public Boolean getUpdate() { + return update; + } + + public void setUpdate(Boolean update) { + this.update = update; + } diff --git a/src/test/resources/zenodo-deposit.json b/src/test/resources/zenodo-deposit.json index 4b31347..d59cdfb 100644 --- a/src/test/resources/zenodo-deposit.json +++ b/src/test/resources/zenodo-deposit.json @@ -1,5 +1,6 @@ { "name": "gcat", + "default_update": false, "default_files": [ { @@ -31,7 +32,8 @@ "release_ticket": null, "code_location": null, "concept_doi_url": "https://doi.org/10.5072/zenodo.1139445", - "doi_url": "https://doi.org/10.5072/zenodo.1139446" + "doi_url": "https://doi.org/10.5072/zenodo.1139446", + "update": true }, { "version": "1.1.0",