package org.gcube.data.publishing.ckan2zenodo.clients; import java.io.File; import java.io.IOException; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Future; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Entity; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.gcube.common.resources.gcore.ServiceEndpoint; import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint; import org.gcube.data.publishing.ckan2zenodo.Fixer; import org.gcube.data.publishing.ckan2zenodo.LocalConfiguration; import org.gcube.data.publishing.ckan2zenodo.LocalConfiguration.Configuration; import org.gcube.data.publishing.ckan2zenodo.commons.IS; import org.gcube.data.publishing.ckan2zenodo.model.ZenodoCredentials; import org.gcube.data.publishing.ckan2zenodo.model.faults.ConfigurationException; import org.gcube.data.publishing.ckan2zenodo.model.faults.ZenodoException; import org.gcube.data.publishing.ckan2zenodo.model.zenodo.DepositionMetadata; import org.gcube.data.publishing.ckan2zenodo.model.zenodo.FileDeposition; import org.gcube.data.publishing.ckan2zenodo.model.zenodo.ZenodoDeposition; import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.media.multipart.FormDataMultiPart; import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.media.multipart.file.FileDataBodyPart; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @RequiredArgsConstructor @Slf4j public class Zenodo { private static final String CONTENT_TYPE="application/json"; private static final String DEPOSITION_BASE_URL="deposit/depositions"; private static final String PUBLISH_URL_POST="actions/publish"; private static final String NEW_VERSION_URL_POST="actions/newversion"; private static final String UNLOCK_URL_POST="actions/edit"; private static final String ACCESS_TOKEN="access_token"; private static ObjectMapper mapper = new ObjectMapper(); static { mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false); mapper.configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false); mapper.setSerializationInclusion(Include.NON_NULL); // mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); } public static final Zenodo get() throws ConfigurationException { String eprCategory=LocalConfiguration.getProperty(Configuration.ZENODO_ENDPOINT_CATEGORY); String eprPlatform=LocalConfiguration.getProperty(Configuration.ZENODO_ENDPOINT_PLATFORM); List eps=IS.queryForServiceEndpoints(eprCategory,eprPlatform); if(eps.isEmpty()) throw new ConfigurationException("No Zenodo Credentials found ("+eprCategory+" : "+eprPlatform+")"); if(eps.size()>1) throw new ConfigurationException("Multiple ["+eps.size()+"] Zenodo Credentials found ("+eprCategory+" : "+eprPlatform+")"); AccessPoint se=eps.get(0).profile().accessPoints().iterator().next(); ZenodoCredentials toSet=new ZenodoCredentials(IS.decryptString(se.password()), se.address()); return new Zenodo(toSet); } @Getter @NonNull private ZenodoCredentials credentials; Client client; private synchronized Client getWebClient() { if(client==null) { client = ClientBuilder.newClient() .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true); client.register(MultiPartFeature.class); } return client; } public ZenodoDeposition readDeposition(Integer id) throws ZenodoException { Response resp= getWebClient().target(credentials.getBaseUrl()). path(DEPOSITION_BASE_URL).path(id+""). queryParam(ACCESS_TOKEN, credentials.getKey()).request(CONTENT_TYPE) .get(); return check(resp,ZenodoDeposition.class); } private static T check(Response resp, Class clazz) throws ZenodoException{ if(resp.getStatus()<200||resp.getStatus()>=300) { String remoteMessage=resp.readEntity(String.class); Integer httpCode=resp.getStatus(); ZenodoException e=new ZenodoException("RESP STATUS IS "+httpCode+". Message : "+remoteMessage); e.setRemoteMessage(remoteMessage); e.setResponseHTTPCode(httpCode); throw e; }else { if(clazz==null) return null; String respString=resp.readEntity(String.class); try { return mapper.readValue(Fixer.fixIncoming(respString), clazz); } catch (IOException e) { throw new ZenodoException("Unable to parse response from Zenodo. Content was : \n "+respString,e); } } } public ZenodoDeposition updateMetadata(ZenodoDeposition dep) throws ZenodoException { return updateMetadata(dep.getId(), dep.getMetadata()); } public FileDeposition uploadFile(ZenodoDeposition deposition, String toUploadName,File toUpload) throws ZenodoException { final ZenodoDeposition dep=(deposition.getSubmitted())?newVersion(deposition.getId()):deposition; log.info("Pushing File {} to Deposition {}",toUploadName,dep); Callable call=new Callable() { @Override public Response call() throws Exception { try { //upload FormDataMultiPart multi=new FormDataMultiPart(); FileDataBodyPart fileDataBodyPart = new FileDataBodyPart("file", toUpload,MediaType.APPLICATION_OCTET_STREAM_TYPE); multi.field("name", toUploadName); multi.bodyPart(fileDataBodyPart); log.debug("Starting transfer of "+toUploadName+" into "+dep.getId()); Response toReturn=getWebClient().target(credentials.getBaseUrl()). path(DEPOSITION_BASE_URL).path(dep.getId()+"").path("files"). queryParam(ACCESS_TOKEN, credentials.getKey()).request(CONTENT_TYPE) .post(Entity.entity(multi,multi.getMediaType())); log.debug("DONE."); return toReturn; }catch(Throwable e) { throw new ZenodoException("Unable to transfer file "+toUploadName,e); } } }; log.debug("Submitting request to upload "+toUploadName+" to Manager"); Future resp=FileUploaderManager.submitForResponse(call); try { return check(resp.get(),FileDeposition.class); }catch(ZenodoException z) { throw z; }catch(Throwable t) { throw new ZenodoException(t.getMessage(),t); } //return } public void deleteFile(ZenodoDeposition dep,FileDeposition toDelete) throws ZenodoException { if(dep.getSubmitted()) dep=newVersion(dep.getId()); Response resp = getWebClient().target(credentials.getBaseUrl()). path(DEPOSITION_BASE_URL). path(dep.getId()+""). path("files"). path(toDelete.getId()). queryParam(ACCESS_TOKEN, credentials.getKey()).request(CONTENT_TYPE) .delete(); try { check(resp,null); }catch(ZenodoException z) { throw z; }catch(Throwable t) { throw new ZenodoException(t.getMessage(),t); } } public ZenodoDeposition newVersion(Integer originalId) throws ZenodoException { Response resp = getWebClient().target(credentials.getBaseUrl()). path(DEPOSITION_BASE_URL). path(originalId+""). path(NEW_VERSION_URL_POST). queryParam(ACCESS_TOKEN, credentials.getKey()).request(CONTENT_TYPE) .post(Entity.json("{}")); ZenodoDeposition d=check(resp,ZenodoDeposition.class); //GET LATEST DRAFT resp=getWebClient().target(d.getLinks().getLatest_draft()). queryParam(ACCESS_TOKEN, credentials.getKey()).request(CONTENT_TYPE).get(); try { return check(resp,ZenodoDeposition.class); }catch(ZenodoException z) { throw z; }catch(Throwable t) { throw new ZenodoException(t.getMessage(),t); } } private ZenodoDeposition updateMetadata(Integer depositionId,DepositionMetadata meta) throws ZenodoException { try{ String serialized="{\"metadata\":"+Fixer.fixIncoming(mapper.writeValueAsString(meta))+"}"; try { Response resp = getWebClient().target(credentials.getBaseUrl()). path(DEPOSITION_BASE_URL).path(depositionId+""). queryParam(ACCESS_TOKEN, credentials.getKey()).request(CONTENT_TYPE) .put(Entity.json(serialized)); return check(resp,ZenodoDeposition.class); }catch(Throwable t) { log.debug("Error while tryin to update "+serialized); throw t; } }catch(JsonProcessingException e) { log.debug("Error while parsing "+meta,e); throw new ZenodoException("Internal error.",e); } } public ZenodoDeposition unlockPublished(Integer depositionId) throws ZenodoException { Response resp = getWebClient().target(credentials.getBaseUrl()). path(DEPOSITION_BASE_URL). path(depositionId+""). path(UNLOCK_URL_POST). queryParam(ACCESS_TOKEN, credentials.getKey()).request(CONTENT_TYPE) .post(Entity.json("{}")); return check(resp,ZenodoDeposition.class); } public void deleteDeposition(Integer depositionId) throws ZenodoException { Response resp = getWebClient().target(credentials.getBaseUrl()). path(DEPOSITION_BASE_URL).path(depositionId+""). queryParam(ACCESS_TOKEN, credentials.getKey()).request(CONTENT_TYPE) .delete(); check(resp,null); } public ZenodoDeposition createNew() throws ZenodoException { Response resp = getWebClient().target(credentials.getBaseUrl()). path(DEPOSITION_BASE_URL). queryParam(ACCESS_TOKEN, credentials.getKey()).request(CONTENT_TYPE) .post(Entity.json("{}")); return check(resp,ZenodoDeposition.class); } public ZenodoDeposition publish(ZenodoDeposition dep) throws ZenodoException{ return publish(dep.getId()); } private ZenodoDeposition publish(Integer depositionId) throws ZenodoException{ Response resp = getWebClient().target(credentials.getBaseUrl()). path(DEPOSITION_BASE_URL). path(depositionId+""). path(PUBLISH_URL_POST). queryParam(ACCESS_TOKEN, credentials.getKey()).request(CONTENT_TYPE) .post(Entity.json("{}")); return check(resp,ZenodoDeposition.class); } }