Compare commits

...

73 Commits

Author SHA1 Message Date
Efstratios Giannopoulos 4c34f1f606 bug fix 2024-06-03 14:31:44 +03:00
Efstratios Giannopoulos b525ea3437 add ignore 2024-05-30 13:32:14 +03:00
Efstratios Giannopoulos 8b5b270300 no message 2024-05-30 13:30:56 +03:00
amentis f2f5ecb474 no message 2024-05-29 12:55:53 +03:00
Efstratios Giannopoulos 5278354b12 no message 2024-05-24 14:04:00 +03:00
Efstratios Giannopoulos cc7a97eee6 clean up names 2024-05-24 14:00:46 +03:00
Efstratios Giannopoulos d6522a4a59 no message 2024-05-23 16:12:23 +03:00
Efstratios Giannopoulos 4d01fd1b9a add semantic publication date logic 2024-05-23 11:05:00 +03:00
Efstratios Giannopoulos 7889dedd47 update commons models 2024-05-22 17:38:19 +03:00
Efstratios Giannopoulos c8b3824073 fix zenodo new version 2024-05-22 16:42:28 +03:00
Efstratios Giannopoulos f716b0b0b1 get token fix 2024-05-21 12:21:42 +03:00
Efstratios Giannopoulos 1a75344727 bug fix 2024-05-17 10:09:11 +03:00
Efstratios Giannopoulos ae573e9de2 add maxInMemorySizeInBytes 2024-05-15 16:37:49 +03:00
Efstratios Giannopoulos 643fa896b0 no message 2024-05-10 14:20:41 +03:00
Efstratios Giannopoulos 0f8aadf6f4 more logging 2024-05-10 14:00:40 +03:00
Efstratios Giannopoulos f080312a59 logging changes 2024-05-10 13:14:39 +03:00
Efstratios Giannopoulos 37eaa4cdf5 fixes 2024-05-10 12:13:29 +03:00
Efstratios Giannopoulos 487833c099 no message 2024-05-10 12:03:12 +03:00
Efstratios Giannopoulos 29d2655b52 add logger 2024-05-10 11:58:31 +03:00
Efstratios Giannopoulos a673e52ff5 update commons 2024-05-03 13:31:39 +03:00
Efstratios Giannopoulos 13d2a5317a update models 2024-05-03 10:11:04 +03:00
Efstratios Giannopoulos 2a9544bad5 rename to OpenCDMP 2024-04-26 14:51:07 +03:00
Efstratios Giannopoulos 6cd68483a9 update common models 2024-04-25 12:06:48 +03:00
Alexandros Mandilaras dfd141628f Revert "no message"
This reverts commit d349a8b1a7.
2024-04-23 16:39:28 +03:00
Alexandros Mandilaras d349a8b1a7 no message 2024-04-23 16:33:42 +03:00
Alexandros Mandilaras 9cb33dff4e no message 2024-04-23 16:27:18 +03:00
Alexandros Mandilaras e9177865c4 add sonar dockerfile and update buiild profile 2024-04-23 16:19:11 +03:00
Efstratios Giannopoulos 77be0dc2a9 no message 2024-04-19 14:05:22 +03:00
Efstratios Giannopoulos 7a5ec1134f Revert "no message"
This reverts commit d2e4726655.
2024-04-19 13:59:03 +03:00
Alexandros Mandilaras bb722b40dd dockerfile update 2024-04-19 13:41:53 +03:00
Efstratios Giannopoulos d2e4726655 no message 2024-04-19 13:25:52 +03:00
Alexandros Mandilaras 8fb376e44e Revert "no message"
This reverts commit 50f68867a5.
2024-04-19 13:20:17 +03:00
Efstratios Giannopoulos 7bac0eacc9 no message 2024-04-19 13:11:25 +03:00
Alexandros Mandilaras 50f68867a5 no message 2024-04-19 13:00:13 +03:00
Efstratios Giannopoulos 576211fca5 no message 2024-04-19 12:57:33 +03:00
Efstratios Giannopoulos d6e2d883f9 no message 2024-04-19 12:49:22 +03:00
Efstratios Giannopoulos 97764b8ea7 no message 2024-04-19 12:46:20 +03:00
Efstratios Giannopoulos bfc8cb1c51 update pom 2024-04-19 11:35:05 +03:00
Efstratios Giannopoulos bf6747c8d7 update pom 2024-04-10 13:32:11 +03:00
Efstratios Giannopoulos 64c0ddf68c bug fixes 2024-03-28 10:51:21 +02:00
Efstratios Giannopoulos 77c813f39e config update 2024-03-27 10:22:50 +02:00
Efstratios Giannopoulos 62020e135e update base package 2024-03-27 10:22:14 +02:00
Efstratios Giannopoulos b1485e3c1c pom changes 2024-03-22 13:23:45 +02:00
Efstratios Giannopoulos 7ae9f50cc9 no message 2024-03-22 13:01:53 +02:00
Efstratios Giannopoulos 270728f753 set java 21 2024-03-22 13:01:21 +02:00
Efstratios Giannopoulos f8e065ece5 update pom 2024-03-22 12:49:11 +02:00
Efstratios Giannopoulos 889833faa8 update configurations 2024-03-12 10:39:32 +02:00
Efstratios Giannopoulos 64181d941e model changes 2024-03-11 13:10:39 +02:00
Efstratios Giannopoulos 9f02c2ad92 update models 2024-03-01 17:55:17 +02:00
Efstratios Giannopoulos 060a4e41f6 add idp claims 2024-01-22 14:31:00 +02:00
Alexandros Mandilaras e701f640d4 config changes 2024-01-22 13:38:21 +02:00
Alexandros Mandilaras ff0eca4378 remove revision name on .jar file 2024-01-22 11:39:21 +02:00
Alexandros Mandilaras bfcba7520d add REVISION on the 2nd build stage too 2024-01-19 11:08:24 +02:00
Alexandros Mandilaras 44d911b7e4 add revision as an argument 2024-01-19 11:04:00 +02:00
Alexandros Mandilaras 691c750dad revert to old build method 2024-01-19 10:54:40 +02:00
Alexandros Mandilaras 7f22c4e697 more dockerfile updates 2024-01-19 10:49:24 +02:00
Alexandros Mandilaras 1dc9d2e946 dockerfile updates 2024-01-19 10:13:03 +02:00
Alexandros Mandilaras 9e25c82763 add revision to the jar file name 2024-01-18 17:20:51 +02:00
Alexandros Mandilaras e2bf6eaea6 add profile env 2024-01-18 17:11:16 +02:00
Alexandros Mandilaras 8130d22716 remove gpg 2024-01-18 17:03:28 +02:00
Alexandros Mandilaras ce3325a637 build changes 2024-01-18 16:53:46 +02:00
Alexandros Mandilaras a19e1110cf remove repo remote 2024-01-18 15:24:04 +02:00
Alexandros Mandilaras 71f4e9d6da add repo remote url 2024-01-18 15:11:19 +02:00
Alexandros Mandilaras 12132060fe add cite maven repo 2024-01-18 14:58:49 +02:00
Efstratios Giannopoulos 430c359043 Merge branch 'dmp-refactoring' into micro-service 2024-01-17 15:34:35 +02:00
Efstratios Giannopoulos 15692d7afa sync with new models 2023-12-13 11:39:56 +02:00
Efstratios Giannopoulos e636baf4b8 Fix new version url 2023-12-11 15:15:03 +02:00
Efstratios Giannopoulos c048d7047f sync with new models 2023-12-11 15:08:00 +02:00
Efstratios Giannopoulos c5570d15b7 sync with new models 2023-12-07 18:13:13 +02:00
George Kalampokis 36892093bd Make Security to work 2023-11-01 14:12:46 +02:00
George Kalampokis 1fe6200d94 First attempt to add Security 2023-10-26 18:00:09 +03:00
George Kalampokis 369b24c673 Replace old configuration loader with standard Spring boot configuration yml 2023-10-24 18:15:15 +03:00
George Kalampokis a8f4d8a297 Transform it to a micro-service 2023-10-18 18:18:32 +03:00
62 changed files with 2317 additions and 1255 deletions

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
.idea/
target/
logs/
web/src/main/resources/config/app.env

View File

@ -1,18 +1,33 @@
####################################### Build stage #######################################
FROM maven:3.6.3-openjdk-11-slim AS build-stage
FROM maven:3.9-eclipse-temurin-21-alpine AS build-stage
ARG MAVEN_ACCOUNT_USR
ARG MAVEN_ACCOUNT_PSW
ARG REVISION
ARG PROFILE
ENV server_username=$MAVEN_ACCOUNT_USR
ENV server_password=$MAVEN_ACCOUNT_PSW
ARG CITE_MAVEN_REPO_URL
COPY pom.xml /build/
COPY core /build/core/
COPY web /build/web/
COPY settings.xml /root/.m2/settings.xml
RUN rm -f /build/web/src/main/resources/config/app.env
RUN rm -f /build/web/src/main/resources/config/*-dev.yml
# RUN rm -f /build/web/src/main/resources/logging/*.xml
COPY . /build/
WORKDIR /build/
RUN mvn clean package -DskipTests
RUN mvn -Drevision=${REVISION} -DciteMavenRepoUrl=${CITE_MAVEN_REPO_URL} -P${PROFILE} dependency:go-offline
RUN mvn -Drevision=${REVISION} -DciteMavenRepoUrl=${CITE_MAVEN_REPO_URL} -P${PROFILE} clean package
######################################## Run Stage ########################################
FROM eclipse-temurin:21-jre-alpine
ARG CREPO_BINARIES_REPO_URL
ARG CREPO_BINARIES_CREDENTIAL
ARG BUILD_VERSION
ENV CREPO_BINARIES_REPO_URL=$CREPO_BINARIES_REPO_URL
ENV CREPO_BINARIES_CREDENTIAL=$CREPO_BINARIES_CREDENTIAL
ENV BUILD_VERSION=$BUILD_VERSION
ARG PROFILE
ARG REVISION
ENV SERVER_PORT=8080
EXPOSE ${SERVER_PORT}
RUN curl --location --request PUT "${CREPO_BINARIES_REPO_URL}opendmp/repository-jars/zenodo/zenodo-deposit-${BUILD_VERSION}.jar" \
--header "Authorization: Basic ${CREPO_BINARIES_CREDENTIAL}" \
--header "Content-Type: application/json" \
--data-binary "@/build/target/repositorydepositzenodo-1.0.0-SNAPSHOT.jar"
COPY --from=build-stage /build/web/target/repository-deposit-web-${REVISION}.jar /app/repository-deposit-web.jar
ENTRYPOINT ["java","-Dspring.config.additional-location=file:/config/","-Dspring.profiles.active=${PROFILE}","-Djava.security.egd=file:/dev/./urandom","-jar","/app/repository-deposit-web.jar"]

27
Dockerfile.Sonar Normal file
View File

@ -0,0 +1,27 @@
####################################### Build stage #######################################
FROM maven:3.9-eclipse-temurin-21-alpine
ARG MAVEN_ACCOUNT_USR
ARG MAVEN_ACCOUNT_PSW
ARG REVISION
ARG PROFILE
ARG ORACLE_URL
ARG ORACLE_TOKEN
ENV server_username=$MAVEN_ACCOUNT_USR
ENV server_password=$MAVEN_ACCOUNT_PSW
ARG CITE_MAVEN_REPO_URL
COPY pom.xml /build/
COPY core /build/core/
COPY web /build/web/
COPY settings.xml /root/.m2/settings.xml
RUN rm -f /build/web/src/main/resources/config/*-dev.yml
RUN rm -f /build/web/src/main/resources/logging/*.xml
COPY oracle.local.cite.gr.crt $JAVA_HOME/conf/security
RUN cd "$JAVA_HOME"/conf/security && keytool -cacerts -storepass changeit -noprompt -trustcacerts -importcert -alias oraclecert -file oracle.local.cite.gr.crt
WORKDIR /build/
RUN mvn -Drevision=${REVISION} -DciteMavenRepoUrl=${CITE_MAVEN_REPO_URL} -P${PROFILE} dependency:go-offline
RUN mvn -Drevision=${REVISION} -DciteMavenRepoUrl=${CITE_MAVEN_REPO_URL} -P${PROFILE} clean package
RUN mvn sonar:sonar -Drevision=${REVISION} -DciteMavenRepoUrl=${CITE_MAVEN_REPO_URL} -P${PROFILE} -Dsonar.projectKey=OpenDMP:repository-deposit-zenodo -Dsonar.login=${ORACLE_TOKEN} -Dsonar.host.url=${ORACLE_URL} -Dsonar.projectName='OpenDMP repository-deposit-zenodo'

139
core/pom.xml Normal file
View File

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.opencdmp</groupId>
<artifactId>repository-deposit-parent</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>repositorydepositzenodo</artifactId>
<version>${revision}</version>
<packaging>jar</packaging>
<name>OpenCDMP Repository Deposit Zenodo</name>
<description>OpenCDMP Repository Deposit Zenodo</description>
<url>https://code-repo.d4science.org/MaDgiK-CITE/repository-deposit-zenodo</url>
<licenses>
<license>
<name>MIT License</name>
<url>https://code-repo.d4science.org/MaDgiK-CITE/repository-deposit-zenodo/src/branch/master/LICENSE.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<name>CITE S.A.</name>
<email>maven-central@cite.gr</email>
<organization>CITE S.A.</organization>
<organizationUrl>https://www.cite.gr</organizationUrl>
</developer>
</developers>
<scm>
<connection>scm:git:git://code-repo.d4science.org</connection>
<developerConnection>scm:git:ssh://code-repo.d4science.org</developerConnection>
<url>https://code-repo.d4science.org/MaDgiK-CITE/repository-deposit-zenodo</url>
</scm>
<properties>
<revision>1.0.0-SNAPSHOT</revision>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.release>21</maven.compiler.release>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20240303</version>
</dependency>
<dependency>
<groupId>gr.cite</groupId>
<artifactId>cache</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>gr.cite</groupId>
<artifactId>logging</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>gr.cite</groupId>
<artifactId>exceptions</artifactId>
<version>2.2.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<archive>
<manifest>
<mainClass>org.opencdmp.deposit.DepositApplication</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-shade-plugin</artifactId>-->
<!-- <version>3.4.1</version>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <phase>package</phase>-->
<!-- <goals>-->
<!-- <goal>shade</goal>-->
<!-- </goals>-->
<!-- <configuration>-->
<!-- <filters>-->
<!-- <filter>-->
<!-- <artifact>*:*</artifact>-->
<!-- <excludes>-->
<!-- <exclude>module-info.class</exclude>-->
<!-- </excludes>-->
<!-- </filter>-->
<!-- </filters>-->
<!-- <relocations>-->
<!-- <relocation>-->
<!-- <pattern>org.json</pattern>-->
<!-- <shadedPattern>zenodorepository.shaded.org.json</shadedPattern>-->
<!-- </relocation>-->
<!-- </relocations>-->
<!-- </configuration>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- </plugin>-->
</plugins>
</build>
</project>

View File

@ -0,0 +1,14 @@
package org.opencdmp.deposit.zenodorepository.audit;
import gr.cite.tools.logging.EventId;
public class AuditableAction {
public static final EventId Deposit_Deposit = new EventId(1000, "Deposit_Deposit");
public static final EventId Deposit_Authenticate = new EventId(1001, "Deposit_Authenticate");
public static final EventId Deposit_GetConfiguration = new EventId(1002, "Deposit_GetConfiguration");
public static final EventId Deposit_GetLogo = new EventId(1003, "Deposit_GetLogo");
}

View File

@ -0,0 +1,12 @@
package org.opencdmp.deposit.zenodorepository.configuration;
import org.opencdmp.deposit.zenodorepository.configuration.funder.FunderProperties;
import org.opencdmp.deposit.zenodorepository.configuration.identifier.IdentifierProperties;
import org.opencdmp.deposit.zenodorepository.configuration.pid.PidProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties({PidProperties.class, FunderProperties.class, IdentifierProperties.class})
public class GenericConfiguration {
}

View File

@ -0,0 +1,40 @@
package org.opencdmp.deposit.zenodorepository.configuration.funder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
@ConfigurationProperties(prefix = "funder")
public class FunderProperties {
private List<DoiFunder> available;
public List<DoiFunder> getAvailable() {
return available;
}
public void setAvailable(List<DoiFunder> available) {
this.available = available;
}
public static class DoiFunder {
private String funder;
private String doi;
public String getFunder() {
return funder;
}
public String getDoi() {
return doi;
}
public void setFunder(String funder) {
this.funder = funder;
}
public void setDoi(String doi) {
this.doi = doi;
}
}
}

View File

@ -0,0 +1,18 @@
package org.opencdmp.deposit.zenodorepository.configuration.identifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
@ConfigurationProperties(prefix = "identifiers")
public class IdentifierProperties {
private List<String> related;
public List<String> getRelated() {
return related;
}
public void setRelated(List<String> related) {
this.related = related;
}
}

View File

@ -0,0 +1,49 @@
package org.opencdmp.deposit.zenodorepository.configuration.pid;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
@ConfigurationProperties(prefix = "pid")
public class PidProperties {
private List<String> acceptedTypes;
private PidFieldNames fields;
public List<String> getAcceptedTypes() {
return acceptedTypes;
}
public PidFieldNames getFields() {
return fields;
}
public void setAcceptedTypes(List<String> acceptedTypes) {
this.acceptedTypes = acceptedTypes;
}
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;
}
}
}

View File

@ -0,0 +1,35 @@
package org.opencdmp.deposit.zenodorepository.enums;
import com.fasterxml.jackson.annotation.JsonValue;
import org.opencdmp.commonmodels.enums.EnumUtils;
import org.opencdmp.commonmodels.enums.EnumValueProvider;
import java.util.Map;
public enum ZenodoAccessRight implements EnumValueProvider<String> {
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<String, ZenodoAccessRight> map = EnumUtils.getEnumValueMap(ZenodoAccessRight.class);
public static ZenodoAccessRight of(String i) {
return map.get(i);
}
}

View File

@ -1,11 +1,11 @@
package eu.eudat.depositinterface.zenodorepository.models;
package org.opencdmp.deposit.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;

View File

@ -1,4 +1,4 @@
package eu.eudat.depositinterface.zenodorepository.models;
package org.opencdmp.deposit.zenodorepository.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;

View File

@ -1,4 +1,4 @@
package eu.eudat.depositinterface.zenodorepository.models;
package org.opencdmp.deposit.zenodorepository.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;

View File

@ -1,4 +1,4 @@
package eu.eudat.depositinterface.zenodorepository.models;
package org.opencdmp.deposit.zenodorepository.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;

View File

@ -1,8 +1,9 @@
package eu.eudat.depositinterface.zenodorepository.models;
package org.opencdmp.deposit.zenodorepository.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.opencdmp.deposit.zenodorepository.enums.ZenodoAccessRight;
import java.util.List;
@ -31,7 +32,7 @@ public class ZenodoDepositMetadata {
private List<String> references;
private List<ZenodoComunity> communities;
private List<ZenodoCommunity> communities;
@JsonProperty("access_right")
private ZenodoAccessRight accessRight;
@ -125,11 +126,11 @@ public class ZenodoDepositMetadata {
this.version = version;
}
public List<ZenodoComunity> getCommunities() {
public List<ZenodoCommunity> getCommunities() {
return communities;
}
public void setCommunities(List<ZenodoComunity> communities) {
public void setCommunities(List<ZenodoCommunity> communities) {
this.communities = communities;
}

View File

@ -1,4 +1,4 @@
package eu.eudat.depositinterface.zenodorepository.models;
package org.opencdmp.deposit.zenodorepository.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;

View File

@ -1,4 +1,4 @@
package eu.eudat.depositinterface.zenodorepository.models;
package org.opencdmp.deposit.zenodorepository.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;

View File

@ -0,0 +1,399 @@
package org.opencdmp.deposit.zenodorepository.model.builder;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.logging.LoggerService;
import org.opencdmp.commonmodels.enums.DmpAccessType;
import org.opencdmp.commonmodels.enums.DmpUserRole;
import org.opencdmp.commonmodels.models.DmpUserModel;
import org.opencdmp.commonmodels.models.description.*;
import org.opencdmp.commonmodels.models.descriptiotemplate.DefinitionModel;
import org.opencdmp.commonmodels.models.descriptiotemplate.fielddata.RadioBoxDataModel;
import org.opencdmp.commonmodels.models.descriptiotemplate.fielddata.SelectDataModel;
import org.opencdmp.commonmodels.models.dmp.DmpBlueprintValueModel;
import org.opencdmp.commonmodels.models.dmp.DmpModel;
import org.opencdmp.commonmodels.models.dmpblueprint.SectionModel;
import org.opencdmp.commonmodels.models.dmpreference.DmpReferenceModel;
import org.opencdmp.commonmodels.models.reference.ReferenceFieldModel;
import org.opencdmp.commonmodels.models.reference.ReferenceModel;
import org.opencdmp.deposit.zenodorepository.configuration.funder.FunderProperties;
import org.opencdmp.deposit.zenodorepository.configuration.identifier.IdentifierProperties;
import org.opencdmp.deposit.zenodorepository.configuration.pid.PidProperties;
import org.opencdmp.deposit.zenodorepository.enums.ZenodoAccessRight;
import org.opencdmp.deposit.zenodorepository.model.*;
import org.opencdmp.deposit.zenodorepository.service.zenodo.ZenodoDepositServiceImpl;
import org.opencdmp.deposit.zenodorepository.service.zenodo.ZenodoServiceProperties;
import org.slf4j.LoggerFactory;
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.time.LocalDate;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ZenodoBuilder {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(ZenodoDepositServiceImpl.class));
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 SEMANTIC_PUBLICATION_DATE = "zenodo.publication_date";
private final PidProperties pidProperties;
private final IdentifierProperties identifierProperties;
private final FunderProperties funderProperties;
private final ZenodoServiceProperties zenodoServiceProperties;
@Autowired
public ZenodoBuilder(PidProperties pidProperties, IdentifierProperties identifierProperties, FunderProperties funderProperties, ZenodoServiceProperties zenodoServiceProperties){
this.pidProperties = pidProperties;
this.identifierProperties = identifierProperties;
this.funderProperties = funderProperties;
this.zenodoServiceProperties = zenodoServiceProperties;
}
public ZenodoDeposit build(DmpModel dmp) {
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() : "<p></p>"));
deposit.getMetadata().setVersion(String.valueOf(dmp.getVersion()));
String zenodoCommunity = zenodoServiceProperties.getCommunity();
if(zenodoCommunity != null && !zenodoCommunity.isEmpty()) {
if (deposit.getMetadata().getCommunities() == null) deposit.getMetadata().setCommunities(new ArrayList<>());
ZenodoCommunity community = new ZenodoCommunity();
community.setIdentifier(zenodoCommunity);
deposit.getMetadata().getCommunities().add(community);
}
org.opencdmp.commonmodels.models.dmpblueprint.FieldModel fieldOfSemantic = this.getFieldOfSemantic(dmp, SEMANTIC_PUBLICATION_DATE);
if (fieldOfSemantic != null){
DmpBlueprintValueModel dmpBlueprintValueModel = this.getDmpBlueprintValue(dmp, fieldOfSemantic.getId());
if (dmpBlueprintValueModel != null && dmpBlueprintValueModel.getDateValue() != null) {
deposit.getMetadata().setPublicationDate(DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.systemDefault()).format(dmpBlueprintValueModel.getDateValue()));
} else if (dmpBlueprintValueModel != null && dmpBlueprintValueModel.getValue() != null && !dmpBlueprintValueModel.getValue().isBlank()){
try {
Instant instant = Instant.parse(dmpBlueprintValueModel.getValue());
deposit.getMetadata().setPublicationDate(DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.systemDefault()).format(instant));
}catch (Exception e){
logger.error(e.getMessage(), e);
}
}
}
this.applyAccessRight(dmp, deposit);
this.applyIsIdenticalTo(dmp, deposit);
this.applyLicenses(dmp, deposit);
this.applyResearchers(dmp, deposit);
this.applyGrants(dmp, deposit);
this.applyContributors(dmp, deposit);
this.applyCreators(dmp, deposit);
return deposit;
}
private DmpBlueprintValueModel getDmpBlueprintValue(DmpModel dmp, UUID id){
if (dmp == null || dmp.getProperties() == null || dmp.getProperties().getDmpBlueprintValues() == null) return null;
return dmp.getProperties().getDmpBlueprintValues().stream().filter(x-> x.getFieldId().equals(id)).findFirst().orElse(null);
}
private org.opencdmp.commonmodels.models.dmpblueprint.FieldModel getFieldOfSemantic(DmpModel dmp, String semanticKey){
if (dmp == null || dmp.getDmpBlueprint() == null || dmp.getDmpBlueprint().getDefinition() == null || dmp.getDmpBlueprint().getDefinition().getSections() == null) return null;
for (SectionModel sectionModel : dmp.getDmpBlueprint().getDefinition().getSections()){
if (sectionModel.getFields() != null){
org.opencdmp.commonmodels.models.dmpblueprint.FieldModel fieldModel = sectionModel.getFields().stream().filter(x-> x.getSemantics() != null && x.getSemantics().contains(semanticKey)).findFirst().orElse(null);
if (fieldModel != null) return fieldModel;
}
}
return null;
}
private List<org.opencdmp.commonmodels.models.descriptiotemplate.FieldModel> findSchematicValues(String relatedId, DefinitionModel definitionModel){
return definitionModel.getAllField().stream().filter(x-> x.getSemantics() != null && x.getSemantics().contains(relatedId)).toList();
}
private List<FieldModel> findValueFieldsByIds(String fieldId, PropertyDefinitionModel definitionModel){
List<FieldModel> models = new ArrayList<>();
if (definitionModel == null || definitionModel.getFieldSets() == null || definitionModel.getFieldSets().isEmpty()) return models;
for (PropertyDefinitionFieldSetModel propertyDefinitionFieldSetModel : definitionModel.getFieldSets().values()){
if (propertyDefinitionFieldSetModel == null ||propertyDefinitionFieldSetModel.getItems() == null || propertyDefinitionFieldSetModel.getItems().isEmpty()) continue;
for (PropertyDefinitionFieldSetItemModel propertyDefinitionFieldSetItemModel : propertyDefinitionFieldSetModel.getItems()){
if (propertyDefinitionFieldSetItemModel == null ||propertyDefinitionFieldSetItemModel.getFields() == null || propertyDefinitionFieldSetItemModel.getFields().isEmpty()) continue;
for (Map.Entry<String, FieldModel> entry : propertyDefinitionFieldSetItemModel.getFields().entrySet()){
if (entry == null || entry.getValue() == null) continue;
if (entry.getKey().equalsIgnoreCase(fieldId)) models.add(entry.getValue());
}
}
}
return models;
}
private Set<String> extractSchematicValues(List<org.opencdmp.commonmodels.models.descriptiotemplate.FieldModel> fields, PropertyDefinitionModel propertyDefinition, List<String> acceptedPidTypes) {
Set<String> values = new HashSet<>();
for (org.opencdmp.commonmodels.models.descriptiotemplate.FieldModel field : fields) {
if (field.getData() == null) continue;
List<FieldModel> valueFields = this.findValueFieldsByIds(field.getId(), propertyDefinition);
for (FieldModel valueField : valueFields) {
switch (field.getData().getFieldType()) {
case FREE_TEXT, TEXT_AREA, RICH_TEXT_AREA -> {
if (valueField.getTextValue() != null && !valueField.getTextValue().isBlank()) values.add(valueField.getTextValue());
}
case BOOLEAN_DECISION, CHECK_BOX -> {
if (valueField.getBooleanValue() != null) values.add(valueField.getBooleanValue().toString());
}
case DATE_PICKER -> {
if (valueField.getDateValue() != null) values.add(DateTimeFormatter.ISO_DATE.format(valueField.getDateValue()));
}
case DATASET_IDENTIFIER, VALIDATION -> {
if (valueField.getExternalIdentifier() != null && valueField.getExternalIdentifier().getIdentifier() != null && !valueField.getExternalIdentifier().getIdentifier().isBlank()) {
values.add(valueField.getExternalIdentifier().getIdentifier());
}
}
case TAGS -> {
if (valueField.getTextListValue() != null && !valueField.getTextListValue().isEmpty()) {
values.addAll(valueField.getTextListValue());
}
}
case SELECT -> {
if (valueField.getTextListValue() != null && !valueField.getTextListValue().isEmpty()) {
SelectDataModel selectDataModel = (SelectDataModel)field.getData();
if (selectDataModel != null && selectDataModel.getOptions() != null && !selectDataModel.getOptions().isEmpty()){
for (SelectDataModel.OptionModel option : selectDataModel.getOptions()){
if (valueField.getTextListValue().contains(option.getValue()) || valueField.getTextListValue().contains(option.getLabel())) values.add(option.getLabel());
}
}
}
}
case RADIO_BOX -> {
if (valueField.getTextListValue() != null && !valueField.getTextListValue().isEmpty()) {
RadioBoxDataModel radioBoxModel = (RadioBoxDataModel)field.getData();
if (radioBoxModel != null && radioBoxModel.getOptions() != null && !radioBoxModel.getOptions().isEmpty()){
for (RadioBoxDataModel.RadioBoxOptionModel option : radioBoxModel.getOptions()){
if (valueField.getTextListValue().contains(option.getValue()) || valueField.getTextListValue().contains(option.getLabel())) values.add(option.getLabel());
}
}
}
}
case REFERENCE_TYPES -> {
if (valueField.getReferences() != null && !valueField.getReferences().isEmpty()) {
for (ReferenceModel referenceModel : valueField.getReferences()) {
if (referenceModel == null
|| referenceModel.getType() == null || referenceModel.getType().getCode() == null || referenceModel.getType().getCode().isBlank()
|| referenceModel.getDefinition() == null || referenceModel.getDefinition().getFields() == null || referenceModel.getDefinition().getFields().isEmpty()) continue;
if (referenceModel.getType().getCode().equals(zenodoServiceProperties.getOrganizationReferenceCode()) || referenceModel.getType().getCode().equals(zenodoServiceProperties.getResearcherReferenceCode())) {
if (referenceModel.getReference() != null && !referenceModel.getReference().isBlank()) {
values.add(referenceModel.getReference());
}
} else {
String pid = referenceModel.getDefinition().getFields().stream().filter(x -> x.getCode() != null && x.getCode().equals(this.pidProperties.getFields().getPidName())).map(ReferenceFieldModel::getValue).findFirst().orElse(null);
String pidType = referenceModel.getDefinition().getFields().stream().filter(x -> x.getCode() != null && x.getCode().equals(this.pidProperties.getFields().getPidTypeName())).map(ReferenceFieldModel::getValue).findFirst().orElse(null);
if (pid != null && !pid.isBlank() && pidType != null && !pidType.isBlank() && acceptedPidTypes.contains(pidType)) {
values.add(pid);
}
}
}
}
}
case INTERNAL_ENTRIES_DESCRIPTIONS, INTERNAL_ENTRIES_DMPS, UPLOAD -> throw new MyApplicationException("Invalid type " + field.getData().getFieldType());
default -> throw new MyApplicationException("Invalid type " + field.getData().getFieldType());
}
}
}
return values;
}
private List<ReferenceModel> getReferenceModelOfType(DmpModel dmp, String code){
List<ReferenceModel> response = new ArrayList<>();
if (dmp.getReferences() == null) return response;
for (DmpReferenceModel dmpReferenceModel : dmp.getReferences()){
if (dmpReferenceModel.getReference() != null && dmpReferenceModel.getReference().getType() != null && dmpReferenceModel.getReference().getType().getCode() != null && dmpReferenceModel.getReference().getType().getCode().equals(code)){
response.add(dmpReferenceModel.getReference());
}
}
return response;
}
private void applyZenodoRelator(DmpModel dmp, ZenodoDeposit deposit) {
if (deposit.getMetadata() == null) deposit.setMetadata(new ZenodoDepositMetadata());
List<String> acceptedPidTypes = this.pidProperties.getAcceptedTypes();
List<ZenodoRelator> relatedIdentifiers = new ArrayList<>();
for(DescriptionModel descriptionModel: dmp.getDescriptions()){
for(String relatedId: this.identifierProperties.getRelated()){
List<org.opencdmp.commonmodels.models.descriptiotemplate.FieldModel> fields = this.findSchematicValues(relatedId, descriptionModel.getDescriptionTemplate().getDefinition());
Set<String> values = extractSchematicValues(fields, descriptionModel.getProperties(), 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(DmpModel 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(DmpModel dmp, ZenodoDeposit deposit){
if (deposit.getMetadata() == null) deposit.setMetadata(new ZenodoDepositMetadata());
if (dmp.getAccessType().equals(DmpAccessType.Public)) {
ZenodoRelator relator = new ZenodoRelator();
relator.setIdentifier(zenodoServiceProperties.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(DmpModel dmp, ZenodoDeposit deposit){
if (deposit.getMetadata() == null) deposit.setMetadata(new ZenodoDepositMetadata());
List<ReferenceModel> dmpLicenses = this.getReferenceModelOfType(dmp, zenodoServiceProperties.getLicensesReferenceCode());
if (!dmpLicenses.isEmpty()) {
for (ReferenceModel dmpLicense : dmpLicenses) {
if (dmpLicense != null && dmpLicense.getReference() != null && !dmpLicense.getReference().isBlank()) {
deposit.getMetadata().setLicense(dmpLicense.getReference());
break;
}
}
}
}
private void applyResearchers(DmpModel dmp, ZenodoDeposit deposit){
if (deposit.getMetadata() == null) deposit.setMetadata(new ZenodoDepositMetadata());
List<ZenodoContributor> researchers = new ArrayList<>();
List<ReferenceModel> dmpResearchers = this.getReferenceModelOfType(dmp, zenodoServiceProperties.getResearcherReferenceCode());
if (dmpResearchers != null && !dmpResearchers.isEmpty()) {
for (ReferenceModel researcher : dmpResearchers) {
ZenodoContributor contributor = new ZenodoContributor();
contributor.setName(researcher.getLabel());
contributor.setType(CONTRIBUTOR_TYPE_RESEARCHER);
contributor.setAffiliation(researcher.getSource());
if (researcher.getSource().equalsIgnoreCase(zenodoServiceProperties.getOrcidResearcherSourceCode())) {
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(DmpModel dmp, ZenodoDeposit deposit){
if (deposit.getMetadata() == null) deposit.setMetadata(new ZenodoDepositMetadata());
List<ReferenceModel> dmpGrants = this.getReferenceModelOfType(dmp, zenodoServiceProperties.getGrantReferenceCode());
List<ReferenceModel> dmpFunders = this.getReferenceModelOfType(dmp, zenodoServiceProperties.getFunderReferenceCode());
if (!dmpGrants.isEmpty()) {
ReferenceModel depositGrant = dmpGrants.stream().filter(x-> x.getSource().equalsIgnoreCase(zenodoServiceProperties.getOpenaireGrantSourceCode())).findFirst().orElse(null);
if (depositGrant != null) {
String grantReferenceTail = depositGrant.getReference().split(":")[2];
List<FunderProperties.DoiFunder> doiFunders = this.funderProperties.getAvailable();
if (!dmpFunders.isEmpty()) {
ReferenceModel depositFunder = dmpFunders.getFirst();
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(DmpModel dmp, ZenodoDeposit deposit){
if (dmp.getUsers() == null) return;
if (deposit.getMetadata() == null) deposit.setMetadata(new ZenodoDepositMetadata());
List<ReferenceModel> dmpOrganizations = this.getReferenceModelOfType(dmp, zenodoServiceProperties.getOrganizationReferenceCode());
String zenodoAffiliation = zenodoServiceProperties.getAffiliation();
List<ZenodoContributor> contributors = new ArrayList<>();
for (DmpUserModel 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(ReferenceModel::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(DmpModel dmp, ZenodoDeposit deposit){
if (dmp.getUsers() == null) return;
if (deposit.getMetadata() == null) deposit.setMetadata(new ZenodoDepositMetadata());
List<ReferenceModel> dmpOrganizations = this.getReferenceModelOfType(dmp, zenodoServiceProperties.getOrganizationReferenceCode());
String zenodoAffiliation = zenodoServiceProperties.getAffiliation();
ZenodoCreator creator = new ZenodoCreator();
DmpUserModel dmpModel = dmp.getUsers().stream().filter(userDMP -> userDMP.getRole().equals(DmpUserRole.Owner)).findFirst().orElse(null);
if (dmpModel == null || dmpModel.getUser() == null) return;
creator.setName(dmpModel.getUser().getName());
if (dmpOrganizations != null && !dmpOrganizations.isEmpty()) {
creator.setAffiliation(dmpOrganizations.stream().map(ReferenceModel::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);
}
}

View File

@ -0,0 +1,7 @@
package org.opencdmp.deposit.zenodorepository.service.storage;
public interface FileStorageService {
String storeFile(byte[] data);
byte[] readFile(String fileRef);
}

View File

@ -0,0 +1,9 @@
package org.opencdmp.deposit.zenodorepository.service.storage;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties({FileStorageServiceProperties.class})
public class FileStorageServiceConfiguration {
}

View File

@ -0,0 +1,51 @@
package org.opencdmp.deposit.zenodorepository.service.storage;
import gr.cite.tools.logging.LoggerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Locale;
import java.util.UUID;
@Service
public class FileStorageServiceImpl implements FileStorageService {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(FileStorageServiceImpl.class));
private final FileStorageServiceProperties properties;
@Autowired
public FileStorageServiceImpl(FileStorageServiceProperties properties) {
this.properties = properties;
}
@Override
public String storeFile(byte[] data) {
try {
String fileName = UUID.randomUUID().toString().replace("-", "").toLowerCase(Locale.ROOT);
Path storagePath = Paths.get(properties.getTransientPath() + "/" + fileName);
Files.write(storagePath, data, StandardOpenOption.CREATE_NEW);
return fileName;
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
return null;
}
@Override
public byte[] readFile(String fileRef) {
try (FileInputStream inputStream = new FileInputStream(properties.getTransientPath() + "/" + fileRef)) {
return inputStream.readAllBytes();
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
return new byte[0];
}
}

View File

@ -0,0 +1,24 @@
package org.opencdmp.deposit.zenodorepository.service.storage;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.ConstructorBinding;
@ConfigurationProperties(prefix = "file.storage")
public class FileStorageServiceProperties {
private final String temp;
private final String transientPath;
@ConstructorBinding
public FileStorageServiceProperties(String temp, String transientPath) {
this.temp = temp;
this.transientPath = transientPath;
}
public String getTemp() {
return temp;
}
public String getTransientPath() {
return transientPath;
}
}

View File

@ -0,0 +1,14 @@
package org.opencdmp.deposit.zenodorepository.service.zenodo;
import org.opencdmp.commonmodels.models.dmp.DmpModel;
import org.opencdmp.depositbase.repository.DepositConfiguration;
public interface ZenodoDepositService {
String deposit(DmpModel dmpDepositModel, String zenodoToken) throws Exception;
DepositConfiguration getConfiguration();
String authenticate(String code);
String getLogo();
}

View File

@ -0,0 +1,413 @@
package org.opencdmp.deposit.zenodorepository.service.zenodo;
import com.fasterxml.jackson.databind.ObjectMapper;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import org.opencdmp.commonmodels.models.FileEnvelopeModel;
import org.opencdmp.commonmodels.models.dmp.DmpModel;
import org.opencdmp.deposit.zenodorepository.model.ZenodoDeposit;
import org.opencdmp.deposit.zenodorepository.model.builder.ZenodoBuilder;
import org.opencdmp.deposit.zenodorepository.service.storage.FileStorageService;
import org.opencdmp.depositbase.repository.DepositConfiguration;
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.MediaType;
import org.springframework.http.ResponseEntity;
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.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
@Component
public class ZenodoDepositServiceImpl implements ZenodoDepositService {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(ZenodoDepositServiceImpl.class));
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 ObjectMapper objectMapper = new ObjectMapper();
private final ZenodoServiceProperties zenodoServiceProperties;
private final ZenodoBuilder zenodoBuilder;
private final FileStorageService storageService;
private byte[] logo;
@Autowired
public ZenodoDepositServiceImpl(ZenodoServiceProperties zenodoServiceProperties, ZenodoBuilder mapper, FileStorageService storageService){
this.zenodoServiceProperties = zenodoServiceProperties;
this.zenodoBuilder = mapper;
this.storageService = storageService;
this.logo = null;
}
@Override
public String deposit(DmpModel dmpModel, 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 = this.getWebClient();
DepositConfiguration zenodoConfig = this.zenodoServiceProperties.getDepositConfiguration();
if (zenodoConfig == null) return null;
ZenodoDeposit deposit = zenodoBuilder.build(dmpModel);
LinkedHashMap<String, String> links;
String previousDOI = dmpModel.getPreviousDOI();
String unpublishedUrl = null;
String publishUrl;
try {
if (previousDOI == null) {
links = deposit(zenodoToken, zenodoUrl, zenodoClient, deposit);
} else {
unpublishedUrl = this.getUnpublishedDOI(zenodoClient, zenodoUrl, previousDOI, zenodoToken, dmpModel.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.
FileEnvelopeModel pdfEnvelope = dmpModel.getPdfFile();
if (links == null || !links.containsKey(ZENODO_LINKS_BUCKET)) throw new MyApplicationException("bucket not found");
String addFileUrl = links.get(ZENODO_LINKS_BUCKET) + "/" + cleanFileName(pdfEnvelope.getFilename()) + "?access_token=" + zenodoToken;
byte[] pdfFileBytes = null;
if (this.getConfiguration().isUseSharedStorage() && pdfEnvelope.getFileRef() != null && !pdfEnvelope.getFileRef().isBlank()) {
pdfFileBytes = this.storageService.readFile(pdfEnvelope.getFileRef());
}
if (pdfFileBytes == null || pdfFileBytes.length == 0){
pdfFileBytes = pdfEnvelope.getFile();
}
zenodoClient.put().uri(addFileUrl)
.body(BodyInserters
.fromResource(new ByteArrayResource(pdfFileBytes)))
.retrieve().toEntity(Map.class).block();
FileEnvelopeModel rdaJsonEnvelope = dmpModel.getRdaJsonFile();
String jsonFileName = cleanFileName(rdaJsonEnvelope.getFilename());
addFileUrl = links.get(ZENODO_LINKS_BUCKET) + "/" + jsonFileName + "?access_token=" + zenodoToken;
byte[] rdaJsonBytes = null;
if (this.getConfiguration().isUseSharedStorage() && rdaJsonEnvelope.getFileRef() != null && !rdaJsonEnvelope.getFileRef().isBlank()) {
rdaJsonBytes = this.storageService.readFile(rdaJsonEnvelope.getFileRef());
}
if (rdaJsonBytes == null || rdaJsonBytes.length == 0){
rdaJsonBytes = rdaJsonEnvelope.getFile();
}
zenodoClient.put().uri(addFileUrl).headers(httpHeaders -> httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM)).body(BodyInserters.fromResource(new ByteArrayResource(rdaJsonBytes))).retrieve().toEntity(Map.class).block();
if (dmpModel.getSupportingFilesZip() != null) {
String supportingFilesZipName = cleanFileName(dmpModel.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(zenodoClient, publishUrl);
} catch (HttpClientErrorException | HttpServerErrorException ex) {
logger.error(ex.getMessage(), ex);
Map<String, String> parsedException = objectMapper.readValue(ex.getResponseBodyAsString(), Map.class);
throw new IOException(parsedException.get("message"), ex);
}
}
return null;
}
private static String cleanFileName(String name){
if (name == null || name.isEmpty()) return null;
int extensionIndex = name.lastIndexOf('.');
String extension = "";
String namePart = "";
if (extensionIndex > 0) {
extension = name.substring(extensionIndex + 1);
namePart = name.substring(0, extensionIndex);
}
if (!namePart.isEmpty()) namePart = namePart.replaceAll("[^a-zA-Z0-9_+ ]", "").replace(" ", "_").replace(",", "_");
return namePart + "." + extension;
}
private static LinkedHashMap<String, String> depositNewVersion(String zenodoToken, String zenodoUrl, String previousDOI, WebClient zenodoClient, ZenodoDeposit deposit) throws Exception {
Map<String, Object> 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, Object>) 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_LATEST_DRAFT)) throw new MyApplicationException("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)
.exchangeToMono(mono ->
mono.statusCode().isError() ?
mono.createException().flatMap(Mono::error) :
mono.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {})
).block();
logger.debug("createResponse-newVersion:");
logger.debug(objectMapper.writeValueAsString(createResponse));
links = createResponse == null ? new LinkedHashMap<>() : (LinkedHashMap<String, String>) createResponse.getOrDefault(ZENODO_LINKS, new LinkedHashMap<>());
//Third, get the new deposit
if (!links.containsKey(ZENODO_LINKS_LATEST_DRAFT)) throw new MyApplicationException("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, Object>>() {})).block();
logger.debug("createResponse-latestDraft:");
logger.debug(objectMapper.writeValueAsString(createResponse));
links = createResponse == null ? new LinkedHashMap<>() : (LinkedHashMap<String, String>) 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;
logger.debug(new MapLogEntry("Deposit New Version")
.And("url", updateUrl)
.And("body", deposit));
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(Void.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);
logger.error(e.getMessage(), e);
zenodoClient.delete().uri(latestDraftUrl).retrieve().toEntity(Void.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;
logger.debug(new MapLogEntry("Deposit")
.And("url", createUrl)
.And("body", deposit));
createResponse = zenodoClient.post().uri(createUrl).headers(httpHeaders -> {
httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
})
.bodyValue(deposit).exchangeToMono(mono ->
mono.statusCode().isError() ?
mono.createException().flatMap(Mono::error) :
mono.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {})).block();
return (LinkedHashMap<String, String>) createResponse.getOrDefault(ZENODO_LINKS, null);
}
private String publish(WebClient webClient, String publishUrl){
logger.debug(new MapLogEntry("publish")
.And("url", publishUrl));
Map<String, Object> publishResponse = webClient.post().uri(publishUrl).bodyValue("").exchangeToMono(mono ->
mono.statusCode().isError() ?
mono.createException().flatMap(Mono::error) :
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 DepositConfiguration getConfiguration() {
return this.zenodoServiceProperties.getDepositConfiguration();
}
@Override
public String authenticate(String code){
DepositConfiguration depositConfiguration = this.getConfiguration();
if(depositConfiguration != null) {
WebClient client = WebClient.builder().filters(exchangeFilterFunctions -> {
exchangeFilterFunctions.add(logRequest());
exchangeFilterFunctions.add(logResponse());
}).defaultHeaders(httpHeaders -> {
httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
}).build();
MultiValueMap<String, String> 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 {
logger.debug(new MapLogEntry("Get Access Token")
.And("url", depositConfiguration.getRepositoryAccessTokenUrl())
.And("body", map));
Map<String, Object> values = client.post().uri(depositConfiguration.getRepositoryAccessTokenUrl()).bodyValue(map).exchangeToMono(mono ->
mono.statusCode().isError() ?
mono.createException().flatMap(Mono::error) :
mono.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
})).block();
return values != null ? (String) values.getOrDefault(ACCESS_TOKEN, null) : null;
} catch (HttpClientErrorException ex) {
logger.error(ex.getMessage(), ex);
return null;
}
}
return null;
}
@Override
public String getLogo() {
DepositConfiguration zenodoConfig = this.zenodoServiceProperties.getDepositConfiguration();
if(zenodoConfig != null && zenodoConfig.isHasLogo() && this.zenodoServiceProperties.getLogo() != null && !this.zenodoServiceProperties.getLogo().isBlank()) {
if (this.logo == null) {
try {
java.io.File logoFile = ResourceUtils.getFile(this.zenodoServiceProperties.getLogo());
if (!logoFile.exists()) return null;
try(InputStream inputStream = new FileInputStream(logoFile)){
this.logo = inputStream.readAllBytes();
};
} catch (IOException e) {
logger.error(e.getMessage(), e);
throw new RuntimeException(e);
}
}
return (this.logo != null && this.logo.length != 0) ? Base64.getEncoder().encodeToString(this.logo) : null;
}
return null;
}
private String getUnpublishedDOI(WebClient client, String zenodoUrl, String doi, String token, Short version) {
try {
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.error(e.getMessage(), e);
return null;
}
}
private WebClient getWebClient(){
return WebClient.builder().filters(exchangeFilterFunctions -> {
exchangeFilterFunctions.add(logRequest());
exchangeFilterFunctions.add(logResponse());
}).codecs(codecs -> codecs
.defaultCodecs()
.maxInMemorySize(this.zenodoServiceProperties.getMaxInMemorySizeInBytes())
).build();
}
private static ExchangeFilterFunction logRequest() {
return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
logger.debug(new MapLogEntry("Request").And("method", clientRequest.method().toString()).And("url", clientRequest.url().toString()));
return Mono.just(clientRequest);
});
}
private static ExchangeFilterFunction logResponse() {
return ExchangeFilterFunction.ofResponseProcessor(response -> {
if (response.statusCode().isError()) {
return response.mutate().build().bodyToMono(String.class)
.flatMap(body -> {
logger.error(new MapLogEntry("Response").And("method", response.request().getMethod().toString()).And("url", response.request().getURI()).And("status", response.statusCode().toString()).And("body", body));
return Mono.just(response);
});
}
return Mono.just(response);
});
}
}

View File

@ -0,0 +1,9 @@
package org.opencdmp.deposit.zenodorepository.service.zenodo;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties({ZenodoServiceProperties.class})
public class ZenodoServiceConfiguration {
}

View File

@ -0,0 +1,130 @@
package org.opencdmp.deposit.zenodorepository.service.zenodo;
import org.opencdmp.depositbase.repository.DepositConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "zenodo")
public class ZenodoServiceProperties {
private String logo;
private String community;
private String domain;
private String affiliation;
private DepositConfiguration depositConfiguration;
private String organizationReferenceCode;
private String grantReferenceCode;
private String funderReferenceCode;
private String researcherReferenceCode;
private String licensesReferenceCode;
private String openaireGrantSourceCode;
private String orcidResearcherSourceCode;
private int maxInMemorySizeInBytes;
public String getLogo() {
return logo;
}
public void setLogo(String logo) {
this.logo = logo;
}
public String getCommunity() {
return community;
}
public void setCommunity(String community) {
this.community = community;
}
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
public String getAffiliation() {
return affiliation;
}
public void setAffiliation(String affiliation) {
this.affiliation = affiliation;
}
public DepositConfiguration getDepositConfiguration() {
return depositConfiguration;
}
public void setDepositConfiguration(DepositConfiguration depositConfiguration) {
this.depositConfiguration = depositConfiguration;
}
public String getOrganizationReferenceCode() {
return organizationReferenceCode;
}
public void setOrganizationReferenceCode(String organizationReferenceCode) {
this.organizationReferenceCode = organizationReferenceCode;
}
public String getGrantReferenceCode() {
return grantReferenceCode;
}
public void setGrantReferenceCode(String grantReferenceCode) {
this.grantReferenceCode = grantReferenceCode;
}
public String getFunderReferenceCode() {
return funderReferenceCode;
}
public void setFunderReferenceCode(String funderReferenceCode) {
this.funderReferenceCode = funderReferenceCode;
}
public String getResearcherReferenceCode() {
return researcherReferenceCode;
}
public void setResearcherReferenceCode(String researcherReferenceCode) {
this.researcherReferenceCode = researcherReferenceCode;
}
public String getLicensesReferenceCode() {
return licensesReferenceCode;
}
public void setLicensesReferenceCode(String licensesReferenceCode) {
this.licensesReferenceCode = licensesReferenceCode;
}
public String getOpenaireGrantSourceCode() {
return openaireGrantSourceCode;
}
public void setOpenaireGrantSourceCode(String openaireGrantSourceCode) {
this.openaireGrantSourceCode = openaireGrantSourceCode;
}
public String getOrcidResearcherSourceCode() {
return orcidResearcherSourceCode;
}
public void setOrcidResearcherSourceCode(String orcidResearcherSourceCode) {
this.orcidResearcherSourceCode = orcidResearcherSourceCode;
}
public int getMaxInMemorySizeInBytes() {
return maxInMemorySizeInBytes;
}
public void setMaxInMemorySizeInBytes(int maxInMemorySizeInBytes) {
this.maxInMemorySizeInBytes = maxInMemorySizeInBytes;
}
}

135
pom.xml
View File

@ -5,114 +5,39 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/>
<version>3.2.4</version>
</parent>
<groupId>gr.cite.opendmp</groupId>
<artifactId>repositorydepositzenodo</artifactId>
<version>${revision}</version>
<packaging>jar</packaging>
<groupId>org.opencdmp</groupId>
<artifactId>repository-deposit-parent</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>
<name>OpenDMP Repository Deposit Zenodo</name>
<description>OpenDMP Repository Deposit Zenodo</description>
<url>https://code-repo.d4science.org/MaDgiK-CITE/repository-deposit-zenodo</url>
<licenses>
<license>
<name>MIT License</name>
<url>https://code-repo.d4science.org/MaDgiK-CITE/repository-deposit-zenodo/src/branch/master/LICENSE.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<name>CITE S.A.</name>
<email>maven-central@cite.gr</email>
<organization>CITE S.A.</organization>
<organizationUrl>https://www.cite.gr</organizationUrl>
</developer>
</developers>
<scm>
<connection>scm:git:git://code-repo.d4science.org</connection>
<developerConnection>scm:git:ssh://code-repo.d4science.org</developerConnection>
<url>https://code-repo.d4science.org/MaDgiK-CITE/repository-deposit-zenodo</url>
</scm>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.release>21</maven.compiler.release>
<java.version>21</java.version>
<log4j.version>1.2.17</log4j.version>
<log4j2.version>2.15.0</log4j2.version>
<revision>1.0.0-SNAPSHOT</revision>
</properties>
<properties>
<revision>1.0.0-SNAPSHOT</revision>
</properties>
<modules>
<module>core</module>
<module>web</module>
</modules>
<dependencies>
<dependency>
<groupId>org.opencdmp</groupId>
<artifactId>repositorydepositbase</artifactId>
<version>2.0.15</version>
</dependency>
<dependency>
<groupId>gr.cite</groupId>
<artifactId>logging</artifactId>
<version>2.2.0</version>
</dependency>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>gr.cite.opendmp</groupId>
<artifactId>repositorydepositbase</artifactId>
<version>1.0.4</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20160810</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<archive>
<manifest>
<mainClass>eu.eudat.EuDatApplication</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>module-info.class</exclude>
</excludes>
</filter>
</filters>
<relocations>
<relocation>
<pattern>org.json</pattern>
<shadedPattern>zenodorepository.shaded.org.json</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</dependencies>
</project>

View File

@ -1,18 +1,33 @@
<settings>
<servers>
<server>
<id>ossrh</id>
<username>${server_username}</username>
<password>${server_password}</password>
</server>
</servers>
<profiles>
<pluginGroups>
<pluginGroup>org.sonarsource.scanner.maven</pluginGroup>
</pluginGroups>
<servers>
<server>
<id>cite-repo</id>
<username>${server_username}</username>
<password>${server_password}</password>
</server>
</servers>
<profiles>
<profile>
<id>ossrh</id>
<properties>
<gpg.passphrase>${gpg_passphrase}</gpg.passphrase>
<gpg.keyname>${gpg_keyname}</gpg.keyname>
</properties>
</profile>
</profiles>
</settings>
<id>release</id>
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>cite-repo</id>
<name>CITE Maven Repo</name>
<url>${citeMavenRepoUrl}</url>
</repository>
</repositories>
</profile>
</profiles>
</settings>

View File

@ -1,13 +0,0 @@
package eu.eudat.depositinterface.zenodorepository.config;
import java.io.InputStream;
import java.util.List;
public interface ConfigLoader {
InputStream getStreamFromPath(String filePath);
List<String> getRelatedIdentifiers();
List<String> getAcceptedPidTypes();
PidFieldNames getPidFieldNames();
byte[] getLogo(String repositoryId);
List<ZenodoConfig> getZenodoConfig();
}

View File

@ -1,110 +0,0 @@
package eu.eudat.depositinterface.zenodorepository.config;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service("zenodoConfigLoader")
public class ConfigLoaderImpl implements ConfigLoader{
private static final Logger logger = LoggerFactory.getLogger(ConfigLoaderImpl.class);
private static final ObjectMapper mapper = new ObjectMapper();
private List<String> relatedIdentifiers = new ArrayList<>();
private List<String> acceptedPidTypes = new ArrayList<>();
private PidFieldNames pidFieldNames = new PidFieldNames();
private List<ZenodoConfig> zenodoConfig = new ArrayList<>();
private final Environment environment;
@Autowired
public ConfigLoaderImpl(Environment environment){
this.environment = environment;
}
@Override
public List<String> getRelatedIdentifiers() {
if (relatedIdentifiers == null || relatedIdentifiers.isEmpty()) {
BufferedReader ids = new BufferedReader(new InputStreamReader(getStreamFromPath("relatedIdentifiers.txt")));
relatedIdentifiers = ids.lines().collect(Collectors.toList());
}
return relatedIdentifiers;
}
@Override
public List<String> getAcceptedPidTypes() {
if (acceptedPidTypes == null || acceptedPidTypes.isEmpty()) {
BufferedReader ids = new BufferedReader(new InputStreamReader(getStreamFromPath("acceptedPidTypes.txt")));
acceptedPidTypes = ids.lines().collect(Collectors.toList());
}
return acceptedPidTypes;
}
@Override
public PidFieldNames getPidFieldNames() {
try {
pidFieldNames = mapper.readValue(getStreamFromPath("datasetFieldsPid.json"), PidFieldNames.class);
}
catch (IOException e){
logger.error(e.getLocalizedMessage(), e);
}
return pidFieldNames;
}
@Override
public List<ZenodoConfig> getZenodoConfig() {
if (zenodoConfig == null || zenodoConfig.isEmpty()) {
try {
zenodoConfig = mapper.readValue(getStreamFromPath(environment.getProperty("zenodo_plugin.configuration.zenodo")), new TypeReference<List<ZenodoConfig>>() {});
} catch (IOException e) {
logger.error(e.getLocalizedMessage(), e);
return null;
}
}
return zenodoConfig;
}
@Override
public byte[] getLogo(String repositoryId) {
if (!zenodoConfig.isEmpty()) {
ZenodoConfig zenodoConfig = getZenodoConfig().stream().filter(x -> x.getRepositoryId().equals(repositoryId)).findFirst().orElse(null);
if (zenodoConfig != null) {
String logo = zenodoConfig.getLogo();
InputStream logoStream;
if (logo != null && !logo.isEmpty()) {
logoStream = getStreamFromPath(logo);
}
else {
logoStream = getClass().getClassLoader().getResourceAsStream("zenodo.jpg");
}
try {
return (logoStream != null) ? logoStream.readAllBytes() : null;
}
catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
return null;
}
return null;
}
@Override
public InputStream getStreamFromPath(String filePath) {
try {
return new FileInputStream(filePath);
} catch (FileNotFoundException e) {
logger.info("loading from classpath");
return getClass().getClassLoader().getResourceAsStream(filePath);
}
}
}

View File

@ -1,27 +0,0 @@
package eu.eudat.depositinterface.zenodorepository.config;
import com.fasterxml.jackson.annotation.JsonProperty;
public class DOIFunder {
@JsonProperty("Funder")
private String funder;
@JsonProperty("DOI")
private String DOI;
public String getFunder() {
return funder;
}
public void setFunder(String funder) {
this.funder = funder;
}
public String getDOI() {
return DOI;
}
public void setDOI(String DOI) {
this.DOI = DOI;
}
}

View File

@ -1,31 +0,0 @@
package eu.eudat.depositinterface.zenodorepository.config;
import com.fasterxml.jackson.annotation.JsonProperty;
public class PidFieldNames {
@JsonProperty("pidName")
private String pidName;
@JsonProperty("pidTypeName")
private String pidTypeName;
public PidFieldNames() {}
public PidFieldNames(String pidName, String pidTypeName) {
this.pidName = pidName;
this.pidTypeName = 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;
}
}

View File

@ -1,195 +0,0 @@
package eu.eudat.depositinterface.zenodorepository.config;
import com.fasterxml.jackson.annotation.JsonProperty;
import eu.eudat.depositinterface.repository.RepositoryDepositConfiguration;
public class ZenodoConfig {
private enum DepositType {
SystemDeposit(0), UserDeposit(1), BothWaysDeposit(2);
private final int value;
DepositType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static DepositType fromInteger(int value) {
switch (value) {
case 0:
return SystemDeposit;
case 1:
return UserDeposit;
case 2:
return BothWaysDeposit;
default:
throw new RuntimeException("Unsupported Deposit Type");
}
}
}
@JsonProperty("depositType")
private int depositType;
@JsonProperty("repositoryId")
private String repositoryId;
@JsonProperty("accessToken")
private String accessToken;
@JsonProperty("repositoryUrl")
private String repositoryUrl;
@JsonProperty("repositoryAuthorizationUrl")
private String repositoryAuthorizationUrl;
@JsonProperty("repositoryRecordUrl")
private String repositoryRecordUrl;
@JsonProperty("repositoryAccessTokenUrl")
private String repositoryAccessTokenUrl;
@JsonProperty("repositoryClientId")
private String repositoryClientId;
@JsonProperty("repositoryClientSecret")
private String repositoryClientSecret;
@JsonProperty("redirectUri")
private String redirectUri;
@JsonProperty("hasLogo")
private boolean hasLogo;
@JsonProperty("logo")
private String logo;
@JsonProperty("doiFunder")
private String doiFunder;
@JsonProperty("community")
private String community;
@JsonProperty("affiliation")
private String affiliation;
@JsonProperty("domain")
private String domain;
public int getDepositType() {
return depositType;
}
public void setDepositType(int depositType) {
this.depositType = depositType;
}
public String getRepositoryId() {
return repositoryId;
}
public void setRepositoryId(String repositoryId) {
this.repositoryId = repositoryId;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getRepositoryUrl() {
return repositoryUrl;
}
public void setRepositoryUrl(String repositoryUrl) {
this.repositoryUrl = repositoryUrl;
}
public String getRepositoryAuthorizationUrl() {
return repositoryAuthorizationUrl;
}
public void setRepositoryAuthorizationUrl(String repositoryAuthorizationUrl) {
this.repositoryAuthorizationUrl = repositoryAuthorizationUrl;
}
public String getRepositoryRecordUrl() {
return repositoryRecordUrl;
}
public void setRepositoryRecordUrl(String repositoryRecordUrl) {
this.repositoryRecordUrl = repositoryRecordUrl;
}
public String getRepositoryAccessTokenUrl() {
return repositoryAccessTokenUrl;
}
public void setRepositoryAccessTokenUrl(String repositoryAccessTokenUrl) {
this.repositoryAccessTokenUrl = repositoryAccessTokenUrl;
}
public String getRepositoryClientId() {
return repositoryClientId;
}
public void setRepositoryClientId(String repositoryClientId) {
this.repositoryClientId = repositoryClientId;
}
public String getRepositoryClientSecret() {
return repositoryClientSecret;
}
public void setRepositoryClientSecret(String repositoryClientSecret) {
this.repositoryClientSecret = repositoryClientSecret;
}
public String getRedirectUri() {
return redirectUri;
}
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
public boolean isHasLogo() {
return hasLogo;
}
public void setHasLogo(boolean hasLogo) {
this.hasLogo = hasLogo;
}
public String getLogo() {
return logo;
}
public void setLogo(String logo) {
this.logo = logo;
}
public String getDoiFunder() {
return doiFunder;
}
public void setDoiFunder(String doiFunder) {
this.doiFunder = doiFunder;
}
public String getCommunity() {
return community;
}
public void setCommunity(String community) {
this.community = community;
}
public String getAffiliation() {
return affiliation;
}
public void setAffiliation(String affiliation) {
this.affiliation = affiliation;
}
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = 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;
}
}

View File

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

View File

@ -1,279 +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.config.ConfigLoader;
import eu.eudat.depositinterface.zenodorepository.config.DOIFunder;
import eu.eudat.depositinterface.zenodorepository.config.PidFieldNames;
import eu.eudat.depositinterface.zenodorepository.config.ZenodoConfig;
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.io.IOException;
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 static ConfigLoader configLoader;
private static PidFieldNames pidFieldNames;
@Autowired
public DMPToZenodoMapper(ConfigLoader configL){
configLoader = configL;
pidFieldNames = configLoader.getPidFieldNames();
}
private static List<DatasetFieldsDepositModel> findSchemanticValues(String relatedId, List<DatasetFieldsDepositModel> fields){
return fields.stream().filter(f -> f.getSchematics().contains(relatedId)).collect(Collectors.toList());
}
private static Set<String> extractSchemanticValues(List<DatasetFieldsDepositModel> fields, List<String> acceptedPidTypes) throws JsonProcessingException{
Set<String> 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<String> selected = objectMapper.readValue(value, new TypeReference<List<String>>() {});
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<String> selected = objectMapper.readValue(value, new TypeReference<List<String>>() {});
for (String s : selected) {
Map<String, String> valueMap = objectMapper.readValue(s, new TypeReference<Map<String, String>>() {});
String pid = valueMap.get(pidFieldNames.getPidName());
String pidType = valueMap.get(pidFieldNames.getPidTypeName());
if (acceptedPidTypes.contains(pidType)) {
values.add(pid);
}
}
}
else {
Map<String, String> valueMap = objectMapper.readValue(value, new TypeReference<Map<String, String>>() {});
String pid = valueMap.get(pidFieldNames.getPidName());
String pidType = valueMap.get(pidFieldNames.getPidTypeName());
if (acceptedPidTypes.contains(pidType)) {
values.add(pid);
}
}
break;
case ORGANIZATIONS:
case RESEARCHERS:
if (field.isMultiple()) {
List<String> selected = objectMapper.readValue(value, new TypeReference<List<String>>() {});
for (String s : selected) {
Map<String, String> valueMap = objectMapper.readValue(s, new TypeReference<Map<String, String>>() {});
String pid = valueMap.get("reference");
if(pid != null) {
values.add(pid);
}
}
}
else {
Map<String, String> valueMap = objectMapper.readValue(value, new TypeReference<Map<String, String>>() {});
String pid = valueMap.get("reference");
if(pid != null) {
values.add(pid);
}
}
break;
case DATASET_IDENTIFIER:
Map<String, String> valueMap = objectMapper.readValue(value, new TypeReference<Map<String, String>>() {});
values.add(valueMap.get("identifier"));
break;
}
}
}
return values;
}
public static ZenodoDeposit fromDMP(DMPDepositModel dmp, ZenodoConfig zenodoConfig) throws JsonProcessingException {
Map<String, Object> extraProperties = dmp.getExtraProperties() != null ? new org.json.JSONObject(dmp.getExtraProperties()).toMap() : new HashMap<>();
ZenodoDeposit deposit = new ZenodoDeposit();
Map<String, Object> schematicsMap = new HashMap<>();
List<ZenodoRelator> relatedIdentifiers = new ArrayList<>();
List<ZenodoComunity> communities = new ArrayList<>();
List<ZenodoContributor> contributors = new ArrayList<>();
List<ZenodoCreator> creators = new ArrayList<>();
List<ZenodoGrant> grants = new ArrayList<>();
List<String> keywords = new ArrayList<>();
List<String> references = new ArrayList<>();
List<String> acceptedPidTypes = configLoader.getAcceptedPidTypes();
for(DatasetDepositModel dataset: dmp.getDatasets()){
for(String relatedId: configLoader.getRelatedIdentifiers()){
List<DatasetFieldsDepositModel> fields = findSchemanticValues(relatedId, dataset.getFields());
Set<String> 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<ZenodoDepositMetadata>(){});
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() : "<p></p>"));
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<ZenodoContributor> 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<ZenodoContributor> 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<DOIFunder> doiFunders = new ArrayList<>();
try {
List<Map<String, Object>> tempdoiFunders = objectMapper.readValue(configLoader.getStreamFromPath(zenodoConfig.getDoiFunder()), List.class);
doiFunders = tempdoiFunders.stream().map(map -> objectMapper.convertValue(map, DOIFunder.class)).collect(Collectors.toList());
} catch (IOException e) {
logger.error(e.getLocalizedMessage(), e);
}
if (dmp.getGrant().getFunder() != null && dmp.getGrant().getFunder().getLabel() != null) {
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;
}
}

View File

@ -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;
}
}

View File

@ -1,70 +0,0 @@
[
{
"Funder": "Australian Research Council",
"DOI": "10.13039/501100000923"
},
{
"Funder": "Austrian Science Fund",
"DOI": "10.13039/501100002428"
},
{
"Funder": "European Commission",
"DOI": "10.13039/501100000780"
},
{
"Funder": "European Environment Agency",
"DOI": "10.13039/501100000806"
},
{
"Funder": "Academy of Finland",
"DOI": "10.13039/501100002341"
},
{
"Funder": "Hrvatska Zaklada za Znanost",
"DOI": "10.13039/501100004488"
},
{
"Funder": "Fundação para a Ciência e a Tecnologia",
"DOI": "10.13039/501100001871"
},
{
"Funder": "Ministarstvo Prosvete, Nauke i Tehnološkog Razvoja",
"DOI": "10.13039/501100004564"
},
{
"Funder": "Ministarstvo Znanosti, Obrazovanja i Sporta",
"DOI": "10.13039/501100006588"
},
{
"Funder": "National Health and Medical Research Council",
"DOI": "10.13039/501100000925"
},
{
"Funder": "National Institutes of Health",
"DOI": "10.13039/100000002"
},
{
"Funder": "National Science Foundation",
"DOI": "10.13039/100000001"
},
{
"Funder": "Nederlandse Organisatie voor Wetenschappelijk Onderzoek",
"DOI": "10.13039/501100003246"
},
{
"Funder": "Research Councils",
"DOI": "10.13039/501100000690"
},
{
"Funder": "Schweizerischer Nationalfonds zur Förderung der wissenschaftlichen Forschung",
"DOI": "10.13039/501100001711"
},
{
"Funder": "Science Foundation Ireland",
"DOI": "10.13039/501100001602"
},
{
"Funder": "Wellcome Trust",
"DOI": "10.13039/100004440"
}
]

View File

@ -1,43 +0,0 @@
actrn
ark
bibcode
nct
drks
doi
euctr
data.europa.eu
epo_id
GRID
gsk
GeoPass
GBIF
hal
handle
isrctn
ichushi
ISNI
jprn
mag_id
NAID
NCID
oai
orcid_pending
orcid
OrgPeg
PANGAEA
PIC
epo_nr_epodoc
pdb
pmc
pmid
RNSR
ROR
RRID
UNKNOWN
VIAF
who
arXiv
info:eu-repo/dai
orcidworkid
urn
w3id

View File

@ -1,2 +0,0 @@
zenodo_plugin.storage.temp=${STORAGE_TMP_ZENODO}
zenodo_plugin.configuration.zenodo=${CONFIGURATION_ZENODO}

View File

@ -1,4 +0,0 @@
{
"pidName": "pid",
"pidTypeName": "pidTypeField"
}

View File

@ -1,32 +0,0 @@
zenodo.related_identifiers.isCitedBy
zenodo.related_identifiers.cites
zenodo.related_identifiers.isSupplementTo
zenodo.related_identifiers.isSupplementedBy
zenodo.related_identifiers.isContinuedBy
zenodo.related_identifiers.continues
zenodo.related_identifiers.isDescribedBy
zenodo.related_identifiers.describes
zenodo.related_identifiers.hasMetadata
zenodo.related_identifiers.isMetadataFor
zenodo.related_identifiers.isNewVersionOf
zenodo.related_identifiers.isPreviousVersionOf
zenodo.related_identifiers.isPartOf
zenodo.related_identifiers.hasPart
zenodo.related_identifiers.isReferencedBy
zenodo.related_identifiers.references
zenodo.related_identifiers.isDocumentedBy
zenodo.related_identifiers.documents
zenodo.related_identifiers.isCompiledBy
zenodo.related_identifiers.compiles
zenodo.related_identifiers.isVariantFormOf
zenodo.related_identifiers.isOriginalFormof
zenodo.related_identifiers.isIdenticalTo
zenodo.related_identifiers.isAlternateIdentifier
zenodo.related_identifiers.isReviewedBy
zenodo.related_identifiers.reviews
zenodo.related_identifiers.isDerivedFrom
zenodo.related_identifiers.isSourceOf
zenodo.related_identifiers.requires
zenodo.related_identifiers.isRequiredBy
zenodo.related_identifiers.isObsoletedBy
zenodo.related_identifiers.obsoletes

73
web/pom.xml Normal file
View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.opencdmp</groupId>
<artifactId>repository-deposit-parent</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>repository-deposit-web</artifactId>
<version>${revision}</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.release>21</maven.compiler.release>
<java.version>21</java.version>
<revision>1.0.0-SNAPSHOT</revision>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>gr.cite</groupId>
<artifactId>oidc-authn</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>gr.cite</groupId>
<artifactId>cache</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>gr.cite</groupId>
<artifactId>exceptions-web</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.opencdmp</groupId>
<artifactId>repositorydepositzenodo</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,17 @@
package org.opencdmp.deposit;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = {
"org.opencdmp.deposit.*",
"org.opencdmp.depositbase.*",
"gr.cite.tools",
"gr.cite.commons"
})
public class DepositApplication {
public static void main(String[] args) {
SpringApplication.run(DepositApplication.class, args);
}
}

View File

@ -0,0 +1,68 @@
package org.opencdmp.deposit.config;
import gr.cite.commons.web.oidc.configuration.WebSecurityProperties;
import gr.cite.commons.web.oidc.configuration.filter.ApiKeyFilter;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.config.Customizer;
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;
import java.util.Set;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
private final WebSecurityProperties webSecurityProperties;
private final AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
@Autowired
public SecurityConfiguration(WebSecurityProperties webSecurityProperties, AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver) {
this.webSecurityProperties = webSecurityProperties;
this.authenticationManagerResolver = authenticationManagerResolver;
}
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.cors(Customizer.withDefaults())
.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry
.requestMatchers(buildAntPatterns(webSecurityProperties.getAuthorizedEndpoints())).authenticated()
.requestMatchers(buildAntPatterns(webSecurityProperties.getAllowedEndpoints())).anonymous())
.sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.NEVER))
.oauth2ResourceServer(oauth2 -> oauth2.authenticationManagerResolver(authenticationManagerResolver));
return http.build();
}
private String[] buildAntPatterns(Set<String> endpoints) {
if (endpoints == null) {
return new String[0];
}
return endpoints.stream()
.filter(endpoint -> endpoint != null && !endpoint.isBlank())
.map(endpoint -> "/" + stripUnnecessaryCharacters(endpoint) + "/**")
.toArray(String[]::new);
}
private String stripUnnecessaryCharacters(String endpoint) {
endpoint = endpoint.strip();
if (endpoint.startsWith("/")) {
endpoint = endpoint.substring(1);
}
if (endpoint.endsWith("/")) {
endpoint = endpoint.substring(0, endpoint.length() - 1);
}
return endpoint;
}
}

View File

@ -0,0 +1,73 @@
package org.opencdmp.deposit.controller;
import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import org.opencdmp.commonmodels.models.dmp.DmpModel;
import org.opencdmp.deposit.zenodorepository.audit.AuditableAction;
import org.opencdmp.depositbase.repository.DepositConfiguration;
import org.opencdmp.deposit.zenodorepository.service.zenodo.ZenodoDepositService;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.AbstractMap;
import java.util.Map;
@RestController
@RequestMapping("/api/deposit")
public class DepositController implements org.opencdmp.depositbase.repository.DepositController {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(DepositController.class));
private final ZenodoDepositService depositClient;
private final AuditService auditService;
@Autowired
public DepositController(ZenodoDepositService depositClient, AuditService auditService) {
this.depositClient = depositClient;
this.auditService = auditService;
}
public String deposit(@RequestBody DmpModel dmpModel, @RequestParam("authToken")String authToken) throws Exception {
logger.debug(new MapLogEntry("deposit " + DmpModel.class.getSimpleName()).And("dmpModel", dmpModel));
String doiId = depositClient.deposit(dmpModel, authToken);
this.auditService.track(AuditableAction.Deposit_Deposit, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("dmpModel", dmpModel)
));
return doiId;
}
public String authenticate(@RequestParam("authToken") String code) {
logger.debug(new MapLogEntry("authenticate " + DmpModel.class.getSimpleName()));
String token = depositClient.authenticate(code);
this.auditService.track(AuditableAction.Deposit_Authenticate);
return token;
}
public DepositConfiguration getConfiguration() {
logger.debug(new MapLogEntry("getConfiguration " + DmpModel.class.getSimpleName()));
DepositConfiguration configuration = depositClient.getConfiguration();
this.auditService.track(AuditableAction.Deposit_GetConfiguration);
return configuration;
}
public String getLogo() {
logger.debug(new MapLogEntry("getLogo " + DmpModel.class.getSimpleName()));
String logo = depositClient.getLogo();
this.auditService.track(AuditableAction.Deposit_GetLogo);
return logo;
}
}

View File

@ -0,0 +1,202 @@
package org.opencdmp.deposit.controller.controllerhandler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import gr.cite.tools.exception.*;
import gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
@ControllerAdvice
public class GlobalExceptionHandler {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(GlobalExceptionHandler.class));
private final ObjectMapper objectMapper;
public GlobalExceptionHandler() {
this.objectMapper = new ObjectMapper();
this.objectMapper.registerModule(new JavaTimeModule());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handleUnexpectedErrors(Exception exception, WebRequest request) throws Exception {
HandledException handled = this.handleException(exception, request);
this.log(handled.getLevel(), exception, MessageFormat.format("returning code {0} and payload {1}", handled.getStatusCode(), handled.getMessage()));
return new ResponseEntity<>(handled.getMessage(), handled.getStatusCode());
}
public String toJsonSafe(Object item) {
if (item == null) return null;
try {
return this.objectMapper.writeValueAsString(item);
} catch (Exception ex) {
return null;
}
}
public void log(System.Logger.Level level, Exception e, String message) {
if (level != null) {
switch (level) {
case TRACE:
logger.trace(message, e);
break;
case DEBUG:
logger.debug(message, e);
break;
case INFO:
logger.info(message, e);
break;
case WARNING:
logger.warn(message, e);
break;
case ERROR:
logger.error(message, e);
break;
default:
logger.error(e);
}
} else {
logger.error(e);
}
}
public HandledException handleException(Exception exception, WebRequest request) throws Exception {
HttpStatus statusCode;
Map<String, Object> result;
System.Logger.Level logLevel;
switch (exception){
case MyNotFoundException myNotFoundException -> {
logLevel = System.Logger.Level.DEBUG;
statusCode = HttpStatus.NOT_FOUND;
int code = myNotFoundException.getCode();
if (code > 0) {
result = Map.ofEntries(
Map.entry("code", code),
Map.entry("error", myNotFoundException.getMessage())
);
}
else {
result = Map.ofEntries(
Map.entry("error", myNotFoundException.getMessage())
);
}
}
case MyUnauthorizedException myUnauthorizedException -> {
logLevel = System.Logger.Level.DEBUG;
statusCode = HttpStatus.UNAUTHORIZED;
int code = myUnauthorizedException.getCode();
if (code > 0) {
result = Map.ofEntries(
Map.entry("code", code),
Map.entry("error", myUnauthorizedException.getMessage())
);
}
else {
result = Map.ofEntries(
Map.entry("error", myUnauthorizedException.getMessage())
);
}
}
case MyForbiddenException myForbiddenException -> {
logLevel = System.Logger.Level.DEBUG;
statusCode = HttpStatus.FORBIDDEN;
int code = myForbiddenException.getCode();
if (code > 0) {
result = Map.ofEntries(
Map.entry("code", code),
Map.entry("error", myForbiddenException.getMessage())
);
}
else {
result = Map.ofEntries(
Map.entry("error", myForbiddenException.getMessage())
);
}
}
case MyValidationException myValidationException -> {
logLevel = System.Logger.Level.DEBUG;
statusCode = HttpStatus.BAD_REQUEST;
int code = myValidationException.getCode();
result = new HashMap<>();
if (code > 0) result.put("code", code);
if (myValidationException.getMessage() != null) result.put("error", myValidationException.getMessage());
if (myValidationException.getErrors() != null) result.put("message", myValidationException.getErrors());
}
case MyApplicationException myApplicationException -> {
logLevel = System.Logger.Level.ERROR;
statusCode = HttpStatus.INTERNAL_SERVER_ERROR;
int code = myApplicationException.getCode();
if (code > 0) {
result = Map.ofEntries(
Map.entry("code", code),
Map.entry("error", myApplicationException.getMessage())
);
}
else {
result = Map.ofEntries(
Map.entry("error", myApplicationException.getMessage())
);
}
}
default -> {
logLevel = System.Logger.Level.ERROR;
statusCode = HttpStatus.INTERNAL_SERVER_ERROR;
result = Map.ofEntries(
Map.entry("error", "System error")
);
}
};
String serialization = this.toJsonSafe(result);
return new HandledException(statusCode, serialization, logLevel);
}
public static class HandledException{
public HttpStatus statusCode;
public String message;
public System.Logger.Level level;
public HandledException(HttpStatus statusCode, String message, System.Logger.Level level) {
this.statusCode = statusCode;
this.message = message;
this.level = level;
}
public HttpStatus getStatusCode() {
return statusCode;
}
public void setStatusCode(HttpStatus statusCode) {
this.statusCode = statusCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public System.Logger.Level getLevel() {
return level;
}
public void setLevel(System.Logger.Level level) {
this.level = level;
}
}
}

View File

@ -0,0 +1,14 @@
spring:
jackson:
default-property-inclusion: non_null
config:
import: optional:classpath:config/app.env[.properties], optional:file:../config/app.env[.properties],
optional:classpath:config/server.yml[.yml], optional:classpath:config/server-${spring.profiles.active}.yml[.yml], optional:file:../config/server-${spring.profiles.active}.yml[.yml],
optional:classpath:config/logging.yml[.yml], optional:classpath:config/logging-${spring.profiles.active}.yml[.yml], optional:file:../config/logging-${spring.profiles.active}.yml[.yml],
optional:classpath:config/zenodo.yml[.yml], optional:classpath:config/zenodo-${spring.profiles.active}.yml[.yml], optional:file:../config/zenodo-${spring.profiles.active}.yml[.yml],
optional:classpath:config/pid.yml[.yml], optional:classpath:config/pid-${spring.profiles.active}.yml[.yml], optional:file:../config/pid-${spring.profiles.active}.yml[.yml],
optional:classpath:config/funder.yml[.yml], optional:classpath:config/funder-${spring.profiles.active}.yml[.yml], optional:file:../config/funder-${spring.profiles.active}.yml[.yml],
optional:classpath:config/identifiers.yml[.yml], optional:classpath:config/identifiers-${spring.profiles.active}.yml[.yml], optional:file:../config/identifiers-${spring.profiles.active}.yml[.yml],
optional:classpath:config/security.yml[.yml], optional:classpath:config/security-${spring.profiles.active}.yml[.yml], optional:file:../config/security-${spring.profiles.active}.yml[.yml],
optional:classpath:config/cache.yml[.yml], optional:classpath:config/cache-${spring.profiles.active}.yml[.yml], optional:file:../config/cache-${spring.profiles.active}.yml[.yml],
optional:classpath:config/idpclaims.yml[.yml], optional:classpath:config/idpclaims-${spring.profiles.active}.yml[.yml], optional:file:../config/idpclaims-${spring.profiles.active}.yml[.yml]

View File

@ -0,0 +1,18 @@
cache:
manager:
fallbackToNoOpCache: true
caffeineCaches:
- names: [ "logoByRepository" ]
allowNullValues: true
initialCapacity: 100
maximumSize: 500
enableRecordStats: false
expireAfterWriteMinutes: 10
expireAfterAccessMinutes: 10
refreshAfterWriteMinutes: 10
mapCaches:
logoByRepository:
name: logoByRepository
keyPattern: zenodoplugin_$repo$:v0

View File

@ -0,0 +1,34 @@
funder:
available:
- funder: "Austrian Science Fund"
doi: "10.13039/501100002428"
- funder: "European Commission"
doi: "10.13039/501100000780"
- funder: "European Environment Agency"
doi: "10.13039/501100000806"
- funder: "Academy of Finland"
doi: "10.13039/501100002341"
- funder: "Hrvatska Zaklada za Znanost"
doi: "10.13039/501100004488"
- funder: "Fundação para a Ciência e a Tecnologia"
doi: "10.13039/501100001871"
- funder: "Ministarstvo Prosvete, Nauke i Tehnološkog Razvoja"
doi: "10.13039/501100004564"
- funder: "Ministarstvo Znanosti, Obrazovanja i Sporta"
doi: "10.13039/501100006588"
- funder: "National Health and Medical Research Council"
doi: "10.13039/501100000925"
- funder: "National Institutes of Health"
doi: "10.13039/100000002"
- funder: "National Science Foundation"
doi: "10.13039/100000001"
- funder: "Nederlandse Organisatie voor Wetenschappelijk Onderzoek"
doi: "10.13039/501100003246"
- funder: "Research Councils"
doi: "10.13039/501100000690"
- funder: "Schweizerischer Nationalfonds zur Förderung der wissenschaftlichen Forschung"
doi: "10.13039/501100001711"
- funder: "Science Foundation Ireland"
doi: "10.13039/501100001602"
- funder: "Wellcome Trust"
doi: "10.13039/100004440"

View File

@ -0,0 +1,34 @@
identifiers:
related:
- zenodo.related_identifiers.isCitedBy
- zenodo.related_identifiers.cites
- zenodo.related_identifiers.isSupplementTo
- zenodo.related_identifiers.isSupplementedBy
- zenodo.related_identifiers.isContinuedBy
- zenodo.related_identifiers.continues
- zenodo.related_identifiers.isDescribedBy
- zenodo.related_identifiers.describes
- zenodo.related_identifiers.hasMetadata
- zenodo.related_identifiers.isMetadataFor
- zenodo.related_identifiers.isNewVersionOf
- zenodo.related_identifiers.isPreviousVersionOf
- zenodo.related_identifiers.isPartOf
- zenodo.related_identifiers.hasPart
- zenodo.related_identifiers.isReferencedBy
- zenodo.related_identifiers.references
- zenodo.related_identifiers.isDocumentedBy
- zenodo.related_identifiers.documents
- zenodo.related_identifiers.isCompiledBy
- zenodo.related_identifiers.compiles
- zenodo.related_identifiers.isVariantFormOf
- zenodo.related_identifiers.isOriginalFormof
- zenodo.related_identifiers.isIdenticalTo
- zenodo.related_identifiers.isAlternateIdentifier
- zenodo.related_identifiers.isReviewedBy
- zenodo.related_identifiers.reviews
- zenodo.related_identifiers.isDerivedFrom
- zenodo.related_identifiers.isSourceOf
- zenodo.related_identifiers.requires
- zenodo.related_identifiers.isRequiredBy
- zenodo.related_identifiers.isObsoletedBy
- zenodo.related_identifiers.obsoletes

View File

@ -0,0 +1,41 @@
idpclient:
claims:
mapping:
Subject:
- type: sub
Name:
- type: name
Client:
- type: client_id
AuthenticationMethod:
- type: amr
NotBefore:
- type: nbf
AuthenticatedAt:
- type: auth_time
ExpiresAt:
- type: exp
Email:
- type: email
Roles:
- type: resource_access
path: dmp_zenodo_bridge.roles
Scope:
- type: scope
AccessToken:
- type: x-access-token
visibility: SENSITIVE
IssuedAt:
- type: iat
Issuer:
- type: iss
Audience:
- type: aud
TokenType:
- type: typ
AuthorizedParty:
- type: azp
Authorities:
- type: authorities
ExternalProviderName:
- type: identity_provider

View File

@ -0,0 +1,36 @@
logging:
config: classpath:logging/logback-${spring.profiles.active}.xml
context:
request:
requestIdKey: req.id
requestRemoteHostKey: req.remoteHost
requestUriKey: req.requestURI
requestQueryStringKey: req.queryString
requestUrlKey : req.requestURL
requestMethodKey: req.method
requestUserAgentKey: req.userAgent
requestForwardedForKey: req.xForwardedFor
requestSchemeKey: req.scheme
requestRemoteAddressKey: req.remoteAddr
requestRemotePortKey: req.remotePort
requestRemoteUserKey: req.remoteUser
principal:
subjectKey: usr.subject
nameKey: usr.name
clientKey: usr.client
audit:
enable: true
requestRemoteHostKey: req.remoteHost
requestUriKey: req.requestURI
requestQueryStringKey: req.queryString
requestUrlKey : req.requestURL
requestMethodKey: req.method
requestUserAgentKey: req.userAgent
requestForwardedForKey: req.xForwardedFor
requestSchemeKey: req.scheme
requestRemoteAddressKey: req.remoteAddr
requestRemotePortKey: req.remotePort
requestRemoteUserKey: req.remoteUser
principalSubjectKey: usr.subject
principalNameKey: usr.name
principalClientKey: usr.client

View File

@ -0,0 +1,35 @@
logging:
context:
request:
requestIdKey: req.id
requestRemoteHostKey: req.remoteHost
requestUriKey: req.requestURI
requestQueryStringKey: req.queryString
requestUrlKey : req.requestURL
requestMethodKey: req.method
requestUserAgentKey: req.userAgent
requestForwardedForKey: req.xForwardedFor
requestSchemeKey: req.scheme
requestRemoteAddressKey: req.remoteAddr
requestRemotePortKey: req.remotePort
requestRemoteUserKey: req.remoteUser
principal:
subjectKey: usr.subject
nameKey: usr.name
clientKey: usr.client
audit:
enable: true
requestRemoteHostKey: req.remoteHost
requestUriKey: req.requestURI
requestQueryStringKey: req.queryString
requestUrlKey : req.requestURL
requestMethodKey: req.method
requestUserAgentKey: req.userAgent
requestForwardedForKey: req.xForwardedFor
requestSchemeKey: req.scheme
requestRemoteAddressKey: req.remoteAddr
requestRemotePortKey: req.remotePort
requestRemoteUserKey: req.remoteUser
principalSubjectKey: usr.subject
principalNameKey: usr.name
principalClientKey: usr.client

View File

@ -0,0 +1,48 @@
pid:
fields:
pid-name: pid
pid-type-name: pidTypeField
accepted-types:
- actrn
- ark
- bibcode
- nct
- drks
- doi
- euctr
- data.europa.eu
- epo_id
- GRID
- gsk
- GeoPass
- GBIF
- hal
- handle
- isrctn
- ichushi
- ISNI
- jprn
- mag_id
- NAID
- NCID
- oai
- orcid_pending
- orcid
- OrgPeg
- PANGAEA
- PIC
- epo_nr_epodoc
- pdb
- pmc
- pmid
- RNSR
- ROR
- RRID
- UNKNOWN
- VIAF
- who
- arXiv
- info:eu-repo/dai
- orcidworkid
- urn
- w3id

View File

@ -0,0 +1,6 @@
web:
security:
idp:
resource:
jwt:
audiences: [ "dmp_zenodo_bridge" ]

View File

@ -0,0 +1,14 @@
web:
security:
enabled: true
authorized-endpoints: [ api ]
allowed-endpoints: [ health ]
idp:
api-key:
enabled: false
resource:
token-type: JWT #| opaque
jwt:
claims: [ role, x-role ]
issuer-uri: ${IDP_ISSUER_URI}
validIssuer: ${IDP_ISSUER_URI}

View File

@ -0,0 +1,12 @@
server:
port: 8082
tomcat:
threads:
max: 20
max-connections: 10000
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB

View File

@ -0,0 +1,4 @@
file:
storage:
temp: ${STORAGE_PATH}/tmp
transient-path: ${STORAGE_PATH}/shared

View File

@ -0,0 +1,8 @@
zenodo:
community: argos
affiliation: ARGOS
domain: https://argos.openaire.eu/
logo: classpath:zenodo.jpg
depositConfiguration:
has-logo: true
useSharedStorage: false

View File

@ -0,0 +1,25 @@
zenodo:
organizationReferenceCode: "organisations"
grantReferenceCode: "grants"
funderReferenceCode: "funders"
researcherReferenceCode: "researchers"
licenceReferenceCode: "licenses"
projectReferenceCode: "projects"
datasetReferenceCode: "datasets"
publicationReferenceCode: "publications"
openaireGrantSourceCode: "openaire"
orcidResearcherSourceCode: "ORCID"
maxInMemorySizeInBytes: 6554000
depositConfiguration:
deposit-type: 2
repository-id: zenodo
access-token: ${ZENODO_ACCESS_TOKEN}
repository-url: ${ZENODO_URL}
repository-authorization-url: ${ZENODO_AUTHORIZATION_URL}
repository-record-url: ${ZENODO_RECORD_URL}
repository-access-token-url: ${ZENODO_ACCESS_TOKEN_URL}
repository-client-id: ${ZENODO_CLIENT_ID}
repository-client-secret: ${ZENODO_CLIENT_SECRET}
redirect-uri: ${REDIRECT_URL}
has-logo: true
useSharedStorage: false

View File

@ -0,0 +1,62 @@
<configuration debug="true">
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>%date{ISO8601} [%thread] %-5level %logger{36} [%X{req.id}] - %message%n</Pattern>
</encoder>
</appender>
<appender name="TROUBLESHOOTING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/logging.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/logging.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>15</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>%date{ISO8601} [%thread] %-5level %logger{36} [%X{req.id}] - %message%n</Pattern>
</encoder>
</appender>
<appender name="AUDITING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/auditing.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/auditing.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>15</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>%date{ISO8601} - %X{req.id} - %message%n</Pattern>
</encoder>
</appender>
<logger name="org.springframework.web" level="INFO" additivity="false">
<appender-ref ref="TROUBLESHOOTING"/>
<appender-ref ref="STDOUT"/>
</logger>
<logger name="org.hibernate" level="INFO" additivity="false">
<appender-ref ref="TROUBLESHOOTING"/>
<appender-ref ref="STDOUT"/>
</logger>
<logger name="gr.cite" level="DEBUG" additivity="false">
<appender-ref ref="TROUBLESHOOTING"/>
<appender-ref ref="STDOUT"/>
</logger>
<logger name="org.opencdmp" level="DEBUG" additivity="false">
<appender-ref ref="TROUBLESHOOTING"/>
<appender-ref ref="STDOUT"/>
</logger>
<logger name="audit" level="INFO" additivity="false">
<appender-ref ref="AUDITING"/>
<appender-ref ref="STDOUT"/>
</logger>
<root level="info">
<appender-ref ref="TROUBLESHOOTING"/>
<appender-ref ref="STDOUT"/>
</root>
</configuration>

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB