sync with new models

This commit is contained in:
Efstratios Giannopoulos 2023-12-11 15:08:00 +02:00
parent c5570d15b7
commit c048d7047f
16 changed files with 617 additions and 391 deletions

View File

@ -58,6 +58,11 @@
<artifactId>json</artifactId> <artifactId>json</artifactId>
<version>20160810</version> <version>20160810</version>
</dependency> </dependency>
<dependency>
<groupId>gr.cite</groupId>
<artifactId>cache</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -8,27 +8,20 @@ import java.util.List;
@ConfigurationProperties(prefix = "funder") @ConfigurationProperties(prefix = "funder")
public class FunderProperties { public class FunderProperties {
private final List<DOIFunder> available; private List<DoiFunder> available;
@ConstructorBinding public List<DoiFunder> getAvailable() {
public FunderProperties(List<DOIFunder> available) {
this.available = available;
}
public List<DOIFunder> getAvailable() {
return available; return available;
} }
public static class DOIFunder { public void setAvailable(List<DoiFunder> available) {
private final String funder; this.available = available;
private final String doi;
@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() { public String getFunder() {
return funder; return funder;
} }
@ -36,5 +29,13 @@ public class FunderProperties {
public String getDoi() { public String getDoi() {
return doi; return doi;
} }
public void setFunder(String funder) {
this.funder = funder;
}
public void setDoi(String doi) {
this.doi = doi;
}
} }
} }

View File

@ -7,14 +7,13 @@ import java.util.List;
@ConfigurationProperties(prefix = "identifiers") @ConfigurationProperties(prefix = "identifiers")
public class IdentifierProperties { public class IdentifierProperties {
private final List<String> related; private List<String> related;
@ConstructorBinding
public IdentifierProperties(List<String> related) {
this.related = related;
}
public List<String> getRelated() { public List<String> getRelated() {
return related; return related;
} }
public void setRelated(List<String> related) {
this.related = related;
}
} }

View File

@ -8,14 +8,8 @@ import java.util.List;
@ConfigurationProperties(prefix = "pid") @ConfigurationProperties(prefix = "pid")
public class PidProperties { public class PidProperties {
private final List<String> acceptedTypes; private List<String> acceptedTypes;
private final PidFieldNames fields; private PidFieldNames fields;
@ConstructorBinding
public PidProperties(List<String> acceptedTypes, PidFieldNames fields) {
this.acceptedTypes = acceptedTypes;
this.fields = fields;
}
public List<String> getAcceptedTypes() { public List<String> getAcceptedTypes() {
return acceptedTypes; return acceptedTypes;
@ -25,22 +19,32 @@ public class PidProperties {
return fields; return fields;
} }
public static class PidFieldNames { public void setAcceptedTypes(List<String> acceptedTypes) {
private final String pidName; this.acceptedTypes = acceptedTypes;
private final String pidTypeName;
@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() { public String getPidName() {
return pidName; return pidName;
} }
public void setPidName(String pidName) {
this.pidName = pidName;
}
public String getPidTypeName() { public String getPidTypeName() {
return pidTypeName; return pidTypeName;
} }
public void setPidTypeName(String pidTypeName) {
this.pidTypeName = pidTypeName;
}
} }
} }

View File

@ -11,15 +11,10 @@ import java.util.Map;
@ConfigurationProperties(prefix = "zenodo") @ConfigurationProperties(prefix = "zenodo")
public class ZenodoProperties { public class ZenodoProperties {
private final String tempStorage; private String tempStorage;
private final List<ZenodoConfig> configuration; private List<ZenodoConfig> configuration;
@ConstructorBinding
public ZenodoProperties(Map<String, String> storage, List<ZenodoConfig> configuration) {
this.tempStorage = storage.get("temp");
this.configuration = configuration;
}
public String getTempStorage() { public String getTempStorage() {
return tempStorage; return tempStorage;
@ -29,108 +24,182 @@ public class ZenodoProperties {
return configuration; return configuration;
} }
public static class ZenodoConfig extends RepositoryDepositConfiguration { public void setTempStorage(String tempStorage) {
private final DepositType depositType; this.tempStorage = tempStorage;
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;
@ConstructorBinding
public ZenodoConfig(DepositType 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 void setConfiguration(List<ZenodoConfig> configuration) {
this.configuration = configuration;
}
public static class ZenodoConfig extends RepositoryDepositConfiguration {
private DepositType depositType;
private String repositoryId;
private String accessToken;
private String repositoryUrl;
private String repositoryAuthorizationUrl;
private String repositoryRecordUrl;
private String repositoryAccessTokenUrl;
private String repositoryClientId;
private String repositoryClientSecret;
private String redirectUri;
private boolean hasLogo;
private String logo;
private String doiFunder;
private String community;
private String affiliation;
private String domain;
@Override
public DepositType getDepositType() { public DepositType getDepositType() {
return depositType; return depositType;
} }
@Override
public void setDepositType(DepositType depositType) {
this.depositType = depositType;
}
@Override
public String getRepositoryId() { public String getRepositoryId() {
return repositoryId; return repositoryId;
} }
@Override
public void setRepositoryId(String repositoryId) {
this.repositoryId = repositoryId;
}
@Override
public String getAccessToken() { public String getAccessToken() {
return accessToken; return accessToken;
} }
@Override
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
@Override
public String getRepositoryUrl() { public String getRepositoryUrl() {
return repositoryUrl; return repositoryUrl;
} }
@Override
public void setRepositoryUrl(String repositoryUrl) {
this.repositoryUrl = repositoryUrl;
}
@Override
public String getRepositoryAuthorizationUrl() { public String getRepositoryAuthorizationUrl() {
return repositoryAuthorizationUrl; return repositoryAuthorizationUrl;
} }
@Override
public void setRepositoryAuthorizationUrl(String repositoryAuthorizationUrl) {
this.repositoryAuthorizationUrl = repositoryAuthorizationUrl;
}
@Override
public String getRepositoryRecordUrl() { public String getRepositoryRecordUrl() {
return repositoryRecordUrl; return repositoryRecordUrl;
} }
@Override
public void setRepositoryRecordUrl(String repositoryRecordUrl) {
this.repositoryRecordUrl = repositoryRecordUrl;
}
@Override
public String getRepositoryAccessTokenUrl() { public String getRepositoryAccessTokenUrl() {
return repositoryAccessTokenUrl; return repositoryAccessTokenUrl;
} }
@Override
public void setRepositoryAccessTokenUrl(String repositoryAccessTokenUrl) {
this.repositoryAccessTokenUrl = repositoryAccessTokenUrl;
}
@Override
public String getRepositoryClientId() { public String getRepositoryClientId() {
return repositoryClientId; return repositoryClientId;
} }
@Override
public void setRepositoryClientId(String repositoryClientId) {
this.repositoryClientId = repositoryClientId;
}
@Override
public String getRepositoryClientSecret() { public String getRepositoryClientSecret() {
return repositoryClientSecret; return repositoryClientSecret;
} }
@Override
public void setRepositoryClientSecret(String repositoryClientSecret) {
this.repositoryClientSecret = repositoryClientSecret;
}
@Override
public String getRedirectUri() { public String getRedirectUri() {
return redirectUri; return redirectUri;
} }
@Override
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
@Override
public boolean isHasLogo() { public boolean isHasLogo() {
return hasLogo; return hasLogo;
} }
@Override
public void setHasLogo(boolean hasLogo) {
this.hasLogo = hasLogo;
}
public String getLogo() { public String getLogo() {
return logo; return logo;
} }
public void setLogo(String logo) {
this.logo = logo;
}
public String getDoiFunder() { public String getDoiFunder() {
return doiFunder; return doiFunder;
} }
public void setDoiFunder(String doiFunder) {
this.doiFunder = doiFunder;
}
public String getCommunity() { public String getCommunity() {
return community; return community;
} }
public void setCommunity(String community) {
this.community = community;
}
public String getAffiliation() { public String getAffiliation() {
return affiliation; return affiliation;
} }
public void setAffiliation(String affiliation) {
this.affiliation = affiliation;
}
public String getDomain() { public String getDomain() {
return domain; return domain;
} }
public void setDomain(String domain) {
this.domain = domain;
}
public RepositoryDepositConfiguration toRepoConfig() { public RepositoryDepositConfiguration toRepoConfig() {
RepositoryDepositConfiguration config = new RepositoryDepositConfiguration(); RepositoryDepositConfiguration config = new RepositoryDepositConfiguration();
config.setDepositType(this.depositType); config.setDepositType(this.depositType);

View File

@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
public class ZenodoComunity { public class ZenodoCommunity {
private String identifier; private String identifier;

View File

@ -32,7 +32,7 @@ public class ZenodoDepositMetadata {
private List<String> references; private List<String> references;
private List<ZenodoComunity> communities; private List<ZenodoCommunity> communities;
@JsonProperty("access_right") @JsonProperty("access_right")
private ZenodoAccessRight accessRight; private ZenodoAccessRight accessRight;
@ -126,11 +126,11 @@ public class ZenodoDepositMetadata {
this.version = version; this.version = version;
} }
public List<ZenodoComunity> getCommunities() { public List<ZenodoCommunity> getCommunities() {
return communities; return communities;
} }
public void setCommunities(List<ZenodoComunity> communities) { public void setCommunities(List<ZenodoCommunity> communities) {
this.communities = communities; this.communities = communities;
} }

View File

@ -1,4 +1,4 @@
package eu.eudat.depositinterface.zenodorepository.mapper; package eu.eudat.depositinterface.zenodorepository.model.builder;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
@ -25,14 +25,25 @@ import java.util.stream.Collectors;
@Component @Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ZenodoBuilderMapper { 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 PidProperties pidProperties;
private final IdentifierProperties identifierProperties; private final IdentifierProperties identifierProperties;
private final FunderProperties funderProperties; private final FunderProperties funderProperties;
@Autowired @Autowired
public ZenodoBuilderMapper(PidProperties pidProperties, IdentifierProperties identifierProperties, FunderProperties funderProperties){ public ZenodoBuilder(PidProperties pidProperties, IdentifierProperties identifierProperties, FunderProperties funderProperties){
this.pidProperties = pidProperties; this.pidProperties = pidProperties;
this.identifierProperties = identifierProperties; this.identifierProperties = identifierProperties;
this.funderProperties = funderProperties; this.funderProperties = funderProperties;
@ -42,13 +53,13 @@ public class ZenodoBuilderMapper {
ZenodoDeposit deposit = new ZenodoDeposit(); ZenodoDeposit deposit = new ZenodoDeposit();
this.applyZenodoRelator(dmp, deposit); this.applyZenodoRelator(dmp, deposit);
deposit.getMetadata().setTitle(dmp.getLabel()); deposit.getMetadata().setTitle(dmp.getLabel());
deposit.getMetadata().setUploadType("publication"); deposit.getMetadata().setUploadType(UPLOAD_TYPE);
deposit.getMetadata().setPublicationType("datamanagementplan"); deposit.getMetadata().setPublicationType(PUBLICATION_TYPE);
deposit.getMetadata().setDescription((dmp.getDescription() != null && !dmp.getDescription().isEmpty() ? dmp.getDescription() : "<p></p>")); deposit.getMetadata().setDescription((dmp.getDescription() != null && !dmp.getDescription().isEmpty() ? dmp.getDescription() : "<p></p>"));
deposit.getMetadata().setVersion(String.valueOf(dmp.getVersion())); deposit.getMetadata().setVersion(String.valueOf(dmp.getVersion()));
String zenodoCommunity = zenodoConfig.getCommunity(); String zenodoCommunity = zenodoConfig.getCommunity();
if(zenodoCommunity != null && !zenodoCommunity.isEmpty()) { if(zenodoCommunity != null && !zenodoCommunity.isEmpty()) {
ZenodoComunity community = new ZenodoComunity(); ZenodoCommunity community = new ZenodoCommunity();
community.setIdentifier(zenodoCommunity); community.setIdentifier(zenodoCommunity);
deposit.getMetadata().getCommunities().add(community); deposit.getMetadata().getCommunities().add(community);
} }
@ -65,7 +76,7 @@ public class ZenodoBuilderMapper {
return deposit; return deposit;
} }
private List<DatasetFieldsDepositModel> findSchemanticValues(String relatedId, List<DatasetFieldsDepositModel> fields){ private List<DatasetFieldsDepositModel> findSchematicValues(String relatedId, List<DatasetFieldsDepositModel> fields){
return fields.stream().filter(f -> f.getSchematics().contains(relatedId)).collect(Collectors.toList()); return fields.stream().filter(f -> f.getSchematics().contains(relatedId)).collect(Collectors.toList());
} }
@ -162,7 +173,7 @@ public class ZenodoBuilderMapper {
List<ZenodoRelator> relatedIdentifiers = new ArrayList<>(); List<ZenodoRelator> relatedIdentifiers = new ArrayList<>();
for(DescriptionDepositModel descriptionDepositModel: dmp.getDescriptions()){ for(DescriptionDepositModel descriptionDepositModel: dmp.getDescriptions()){
for(String relatedId: this.identifierProperties.getRelated()){ for(String relatedId: this.identifierProperties.getRelated()){
List<DatasetFieldsDepositModel> fields = findSchemanticValues(relatedId, descriptionDepositModel.getFields()); List<DatasetFieldsDepositModel> fields = findSchematicValues(relatedId, descriptionDepositModel.getFields());
Set<String> values = extractSchematicValues(fields, acceptedPidTypes); Set<String> values = extractSchematicValues(fields, acceptedPidTypes);
for(String value: values){ for(String value: values){
ZenodoRelator relator = new ZenodoRelator(); ZenodoRelator relator = new ZenodoRelator();
@ -205,7 +216,7 @@ public class ZenodoBuilderMapper {
if (dmp.getAccessType().equals(DmpAccessType.Public)) { if (dmp.getAccessType().equals(DmpAccessType.Public)) {
ZenodoRelator relator = new ZenodoRelator(); ZenodoRelator relator = new ZenodoRelator();
relator.setIdentifier(zenodoConfig.getDomain() + "/external/zenodo/" + dmp.getId().toString()); relator.setIdentifier(zenodoConfig.getDomain() + "/external/zenodo/" + dmp.getId().toString());
relator.setRelation("isIdenticalTo"); relator.setRelation(IS_IDENTICAL_TO);
if (deposit.getMetadata().getRelatedIdentifiers() == null)deposit.getMetadata().setRelatedIdentifiers(new ArrayList<>()); if (deposit.getMetadata().getRelatedIdentifiers() == null)deposit.getMetadata().setRelatedIdentifiers(new ArrayList<>());
deposit.getMetadata().getRelatedIdentifiers().add(relator); deposit.getMetadata().getRelatedIdentifiers().add(relator);
} }
@ -217,7 +228,7 @@ public class ZenodoBuilderMapper {
List<ReferenceDepositModel> dmpLicenses = this.getReferenceDepositModelOfType(dmp, ReferenceType.Licenses); List<ReferenceDepositModel> dmpLicenses = this.getReferenceDepositModelOfType(dmp, ReferenceType.Licenses);
if (dmpLicenses != null && !dmpLicenses.isEmpty()) { if (dmpLicenses != null && !dmpLicenses.isEmpty()) {
for (ReferenceDepositModel dmpLicense : dmpLicenses) { for (ReferenceDepositModel dmpLicense : dmpLicenses) {
String pid = dmpLicense.getDefinition().getFields().stream().filter(x-> "pid".equalsIgnoreCase(x.getCode())).map(FieldDepositModel::getValue).findFirst().orElse(null); String pid = dmpLicense.getDefinition().getFields().stream().filter(x-> PID.equalsIgnoreCase(x.getCode())).map(FieldDepositModel::getValue).findFirst().orElse(null);
if (pid != null && !pid.isBlank()) { if (pid != null && !pid.isBlank()) {
deposit.getMetadata().setLicense(pid); deposit.getMetadata().setLicense(pid);
break; break;
@ -235,9 +246,9 @@ public class ZenodoBuilderMapper {
for (ReferenceDepositModel researcher : dmpResearchers) { for (ReferenceDepositModel researcher : dmpResearchers) {
ZenodoContributor contributor = new ZenodoContributor(); ZenodoContributor contributor = new ZenodoContributor();
contributor.setName(researcher.getLabel()); contributor.setName(researcher.getLabel());
contributor.setType("Researcher"); contributor.setType(CONTRIBUTOR_TYPE_RESEARCHER);
contributor.setAffiliation(researcher.getSource()); contributor.setAffiliation(researcher.getSource());
if (researcher.getSource().equalsIgnoreCase("ORCID")) { if (researcher.getSource().equalsIgnoreCase(SOURCE_ORCID)) {
contributor.setOrcid(researcher.getReference()); contributor.setOrcid(researcher.getReference());
} }
researchers.add(contributor); researchers.add(contributor);
@ -256,14 +267,14 @@ public class ZenodoBuilderMapper {
if (dmpGrants != null && !dmpGrants.isEmpty()) { if (dmpGrants != null && !dmpGrants.isEmpty()) {
ReferenceDepositModel depositGrant = dmpGrants.stream().filter(x-> x.getSource().equals("openaire")).findFirst().orElse(null); ReferenceDepositModel depositGrant = dmpGrants.stream().filter(x-> x.getSource().equals(SOURCE_OPENAIRE)).findFirst().orElse(null);
if (depositGrant != null) { if (depositGrant != null) {
String grantReferenceTail = depositGrant.getReference().split(":")[2]; String grantReferenceTail = depositGrant.getReference().split(":")[2];
List<FunderProperties.DOIFunder> doiFunders = this.funderProperties.getAvailable(); List<FunderProperties.DoiFunder> doiFunders = this.funderProperties.getAvailable();
if (dmpFunders != null && !dmpFunders.isEmpty()) { if (dmpFunders != null && !dmpFunders.isEmpty()) {
ReferenceDepositModel depositFunder = dmpFunders.get(0); ReferenceDepositModel depositFunder = dmpFunders.get(0);
FunderProperties.DOIFunder doiFunder = doiFunders.stream() FunderProperties.DoiFunder doiFunder = doiFunders.stream()
.filter(doiFunder1 -> depositFunder.getLabel().contains(doiFunder1.getFunder()) || doiFunder1.getFunder().contains(depositFunder.getLabel())) .filter(doiFunder1 -> depositFunder.getLabel().contains(doiFunder1.getFunder()) || doiFunder1.getFunder().contains(depositFunder.getLabel()))
.findFirst().orElse(null); .findFirst().orElse(null);
if (doiFunder != null) { if (doiFunder != null) {
@ -290,7 +301,7 @@ public class ZenodoBuilderMapper {
for (UserDmpDepositModel userDMP: dmp.getUsers()) { for (UserDmpDepositModel userDMP: dmp.getUsers()) {
ZenodoContributor contributor = new ZenodoContributor(); ZenodoContributor contributor = new ZenodoContributor();
contributor.setName(userDMP.getUser().getName()); contributor.setName(userDMP.getUser().getName());
contributor.setType("ProjectMember"); contributor.setType(CONTRIBUTOR_TYPE_PROJECT_MANAGER);
if (dmpOrganizations != null && !dmpOrganizations.isEmpty()) { if (dmpOrganizations != null && !dmpOrganizations.isEmpty()) {
contributor.setAffiliation(dmpOrganizations.stream().map(ReferenceDepositModel::getLabel).collect(Collectors.joining(", "))); contributor.setAffiliation(dmpOrganizations.stream().map(ReferenceDepositModel::getLabel).collect(Collectors.joining(", ")));
} else { } else {

View File

@ -0,0 +1,10 @@
package eu.eudat.depositinterface.zenodorepository.service;
import gr.cite.tools.cache.CacheOptions;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "cache.logo-by-repository")
public class RepositoryLogoCacheOptions extends CacheOptions {
}

View File

@ -0,0 +1,65 @@
package eu.eudat.depositinterface.zenodorepository.service;
import gr.cite.tools.cache.CacheService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
@Service
public class RepositoryLogoCacheService extends CacheService<RepositoryLogoCacheService.RepositoryLogoCacheValue> {
public static class RepositoryLogoCacheValue {
public RepositoryLogoCacheValue() {
}
public RepositoryLogoCacheValue(String repositoryId, byte[] logo) {
this.repositoryId = repositoryId;
this.logo = logo;
}
private String repositoryId;
public String getRepositoryId() {
return repositoryId;
}
public void setRepositoryId(String repositoryId) {
this.repositoryId = repositoryId;
}
private byte[] logo;
public byte[] getLogo() {
return logo;
}
public void setLogo(byte[] logo) {
this.logo = logo;
}
}
@Autowired
public RepositoryLogoCacheService(RepositoryLogoCacheOptions options) {
super(options);
}
@Override
protected Class<RepositoryLogoCacheValue> valueClass() {
return RepositoryLogoCacheValue.class;
}
@Override
public String keyOf(RepositoryLogoCacheValue value) {
return this.buildKey(value.getRepositoryId());
}
public String buildKey(String repositoryId) {
HashMap<String, String> keyParts = new HashMap<>();
keyParts.put("$repo$", repositoryId);
return this.generateKey(keyParts);
}
}

View File

@ -0,0 +1,335 @@
package eu.eudat.depositinterface.zenodorepository.service;
import com.fasterxml.jackson.core.JsonProcessingException;
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.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.*;
import java.util.stream.Collectors;
@Component
public class ZenodoDepositService implements RepositoryDeposit {
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(ZenodoDepositService.class);
private static final ObjectMapper objectMapper = new ObjectMapper();
private final ZenodoProperties zenodoProperties;
private final ZenodoBuilder mapper;
private final RepositoryLogoCacheService repositoryLogoCacheService;
@Autowired
public ZenodoDepositService(ZenodoProperties zenodoProperties, ZenodoBuilder mapper, RepositoryLogoCacheService repositoryLogoCacheService){
this.zenodoProperties = zenodoProperties;
this.mapper = mapper;
this.repositoryLogoCacheService = repositoryLogoCacheService;
}
@Override
public String deposit(String repositoryId, DmpDepositModel dmpDepositModel, String zenodoToken) throws Exception {
RepositoryDepositConfiguration repositoryDepositConfiguration = this.getConfiguration().stream().filter(x -> x.getRepositoryId().equals(repositoryId)).findFirst().orElse(null);
if(repositoryDepositConfiguration != null) {
if (zenodoToken == null || zenodoToken.isEmpty()) {
zenodoToken = repositoryDepositConfiguration.getAccessToken();
}
String zenodoUrl = repositoryDepositConfiguration.getRepositoryUrl();
// First step, post call to Zenodo, to create the entry.
WebClient zenodoClient = WebClient.builder().build();
ZenodoProperties.ZenodoConfig zenodoConfig = this.zenodoProperties.getConfiguration().stream().filter(x -> x.getRepositoryId().equals(repositoryId)).findFirst().orElse(null);
if (zenodoConfig == null) return null;
eu.eudat.depositinterface.zenodorepository.model.ZenodoDeposit deposit = mapper.build(dmpDepositModel, zenodoConfig);
LinkedHashMap<String, String> 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<String, String> parsedException = objectMapper.readValue(ex.getResponseBodyAsString(), Map.class);
throw new IOException(parsedException.get("message"), ex);
}
}
return null;
}
private static LinkedHashMap<String, String> depositNewVersion(String zenodoToken, String zenodoUrl, String previousDOI, WebClient zenodoClient, ZenodoDeposit deposit) throws Exception {
Map<String, LinkedHashMap<String, String>> createResponse;
LinkedHashMap<String, String> links;
String listUrl = zenodoUrl + "deposit/depositions" + "?q=conceptdoi:\"" + previousDOI + "\"&access_token=" + zenodoToken;
logger.debug("listUrl = " + listUrl);
ResponseEntity<List<Map>> listResponses = zenodoClient.get().uri(listUrl).retrieve().toEntityList(Map.class).block();
if (listResponses == null || listResponses.getBody() == null || listResponses.getBody().isEmpty()) return null;
createResponse = (Map<String, LinkedHashMap<String, String>>) listResponses.getBody().get(0);
logger.debug("createResponse-previousDoi:");
logger.debug(objectMapper.writeValueAsString(createResponse));
links = (LinkedHashMap<String, String>) createResponse.getOrDefault(ZENODO_LINKS, new LinkedHashMap<>());
//Second, make the new version (not in the links?)
if (!links.containsKey(ZENODO_LINKS_SELF)) throw new Exception("previousDOI not found");
String newVersionUrl = links.get(ZENODO_LINKS_SELF) + "/actions/newversion" + "?access_token=" + zenodoToken;
logger.debug("new version url: " + newVersionUrl);
createResponse = zenodoClient.post().uri(newVersionUrl)
.bodyValue(null).exchangeToMono(mono -> mono.bodyToMono(new ParameterizedTypeReference<Map<String, LinkedHashMap<String, String>>>() {})).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<Map<String, LinkedHashMap<String, String>>>() {})).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<List<Map>> 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<String, String> depositFromPreviousDoi(String zenodoToken, String zenodoUrl, String previousDOI, WebClient zenodoClient) {
Map<String, LinkedHashMap<String, String>> createResponse;
String listUrl = zenodoUrl + "deposit/depositions" + "?q=conceptdoi:\"" + previousDOI + "\"&access_token=" + zenodoToken;
ResponseEntity<List<Map>> listResponses = zenodoClient.get().uri(listUrl).retrieve().toEntityList(Map.class).block();
if (listResponses == null || listResponses.getBody() == null || listResponses.getBody().isEmpty()) return null;
createResponse = (Map<String, LinkedHashMap<String, String>>) listResponses.getBody().get(0);
return createResponse.getOrDefault(ZENODO_LINKS, null);
}
private LinkedHashMap<String, String> deposit(String zenodoToken, String zenodoUrl, WebClient zenodoClient, ZenodoDeposit deposit) {
Map<String, Object> 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<Map<String, Object>>() {})).block();
return (LinkedHashMap<String, String>) createResponse.getOrDefault(ZENODO_LINKS, null);
}
private String publish(String publishUrl){
WebClient webClient = WebClient.builder().build();
Map<String, Object> 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<Map<String, Object>>() {
});
}).block();
if (publishResponse == null) throw new UnsupportedOperationException("Failed to publish to Zenodo");
return (String) publishResponse.get(PUBLISH_ID);
}
@Override
public List<RepositoryDepositConfiguration> getConfiguration() {
List<ZenodoProperties.ZenodoConfig> 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 repositoryDepositConfiguration = this.getConfiguration().stream().filter(x -> x.getRepositoryId().equals(repositoryId)).findFirst().orElse(null);
if(repositoryDepositConfiguration != null) {
WebClient client = WebClient.builder().defaultHeaders(httpHeaders -> {
httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
}).build();
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add(CLIENT_ID, repositoryDepositConfiguration.getRepositoryClientId());
map.add(CLIENT_SECRET, repositoryDepositConfiguration.getRepositoryClientSecret());
map.add(GRANT_TYPE, AUTHORIZATION_CODE);
map.add(CODE, code);
map.add(REDIRECT_URI, repositoryDepositConfiguration.getRedirectUri());
try {
Map<String, Object> values = client.post().uri(repositoryDepositConfiguration.getRepositoryAccessTokenUrl()).bodyValue(map).exchangeToMono(mono -> {
if (!mono.statusCode().is2xxSuccessful()) {
mono.createException();
throw new HttpClientErrorException(mono.statusCode());
}
return mono.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
});
}).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(String repositoryId) {
ZenodoProperties.ZenodoConfig zenodoConfig = this.zenodoProperties.getConfiguration().stream().filter(x -> x.getRepositoryId().equals(repositoryId)).findFirst().orElse(null);
if(zenodoConfig != null && zenodoConfig.isHasLogo() && zenodoConfig.getLogo() != null && !zenodoConfig.getLogo().isBlank()) {
RepositoryLogoCacheService.RepositoryLogoCacheValue cacheValue = this.repositoryLogoCacheService.lookup(this.repositoryLogoCacheService.buildKey(repositoryId));
byte[] logo = null;
if (cacheValue != null) {
logo = cacheValue.getLogo();
} else {
try {
java.io.File logoFile = ResourceUtils.getFile(zenodoConfig.getLogo());
if (!logoFile.exists()) return null;
try(InputStream inputStream = new FileInputStream(logoFile)){
logo = inputStream.readAllBytes();
};
} catch (IOException e) {
throw new RuntimeException(e);
}
this.repositoryLogoCacheService.put(new RepositoryLogoCacheService.RepositoryLogoCacheValue(repositoryId, logo));
}
return (logo != null && logo.length != 0) ? Base64.getEncoder().encodeToString(logo) : null;
}
return null;
}
private String getUnpublishedDOI(String zenodoUrl, String doi, String token, Short version) {
try {
WebClient client = WebClient.builder().build();
Map<String, LinkedHashMap<String, String>> createResponse = null;
LinkedHashMap<String, String> links;
LinkedHashMap<String, String> metadata;
String listUrl = zenodoUrl + "deposit/depositions" + "?q=conceptdoi:\"" + doi + "\"&access_token=" + token;
ResponseEntity<List<Map>> listResponses = client.get().uri(listUrl).retrieve().toEntityList(Map.class).block();
if (listResponses == null || listResponses.getBody() == null || listResponses.getBody().isEmpty()) return null;
createResponse = (Map<String, LinkedHashMap<String, String>>) 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;
}
}
}

View File

@ -1,275 +0,0 @@
package eu.eudat.depositinterface.zenodorepository.service.zenodo;
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.ZenodoBuilderMapper;
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.*;
import java.util.stream.Collectors;
@Component
public class ZenodoDepositService implements RepositoryDeposit {
private static final Logger logger = LoggerFactory.getLogger(ZenodoDepositService.class);
private static final ObjectMapper objectMapper = new ObjectMapper();
private final ZenodoProperties zenodoProperties;
private final ZenodoBuilderMapper mapper;
@Autowired
public ZenodoDepositService(ZenodoProperties zenodoProperties, ZenodoBuilderMapper mapper){
this.zenodoProperties = zenodoProperties;
this.mapper = mapper;
}
@Override
public String deposit(String repositoryId, DmpDepositModel dmpDepositModel, String zenodoToken) throws Exception {
RepositoryDepositConfiguration repositoryDepositConfiguration = this.getConfiguration().stream().filter(x -> x.getRepositoryId().equals(repositoryId)).findFirst().orElse(null);
if(repositoryDepositConfiguration != null) {
if (zenodoToken == null || zenodoToken.isEmpty()) {
zenodoToken = repositoryDepositConfiguration.getAccessToken();
}
String zenodoUrl = repositoryDepositConfiguration.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.model.ZenodoDeposit deposit = mapper.build(dmpDepositModel, zenodoConfig);
Map createResponse;
LinkedHashMap<String, String> 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<Map<String, Object>>() {})).block();
links = (LinkedHashMap<String, String>) createResponse.get("links");
} else {
unpublishedUrl = this.getUnpublishedDOI(zenodoUrl, previousDOI, zenodoToken, dmpDepositModel.getVersion());
if (unpublishedUrl == null) {
//It requires more than one step to create a new version
//First, get the deposit related to the concept DOI
String listUrl = zenodoUrl + "deposit/depositions" + "?q=conceptdoi:\"" + previousDOI + "\"&access_token=" + zenodoToken;
logger.debug("listUrl = " + listUrl);
ResponseEntity<List<Map>> 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<String, String>) 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<String, String>) 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<String, String>) createResponse.get("links");
//At this point it might fail to perform the next requests so enclose them with try catch
try {
//Forth, update the new deposit's metadata
String updateUrl = links.get("self") + "?access_token=" + zenodoToken;
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<List<Map>> 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<List<Map>> listResponses = zenodoClient.get().uri(listUrl).retrieve().toEntityList(Map.class).block();//restTemplate.getForEntity(listUrl, Map[].class);
createResponse = listResponses.getBody().get(0);
links = (LinkedHashMap<String, String>) 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(pdfEnvelope.getFile())))
.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(rdaJsonEnvelope.getFile()))).retrieve().toEntity(Map.class).block();
if (dmpDepositModel.getSupportingFilesZip() != null) {
String supportinFilesZipName = dmpDepositModel.getSupportingFilesZip().getFilename();
addFileUrl = links.get("bucket") + "/" + supportinFilesZipName + "?access_token=" + zenodoToken;
zenodoClient.put().uri(addFileUrl).body(BodyInserters.fromResource(new ByteArrayResource(supportinFilesZipName.getBytes()))).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<String, String> parsedException = objectMapper.readValue(ex.getResponseBodyAsString(), HashMap.class);
throw new IOException(parsedException.get("message"), ex);
}
}
return null;
}
private String publish(String publishUrl){
WebClient webClient = WebClient.builder().build();
Map<String, Object> 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<Map<String, Object>>() {
});
}).block();
return (String) publishResponce.get("conceptdoi");
}
@Override
public List<RepositoryDepositConfiguration> getConfiguration() {
List<ZenodoProperties.ZenodoConfig> 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 repositoryDepositConfiguration = this.getConfiguration().stream().filter(x -> x.getRepositoryId().equals(repositoryId)).findFirst().orElse(null);
if(repositoryDepositConfiguration != null) {
WebClient client = WebClient.builder().defaultHeaders(httpHeaders -> {
httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
}).build();
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("client_id", repositoryDepositConfiguration.getRepositoryClientId());
map.add("client_secret", repositoryDepositConfiguration.getRepositoryClientSecret());
map.add("grant_type", "authorization_code");
map.add("code", code);
map.add("redirect_uri", repositoryDepositConfiguration.getRedirectUri());
try {
Map<String, Object> values = client.post().uri(repositoryDepositConfiguration.getRepositoryAccessTokenUrl()).bodyValue(map).retrieve().bodyToMono(Map.class).block();
return values != null ? (String) values.get("access_token") : null;
} 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 {
java.io.File logoFile = ResourceUtils.getFile(conf.getLogo());
if (!logoFile.exists()) return null;
try(InputStream inputStream = new FileInputStream(logoFile)){
logo = inputStream.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, Short version) {
try {
WebClient client = WebClient.builder().build();
Map createResponse = null;
LinkedHashMap<String, String> links;
LinkedHashMap<String, String> metadata;
String listUrl = zenodoUrl + "deposit/depositions" + "?q=conceptdoi:\"" + doi + "\"&access_token=" + token;
ResponseEntity<List<Map>> listResponses = client.get().uri(listUrl).retrieve().toEntityList(Map.class).block();
createResponse = listResponses.getBody().get(0);
metadata = (LinkedHashMap<String, String>) createResponse.get("metadata");
links = (LinkedHashMap<String, String>) createResponse.get("links");
if (metadata.get("version").equals(version.toString())) {
return links.get("publish");
} else {
return null;
}
}catch (Exception e) {
logger.warn(e.getMessage(), e);
return null;
}
}
}

View File

@ -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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy; 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.SecurityFilterChain;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
@ -21,13 +24,11 @@ import java.util.Set;
@EnableWebSecurity @EnableWebSecurity
public class SecurityConfiguration { public class SecurityConfiguration {
private final ApiKeyFilter apiKeyFilter;
private final WebSecurityProperties webSecurityProperties; private final WebSecurityProperties webSecurityProperties;
private final AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver; private final AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
@Autowired @Autowired
public SecurityConfiguration(ApiKeyFilter apiKeyFilter, WebSecurityProperties webSecurityProperties, AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver) { public SecurityConfiguration(WebSecurityProperties webSecurityProperties, AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver) {
this.apiKeyFilter = apiKeyFilter;
this.webSecurityProperties = webSecurityProperties; this.webSecurityProperties = webSecurityProperties;
this.authenticationManagerResolver = authenticationManagerResolver; this.authenticationManagerResolver = authenticationManagerResolver;
} }
@ -36,7 +37,6 @@ public class SecurityConfiguration {
protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception { protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable) http.csrf(AbstractHttpConfigurer::disable)
.cors(Customizer.withDefaults()) .cors(Customizer.withDefaults())
.addFilterBefore(apiKeyFilter, AbstractPreAuthenticatedProcessingFilter.class)
.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry .authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry
.requestMatchers(buildAntPatterns(webSecurityProperties.getAuthorizedEndpoints())).authenticated() .requestMatchers(buildAntPatterns(webSecurityProperties.getAuthorizedEndpoints())).authenticated()
.requestMatchers(buildAntPatterns(webSecurityProperties.getAllowedEndpoints())).anonymous()) .requestMatchers(buildAntPatterns(webSecurityProperties.getAllowedEndpoints())).anonymous())

View File

@ -2,7 +2,7 @@ cache:
manager: manager:
fallbackToNoOpCache: true fallbackToNoOpCache: true
caffeineCaches: caffeineCaches:
- names: [ "apikey" ] - names: [ "logoByRepository" ]
allowNullValues: true allowNullValues: true
initialCapacity: 100 initialCapacity: 100
maximumSize: 500 maximumSize: 500
@ -11,6 +11,8 @@ cache:
expireAfterAccessMinutes: 10 expireAfterAccessMinutes: 10
refreshAfterWriteMinutes: 10 refreshAfterWriteMinutes: 10
mapCaches: mapCaches:
apiKey: logoByRepository:
name: apikey name: logoByRepository
keyPattern: resolve_$keyhash$:v0 keyPattern: zenodoplugin_$repo$:v0

View File

@ -0,0 +1,7 @@
web:
security:
idp:
resource:
jwt:
audiences: [ "dmp_zenodo_bridge" ]
validIssuer: ${IDP_ISSUER_URI:}

View File

@ -5,16 +5,9 @@ web:
allowed-endpoints: [ health ] allowed-endpoints: [ health ]
idp: idp:
api-key: api-key:
enabled: true enabled: false
authorization-header: Authorization
client-id: ${IDP_APIKEY_CLIENT_ID:}
client-secret: ${IDP_APIKEY_CLIENT_SECRET:}
scope: ${IDP_APIKEY_SCOPE:}
resource: resource:
token-type: JWT #| opaque token-type: JWT #| opaque
opaque:
client-id: ${IDP_OPAQUE_CLIENT_ID:}
client-secret: ${IDP_OPAQUE_CLIENT_SECRET:}
jwt: jwt:
claims: [ role, x-role ] claims: [ role, x-role ]
issuer-uri: ${IDP_ISSUER_URI:} issuer-uri: ${IDP_ISSUER_URI:}