diff --git a/core/pom.xml b/core/pom.xml index 32c2bdd..07a8016 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -45,7 +45,7 @@ gr.cite.opendmp repositorydepositbase - 1.0.0-SNAPSHOT + 2.0.0 @@ -58,6 +58,11 @@ json 20160810 + + gr.cite + cache + 2.1.0 + diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/configuration/funder/FunderProperties.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/configuration/funder/FunderProperties.java index 448b172..2a1bc77 100644 --- a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/configuration/funder/FunderProperties.java +++ b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/configuration/funder/FunderProperties.java @@ -8,26 +8,19 @@ import java.util.List; @ConfigurationProperties(prefix = "funder") public class FunderProperties { - private final List available; + private List available; - @ConstructorBinding - public FunderProperties(List available) { - this.available = available; - } - - public List getAvailable() { + public List getAvailable() { return available; } - public static class DOIFunder { - private final String funder; - private final String doi; + public void setAvailable(List available) { + this.available = available; + } - @ConstructorBinding - public DOIFunder(String funder, String doi) { - this.funder = funder; - this.doi = doi; - } + public static class DoiFunder { + private String funder; + private String doi; public String getFunder() { return funder; @@ -36,5 +29,13 @@ public class FunderProperties { public String getDoi() { return doi; } + + public void setFunder(String funder) { + this.funder = funder; + } + + public void setDoi(String doi) { + this.doi = doi; + } } } diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/configuration/identifier/IdentifierProperties.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/configuration/identifier/IdentifierProperties.java index dcd7676..f0026d9 100644 --- a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/configuration/identifier/IdentifierProperties.java +++ b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/configuration/identifier/IdentifierProperties.java @@ -7,14 +7,13 @@ import java.util.List; @ConfigurationProperties(prefix = "identifiers") public class IdentifierProperties { - private final List related; - - @ConstructorBinding - public IdentifierProperties(List related) { - this.related = related; - } + private List related; public List getRelated() { return related; } + + public void setRelated(List related) { + this.related = related; + } } diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/configuration/pid/PidProperties.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/configuration/pid/PidProperties.java index f65043b..abee71c 100644 --- a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/configuration/pid/PidProperties.java +++ b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/configuration/pid/PidProperties.java @@ -8,14 +8,8 @@ import java.util.List; @ConfigurationProperties(prefix = "pid") public class PidProperties { - private final List acceptedTypes; - private final PidFieldNames fields; - - @ConstructorBinding - public PidProperties(List acceptedTypes, PidFieldNames fields) { - this.acceptedTypes = acceptedTypes; - this.fields = fields; - } + private List acceptedTypes; + private PidFieldNames fields; public List getAcceptedTypes() { return acceptedTypes; @@ -25,22 +19,32 @@ public class PidProperties { return fields; } - public static class PidFieldNames { - private final String pidName; - private final String pidTypeName; + public void setAcceptedTypes(List acceptedTypes) { + this.acceptedTypes = acceptedTypes; + } - @ConstructorBinding - public PidFieldNames(String pidName, String pidTypeName) { - this.pidName = pidName; - this.pidTypeName = pidTypeName; - } + public void setFields(PidFieldNames fields) { + this.fields = fields; + } + + public static class PidFieldNames { + private String pidName; + private String pidTypeName; public String getPidName() { return pidName; } + public void setPidName(String pidName) { + this.pidName = pidName; + } + public String getPidTypeName() { return pidTypeName; } + + public void setPidTypeName(String pidTypeName) { + this.pidTypeName = pidTypeName; + } } } diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/configuration/zenodo/ZenodoProperties.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/configuration/zenodo/ZenodoProperties.java index cc4cc35..d9bfc3c 100644 --- a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/configuration/zenodo/ZenodoProperties.java +++ b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/configuration/zenodo/ZenodoProperties.java @@ -1,149 +1,57 @@ package eu.eudat.depositinterface.zenodorepository.configuration.zenodo; -import eu.eudat.depositinterface.repository.RepositoryDepositConfiguration; +import eu.eudat.depositinterface.repository.DepositConfiguration; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.ConstructorBinding; - -import java.util.List; -import java.util.Map; @ConfigurationProperties(prefix = "zenodo") public class ZenodoProperties { + private String logo; - private final String tempStorage; + private String community; - private final List configuration; + private String domain; - @ConstructorBinding - public ZenodoProperties(Map storage, List configuration) { - this.tempStorage = storage.get("temp"); - this.configuration = configuration; + private String affiliation; + + private DepositConfiguration depositConfiguration; + + public void setLogo(String logo) { + this.logo = logo; } - public String getTempStorage() { - return tempStorage; + public String getLogo() { + return logo; } - public List getConfiguration() { - return configuration; + public String getCommunity() { + return community; } - public static class ZenodoConfig extends RepositoryDepositConfiguration { - private final int depositType; - private final String repositoryId; - private final String accessToken; - private final String repositoryUrl; - private final String repositoryAuthorizationUrl; - private final String repositoryRecordUrl; - private final String repositoryAccessTokenUrl; - private final String repositoryClientId; - private final String repositoryClientSecret; - private final String redirectUri; - private final boolean hasLogo; - private final String logo; - private final String doiFunder; - private final String community; - private final String affiliation; - private final String domain; + public void setCommunity(String community) { + this.community = community; + } - @ConstructorBinding - public ZenodoConfig(int depositType, String repositoryId, String accessToken, String repositoryUrl, String repositoryAuthorizationUrl, String repositoryRecordUrl, String repositoryAccessTokenUrl, String repositoryClientId, String repositoryClientSecret, String redirectUri, boolean hasLogo, String logo, String doiFunder, String community, String affiliation, String domain) { - this.depositType = depositType; - this.repositoryId = repositoryId; - this.accessToken = accessToken; - this.repositoryUrl = repositoryUrl; - this.repositoryAuthorizationUrl = repositoryAuthorizationUrl; - this.repositoryRecordUrl = repositoryRecordUrl; - this.repositoryAccessTokenUrl = repositoryAccessTokenUrl; - this.repositoryClientId = repositoryClientId; - this.repositoryClientSecret = repositoryClientSecret; - this.redirectUri = redirectUri; - this.hasLogo = hasLogo; - this.logo = logo; - this.doiFunder = doiFunder; - this.community = community; - this.affiliation = affiliation; - this.domain = domain; - } + public String getDomain() { + return domain; + } - public int getDepositType() { - return depositType; - } + public void setDomain(String domain) { + this.domain = domain; + } - public String getRepositoryId() { - return repositoryId; - } + public String getAffiliation() { + return affiliation; + } - public String getAccessToken() { - return accessToken; - } + public void setAffiliation(String affiliation) { + this.affiliation = affiliation; + } - public String getRepositoryUrl() { - return repositoryUrl; - } + public DepositConfiguration getDepositConfiguration() { + return depositConfiguration; + } - public String getRepositoryAuthorizationUrl() { - return repositoryAuthorizationUrl; - } - - public String getRepositoryRecordUrl() { - return repositoryRecordUrl; - } - - public String getRepositoryAccessTokenUrl() { - return repositoryAccessTokenUrl; - } - - public String getRepositoryClientId() { - return repositoryClientId; - } - - public String getRepositoryClientSecret() { - return repositoryClientSecret; - } - - public String getRedirectUri() { - return redirectUri; - } - - public boolean isHasLogo() { - return hasLogo; - } - - public String getLogo() { - return logo; - } - - public String getDoiFunder() { - return doiFunder; - } - - public String getCommunity() { - return community; - } - - public String getAffiliation() { - return affiliation; - } - - public String getDomain() { - return domain; - } - - public RepositoryDepositConfiguration toRepoConfig() { - RepositoryDepositConfiguration config = new RepositoryDepositConfiguration(); - config.setDepositType(this.depositType); - config.setRepositoryId(this.repositoryId); - config.setAccessToken(this.accessToken); - config.setRepositoryUrl(this.repositoryUrl); - config.setRepositoryAuthorizationUrl(this.repositoryAuthorizationUrl); - config.setRepositoryRecordUrl(this.repositoryRecordUrl); - config.setRepositoryAccessTokenUrl(this.repositoryAccessTokenUrl); - config.setRepositoryClientId(this.repositoryClientId); - config.setRepositoryClientSecret(this.repositoryClientSecret); - config.setRedirectUri(this.redirectUri); - config.setHasLogo(this.hasLogo); - return config; - } + public void setDepositConfiguration(DepositConfiguration depositConfiguration) { + this.depositConfiguration = depositConfiguration; } } diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/enums/ZenodoAccessRight.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/enums/ZenodoAccessRight.java new file mode 100644 index 0000000..fe58d5d --- /dev/null +++ b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/enums/ZenodoAccessRight.java @@ -0,0 +1,36 @@ +package eu.eudat.depositinterface.zenodorepository.enums; + +import com.fasterxml.jackson.annotation.JsonValue; +import eu.eudat.depositinterface.enums.EnumUtils; +import eu.eudat.depositinterface.enums.EnumValueProvider; +import eu.eudat.depositinterface.enums.FieldType; + +import java.util.Map; + +public enum ZenodoAccessRight implements EnumValueProvider { + RESTRICTED(Names.Restricted), EMBARGOED(Names.Embargoed), OPEN(Names.Open); + + private final String value; + + public static class Names { + public static final String Restricted = "restricted"; + public static final String Embargoed = "embargoed"; + public static final String Open = "open"; + } + + ZenodoAccessRight(String value) { + this.value = value; + } + + @Override + @JsonValue + public String getValue() { + return value; + } + + private static final Map map = EnumUtils.getEnumValueMap(ZenodoAccessRight.class); + + public static ZenodoAccessRight of(String i) { + return map.get(i); + } +} diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/interfaces/ZenodoDeposit.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/interfaces/ZenodoDeposit.java deleted file mode 100644 index 16e0b0c..0000000 --- a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/interfaces/ZenodoDeposit.java +++ /dev/null @@ -1,284 +0,0 @@ -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.configuration.zenodo.ZenodoProperties; -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.ParameterizedTypeReference; -import org.springframework.core.io.ByteArrayResource; -import org.springframework.http.*; -import org.springframework.stereotype.Component; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.util.ResourceUtils; -import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.HttpServerErrorException; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.client.WebClient; - -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 ZenodoProperties zenodoProperties; - private final DMPToZenodoMapper mapper; - - @Autowired - public ZenodoDeposit(ZenodoProperties zenodoProperties, DMPToZenodoMapper mapper){ - this.zenodoProperties = zenodoProperties; - this.mapper = mapper; - } - - @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.isEmpty()) { - zenodoToken = conf.getAccessToken(); - } - - String zenodoUrl = conf.getRepositoryUrl(); - - // First step, post call to Zenodo, to create the entry. - WebClient zenodoClient = WebClient.builder().build(); - HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - headers.setContentType(MediaType.APPLICATION_JSON); - - ZenodoProperties.ZenodoConfig zenodoConfig = this.zenodoProperties.getConfiguration().stream().filter(x -> x.getRepositoryId().equals(repositoryId)).findFirst().orElse(null); - eu.eudat.depositinterface.zenodorepository.models.ZenodoDeposit deposit = mapper.fromDMP(dmpDepositModel, zenodoConfig); - - Map createResponse; - LinkedHashMap links; - String previousDOI = dmpDepositModel.getPreviousDOI(); - String unpublishedUrl = null; - String publishUrl; - try { - - if (previousDOI == null) { - String createUrl = zenodoUrl + "deposit/depositions" + "?access_token=" + zenodoToken; - createResponse = zenodoClient.post().uri(createUrl).headers(httpHeaders -> { - httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - httpHeaders.setContentType(MediaType.APPLICATION_JSON); - }) - .bodyValue(deposit).exchangeToMono(mono -> mono.bodyToMono(new ParameterizedTypeReference>() {})).block(); - links = (LinkedHashMap) 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> listResponses = zenodoClient.get().uri(listUrl).retrieve().toEntityList(Map.class).block(); - createResponse = listResponses.getBody().get(0); - logger.debug("createResponse-previousDoi:"); - logger.debug(objectMapper.writeValueAsString(createResponse)); - links = (LinkedHashMap) createResponse.get("links"); - //Second, make the new version (not in the links?) - String newVersionUrl = links.get("self") + "/actions/newversion" + "?access_token=" + zenodoToken; - logger.debug("new version url: " + newVersionUrl); - createResponse = zenodoClient.post().uri(newVersionUrl) - .bodyValue(null).exchangeToMono(mono -> mono.bodyToMono(Map.class)).block(); - logger.debug("createResponse-newVersion:"); - logger.debug(objectMapper.writeValueAsString(createResponse)); - links = (LinkedHashMap) createResponse.get("links"); - //Third, get the new deposit - String latestDraftUrl = links.get("latest_draft") + "?access_token=" + zenodoToken; - createResponse = zenodoClient.get().uri(latestDraftUrl) - .exchangeToMono(mono -> mono.bodyToMono(Map.class)).block(); - logger.debug("createResponse-latestDraft:"); - logger.debug(objectMapper.writeValueAsString(createResponse)); - links = (LinkedHashMap) 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; - zenodoClient.put().uri(updateUrl) - .headers(httpHeaders -> { - httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - httpHeaders.setContentType(MediaType.APPLICATION_JSON); - }) - .bodyValue(deposit).retrieve().toEntity(Map.class).block(); - //And finally remove pre-existing files from it - String fileListUrl = links.get("self") + "/files" + "?access_token=" + zenodoToken; - ResponseEntity> fileListResponse = zenodoClient.get().uri(fileListUrl).retrieve().toEntityList(Map.class).block(); - for (Map file : fileListResponse.getBody()) { - String fileDeleteUrl = links.get("self") + "/files/" + file.get("id") + "?access_token=" + zenodoToken; - zenodoClient.delete().uri(fileDeleteUrl).retrieve().toEntity(Map.class).block(); - } - } 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); - zenodoClient.delete().uri(latestDraftUrl).retrieve().toEntity(Map.class).block(); - throw e; - } - } else { - String listUrl = zenodoUrl + "deposit/depositions" + "?q=conceptdoi:\"" + previousDOI + "\"&access_token=" + zenodoToken; - ResponseEntity> listResponses = zenodoClient.get().uri(listUrl).retrieve().toEntityList(Map.class).block();//restTemplate.getForEntity(listUrl, Map[].class); - createResponse = listResponses.getBody().get(0); - links = (LinkedHashMap) createResponse.get("links"); - } - } - - if (unpublishedUrl == null) { - // Second step, add the file to the entry. - FileEnvelope pdfEnvelope = dmpDepositModel.getPdfFile(); - - String addFileUrl = links.get("bucket") + "/" + pdfEnvelope.getFilename() + "?access_token=" + zenodoToken; - zenodoClient.put().uri(addFileUrl) - .body(BodyInserters - .fromResource(new ByteArrayResource(Files.readAllBytes(pdfEnvelope.getFile().toPath())))) - .retrieve().toEntity(Map.class).block(); - FileEnvelope rdaJsonEnvelope = dmpDepositModel.getRdaJsonFile(); - - String jsonFileName = rdaJsonEnvelope.getFilename(); - addFileUrl = links.get("bucket") + "/" + jsonFileName + "?access_token=" + zenodoToken; - zenodoClient.put().uri(addFileUrl).headers(httpHeaders -> httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM)).body(BodyInserters.fromResource(new ByteArrayResource(Files.readAllBytes(rdaJsonEnvelope.getFile().toPath())))).retrieve().toEntity(Map.class).block(); - - if (dmpDepositModel.getSupportingFilesZip() != null) { - File supportinFilesZip = dmpDepositModel.getSupportingFilesZip(); - String supportinFilesZipName = dmpDepositModel.getSupportingFilesZip().getName(); - - addFileUrl = links.get("bucket") + "/" + supportinFilesZipName + "?access_token=" + zenodoToken; - zenodoClient.put().uri(addFileUrl).body(BodyInserters.fromResource(new ByteArrayResource(Files.readAllBytes(supportinFilesZip.toPath())))).retrieve().toEntity(Map.class).block(); - } - - // 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 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(); - WebClient webClient = WebClient.builder().build(); - Map publishResponce = webClient.post().uri(publishUrl).bodyValue("").exchangeToMono(mono -> { - if (!mono.statusCode().is2xxSuccessful()) { - mono.createException(); - throw new UnsupportedOperationException("Failed to publish to Zenodo"); - } - return mono.bodyToMono(new ParameterizedTypeReference>() { - }); - }).block(); - return (String) publishResponce.get("conceptdoi"); - } - - - @Override - public List getConfiguration() { - List zenodoConfigs = this.zenodoProperties.getConfiguration(); - return (zenodoConfigs != null) ? zenodoConfigs.stream().map(ZenodoProperties.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) { - - WebClient client = WebClient.builder().defaultHeaders(httpHeaders -> { - httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA); - }).build(); - HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - headers.setContentType(MediaType.MULTIPART_FORM_DATA); - - MultiValueMap 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> request = new HttpEntity<>(map, headers); - - try { - Map values = client.post().uri(conf.getRepositoryAccessTokenUrl()).bodyValue(map).retrieve().bodyToMono(Map.class).block(); - Map user = (Map) values.get("user"); - return (String) values.get("access_token"); - } catch (HttpClientErrorException ex) { - logger.error(ex.getResponseBodyAsString(), ex); - } - - return null; - - } - - return null; - } - - @Override - public String getLogo(String repositoryId) { - ZenodoProperties.ZenodoConfig conf = this.zenodoProperties.getConfiguration().stream().filter(x -> x.getRepositoryId().equals(repositoryId)).findFirst().orElse(null); - if(conf != null) { - if(conf.isHasLogo()){ - byte[] logo; - try { - File logoFile = ResourceUtils.getFile(conf.getLogo()); - InputStream logoStream = new FileInputStream(logoFile); - logo = logoStream.readAllBytes(); - } catch (IOException e) { - throw new RuntimeException(e); - } - 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 { - WebClient client = WebClient.builder().build(); - HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - headers.setContentType(MediaType.APPLICATION_JSON); - Map createResponse = null; - LinkedHashMap links; - LinkedHashMap metadata; - String listUrl = zenodoUrl + "deposit/depositions" + "?q=conceptdoi:\"" + DOI + "\"&access_token=" + token; - ResponseEntity> listResponses = client.get().uri(listUrl).retrieve().toEntityList(Map.class).block(); - createResponse = listResponses.getBody().get(0); - metadata = (LinkedHashMap) createResponse.get("metadata"); - links = (LinkedHashMap) 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; - } - } -} diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/mapper/DMPToZenodoMapper.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/mapper/DMPToZenodoMapper.java deleted file mode 100644 index 4a2e72e..0000000 --- a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/mapper/DMPToZenodoMapper.java +++ /dev/null @@ -1,274 +0,0 @@ -package eu.eudat.depositinterface.zenodorepository.mapper; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import eu.eudat.depositinterface.enums.FieldType; -import eu.eudat.depositinterface.models.*; -import eu.eudat.depositinterface.zenodorepository.configuration.funder.FunderProperties; -import eu.eudat.depositinterface.zenodorepository.configuration.identifier.IdentifierProperties; -import eu.eudat.depositinterface.zenodorepository.configuration.pid.PidProperties; -import eu.eudat.depositinterface.zenodorepository.configuration.zenodo.ZenodoProperties; -import eu.eudat.depositinterface.zenodorepository.models.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.time.Instant; -import java.util.*; -import java.util.stream.Collectors; - -@Component -public class DMPToZenodoMapper { - - private static final Logger logger = LoggerFactory.getLogger(DMPToZenodoMapper.class); - private static final ObjectMapper objectMapper = new ObjectMapper(); - - private final PidProperties pidProperties; - private final IdentifierProperties identifierProperties; - private final FunderProperties funderProperties; - - @Autowired - public DMPToZenodoMapper(PidProperties pidProperties, IdentifierProperties identifierProperties, FunderProperties funderProperties){ - this.pidProperties = pidProperties; - this.identifierProperties = identifierProperties; - this.funderProperties = funderProperties; - } - - private List findSchemanticValues(String relatedId, List fields){ - return fields.stream().filter(f -> f.getSchematics().contains(relatedId)).collect(Collectors.toList()); - } - - private Set extractSchemanticValues(List fields, List acceptedPidTypes) throws JsonProcessingException{ - Set values = new HashSet<>(); - for(DatasetFieldsDepositModel field: fields){ - String value = (String) field.getValue(); - if(value != null && !value.isEmpty()) { - switch (FieldType.fromName(field.getRenderStyleType())) { - case FREE_TEXT: - case TEXT_AREA: - case RICH_TEXT_AREA: - case RADIO_BOX: - case DATE_PICKER: - values.add(value); - break; - case COMBO_BOX: - if (field.isMultiple()) { - List selected = objectMapper.readValue(value, new TypeReference>() {}); - values.addAll(selected); - } - else { - values.add(value); - } - break; - case REGISTRIES: - case SERVICES: - case EXTERNAL_DATASETS: - case DATA_REPOSITORIES: - case PUB_REPOSITORIES: - case JOURNAL_REPOSITORIES: - case TAXONOMIES: - case PUBLICATIONS: - if (field.isMultiple()) { - List selected = objectMapper.readValue(value, new TypeReference>() {}); - for (String s : selected) { - Map valueMap = objectMapper.readValue(s, new TypeReference>() {}); - String pid = valueMap.get(this.pidProperties.getFields().getPidName()); - String pidType = valueMap.get(this.pidProperties.getFields().getPidTypeName()); - if (acceptedPidTypes.contains(pidType)) { - values.add(pid); - } - } - } - else { - Map valueMap = objectMapper.readValue(value, new TypeReference>() {}); - String pid = valueMap.get(this.pidProperties.getFields().getPidName()); - String pidType = valueMap.get(this.pidProperties.getFields().getPidTypeName()); - if (acceptedPidTypes.contains(pidType)) { - values.add(pid); - } - } - break; - case ORGANIZATIONS: - case RESEARCHERS: - if (field.isMultiple()) { - List selected = objectMapper.readValue(value, new TypeReference>() {}); - for (String s : selected) { - Map valueMap = objectMapper.readValue(s, new TypeReference>() {}); - String pid = valueMap.get("reference"); - if(pid != null) { - values.add(pid); - } - } - } - else { - Map valueMap = objectMapper.readValue(value, new TypeReference>() {}); - String pid = valueMap.get("reference"); - if(pid != null) { - values.add(pid); - } - } - break; - case DATASET_IDENTIFIER: - Map valueMap = objectMapper.readValue(value, new TypeReference>() {}); - values.add(valueMap.get("identifier")); - break; - } - } - } - return values; - } - - public ZenodoDeposit fromDMP(DMPDepositModel dmp, ZenodoProperties.ZenodoConfig zenodoConfig) throws JsonProcessingException { - Map extraProperties = dmp.getExtraProperties() != null ? new org.json.JSONObject(dmp.getExtraProperties()).toMap() : new HashMap<>(); - ZenodoDeposit deposit = new ZenodoDeposit(); - - Map schematicsMap = new HashMap<>(); - - List relatedIdentifiers = new ArrayList<>(); - List communities = new ArrayList<>(); - List contributors = new ArrayList<>(); - List creators = new ArrayList<>(); - List grants = new ArrayList<>(); - List keywords = new ArrayList<>(); - List references = new ArrayList<>(); - - List acceptedPidTypes = this.pidProperties.getAcceptedTypes(); - - for(DatasetDepositModel dataset: dmp.getDatasets()){ - - for(String relatedId: this.identifierProperties.getRelated()){ - - List fields = findSchemanticValues(relatedId, dataset.getFields()); - Set values = extractSchemanticValues(fields, acceptedPidTypes); - for(String value: values){ - ZenodoRelator relator = new ZenodoRelator(); - relator.setRelation(relatedId.substring(relatedId.lastIndexOf(".") + 1)); - relator.setIdentifier(value); - relatedIdentifiers.add(relator); - } - - } - } - schematicsMap.put("related_identifiers", relatedIdentifiers); - schematicsMap.put("communities", communities); - schematicsMap.put("contributors", contributors); - schematicsMap.put("creators", creators); - schematicsMap.put("grants", grants); - schematicsMap.put("keywords", keywords); - schematicsMap.put("references", references); - - String schematicsString = objectMapper.writeValueAsString(schematicsMap); - objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); - ZenodoDepositMetadata metadata = objectMapper.readValue(schematicsString, new TypeReference(){}); - - deposit.setMetadata(metadata); - deposit.getMetadata().setTitle(dmp.getLabel()); - deposit.getMetadata().setUploadType("publication"); - deposit.getMetadata().setPublicationType("datamanagementplan"); - deposit.getMetadata().setDescription((dmp.getDescription() != null && !dmp.getDescription().isEmpty() ? dmp.getDescription() : "

")); - deposit.getMetadata().setVersion(String.valueOf(dmp.getVersion())); - String zenodoCommunity = zenodoConfig.getCommunity(); - if(zenodoCommunity != null && !zenodoCommunity.isEmpty()) { - ZenodoComunity community = new ZenodoComunity(); - community.setIdentifier(zenodoCommunity); - deposit.getMetadata().getCommunities().add(community); - } - if (extraProperties.get("visible") == null) { - deposit.getMetadata().setAccessRight(ZenodoAccessRight.RESTRICTED); - deposit.getMetadata().setAccessConditions(""); - } else { - if (((Boolean) extraProperties.get("visible"))) { - Instant publicationDate = Instant.parse(extraProperties.get("publicDate").toString()); - if (publicationDate.isBefore(Instant.now())) { - deposit.getMetadata().setAccessRight(ZenodoAccessRight.OPEN); - } else { - deposit.getMetadata().setAccessRight(ZenodoAccessRight.EMBARGOED); - deposit.getMetadata().setEmbargoDate(publicationDate.toString()); - } - - if (extraProperties.get("license") != null) { - deposit.getMetadata().setLicense(((Map) extraProperties.get("license")).get("pid").toString()); - } - } else { - deposit.getMetadata().setAccessRight(ZenodoAccessRight.RESTRICTED); - deposit.getMetadata().setAccessConditions(""); - } - } - if (dmp.isPublic()) { - ZenodoRelator relator = new ZenodoRelator(); - relator.setIdentifier(zenodoConfig.getDomain() + "/external/zenodo/" + dmp.getId().toString()); - relator.setRelation("isIdenticalTo"); - deposit.getMetadata().getRelatedIdentifiers().add(relator); - } - String zenodoAffiliation = zenodoConfig.getAffiliation(); - List contributors1 = dmp.getUsers().stream().map(userDMP -> { - ZenodoContributor contributor = new ZenodoContributor(); - contributor.setName(userDMP.getUser().getName()); - contributor.setType("ProjectMember"); - if (dmp.getOrganisations() != null && !dmp.getOrganisations().isEmpty()) { - contributor.setAffiliation(dmp.getOrganisations() - .stream().map(OrganisationDepositModel::getLabel).collect(Collectors.joining(", "))); - } else { - if(zenodoAffiliation != null && !zenodoAffiliation.isEmpty()) { - contributor.setAffiliation(zenodoAffiliation); - } - } - return contributor; - }).collect(Collectors.toList()); - - List researchers = dmp.getResearchers().stream().map(researcher -> { - ZenodoContributor contributor = new ZenodoContributor(); - contributor.setName(researcher.getLabel()); - contributor.setType("Researcher"); - String referenceHead = researcher.getReference().split(":")[0]; - String referenceTail = researcher.getReference().replace(referenceHead + ":", ""); - contributor.setAffiliation(referenceHead); - if (referenceHead.equalsIgnoreCase("ORCID")) { - contributor.setOrcid(referenceTail); - } - return contributor; - }).collect(Collectors.toList()); - - deposit.getMetadata().getContributors().addAll(contributors1); - deposit.getMetadata().getContributors().addAll(researchers); - - if (dmp.getGrant() != null) { - if (dmp.getGrant().getReference() == null) { - dmp.getGrant().setReference("dmp:" + dmp.getGrant().getId()); - } - String grantReferenceHead = dmp.getGrant().getReference().split(":")[0]; - if (grantReferenceHead.equals("openaire")) { - String grantReferenceTail = dmp.getGrant().getReference().split(":")[3]; - List doiFunders = this.funderProperties.getAvailable(); - if (dmp.getGrant().getFunder() != null && dmp.getGrant().getFunder().getLabel() != null) { - FunderProperties.DOIFunder doiFunder = doiFunders.stream() - .filter(doiFunder1 -> dmp.getGrant().getFunder().getLabel().contains(doiFunder1.getFunder()) || doiFunder1.getFunder().contains(dmp.getGrant().getFunder().getLabel())) - .findFirst().orElse(null); - if (doiFunder != null) { - String finalId = doiFunder.getDoi() + "::" + grantReferenceTail; - ZenodoGrant grant = new ZenodoGrant(); - grant.setId(finalId); - deposit.getMetadata().getGrants().add(grant); - } - } - } - } - ZenodoCreator creator = new ZenodoCreator(); - creator.setName(dmp.getUsers().stream().filter(userDMP -> userDMP.getRole().equals(UserDMPDepositModel.UserDMPRoles.OWNER.getValue())).findFirst().get().getUser().getName()); - if (dmp.getOrganisations() != null && !dmp.getOrganisations().isEmpty()) { - creator.setAffiliation(dmp.getOrganisations() - .stream().map(OrganisationDepositModel::getLabel).collect(Collectors.joining(", "))); - } else { - if(zenodoAffiliation != null && !zenodoAffiliation.isEmpty()) { - creator.setAffiliation(zenodoAffiliation); - } - } - deposit.getMetadata().getCreators().add(creator); - - return deposit; - } -} - diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoComunity.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoCommunity.java similarity index 81% rename from core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoComunity.java rename to core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoCommunity.java index a04a19c..4da7e85 100644 --- a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoComunity.java +++ b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoCommunity.java @@ -1,11 +1,11 @@ -package eu.eudat.depositinterface.zenodorepository.models; +package eu.eudat.depositinterface.zenodorepository.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) -public class ZenodoComunity { +public class ZenodoCommunity { private String identifier; diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoContributor.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoContributor.java similarity index 93% rename from core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoContributor.java rename to core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoContributor.java index c172d52..7e7f64b 100644 --- a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoContributor.java +++ b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoContributor.java @@ -1,4 +1,4 @@ -package eu.eudat.depositinterface.zenodorepository.models; +package eu.eudat.depositinterface.zenodorepository.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoCreator.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoCreator.java similarity index 92% rename from core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoCreator.java rename to core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoCreator.java index 2cf5756..8d78ed2 100644 --- a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoCreator.java +++ b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoCreator.java @@ -1,4 +1,4 @@ -package eu.eudat.depositinterface.zenodorepository.models; +package eu.eudat.depositinterface.zenodorepository.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoDeposit.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoDeposit.java similarity index 88% rename from core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoDeposit.java rename to core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoDeposit.java index c27c253..a76668b 100644 --- a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoDeposit.java +++ b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoDeposit.java @@ -1,4 +1,4 @@ -package eu.eudat.depositinterface.zenodorepository.models; +package eu.eudat.depositinterface.zenodorepository.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoDepositMetadata.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoDepositMetadata.java similarity index 93% rename from core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoDepositMetadata.java rename to core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoDepositMetadata.java index 87c3596..7cc3c47 100644 --- a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoDepositMetadata.java +++ b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoDepositMetadata.java @@ -1,8 +1,9 @@ -package eu.eudat.depositinterface.zenodorepository.models; +package eu.eudat.depositinterface.zenodorepository.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import eu.eudat.depositinterface.zenodorepository.enums.ZenodoAccessRight; import java.util.List; @@ -31,7 +32,7 @@ public class ZenodoDepositMetadata { private List references; - private List communities; + private List communities; @JsonProperty("access_right") private ZenodoAccessRight accessRight; @@ -125,11 +126,11 @@ public class ZenodoDepositMetadata { this.version = version; } - public List getCommunities() { + public List getCommunities() { return communities; } - public void setCommunities(List communities) { + public void setCommunities(List communities) { this.communities = communities; } diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoGrant.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoGrant.java similarity index 86% rename from core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoGrant.java rename to core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoGrant.java index d37c657..88da2cf 100644 --- a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoGrant.java +++ b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoGrant.java @@ -1,4 +1,4 @@ -package eu.eudat.depositinterface.zenodorepository.models; +package eu.eudat.depositinterface.zenodorepository.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoRelator.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoRelator.java similarity index 91% rename from core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoRelator.java rename to core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoRelator.java index 8196f63..36ef0c4 100644 --- a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoRelator.java +++ b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/ZenodoRelator.java @@ -1,4 +1,4 @@ -package eu.eudat.depositinterface.zenodorepository.models; +package eu.eudat.depositinterface.zenodorepository.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/builder/ZenodoBuilder.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/builder/ZenodoBuilder.java new file mode 100644 index 0000000..4cc51c5 --- /dev/null +++ b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/model/builder/ZenodoBuilder.java @@ -0,0 +1,319 @@ +package eu.eudat.depositinterface.zenodorepository.model.builder; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.eudat.depositinterface.enums.DmpAccessType; +import eu.eudat.depositinterface.enums.DmpUserRole; +import eu.eudat.depositinterface.enums.ReferenceType; +import eu.eudat.depositinterface.models.*; +import eu.eudat.depositinterface.models.reference.FieldDepositModel; +import eu.eudat.depositinterface.zenodorepository.configuration.funder.FunderProperties; +import eu.eudat.depositinterface.zenodorepository.configuration.identifier.IdentifierProperties; +import eu.eudat.depositinterface.zenodorepository.configuration.pid.PidProperties; +import eu.eudat.depositinterface.zenodorepository.configuration.zenodo.ZenodoProperties; +import eu.eudat.depositinterface.zenodorepository.enums.ZenodoAccessRight; +import eu.eudat.depositinterface.zenodorepository.model.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.*; +import java.util.stream.Collectors; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class ZenodoBuilder { + + private static final String UPLOAD_TYPE = "publication"; + private static final String PUBLICATION_TYPE = "datamanagementplan"; + private static final String IS_IDENTICAL_TO = "isIdenticalTo"; + private static final String CONTRIBUTOR_TYPE_RESEARCHER = "Researcher"; + private static final String CONTRIBUTOR_TYPE_PROJECT_MANAGER = "ProjectMember"; + private static final String SOURCE_OPENAIRE = "openaire"; + private static final String SOURCE_ORCID = "ORCID"; + private static final String PID = "pid"; + + + + private final PidProperties pidProperties; + private final IdentifierProperties identifierProperties; + private final FunderProperties funderProperties; + + @Autowired + public ZenodoBuilder(PidProperties pidProperties, IdentifierProperties identifierProperties, FunderProperties funderProperties){ + this.pidProperties = pidProperties; + this.identifierProperties = identifierProperties; + this.funderProperties = funderProperties; + } + + public ZenodoDeposit build(DmpDepositModel dmp, ZenodoProperties zenodoConfig) throws JsonProcessingException { + ZenodoDeposit deposit = new ZenodoDeposit(); + this.applyZenodoRelator(dmp, deposit); + deposit.getMetadata().setTitle(dmp.getLabel()); + deposit.getMetadata().setUploadType(UPLOAD_TYPE); + deposit.getMetadata().setPublicationType(PUBLICATION_TYPE); + deposit.getMetadata().setDescription((dmp.getDescription() != null && !dmp.getDescription().isEmpty() ? dmp.getDescription() : "

")); + deposit.getMetadata().setVersion(String.valueOf(dmp.getVersion())); + String zenodoCommunity = zenodoConfig.getCommunity(); + if(zenodoCommunity != null && !zenodoCommunity.isEmpty()) { + ZenodoCommunity community = new ZenodoCommunity(); + community.setIdentifier(zenodoCommunity); + deposit.getMetadata().getCommunities().add(community); + } + this.applyAccessRight(dmp, deposit); + this.applyIsIdenticalTo(dmp, deposit, zenodoConfig); + this.applyLicenses(dmp, deposit); + this.applyResearchers(dmp, deposit); + this.applyGrants(dmp, deposit); + this.applyContributors(dmp, deposit, zenodoConfig); + this.applyCreators(dmp, deposit, zenodoConfig); + + + + return deposit; + } + + private List findSchematicValues(String relatedId, List fields){ + return fields.stream().filter(f -> f.getSchematics().contains(relatedId)).collect(Collectors.toList()); + } + + private Set extractSchematicValues(List fields, List acceptedPidTypes) throws JsonProcessingException{ + ObjectMapper objectMapper = new ObjectMapper(); + Set values = new HashSet<>(); + for(DescriptionFieldDepositModel field: fields){ + //TODO: Check how to parse this values after update the logic of description persist from the main app + List value =field.getValues() == null ? new ArrayList<>() : field.getValues().stream().map(x-> (String)x).filter(x-> x == null || x.isEmpty()).toList(); + if(!value.isEmpty()) { + switch (field.getFieldType()) { + case FREE_TEXT: + case TEXT_AREA: + case RICH_TEXT_AREA: + case RADIO_BOX: + case DATE_PICKER: + case AUTO_COMPLETE: + case WORD_LIST: + values.addAll(value); + break; + case SERVICES: + case EXTERNAL_DATASETS: + case DATA_REPOSITORIES: + case PUB_REPOSITORIES: + case JOURNAL_REPOSITORIES: + case TAXONOMIES: + case PUBLICATIONS: + for (String s : value) { + Map valueMap = objectMapper.readValue(s, new TypeReference>() { + }); + String pid = valueMap.get(this.pidProperties.getFields().getPidName()); + String pidType = valueMap.get(this.pidProperties.getFields().getPidTypeName()); + if (acceptedPidTypes.contains(pidType)) { + values.add(pid); + } + } + break; + case ORGANIZATIONS: + case RESEARCHERS: + for (String s : value) { + Map valueMap = objectMapper.readValue(s, new TypeReference>() {}); + String pid = valueMap.get("reference"); + if(pid != null) { + values.add(pid); + } + } + break; + case DATASET_IDENTIFIER: + for (String s : value) { + Map valueMap = objectMapper.readValue(s, new TypeReference>() {}); + values.add(valueMap.get("identifier")); + } + break; + } + } + } + return values; + } + + private List getReferenceDepositModelOfType(DmpDepositModel dmp, ReferenceType referenceType){ + if (dmp.getReferences() == null) return new ArrayList<>(); + return dmp.getReferences().stream().filter(x -> x.getType().equals(referenceType)).toList(); + } + + private void applyZenodoRelator(DmpDepositModel dmp, ZenodoDeposit deposit) throws JsonProcessingException { + if (deposit.getMetadata() == null) deposit.setMetadata(new ZenodoDepositMetadata()); + + List acceptedPidTypes = this.pidProperties.getAcceptedTypes(); + List relatedIdentifiers = new ArrayList<>(); + for(DescriptionDepositModel descriptionDepositModel: dmp.getDescriptions()){ + for(String relatedId: this.identifierProperties.getRelated()){ + List fields = findSchematicValues(relatedId, descriptionDepositModel.getFields()); + Set values = extractSchematicValues(fields, acceptedPidTypes); + for(String value: values){ + ZenodoRelator relator = new ZenodoRelator(); + relator.setRelation(relatedId.substring(relatedId.lastIndexOf(".") + 1)); + relator.setIdentifier(value); + relatedIdentifiers.add(relator); + } + } + } + deposit.getMetadata().setRelatedIdentifiers(relatedIdentifiers); + } + + private void applyAccessRight(DmpDepositModel dmp, ZenodoDeposit deposit){ + if (deposit.getMetadata() == null) deposit.setMetadata(new ZenodoDepositMetadata()); + + if (dmp.getAccessType() == null) { + deposit.getMetadata().setAccessRight(ZenodoAccessRight.RESTRICTED); + deposit.getMetadata().setAccessConditions(""); + } else { + if (dmp.getAccessType().equals(DmpAccessType.Public)) { + Instant publicationDate = dmp.getFinalizedAt(); + if (publicationDate == null) publicationDate = Instant.now().minusSeconds(1); + + if (publicationDate.isBefore(Instant.now())) { + deposit.getMetadata().setAccessRight(ZenodoAccessRight.OPEN); + } else { + deposit.getMetadata().setAccessRight(ZenodoAccessRight.EMBARGOED); + deposit.getMetadata().setEmbargoDate(publicationDate.toString()); + } + } else { + deposit.getMetadata().setAccessRight(ZenodoAccessRight.RESTRICTED); + deposit.getMetadata().setAccessConditions(""); + } + } + } + + private void applyIsIdenticalTo(DmpDepositModel dmp, ZenodoDeposit deposit, ZenodoProperties zenodoConfig){ + if (deposit.getMetadata() == null) deposit.setMetadata(new ZenodoDepositMetadata()); + + if (dmp.getAccessType().equals(DmpAccessType.Public)) { + ZenodoRelator relator = new ZenodoRelator(); + relator.setIdentifier(zenodoConfig.getDomain() + "/external/zenodo/" + dmp.getId().toString()); + relator.setRelation(IS_IDENTICAL_TO); + if (deposit.getMetadata().getRelatedIdentifiers() == null)deposit.getMetadata().setRelatedIdentifiers(new ArrayList<>()); + deposit.getMetadata().getRelatedIdentifiers().add(relator); + } + } + + private void applyLicenses(DmpDepositModel dmp, ZenodoDeposit deposit){ + if (deposit.getMetadata() == null) deposit.setMetadata(new ZenodoDepositMetadata()); + + List dmpLicenses = this.getReferenceDepositModelOfType(dmp, ReferenceType.Licenses); + if (dmpLicenses != null && !dmpLicenses.isEmpty()) { + for (ReferenceDepositModel dmpLicense : dmpLicenses) { + String pid = dmpLicense.getDefinition().getFields().stream().filter(x-> PID.equalsIgnoreCase(x.getCode())).map(FieldDepositModel::getValue).findFirst().orElse(null); + if (pid != null && !pid.isBlank()) { + deposit.getMetadata().setLicense(pid); + break; + } + } + } + } + + private void applyResearchers(DmpDepositModel dmp, ZenodoDeposit deposit){ + if (deposit.getMetadata() == null) deposit.setMetadata(new ZenodoDepositMetadata()); + + List researchers = new ArrayList<>(); + List dmpResearchers = this.getReferenceDepositModelOfType(dmp, ReferenceType.Researcher); + if (dmpResearchers != null && !dmpResearchers.isEmpty()) { + for (ReferenceDepositModel researcher : dmpResearchers) { + ZenodoContributor contributor = new ZenodoContributor(); + contributor.setName(researcher.getLabel()); + contributor.setType(CONTRIBUTOR_TYPE_RESEARCHER); + contributor.setAffiliation(researcher.getSource()); + if (researcher.getSource().equalsIgnoreCase(SOURCE_ORCID)) { + contributor.setOrcid(researcher.getReference()); + } + researchers.add(contributor); + } + } + + if (deposit.getMetadata().getContributors() == null)deposit.getMetadata().setContributors(new ArrayList<>()); + + deposit.getMetadata().getContributors().addAll(researchers); + } + + private void applyGrants(DmpDepositModel dmp, ZenodoDeposit deposit){ + if (deposit.getMetadata() == null) deposit.setMetadata(new ZenodoDepositMetadata()); + List dmpGrants = this.getReferenceDepositModelOfType(dmp, ReferenceType.Grants); + List dmpFunders = this.getReferenceDepositModelOfType(dmp, ReferenceType.Funder); + + + if (dmpGrants != null && !dmpGrants.isEmpty()) { + ReferenceDepositModel depositGrant = dmpGrants.stream().filter(x-> x.getSource().equals(SOURCE_OPENAIRE)).findFirst().orElse(null); + + if (depositGrant != null) { + String grantReferenceTail = depositGrant.getReference().split(":")[2]; + List doiFunders = this.funderProperties.getAvailable(); + if (dmpFunders != null && !dmpFunders.isEmpty()) { + ReferenceDepositModel depositFunder = dmpFunders.get(0); + FunderProperties.DoiFunder doiFunder = doiFunders.stream() + .filter(doiFunder1 -> depositFunder.getLabel().contains(doiFunder1.getFunder()) || doiFunder1.getFunder().contains(depositFunder.getLabel())) + .findFirst().orElse(null); + if (doiFunder != null) { + String finalId = doiFunder.getDoi() + "::" + grantReferenceTail; + ZenodoGrant grant = new ZenodoGrant(); + grant.setId(finalId); + if (deposit.getMetadata().getGrants() == null)deposit.getMetadata().setGrants(new ArrayList<>()); + deposit.getMetadata().getGrants().add(grant); + } + } + } + } + } + + private void applyContributors(DmpDepositModel dmp, ZenodoDeposit deposit, ZenodoProperties zenodoConfig){ + if (dmp.getUsers() == null) return; + + if (deposit.getMetadata() == null) deposit.setMetadata(new ZenodoDepositMetadata()); + + List dmpOrganizations = this.getReferenceDepositModelOfType(dmp, ReferenceType.Organizations); + String zenodoAffiliation = zenodoConfig.getAffiliation(); + + List contributors = new ArrayList<>(); + for (DmpUserDepositModel userDMP: dmp.getUsers()) { + ZenodoContributor contributor = new ZenodoContributor(); + contributor.setName(userDMP.getUser().getName()); + contributor.setType(CONTRIBUTOR_TYPE_PROJECT_MANAGER); + if (dmpOrganizations != null && !dmpOrganizations.isEmpty()) { + contributor.setAffiliation(dmpOrganizations.stream().map(ReferenceDepositModel::getLabel).collect(Collectors.joining(", "))); + } else { + if(zenodoAffiliation != null && !zenodoAffiliation.isEmpty()) { + contributor.setAffiliation(zenodoAffiliation); + } + } + contributors.add(contributor); + } + if (deposit.getMetadata().getContributors() == null)deposit.getMetadata().setContributors(new ArrayList<>()); + + deposit.getMetadata().getContributors().addAll(contributors); + + } + + private void applyCreators(DmpDepositModel dmp, ZenodoDeposit deposit, ZenodoProperties zenodoConfig){ + if (dmp.getUsers() == null) return; + + if (deposit.getMetadata() == null) deposit.setMetadata(new ZenodoDepositMetadata()); + + List dmpOrganizations = this.getReferenceDepositModelOfType(dmp, ReferenceType.Organizations); + String zenodoAffiliation = zenodoConfig.getAffiliation(); + + ZenodoCreator creator = new ZenodoCreator(); + DmpUserDepositModel dmpDepositModel = dmp.getUsers().stream().filter(userDMP -> userDMP.getRole().equals(DmpUserRole.Owner)).findFirst().orElse(null); + if (dmpDepositModel == null || dmpDepositModel.getUser() == null) return; + + creator.setName(dmpDepositModel.getUser().getName()); + if (dmpOrganizations != null && !dmpOrganizations.isEmpty()) { + creator.setAffiliation(dmpOrganizations.stream().map(ReferenceDepositModel::getLabel).collect(Collectors.joining(", "))); + } else { + if(zenodoAffiliation != null && !zenodoAffiliation.isEmpty()) { + creator.setAffiliation(zenodoAffiliation); + } + } + if (deposit.getMetadata().getCreators() == null)deposit.getMetadata().setCreators(new ArrayList<>()); + deposit.getMetadata().getCreators().add(creator); + } +} + diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoAccessRight.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoAccessRight.java deleted file mode 100644 index 0cf6cd4..0000000 --- a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/models/ZenodoAccessRight.java +++ /dev/null @@ -1,18 +0,0 @@ -package eu.eudat.depositinterface.zenodorepository.models; - -import com.fasterxml.jackson.annotation.JsonValue; - -public enum ZenodoAccessRight { - RESTRICTED("restricted"), EMBARGOED("embargoed"), OPEN("open"); - - private final String value; - - ZenodoAccessRight(String value) { - this.value = value; - } - - @JsonValue - public String getValue() { - return value; - } -} diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/service/ZenodoDepositService.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/service/ZenodoDepositService.java new file mode 100644 index 0000000..cbd1c12 --- /dev/null +++ b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/service/ZenodoDepositService.java @@ -0,0 +1,14 @@ +package eu.eudat.depositinterface.zenodorepository.service; + +import eu.eudat.depositinterface.models.DmpDepositModel; +import eu.eudat.depositinterface.repository.DepositConfiguration; + +public interface ZenodoDepositService { + String deposit(DmpDepositModel dmpDepositModel, String zenodoToken) throws Exception; + + DepositConfiguration getConfiguration(); + + String authenticate(String code); + + String getLogo(); +} diff --git a/core/src/main/java/eu/eudat/depositinterface/zenodorepository/service/ZenodoDepositServiceImpl.java b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/service/ZenodoDepositServiceImpl.java new file mode 100644 index 0000000..756727e --- /dev/null +++ b/core/src/main/java/eu/eudat/depositinterface/zenodorepository/service/ZenodoDepositServiceImpl.java @@ -0,0 +1,326 @@ +package eu.eudat.depositinterface.zenodorepository.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.eudat.depositinterface.models.DmpDepositModel; +import eu.eudat.depositinterface.models.FileEnvelope; +import eu.eudat.depositinterface.repository.DepositConfiguration; +import eu.eudat.depositinterface.zenodorepository.configuration.zenodo.ZenodoProperties; +import eu.eudat.depositinterface.zenodorepository.model.ZenodoDeposit; +import eu.eudat.depositinterface.zenodorepository.model.builder.ZenodoBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.*; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.util.ResourceUtils; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; + +import java.io.*; +import java.util.*; + +@Component +public class ZenodoDepositServiceImpl implements ZenodoDepositService { + private static final String PUBLISH_ID = "conceptdoi"; + + private static final String CLIENT_ID = "client_id"; + private static final String CLIENT_SECRET = "client_secret"; + private static final String GRANT_TYPE = "grant_type"; + private static final String AUTHORIZATION_CODE = "authorization_code"; + private static final String CODE = "code"; + private static final String ZENODO_LINKS = "links"; + private static final String REDIRECT_URI = "redirect_uri"; + private static final String ACCESS_TOKEN = "access_token"; + private static final String ZENODO_LINKS_BUCKET = "bucket"; + private static final String ZENODO_LINKS_PUBLISH = "publish"; + private static final String ZENODO_LINKS_SELF = "self"; + private static final String ZENODO_LINKS_LATEST_DRAFT = "latest_draft"; + private static final String ZENODO_METADATA = "metadata"; + private static final String ZENODO_METADATA_VERSION = "version"; + + private static final Logger logger = LoggerFactory.getLogger(ZenodoDepositServiceImpl.class); + private static final ObjectMapper objectMapper = new ObjectMapper(); + + private final ZenodoProperties zenodoProperties; + private final ZenodoBuilder mapper; + + private byte[] logo; + + @Autowired + public ZenodoDepositServiceImpl(ZenodoProperties zenodoProperties, ZenodoBuilder mapper){ + this.zenodoProperties = zenodoProperties; + this.mapper = mapper; + this.logo = null; + } + + @Override + public String deposit(DmpDepositModel dmpDepositModel, String zenodoToken) throws Exception { + + DepositConfiguration depositConfiguration = this.getConfiguration(); + + if(depositConfiguration != null) { + + if (zenodoToken == null || zenodoToken.isEmpty()) { + zenodoToken = depositConfiguration.getAccessToken(); + } + + String zenodoUrl = depositConfiguration.getRepositoryUrl(); + + // First step, post call to Zenodo, to create the entry. + WebClient zenodoClient = WebClient.builder().build(); + + DepositConfiguration zenodoConfig = this.zenodoProperties.getDepositConfiguration(); + if (zenodoConfig == null) return null; + eu.eudat.depositinterface.zenodorepository.model.ZenodoDeposit deposit = mapper.build(dmpDepositModel, this.zenodoProperties); + + LinkedHashMap links; + String previousDOI = dmpDepositModel.getPreviousDOI(); + String unpublishedUrl = null; + String publishUrl; + try { + + if (previousDOI == null) { + links = deposit(zenodoToken, zenodoUrl, zenodoClient, deposit); + } 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 + links = depositNewVersion(zenodoToken, zenodoUrl, previousDOI, zenodoClient, deposit); + } else { + links = depositFromPreviousDoi(zenodoToken, zenodoUrl, previousDOI, zenodoClient); + } + } + + if (unpublishedUrl == null) { + // Second step, add the file to the entry. + FileEnvelope pdfEnvelope = dmpDepositModel.getPdfFile(); + + if (links == null || !links.containsKey(ZENODO_LINKS_BUCKET)) throw new Exception("bucket not found"); + + String addFileUrl = links.get(ZENODO_LINKS_BUCKET) + "/" + pdfEnvelope.getFilename() + "?access_token=" + zenodoToken; + zenodoClient.put().uri(addFileUrl) + .body(BodyInserters + .fromResource(new ByteArrayResource(pdfEnvelope.getFile()))) + .retrieve().toEntity(Map.class).block(); + FileEnvelope rdaJsonEnvelope = dmpDepositModel.getRdaJsonFile(); + + String jsonFileName = rdaJsonEnvelope.getFilename(); + addFileUrl = links.get(ZENODO_LINKS_BUCKET) + "/" + jsonFileName + "?access_token=" + zenodoToken; + zenodoClient.put().uri(addFileUrl).headers(httpHeaders -> httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM)).body(BodyInserters.fromResource(new ByteArrayResource(rdaJsonEnvelope.getFile()))).retrieve().toEntity(Map.class).block(); + + if (dmpDepositModel.getSupportingFilesZip() != null) { + String supportingFilesZipName = dmpDepositModel.getSupportingFilesZip().getFilename(); + + addFileUrl = links.get(ZENODO_LINKS_BUCKET) + "/" + supportingFilesZipName + "?access_token=" + zenodoToken; + zenodoClient.put().uri(addFileUrl).body(BodyInserters.fromResource(new ByteArrayResource(supportingFilesZipName.getBytes()))).retrieve().toEntity(Map.class).block(); + } + + // Third post call to Zenodo to publish the entry and return the DOI. + publishUrl = links.get(ZENODO_LINKS_PUBLISH) + "?access_token=" + zenodoToken; + } else { + publishUrl = unpublishedUrl + "?access_token=" + zenodoToken; + } + + return this.publish(publishUrl); + + } catch (HttpClientErrorException | HttpServerErrorException ex) { + Map parsedException = objectMapper.readValue(ex.getResponseBodyAsString(), Map.class); + throw new IOException(parsedException.get("message"), ex); + } + + } + + return null; + + } + + private static LinkedHashMap depositNewVersion(String zenodoToken, String zenodoUrl, String previousDOI, WebClient zenodoClient, ZenodoDeposit deposit) throws Exception { + Map> createResponse; + LinkedHashMap links; + String listUrl = zenodoUrl + "deposit/depositions" + "?q=conceptdoi:\"" + previousDOI + "\"&access_token=" + zenodoToken; + logger.debug("listUrl = " + listUrl); + ResponseEntity> listResponses = zenodoClient.get().uri(listUrl).retrieve().toEntityList(Map.class).block(); + if (listResponses == null || listResponses.getBody() == null || listResponses.getBody().isEmpty()) return null; + createResponse = (Map>) listResponses.getBody().get(0); + logger.debug("createResponse-previousDoi:"); + logger.debug(objectMapper.writeValueAsString(createResponse)); + links = (LinkedHashMap) createResponse.getOrDefault(ZENODO_LINKS, new LinkedHashMap<>()); + + //Second, make the new version (not in the links?) + if (!links.containsKey(ZENODO_LINKS_LATEST_DRAFT)) throw new Exception("previousDOI not found"); + String newVersionUrl = links.get(ZENODO_LINKS_LATEST_DRAFT) + "/actions/newversion" + "?access_token=" + zenodoToken; + logger.debug("new version url: " + newVersionUrl); + createResponse = zenodoClient.post().uri(newVersionUrl) + .bodyValue(null).exchangeToMono(mono -> mono.bodyToMono(new ParameterizedTypeReference>>() {})).block(); + logger.debug("createResponse-newVersion:"); + logger.debug(objectMapper.writeValueAsString(createResponse)); + links = createResponse == null ? new LinkedHashMap<>() : createResponse.getOrDefault(ZENODO_LINKS, new LinkedHashMap<>()); + + //Third, get the new deposit + if (!links.containsKey(ZENODO_LINKS_LATEST_DRAFT)) throw new Exception("can not create latest draft"); + String latestDraftUrl = links.get(ZENODO_LINKS_LATEST_DRAFT) + "?access_token=" + zenodoToken; + createResponse = zenodoClient.get().uri(latestDraftUrl) + .exchangeToMono(mono -> mono.bodyToMono(new ParameterizedTypeReference>>() {})).block(); + logger.debug("createResponse-latestDraft:"); + logger.debug(objectMapper.writeValueAsString(createResponse)); + links = createResponse == null ? new LinkedHashMap<>() : createResponse.getOrDefault(ZENODO_LINKS, new LinkedHashMap<>()); + + //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(ZENODO_LINKS_SELF) + "?access_token=" + zenodoToken; + zenodoClient.put().uri(updateUrl) + .headers(httpHeaders -> { + httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + httpHeaders.setContentType(MediaType.APPLICATION_JSON); + }) + .bodyValue(deposit).retrieve().toEntity(Map.class).block(); + //And finally remove pre-existing files from it + String fileListUrl = links.get(ZENODO_LINKS_SELF) + "/files" + "?access_token=" + zenodoToken; + ResponseEntity> fileListResponse = zenodoClient.get().uri(fileListUrl).retrieve().toEntityList(Map.class).block(); + for (Map file : fileListResponse.getBody()) { + String fileDeleteUrl = links.get(ZENODO_LINKS_SELF) + "/files/" + file.get("id") + "?access_token=" + zenodoToken; + zenodoClient.delete().uri(fileDeleteUrl).retrieve().toEntity(Map.class).block(); + } + } 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); + zenodoClient.delete().uri(latestDraftUrl).retrieve().toEntity(Map.class).block(); + throw e; + } + return links; + } + + private static LinkedHashMap depositFromPreviousDoi(String zenodoToken, String zenodoUrl, String previousDOI, WebClient zenodoClient) { + Map> createResponse; + String listUrl = zenodoUrl + "deposit/depositions" + "?q=conceptdoi:\"" + previousDOI + "\"&access_token=" + zenodoToken; + ResponseEntity> listResponses = zenodoClient.get().uri(listUrl).retrieve().toEntityList(Map.class).block(); + if (listResponses == null || listResponses.getBody() == null || listResponses.getBody().isEmpty()) return null; + + createResponse = (Map>) listResponses.getBody().get(0); + + return createResponse.getOrDefault(ZENODO_LINKS, null); + } + + private LinkedHashMap deposit(String zenodoToken, String zenodoUrl, WebClient zenodoClient, ZenodoDeposit deposit) { + Map createResponse; + String createUrl = zenodoUrl + "deposit/depositions" + "?access_token=" + zenodoToken; + createResponse = zenodoClient.post().uri(createUrl).headers(httpHeaders -> { + httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + httpHeaders.setContentType(MediaType.APPLICATION_JSON); + }) + .bodyValue(deposit).exchangeToMono(mono -> mono.bodyToMono(new ParameterizedTypeReference>() {})).block(); + return (LinkedHashMap) createResponse.getOrDefault(ZENODO_LINKS, null); + } + + private String publish(String publishUrl){ + WebClient webClient = WebClient.builder().build(); + Map publishResponse = webClient.post().uri(publishUrl).bodyValue("").exchangeToMono(mono -> { + if (!mono.statusCode().is2xxSuccessful()) { + mono.createException(); + throw new UnsupportedOperationException("Failed to publish to Zenodo"); + } + return mono.bodyToMono(new ParameterizedTypeReference>() { + }); + }).block(); + if (publishResponse == null) throw new UnsupportedOperationException("Failed to publish to Zenodo"); + return (String) publishResponse.get(PUBLISH_ID); + } + + + @Override + public DepositConfiguration getConfiguration() { + return this.zenodoProperties.getDepositConfiguration(); + } + + @Override + public String authenticate(String code){ + + DepositConfiguration depositConfiguration = this.getConfiguration(); + + if(depositConfiguration != null) { + + WebClient client = WebClient.builder().defaultHeaders(httpHeaders -> { + httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA); + }).build(); + + MultiValueMap map = new LinkedMultiValueMap<>(); + map.add(CLIENT_ID, depositConfiguration.getRepositoryClientId()); + map.add(CLIENT_SECRET, depositConfiguration.getRepositoryClientSecret()); + map.add(GRANT_TYPE, AUTHORIZATION_CODE); + map.add(CODE, code); + map.add(REDIRECT_URI, depositConfiguration.getRedirectUri()); + + try { + Map values = client.post().uri(depositConfiguration.getRepositoryAccessTokenUrl()).bodyValue(map).exchangeToMono(mono -> { + if (!mono.statusCode().is2xxSuccessful()) { + mono.createException(); + throw new HttpClientErrorException(mono.statusCode()); + } + return mono.bodyToMono(new ParameterizedTypeReference>() { + }); + }).block(); + + return values != null ? (String) values.getOrDefault(ACCESS_TOKEN, null) : null; + } catch (HttpClientErrorException ex) { + logger.error(ex.getResponseBodyAsString(), ex); + return null; + } + } + + return null; + } + + @Override + public String getLogo() { + DepositConfiguration zenodoConfig = this.zenodoProperties.getDepositConfiguration(); + if(zenodoConfig != null && zenodoConfig.isHasLogo() && this.zenodoProperties.getLogo() != null && !this.zenodoProperties.getLogo().isBlank()) { + if (this.logo == null) { + try { + java.io.File logoFile = ResourceUtils.getFile(this.zenodoProperties.getLogo()); + if (!logoFile.exists()) return null; + try(InputStream inputStream = new FileInputStream(logoFile)){ + this.logo = inputStream.readAllBytes(); + }; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return (this.logo != null && this.logo.length != 0) ? Base64.getEncoder().encodeToString(this.logo) : null; + } + return null; + } + + private String getUnpublishedDOI(String zenodoUrl, String doi, String token, Short version) { + try { + WebClient client = WebClient.builder().build(); + Map> createResponse = null; + LinkedHashMap links; + LinkedHashMap metadata; + String listUrl = zenodoUrl + "deposit/depositions" + "?q=conceptdoi:\"" + doi + "\"&access_token=" + token; + ResponseEntity> listResponses = client.get().uri(listUrl).retrieve().toEntityList(Map.class).block(); + if (listResponses == null || listResponses.getBody() == null || listResponses.getBody().isEmpty()) return null; + + createResponse = (Map>) listResponses.getBody().get(0); + metadata = createResponse.getOrDefault(ZENODO_METADATA, new LinkedHashMap<>()); + links = createResponse.getOrDefault(ZENODO_LINKS, new LinkedHashMap<>()); + + if (metadata.get(ZENODO_METADATA_VERSION).equals(version.toString())) { + return links.get(ZENODO_LINKS_PUBLISH); + } else { + return null; + } + }catch (Exception e) { + logger.warn(e.getMessage(), e); + return null; + } + } +} diff --git a/web/src/main/java/eu/eudat/deposit/config/SecurityConfiguration.java b/web/src/main/java/eu/eudat/deposit/config/SecurityConfiguration.java index 5121f50..7420b86 100644 --- a/web/src/main/java/eu/eudat/deposit/config/SecurityConfiguration.java +++ b/web/src/main/java/eu/eudat/deposit/config/SecurityConfiguration.java @@ -12,6 +12,9 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.security.oauth2.jwt.JwtDecoders; +import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; @@ -21,13 +24,11 @@ import java.util.Set; @EnableWebSecurity public class SecurityConfiguration { - private final ApiKeyFilter apiKeyFilter; private final WebSecurityProperties webSecurityProperties; private final AuthenticationManagerResolver authenticationManagerResolver; @Autowired - public SecurityConfiguration(ApiKeyFilter apiKeyFilter, WebSecurityProperties webSecurityProperties, AuthenticationManagerResolver authenticationManagerResolver) { - this.apiKeyFilter = apiKeyFilter; + public SecurityConfiguration(WebSecurityProperties webSecurityProperties, AuthenticationManagerResolver authenticationManagerResolver) { this.webSecurityProperties = webSecurityProperties; this.authenticationManagerResolver = authenticationManagerResolver; } @@ -36,7 +37,6 @@ public class SecurityConfiguration { protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf(AbstractHttpConfigurer::disable) .cors(Customizer.withDefaults()) - .addFilterBefore(apiKeyFilter, AbstractPreAuthenticatedProcessingFilter.class) .authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry .requestMatchers(buildAntPatterns(webSecurityProperties.getAuthorizedEndpoints())).authenticated() .requestMatchers(buildAntPatterns(webSecurityProperties.getAllowedEndpoints())).anonymous()) diff --git a/web/src/main/java/eu/eudat/deposit/controller/DepositController.java b/web/src/main/java/eu/eudat/deposit/controller/DepositController.java index dc90111..1f28e60 100644 --- a/web/src/main/java/eu/eudat/deposit/controller/DepositController.java +++ b/web/src/main/java/eu/eudat/deposit/controller/DepositController.java @@ -1,8 +1,8 @@ package eu.eudat.deposit.controller; -import eu.eudat.depositinterface.models.DMPDepositModel; -import eu.eudat.depositinterface.repository.RepositoryDeposit; -import eu.eudat.depositinterface.repository.RepositoryDepositConfiguration; +import eu.eudat.depositinterface.models.DmpDepositModel; +import eu.eudat.depositinterface.repository.DepositConfiguration; +import eu.eudat.depositinterface.zenodorepository.service.ZenodoDepositService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -10,33 +10,29 @@ import java.util.List; @RestController @RequestMapping("/api/deposit") -public class DepositController { +public class DepositController implements eu.eudat.depositinterface.repository.DepositController { - private final RepositoryDeposit repositoryDeposit; + private final ZenodoDepositService depositClient; @Autowired - public DepositController(RepositoryDeposit repositoryDeposit) { - this.repositoryDeposit = repositoryDeposit; + public DepositController(ZenodoDepositService depositClient) { + this.depositClient = depositClient; } - @PostMapping("/{id}") - public String deposit(@PathVariable("id") String repositoryId, @RequestBody DMPDepositModel dmpDepositModel, @RequestParam("authToken")String authToken) throws Exception { - return repositoryDeposit.deposit(repositoryId, dmpDepositModel, authToken); + public String deposit(@RequestBody DmpDepositModel dmpDepositModel, @RequestParam("authToken")String authToken) throws Exception { + return depositClient.deposit(dmpDepositModel, authToken); } - @GetMapping("/authenticate/{id}") - public String authenticate(@PathVariable("id") String repositoryId, @RequestParam("authToken") String authToken) { - return repositoryDeposit.authenticate(repositoryId, authToken); + public String authenticate(@RequestParam("authToken") String code) { + return depositClient.authenticate(code); } - @GetMapping("/configuration") - public List getConfiguration() { - return repositoryDeposit.getConfiguration(); + public DepositConfiguration getConfiguration() { + return depositClient.getConfiguration(); } - @GetMapping("/logo/{id}") - public String getLogo(@PathVariable("id") String repositoryId) { - return repositoryDeposit.getLogo(repositoryId); + public String getLogo() { + return depositClient.getLogo(); } } diff --git a/web/src/main/resources/config/cache.yml b/web/src/main/resources/config/cache.yml index 005e6de..6351979 100644 --- a/web/src/main/resources/config/cache.yml +++ b/web/src/main/resources/config/cache.yml @@ -2,7 +2,7 @@ cache: manager: fallbackToNoOpCache: true caffeineCaches: - - names: [ "apikey" ] + - names: [ "logoByRepository" ] allowNullValues: true initialCapacity: 100 maximumSize: 500 @@ -11,6 +11,8 @@ cache: expireAfterAccessMinutes: 10 refreshAfterWriteMinutes: 10 mapCaches: - apiKey: - name: apikey - keyPattern: resolve_$keyhash$:v0 \ No newline at end of file + logoByRepository: + name: logoByRepository + keyPattern: zenodoplugin_$repo$:v0 + + \ No newline at end of file diff --git a/web/src/main/resources/config/security-dev.yml b/web/src/main/resources/config/security-dev.yml new file mode 100644 index 0000000..d85a91f --- /dev/null +++ b/web/src/main/resources/config/security-dev.yml @@ -0,0 +1,7 @@ +web: + security: + idp: + resource: + jwt: + audiences: [ "dmp_zenodo_bridge" ] + validIssuer: ${IDP_ISSUER_URI:} \ No newline at end of file diff --git a/web/src/main/resources/config/security.yml b/web/src/main/resources/config/security.yml index ad38bd8..fc4b49f 100644 --- a/web/src/main/resources/config/security.yml +++ b/web/src/main/resources/config/security.yml @@ -5,16 +5,9 @@ web: allowed-endpoints: [ health ] idp: api-key: - enabled: true - authorization-header: Authorization - client-id: ${IDP_APIKEY_CLIENT_ID:} - client-secret: ${IDP_APIKEY_CLIENT_SECRET:} - scope: ${IDP_APIKEY_SCOPE:} + enabled: false resource: token-type: JWT #| opaque - opaque: - client-id: ${IDP_OPAQUE_CLIENT_ID:} - client-secret: ${IDP_OPAQUE_CLIENT_SECRET:} jwt: claims: [ role, x-role ] issuer-uri: ${IDP_ISSUER_URI:} \ No newline at end of file diff --git a/web/src/main/resources/config/zenodo-dev.yml b/web/src/main/resources/config/zenodo-dev.yml index c3c2b44..fce99db 100644 --- a/web/src/main/resources/config/zenodo-dev.yml +++ b/web/src/main/resources/config/zenodo-dev.yml @@ -1,18 +1,17 @@ zenodo: - configuration: - - deposit-type: 2 - repository-id: Zenodo - access-token: ${ZENODO_ACCESS_TOKEN} - repository-url: https://sandbox.zenodo.org/api/ - repository-authorization-url: https://sandbox.zenodo.org/oauth/authorize - repository-record-url: https://sandbox.zenodo.org/record/ - repository-access-token-url: https://sandbox.zenodo.org/oauth/token - repository-client-id: - repository-client-secret: - redirect-uri: http://localhost:4200/login/external/zenodo - has-logo: true - logo: classpath:zenodo.jpg - doi-funder: - community: argos - affiliation: ARGOS - domain: https://argos.openaire.eu/ \ No newline at end of file + community: argos + affiliation: ARGOS + domain: https://argos.openaire.eu/ + logo: classpath:zenodo.jpg + depositConfiguration: + deposit-type: 2 + repository-id: Zenodo + access-token: ${ZENODO_ACCESS_TOKEN} + repository-url: https://sandbox.zenodo.org/api/ + repository-authorization-url: https://sandbox.zenodo.org/oauth/authorize + repository-record-url: https://sandbox.zenodo.org/record/ + repository-access-token-url: https://sandbox.zenodo.org/oauth/token + repository-client-id: + repository-client-secret: + redirect-uri: http://localhost:4200/login/external/zenodo + has-logo: true \ No newline at end of file diff --git a/web/src/main/resources/config/zenodo.yml b/web/src/main/resources/config/zenodo.yml index 6b6aa66..4d561fd 100644 --- a/web/src/main/resources/config/zenodo.yml +++ b/web/src/main/resources/config/zenodo.yml @@ -1,3 +1 @@ zenodo: - storage: - temp: ${STORAGE_TMP_ZENODO} \ No newline at end of file