From 5465eddb0695de65748d54b1fd13c28d6175fdb4 Mon Sep 17 00:00:00 2001 From: David Eckhard Date: Thu, 15 Aug 2024 18:12:06 +0200 Subject: [PATCH] invenio-damap: enable connection from InvenioRDM * Update re3data XML with root element * Update library usages due to library migration --------- Co-authored-by: Sotiris Tsepelakis Co-authored-by: ValentinFutterer --- pom.xml | 27 +-- .../org/damap/base/enums/EDataAccessType.java | 32 +++ .../r3data/RepositoriesRemoteResource.java | 39 ++-- .../base/r3data/RepositoriesResource.java | 10 +- .../base/r3data/RepositoriesService.java | 5 +- .../org/damap/base/rest/AccessResource.java | 1 - .../base/rest/DataManagementPlanResource.java | 12 +- .../org/damap/base/rest/FitsResource.java | 3 +- .../base/rest/InternalStorageResource.java | 4 +- .../org/damap/base/rest/OpenAireResource.java | 4 +- .../org/damap/base/rest/VersionResource.java | 4 +- .../base/rest/dmp/domain/MultipartBodyDO.java | 6 +- .../base/rest/dmp/service/DmpService.java | 16 ++ .../base/rest/fits/dto/MultipartBodyDTO.java | 8 +- .../rest/fits/service/FitsRestService.java | 3 +- .../base/rest/invenio_damap/DMPPayload.java | 13 ++ .../invenio_damap/InvenioDAMAPResource.java | 73 +++++++ .../invenio_damap/InvenioDAMAPService.java | 116 +++++++++++ .../InvenioDamapResourceMapper.java | 175 ++++++++++++++++ .../rest/openaire/OpenAireRemoteResource.java | 2 +- .../damap/base/security/SecurityService.java | 52 +++++ src/main/resources/application.yaml | 4 + .../base/spec/re3data/re3data_index_V1-0.xsd | 32 ++- .../java/org/damap/base/TestProfiles.java | 21 ++ .../InvenioDamapResourceDisabledTest.java | 21 ++ .../rest/InvenioDamapResourceEnabledTest.java | 197 ++++++++++++++++++ 26 files changed, 806 insertions(+), 74 deletions(-) create mode 100644 src/main/java/org/damap/base/rest/invenio_damap/DMPPayload.java create mode 100644 src/main/java/org/damap/base/rest/invenio_damap/InvenioDAMAPResource.java create mode 100644 src/main/java/org/damap/base/rest/invenio_damap/InvenioDAMAPService.java create mode 100644 src/main/java/org/damap/base/rest/invenio_damap/InvenioDamapResourceMapper.java create mode 100644 src/test/java/org/damap/base/TestProfiles.java create mode 100644 src/test/java/org/damap/base/rest/InvenioDamapResourceDisabledTest.java create mode 100644 src/test/java/org/damap/base/rest/InvenioDamapResourceEnabledTest.java diff --git a/pom.xml b/pom.xml index 37e940f2..f832bed3 100644 --- a/pom.xml +++ b/pom.xml @@ -94,6 +94,11 @@ quarkus-junit5 test + + io.quarkus + quarkus-junit5-component + test + io.quarkus quarkus-junit5-mockito @@ -126,36 +131,32 @@ io.quarkus - quarkus-resteasy-multipart + quarkus-rest io.quarkus - quarkus-resteasy + quarkus-rest-jackson io.quarkus - quarkus-resteasy-client + quarkus-rest-client io.quarkus - quarkus-resteasy-client-jaxb + quarkus-rest-client-jaxb io.quarkus - quarkus-resteasy-jaxb - - - io.quarkus - quarkus-resteasy-jackson + quarkus-rest-client-jackson + io.quarkus - quarkus-jaxb + quarkus-oidc - io.quarkus - quarkus-oidc + quarkus-smallrye-jwt @@ -260,7 +261,7 @@ org.codehaus.mojo jaxb2-maven-plugin - 3.1.0 + 3.2.0 xjc diff --git a/src/main/java/org/damap/base/enums/EDataAccessType.java b/src/main/java/org/damap/base/enums/EDataAccessType.java index d7ce3e86..e59516ed 100644 --- a/src/main/java/org/damap/base/enums/EDataAccessType.java +++ b/src/main/java/org/damap/base/enums/EDataAccessType.java @@ -46,4 +46,36 @@ public static EDataAccessType getByValue(String value) { MAP.put(type.getValue(), type); } } + + /** + * Compares this {@code EDataAccessType} instance with another {@code EDataAccessType} to + * determine which is more restrictive. The order of restriction is defined as: + * + *
    + *
  • {@code CLOSED} is the most restrictive. + *
  • {@code RESTRICTED} is more restrictive than {@code OPEN} but less restrictive than {@code + * CLOSED}. + *
  • {@code OPEN} is the least restrictive. + *
+ * + * @param other the other {@code EDataAccessType} to compare to; can be {@code null}. If {@code + * null}, this instance is considered more restrictive. + * @return {@code 1} if this instance is more restrictive than {@code other}, {@code -1} if this + * instance is less restrictive than {@code other}, and {@code 0} if both instances are equal. + */ + public int compare(EDataAccessType other) { + if (other == null) { + return 1; + } + + if (this == other) { + return 0; + } + + return switch (this) { + case CLOSED -> 1; + case RESTRICTED -> (other == CLOSED) ? -1 : 1; + case OPEN -> -1; + }; + } } diff --git a/src/main/java/org/damap/base/r3data/RepositoriesRemoteResource.java b/src/main/java/org/damap/base/r3data/RepositoriesRemoteResource.java index 80539ee3..73419262 100644 --- a/src/main/java/org/damap/base/r3data/RepositoriesRemoteResource.java +++ b/src/main/java/org/damap/base/r3data/RepositoriesRemoteResource.java @@ -1,14 +1,13 @@ package org.damap.base.r3data; -import generated.Repository; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import java.util.List; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; -import org.jboss.resteasy.annotations.jaxrs.PathParam; -import org.jboss.resteasy.annotations.jaxrs.QueryParam; +import org.jboss.resteasy.reactive.RestPath; +import org.jboss.resteasy.reactive.RestQuery; import org.re3data.schema._2_2.Re3Data; /** RepositoriesRemoteResource interface. */ @@ -23,7 +22,7 @@ public interface RepositoriesRemoteResource { */ @GET @Path("/v1/repositories") - List getAll(); + generated.List getAll(); /** * getById. @@ -33,7 +32,7 @@ public interface RepositoriesRemoteResource { */ @GET @Path("/v1/repository/{id}") - Re3Data getById(@PathParam String id); + Re3Data getById(@RestPath String id); /** * search. @@ -56,19 +55,19 @@ public interface RepositoriesRemoteResource { */ @GET @Path("/beta/repositories") - List search( - @QueryParam("subjects[]") List subjects, - @QueryParam("contentTypes[]") List contentTypes, - @QueryParam("countries[]") List countries, - @QueryParam("certificates[]") List certificates, - @QueryParam("pidSystems[]") List pidSystems, - @QueryParam("aidSystems[]") List aidSystems, - @QueryParam("repositoryAccess[]") List repositoryAccess, - @QueryParam("dataAccess[]") List dataAccess, - @QueryParam("dataUpload[]") List dataUpload, - @QueryParam("dataLicenses[]") List dataLicenses, - @QueryParam("repositoryTypes[]") List repositoryTypes, - @QueryParam("institutionTypes[]") List institutionTypes, - @QueryParam("versioning[]") List versioning, - @QueryParam("metadataStandards[]") List metadataStandards); + generated.List search( + @RestQuery("subjects[]") List subjects, + @RestQuery("contentTypes[]") List contentTypes, + @RestQuery("countries[]") List countries, + @RestQuery("certificates[]") List certificates, + @RestQuery("pidSystems[]") List pidSystems, + @RestQuery("aidSystems[]") List aidSystems, + @RestQuery("repositoryAccess[]") List repositoryAccess, + @RestQuery("dataAccess[]") List dataAccess, + @RestQuery("dataUpload[]") List dataUpload, + @RestQuery("dataLicenses[]") List dataLicenses, + @RestQuery("repositoryTypes[]") List repositoryTypes, + @RestQuery("institutionTypes[]") List institutionTypes, + @RestQuery("versioning[]") List versioning, + @RestQuery("metadataStandards[]") List metadataStandards); } diff --git a/src/main/java/org/damap/base/r3data/RepositoriesResource.java b/src/main/java/org/damap/base/r3data/RepositoriesResource.java index 9cb8aca3..d36817a8 100644 --- a/src/main/java/org/damap/base/r3data/RepositoriesResource.java +++ b/src/main/java/org/damap/base/r3data/RepositoriesResource.java @@ -1,6 +1,6 @@ package org.damap.base.r3data; -import generated.Repository; +import generated.List.Repository; import io.quarkus.security.Authenticated; import jakarta.inject.Inject; import jakarta.ws.rs.GET; @@ -14,7 +14,7 @@ import lombok.extern.jbosslog.JBossLog; import org.damap.base.r3data.dto.RepositoryDetails; import org.damap.base.r3data.mapper.RepositoryMapper; -import org.jboss.resteasy.annotations.jaxrs.PathParam; +import org.jboss.resteasy.reactive.RestPath; /** RepositoriesResource class. */ @Path("/api/repositories") @@ -33,7 +33,7 @@ public class RepositoriesResource { @GET public List getAll() { log.info("Get all repositories"); - return repositoriesService.getAll(); + return repositoriesService.getAll().getRepository(); } /** @@ -56,7 +56,7 @@ public List getRecommended() { */ @GET @Path("/{id}") - public RepositoryDetails getById(@PathParam String id) { + public RepositoryDetails getById(@RestPath String id) { log.info("Get repository with id: " + id); return RepositoryMapper.mapToRepositoryDetails(repositoriesService.getById(id), id); } @@ -72,6 +72,6 @@ public RepositoryDetails getById(@PathParam String id) { public List search(@Context UriInfo uriInfo) { log.info("Search repositories: " + uriInfo.getQueryParameters()); MultivaluedMap params = uriInfo.getQueryParameters(); - return repositoriesService.search(params); + return repositoriesService.search(params).getRepository(); } } diff --git a/src/main/java/org/damap/base/r3data/RepositoriesService.java b/src/main/java/org/damap/base/r3data/RepositoriesService.java index 87292f6c..7eea7ac6 100644 --- a/src/main/java/org/damap/base/r3data/RepositoriesService.java +++ b/src/main/java/org/damap/base/r3data/RepositoriesService.java @@ -1,6 +1,5 @@ package org.damap.base.r3data; -import generated.Repository; import io.quarkus.cache.CacheResult; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -32,7 +31,7 @@ public class RepositoriesService { * @return a {@link java.util.List} object */ @CacheResult(cacheName = "repositories") - public List getAll() { + public generated.List getAll() { return repositoriesRemoteResource.getAll(); } @@ -78,7 +77,7 @@ public Re3Data getById(String id) { * @param params a {@link jakarta.ws.rs.core.MultivaluedMap} object * @return a {@link java.util.List} object */ - public List search(MultivaluedMap params) { + public generated.List search(MultivaluedMap params) { List subjects = params.get("subjects"); List contentTypes = params.get("contentTypes"); List certificates = params.get("certificates"); diff --git a/src/main/java/org/damap/base/rest/AccessResource.java b/src/main/java/org/damap/base/rest/AccessResource.java index 47f0d167..445f48af 100644 --- a/src/main/java/org/damap/base/rest/AccessResource.java +++ b/src/main/java/org/damap/base/rest/AccessResource.java @@ -11,7 +11,6 @@ import org.damap.base.rest.access.service.AccessService; import org.damap.base.rest.dmp.domain.ContributorDO; import org.damap.base.validation.AccessValidator; -import org.jboss.resteasy.annotations.jaxrs.PathParam; /** AccessResource class. */ @Path("/api/access") diff --git a/src/main/java/org/damap/base/rest/DataManagementPlanResource.java b/src/main/java/org/damap/base/rest/DataManagementPlanResource.java index f5ca7b0a..27987aac 100644 --- a/src/main/java/org/damap/base/rest/DataManagementPlanResource.java +++ b/src/main/java/org/damap/base/rest/DataManagementPlanResource.java @@ -15,7 +15,7 @@ import org.damap.base.rest.dmp.service.DmpService; import org.damap.base.security.SecurityService; import org.damap.base.validation.AccessValidator; -import org.jboss.resteasy.annotations.jaxrs.PathParam; +import org.jboss.resteasy.reactive.RestPath; /** DataManagementPlanResource class. */ @Path("/api/dmps") @@ -52,7 +52,7 @@ public List getAll() { /*@GET @Path("/person/{personId}") @RolesAllowed("Damap Admin") - public List getDmpListByPerson(@PathParam String personId) { + public List getDmpListByPerson(@RestPath String personId) { log.info("Return dmp for person id: " + personId); return dmpService.getDmpListByPersonId(personId); }*/ @@ -92,7 +92,7 @@ public List getDmpsSubordinates() { */ @GET @Path("/{id}") - public DmpDO getDmpById(@PathParam String id) { + public DmpDO getDmpById(@RestPath String id) { log.info("Return dmp with id: " + id); String personId = this.getPersonId(); long dmpId = Long.parseLong(id); @@ -126,7 +126,7 @@ public DmpDO saveDmp(@Valid DmpDO dmpDO) { @PUT @Path("/{id}") @Consumes(MediaType.APPLICATION_JSON) - public DmpDO updateDmp(@PathParam String id, @Valid DmpDO dmpDO) { + public DmpDO updateDmp(@RestPath String id, @Valid DmpDO dmpDO) { log.info("Update dmp with id: " + id); String personId = this.getPersonId(); long dmpId = Long.parseLong(id); @@ -143,7 +143,7 @@ public DmpDO updateDmp(@PathParam String id, @Valid DmpDO dmpDO) { */ @DELETE @Path("/{id}") - public void deleteDmp(@PathParam String id) { + public void deleteDmp(@RestPath String id) { log.info("Delete dmp with id: " + id); String personId = this.getPersonId(); long dmpId = Long.parseLong(id); @@ -169,7 +169,7 @@ private String getPersonId() { */ @GET @Path("/{id}/{revision}") - public DmpDO getDmpByIdAndRevision(@PathParam String id, @PathParam long revision) { + public DmpDO getDmpByIdAndRevision(@RestPath String id, @RestPath long revision) { log.info("Return dmp with id: " + id + " and revision number: " + revision); String personId = this.getPersonId(); long dmpId = Long.parseLong(id); diff --git a/src/main/java/org/damap/base/rest/FitsResource.java b/src/main/java/org/damap/base/rest/FitsResource.java index 666cbda4..6beb0f9b 100644 --- a/src/main/java/org/damap/base/rest/FitsResource.java +++ b/src/main/java/org/damap/base/rest/FitsResource.java @@ -9,7 +9,6 @@ import org.damap.base.rest.dmp.domain.MultipartBodyDO; import org.damap.base.rest.dmp.mapper.DatasetDOMapper; import org.damap.base.rest.fits.service.FitsService; -import org.jboss.resteasy.annotations.providers.multipart.MultipartForm; /** FitsResource class. */ @Path("/api/fits") @@ -29,7 +28,7 @@ public class FitsResource { */ @POST @Path("/examine") - public DatasetDO examine(@MultipartForm MultipartBodyDO data) { + public DatasetDO examine(MultipartBodyDO data) { log.info("Analyse file"); return DatasetDOMapper.mapEntityToDO(fitsService.analyseFile(data), new DatasetDO()); } diff --git a/src/main/java/org/damap/base/rest/InternalStorageResource.java b/src/main/java/org/damap/base/rest/InternalStorageResource.java index 3a34ebc5..704395ff 100644 --- a/src/main/java/org/damap/base/rest/InternalStorageResource.java +++ b/src/main/java/org/damap/base/rest/InternalStorageResource.java @@ -10,7 +10,7 @@ import lombok.extern.jbosslog.JBossLog; import org.damap.base.rest.storage.InternalStorageDO; import org.damap.base.rest.storage.InternalStorageService; -import org.jboss.resteasy.annotations.jaxrs.PathParam; +import org.jboss.resteasy.reactive.RestPath; /** InternalStorageResource class. */ @Path("/api/storages") @@ -29,7 +29,7 @@ public class InternalStorageResource { */ @GET @Path("/{languageCode}") - public List getAllByLanguage(@PathParam String languageCode) { + public List getAllByLanguage(@RestPath String languageCode) { log.debug("Return all internal storage options for language " + languageCode); return internalStorageService.getAllByLanguage(languageCode); } diff --git a/src/main/java/org/damap/base/rest/OpenAireResource.java b/src/main/java/org/damap/base/rest/OpenAireResource.java index 52a1afdf..fd00ae72 100644 --- a/src/main/java/org/damap/base/rest/OpenAireResource.java +++ b/src/main/java/org/damap/base/rest/OpenAireResource.java @@ -10,7 +10,7 @@ import org.damap.base.rest.dmp.domain.DatasetDO; import org.damap.base.rest.openaire.mapper.OpenAireMapper; import org.damap.base.rest.openaire.service.OpenAireService; -import org.jboss.resteasy.annotations.jaxrs.QueryParam; +import org.jboss.resteasy.reactive.RestQuery; /** OpenAireResource class. */ @Path("/api/openaire") @@ -28,7 +28,7 @@ public class OpenAireResource { * @return a {@link org.damap.base.rest.dmp.domain.DatasetDO} object */ @GET - public DatasetDO search(@QueryParam String doi) { + public DatasetDO search(@RestQuery String doi) { log.info("Search for dataset with DOI: " + doi); return OpenAireMapper.mapAtoB(doi, openAireService.search(doi), new DatasetDO()); } diff --git a/src/main/java/org/damap/base/rest/VersionResource.java b/src/main/java/org/damap/base/rest/VersionResource.java index a9c7c9d8..a1565afd 100644 --- a/src/main/java/org/damap/base/rest/VersionResource.java +++ b/src/main/java/org/damap/base/rest/VersionResource.java @@ -12,7 +12,7 @@ import org.damap.base.rest.version.VersionService; import org.damap.base.security.SecurityService; import org.damap.base.validation.AccessValidator; -import org.jboss.resteasy.annotations.jaxrs.PathParam; +import org.jboss.resteasy.reactive.RestPath; /** VersionResource class. */ @Path("/api/versions") @@ -35,7 +35,7 @@ public class VersionResource { */ @GET @Path("/list/{id}") - public List getDmpVersions(@PathParam String id) { + public List getDmpVersions(@RestPath String id) { log.debug("Return dmp versions for dmp with id: " + id); String personId = this.getPersonId(); long dmpId = Long.parseLong(id); diff --git a/src/main/java/org/damap/base/rest/dmp/domain/MultipartBodyDO.java b/src/main/java/org/damap/base/rest/dmp/domain/MultipartBodyDO.java index 5fb824db..6444940d 100644 --- a/src/main/java/org/damap/base/rest/dmp/domain/MultipartBodyDO.java +++ b/src/main/java/org/damap/base/rest/dmp/domain/MultipartBodyDO.java @@ -2,13 +2,13 @@ import jakarta.ws.rs.FormParam; import jakarta.ws.rs.core.MediaType; -import java.io.InputStream; -import org.jboss.resteasy.annotations.providers.multipart.PartType; +import java.io.File; +import org.jboss.resteasy.reactive.PartType; /** MultipartBodyDO class. */ public class MultipartBodyDO { @FormParam("file") @PartType(MediaType.APPLICATION_OCTET_STREAM) - public InputStream file; + public File file; } diff --git a/src/main/java/org/damap/base/rest/dmp/service/DmpService.java b/src/main/java/org/damap/base/rest/dmp/service/DmpService.java index a1bde729..3d015667 100644 --- a/src/main/java/org/damap/base/rest/dmp/service/DmpService.java +++ b/src/main/java/org/damap/base/rest/dmp/service/DmpService.java @@ -99,6 +99,22 @@ public DmpDO getDmpById(long dmpId) { return DmpDOMapper.mapEntityToDO(dmpRepo.findById(dmpId), new DmpDO()); } + /** + * getDmpDOListByPersonId. + * + * @param personId a {@link java.lang.String} object + * @return a {@link java.util.List} object + */ + @Transactional + public List getDmpDOListByPersonId(String personId) { + List accessList = accessRepo.getAllDmpByUniversityId(personId); + + List dmpDOS = new ArrayList<>(); + accessList.forEach( + access -> dmpDOS.add(DmpDOMapper.mapEntityToDO(access.getDmp(), new DmpDO()))); + return dmpDOS; + } + /** * create. * diff --git a/src/main/java/org/damap/base/rest/fits/dto/MultipartBodyDTO.java b/src/main/java/org/damap/base/rest/fits/dto/MultipartBodyDTO.java index af2aaf01..7beaf0a2 100644 --- a/src/main/java/org/damap/base/rest/fits/dto/MultipartBodyDTO.java +++ b/src/main/java/org/damap/base/rest/fits/dto/MultipartBodyDTO.java @@ -2,9 +2,9 @@ import jakarta.ws.rs.FormParam; import jakarta.ws.rs.core.MediaType; -import java.io.InputStream; -import org.jboss.resteasy.annotations.providers.multipart.PartFilename; -import org.jboss.resteasy.annotations.providers.multipart.PartType; +import java.io.File; +import org.jboss.resteasy.reactive.PartFilename; +import org.jboss.resteasy.reactive.PartType; /** MultipartBodyDTO class. */ public class MultipartBodyDTO { @@ -13,5 +13,5 @@ public class MultipartBodyDTO { @PartType(MediaType.APPLICATION_OCTET_STREAM) @PartFilename( "filename") // Adds filename to Content-Disposition header, because request fails without one - public InputStream file; + public File file; } diff --git a/src/main/java/org/damap/base/rest/fits/service/FitsRestService.java b/src/main/java/org/damap/base/rest/fits/service/FitsRestService.java index 68b7bf4e..4db83820 100644 --- a/src/main/java/org/damap/base/rest/fits/service/FitsRestService.java +++ b/src/main/java/org/damap/base/rest/fits/service/FitsRestService.java @@ -8,7 +8,6 @@ import jakarta.ws.rs.core.MediaType; import org.damap.base.rest.fits.dto.MultipartBodyDTO; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; -import org.jboss.resteasy.annotations.providers.multipart.MultipartForm; /** FitsRestService interface. */ @RegisterRestClient(configKey = "rest.fits") @@ -24,5 +23,5 @@ public interface FitsRestService { */ @POST @Path("/examine") - Fits analyseFile(@MultipartForm MultipartBodyDTO datafile); + Fits analyseFile(MultipartBodyDTO datafile); } diff --git a/src/main/java/org/damap/base/rest/invenio_damap/DMPPayload.java b/src/main/java/org/damap/base/rest/invenio_damap/DMPPayload.java new file mode 100644 index 00000000..160292d8 --- /dev/null +++ b/src/main/java/org/damap/base/rest/invenio_damap/DMPPayload.java @@ -0,0 +1,13 @@ +package org.damap.base.rest.invenio_damap; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import org.damap.base.rest.madmp.dto.Dataset; + +@Data +public class DMPPayload { + @JsonProperty("dmp_id") + private long dmpId; + + private Dataset dataset; +} diff --git a/src/main/java/org/damap/base/rest/invenio_damap/InvenioDAMAPResource.java b/src/main/java/org/damap/base/rest/invenio_damap/InvenioDAMAPResource.java new file mode 100644 index 00000000..0184218c --- /dev/null +++ b/src/main/java/org/damap/base/rest/invenio_damap/InvenioDAMAPResource.java @@ -0,0 +1,73 @@ +package org.damap.base.rest.invenio_damap; + +import io.quarkus.resteasy.reactive.server.EndpointDisabled; +import io.quarkus.security.ForbiddenException; +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import java.util.AbstractMap.SimpleEntry; +import java.util.List; +import lombok.extern.jbosslog.JBossLog; +import org.damap.base.rest.dmp.domain.DmpDO; +import org.damap.base.rest.madmp.dto.Dataset; +import org.damap.base.security.SecurityService; +import org.damap.base.validation.AccessValidator; + +@Path("/api/madmps") +@RequestScoped +@Produces(MediaType.APPLICATION_JSON) +@JBossLog +@EndpointDisabled(name = "invenio.disabled", stringValue = "true") +public class InvenioDAMAPResource { + + @Inject + public InvenioDAMAPResource( + AccessValidator accessValidator, + InvenioDAMAPService invenioDAMAPService, + SecurityService securityService) { + this.accessValidator = accessValidator; + this.invenioDAMAPService = invenioDAMAPService; + this.securityService = securityService; + } + + AccessValidator accessValidator; + InvenioDAMAPService invenioDAMAPService; + SecurityService securityService; + + @GET + public List getDmpListByPerson(@Context HttpHeaders headers) { + SimpleEntry, String> result = + invenioDAMAPService.resolveDmpsAndIds(securityService.checkIfUserIsAuthorized(headers)); + return result.getKey(); + } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + public DmpDO addDataSetToDmp(@Context HttpHeaders headers, DMPPayload payload) { + SimpleEntry, String> result = + invenioDAMAPService.resolveDmpsAndIds(securityService.checkIfUserIsAuthorized(headers)); + + long dmpId = payload.getDmpId(); + log.info("Add dataset to dmp with id: " + dmpId); + Dataset dataset = payload.getDataset(); + + if (result.getKey().stream().noneMatch(dmpDO -> dmpDO.getId().equals(dmpId))) { + throw new NotFoundException("DMP with id " + dmpId + " could not be found."); + } + + String personId = result.getValue(); + // Assuming all IDs provided fetch the same DMPs, check permissions. + if (!accessValidator.canEditDmp(dmpId, personId)) + throw new ForbiddenException( + "Person " + personId + "Not authorized to access dmp with id " + dmpId); + return invenioDAMAPService.addDataSetToDMP(dmpId, dataset); + } +} diff --git a/src/main/java/org/damap/base/rest/invenio_damap/InvenioDAMAPService.java b/src/main/java/org/damap/base/rest/invenio_damap/InvenioDAMAPService.java new file mode 100644 index 00000000..8f794a6a --- /dev/null +++ b/src/main/java/org/damap/base/rest/invenio_damap/InvenioDAMAPService.java @@ -0,0 +1,116 @@ +package org.damap.base.rest.invenio_damap; + +import io.quarkus.security.UnauthorizedException; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.json.JsonObject; +import jakarta.transaction.Transactional; +import java.text.MessageFormat; +import java.util.AbstractMap.SimpleEntry; +import java.util.Date; +import java.util.List; +import org.damap.base.rest.dmp.domain.DatasetDO; +import org.damap.base.rest.dmp.domain.DmpDO; +import org.damap.base.rest.dmp.service.DmpService; +import org.damap.base.rest.madmp.dto.Dataset; +import org.damap.base.rest.version.VersionDO; +import org.damap.base.rest.version.VersionService; +import org.eclipse.microprofile.jwt.JsonWebToken; + +@ApplicationScoped +public class InvenioDAMAPService { + + protected DmpService dmpService; + protected VersionService versionService; + + @Inject + InvenioDAMAPService(DmpService dmpService, VersionService versionService) { + this.dmpService = dmpService; + this.versionService = versionService; + } + + // Resolves user identity based on the DMPs and returns dmp list and list of + // identifiers if successful. + public SimpleEntry, String> resolveDmpsAndIds(JsonWebToken jwt) { + JsonObject invenioDamapClaim = jwt.getClaim("invenio-damap"); + if (invenioDamapClaim == null) { + throw new UnauthorizedException("Missing invenio-damap claim in jwt."); + } + + JsonObject identifiers = invenioDamapClaim.getJsonObject("identifiers"); + if (identifiers == null || identifiers.isEmpty()) { + throw new UnauthorizedException("No valid authentication schema was provided."); + } + + List personDmpList = null; + String matchingIdentifier = null; + + for (String key : identifiers.keySet()) { + String identifier = identifiers.getString(key); + List dmps = dmpService.getDmpDOListByPersonId(identifier); + + // Check if returned DMPs list is empty. + // If yes, no resolving can take place, move to the next identifier. + if (dmps.isEmpty()) continue; + + // If a second identifier matches, something went wrong + if (matchingIdentifier == null) { + matchingIdentifier = identifier; + personDmpList = dmps; + } else { + throw new UnauthorizedException("Mismatch in resolved identities."); + } + } + + // Check if identifier is null. This means that no resolving was possible. + if (matchingIdentifier == null) + throw new UnauthorizedException("Identities could not be resolved."); + + return new SimpleEntry<>(personDmpList, matchingIdentifier); + } + + @Transactional + public DmpDO addDataSetToDMP(long dmpId, Dataset dataset) { + + DmpDO dmpDO = dmpService.getDmpById(dmpId); + var datasetDO = + dmpDO.getDatasets().stream() + .filter( + ds -> { + var localIdentifier = ds.getDatasetId(); + var externalIdentifier = dataset.getDatasetId(); + + if (localIdentifier == null || externalIdentifier == null) { + return false; + } + + return localIdentifier.getIdentifier() != null + && externalIdentifier.getIdentifier() != null + && localIdentifier.getType() != null + && externalIdentifier.getType() != null + && localIdentifier.getIdentifier().equals(externalIdentifier.getIdentifier()) + && localIdentifier + .getType() + .toString() + .equalsIgnoreCase(externalIdentifier.getType().name()); + }) + .findFirst() + .orElse(null); + + if (datasetDO == null) { + datasetDO = new DatasetDO(); + dmpDO.getDatasets().add(datasetDO); + } + InvenioDamapResourceMapper.mapMaDMPDatasetToDatasetDO(dataset, datasetDO, dmpDO); + dmpDO = dmpService.update(dmpDO); + + VersionDO version = new VersionDO(); + version.setDmpId(dmpId); + version.setVersionName( + MessageFormat.format("Added dataset `{0}` from remote datasource", dataset.getTitle())); + version.setVersionDate(new Date()); + versionService.create(version); + + return dmpDO; + } +} diff --git a/src/main/java/org/damap/base/rest/invenio_damap/InvenioDamapResourceMapper.java b/src/main/java/org/damap/base/rest/invenio_damap/InvenioDamapResourceMapper.java new file mode 100644 index 00000000..90896332 --- /dev/null +++ b/src/main/java/org/damap/base/rest/invenio_damap/InvenioDamapResourceMapper.java @@ -0,0 +1,175 @@ +package org.damap.base.rest.invenio_damap; + +import java.util.*; +import lombok.experimental.UtilityClass; +import lombok.extern.jbosslog.JBossLog; +import org.damap.base.enums.EAccessRight; +import org.damap.base.enums.EDataAccessType; +import org.damap.base.enums.EDataKind; +import org.damap.base.enums.EDataSource; +import org.damap.base.enums.EDataType; +import org.damap.base.enums.EIdentifierType; +import org.damap.base.enums.ELicense; +import org.damap.base.rest.dmp.domain.DatasetDO; +import org.damap.base.rest.dmp.domain.DmpDO; +import org.damap.base.rest.dmp.domain.ExternalStorageDO; +import org.damap.base.rest.dmp.domain.IdentifierDO; +import org.damap.base.rest.madmp.dto.Dataset; +import org.damap.base.rest.madmp.dto.Distribution; +import org.damap.base.rest.madmp.dto.Host; + +@JBossLog +@UtilityClass +public class InvenioDamapResourceMapper { + public DatasetDO mapMaDMPDatasetToDatasetDO( + Dataset madmpDataset, DatasetDO datasetDO, DmpDO dmpDO) { + + // Disclaimer: This is by no means complete. Not all fields of the + // Dataset or DMP are set. Null value checks should also be performed. + var datasetId = madmpDataset.getDatasetId(); + if (datasetId != null) { + IdentifierDO newId = new IdentifierDO(); + newId.setIdentifier(datasetId.getIdentifier()); + newId.setType(EIdentifierType.valueOf(datasetId.getType().name().toUpperCase())); + datasetDO.setDatasetId(newId); + } + + if (datasetDO.getReferenceHash() != null) { + datasetDO.setReferenceHash("invenio" + new Date()); + } + + datasetDO.setDateOfDeletion(null); + datasetDO.setDelete(false); + datasetDO.setDeletionPerson(null); + + if (madmpDataset.getDescription() != null && !madmpDataset.getDescription().isEmpty()) { + datasetDO.setDescription(madmpDataset.getDescription()); + } + + mapDistribution(madmpDataset, datasetDO, dmpDO); + + if (datasetDO.getOtherProjectMembersAccess() == null) { + datasetDO.setOtherProjectMembersAccess(EAccessRight.READ); + } + + if (madmpDataset.getPersonalData() != null || datasetDO.getPersonalData() == null) { + Boolean personalData = + switch (Objects.requireNonNullElse( + madmpDataset.getPersonalData(), Dataset.PersonalData.UNKNOWN)) { + case NO -> false; + default -> true; + }; + datasetDO.setPersonalData(personalData); + dmpDO.setPersonalData(dmpDO.getPersonalData() || personalData); + } + + if (datasetDO.getPublicAccess() == null) { + datasetDO.setPublicAccess(EAccessRight.READ); + } + if (datasetDO.getSelectedProjectMembersAccess() == null) { + datasetDO.setSelectedProjectMembersAccess(EAccessRight.READ); + } + + if (madmpDataset.getSensitiveData() != null || datasetDO.getSensitiveData() == null) { + Boolean sensitiveData = + switch (Objects.requireNonNullElse( + madmpDataset.getSensitiveData(), Dataset.SensitiveData.UNKNOWN)) { + case NO -> false; + default -> true; + }; + datasetDO.setSensitiveData(sensitiveData); + dmpDO.setSensitiveData(dmpDO.getSensitiveData() || sensitiveData); + } + + // TODO: Let user decide? + if (datasetDO.getSource() == null) { + datasetDO.setSource(EDataSource.NEW); + // This should match the dataset. If new, setDataKind. else setReusedDataKind + dmpDO.setDataKind(EDataKind.SPECIFY); + dmpDO.setReusedDataKind(EDataKind.SPECIFY); + } + + if (madmpDataset.getTitle() != null) { + datasetDO.setTitle(madmpDataset.getTitle()); + } + + if (madmpDataset.getType() != null) { + // Setting data type + EDataType type = EDataType.getByValue(madmpDataset.getType()); + if (type == null) { + log.info("Could not infer EDataType from provided value: " + madmpDataset.getType()); + } else { + var types = Objects.requireNonNullElse(datasetDO.getType(), new ArrayList()); + if (!types.contains(type)) { + types.add(type); + } + datasetDO.setType(types); + } + } + + return datasetDO; + } + + private static void mapDistribution(Dataset madmpDataset, DatasetDO datasetDO, DmpDO dmpDO) { + if (madmpDataset.getDistribution() == null) { + return; + } + + Set licenses = new HashSet<>(); + for (Distribution d : madmpDataset.getDistribution()) { + if (d.getDataAccess() != null) { + var dataAccess = EDataAccessType.getByValue(d.getDataAccess().value()); + if (dataAccess != null && dataAccess.compare(datasetDO.getDataAccess()) == 1) { + datasetDO.setDataAccess(dataAccess); + } + } + licenses.addAll(d.getLicense().stream().map(l -> l.getLicenseRef().toString()).toList()); + + if (d.getByteSize() != null + && d.getByteSize() > Objects.requireNonNullElse(datasetDO.getSize(), 0L)) { + datasetDO.setSize(d.getByteSize().longValue()); + } + + if (d.getHost() == null) { + // Nothing more to do + continue; + } + + Host host = d.getHost(); + String hostPath = host.getUrl() == null ? null : host.getUrl().getPath(); + + // Check if the provided storage is already set on the DMP. + var externalStorages = dmpDO.getExternalStorage(); + ExternalStorageDO externalStorageDO = + externalStorages.stream() + .filter(s -> s.getUrl() != null && s.getUrl().equals(hostPath)) + .findFirst() + .orElse(null); + + if (externalStorageDO == null) { + externalStorageDO = new ExternalStorageDO(); + externalStorageDO.setBackupFrequency(host.getBackupFrequency()); + externalStorageDO.setStorageLocation( + host.getGeoLocation() != null ? host.getGeoLocation().toString() : null); + externalStorageDO.setTitle(host.getTitle()); + externalStorageDO.setUrl(hostPath); + externalStorages.add(externalStorageDO); + } + + var datasetHashes = externalStorageDO.getDatasets(); + if (datasetHashes.contains(datasetDO.getReferenceHash())) { + datasetHashes.add(datasetDO.getReferenceHash()); + } + } + + // TODO: Support multiple licenses + for (String license : licenses) { + for (ELicense eLicense : ELicense.values()) { + if (license.equals(eLicense.getUrl())) { + datasetDO.setLicense(eLicense); + break; + } + } + } + } +} diff --git a/src/main/java/org/damap/base/rest/openaire/OpenAireRemoteResource.java b/src/main/java/org/damap/base/rest/openaire/OpenAireRemoteResource.java index 6a07ac75..529f145b 100644 --- a/src/main/java/org/damap/base/rest/openaire/OpenAireRemoteResource.java +++ b/src/main/java/org/damap/base/rest/openaire/OpenAireRemoteResource.java @@ -4,9 +4,9 @@ import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; -import org.jboss.resteasy.annotations.jaxrs.QueryParam; /** OpenAireRemoteResource interface. */ @RegisterRestClient(configKey = "rest.openaire") diff --git a/src/main/java/org/damap/base/security/SecurityService.java b/src/main/java/org/damap/base/security/SecurityService.java index b95148ed..fcf7c57e 100644 --- a/src/main/java/org/damap/base/security/SecurityService.java +++ b/src/main/java/org/damap/base/security/SecurityService.java @@ -1,25 +1,37 @@ package org.damap.base.security; +import io.quarkus.arc.DefaultBean; import io.quarkus.arc.Unremovable; import io.quarkus.oidc.runtime.OidcJwtCallerPrincipal; +import io.quarkus.security.UnauthorizedException; import io.quarkus.security.identity.SecurityIdentity; +import io.smallrye.jwt.auth.principal.JWTParser; +import io.smallrye.jwt.auth.principal.ParseException; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import jakarta.ws.rs.core.HttpHeaders; import java.security.Principal; import lombok.extern.jbosslog.JBossLog; import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.jwt.JsonWebToken; /** SecurityService class. */ @JBossLog @Unremovable // @UnlessBuildProfile("test") @ApplicationScoped +@DefaultBean public class SecurityService { @Inject SecurityIdentity securityIdentity; @ConfigProperty(name = "damap.auth.user") String authUser; + @ConfigProperty(name = "invenio.shared-secret") + String sharedSecret; + + @Inject JWTParser parser; + /** * getUserId. * @@ -51,4 +63,44 @@ public String getUserName() { public boolean isAdmin() { return securityIdentity.hasRole("Damap Admin"); } + + /** + * Validates a JWT token from the X-Auth header. + * + * @param headers HttpHeaders containing the Authorization token. + * @return JsonWebToken if valid, null otherwise. + */ + public JsonWebToken validateAuthHeader(HttpHeaders headers) { + String jwtToken = headers.getHeaderString("X-Auth"); + + if (jwtToken != null && !jwtToken.isEmpty()) { + try { + JsonWebToken jwt = parser.verify(jwtToken, sharedSecret); + + long exp = jwt.getExpirationTime(); + long currentTime = System.currentTimeMillis() / 1000; + + if (currentTime >= exp) throw new UnauthorizedException("Token expired."); + + return jwt; + } catch (ParseException e) { + log.error("Failed to parse JWT: ", e); + return null; + } + } + return null; // No Authorization header or not in the correct format + } + + /** + * Checks if the user is authorized based on the JWT token. + * + * @param headers HttpHeaders containing the Authorization token. + * @return JsonWebToken if the user is authorized. + */ + public JsonWebToken checkIfUserIsAuthorized(HttpHeaders headers) { + JsonWebToken jwt = validateAuthHeader(headers); + if (jwt == null) throw new UnauthorizedException("User unauthorized."); + + return jwt; + } } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index aeabfc20..ca37fc9e 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -32,6 +32,10 @@ damap: query-value: 'ORCID' class-name: 'org.damap.base.rest.persons.orcid.ORCIDPersonServiceImpl' +invenio: + disabled: true + shared-secret: "thisIsAVerySecretKeyOfAtLeast32Chars" + # general config settings quarkus: http: diff --git a/src/main/resources/org/damap/base/spec/re3data/re3data_index_V1-0.xsd b/src/main/resources/org/damap/base/spec/re3data/re3data_index_V1-0.xsd index 7a41d4a4..a49f37ce 100644 --- a/src/main/resources/org/damap/base/spec/re3data/re3data_index_V1-0.xsd +++ b/src/main/resources/org/damap/base/spec/re3data/re3data_index_V1-0.xsd @@ -1,11 +1,27 @@ - - - + + + + + + - - + + + + + + + + + + + + + - - + + + + + - diff --git a/src/test/java/org/damap/base/TestProfiles.java b/src/test/java/org/damap/base/TestProfiles.java new file mode 100644 index 00000000..2d58c6b5 --- /dev/null +++ b/src/test/java/org/damap/base/TestProfiles.java @@ -0,0 +1,21 @@ +package org.damap.base; + +import io.quarkus.test.junit.QuarkusTestProfile; +import java.util.Collections; +import java.util.Map; + +public class TestProfiles { + public static class InvenioDAMAPEnabledProfile implements QuarkusTestProfile { + + /** + * Returns additional config to be applied to the test. This will override any existing config + * (including in application.(properties,yaml), however existing config will be merged with this + * (i.e. application.(properties,yaml) config will still take effect, unless a specific config + * key has been overridden). + */ + @Override + public Map getConfigOverrides() { + return Collections.singletonMap("invenio.disabled", "false"); + } + } +} diff --git a/src/test/java/org/damap/base/rest/InvenioDamapResourceDisabledTest.java b/src/test/java/org/damap/base/rest/InvenioDamapResourceDisabledTest.java new file mode 100644 index 00000000..02eadc50 --- /dev/null +++ b/src/test/java/org/damap/base/rest/InvenioDamapResourceDisabledTest.java @@ -0,0 +1,21 @@ +package org.damap.base.rest; + +import static io.restassured.RestAssured.given; + +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; +import org.damap.base.rest.invenio_damap.InvenioDAMAPResource; +import org.junit.jupiter.api.Test; + +@QuarkusTest +@TestHTTPEndpoint(InvenioDAMAPResource.class) +class InvenioDamapResourceDisabledTest { + @Test + void givenConfigNotEnabled_thenNoEndpointsFound() { + // GET DMPs endpoint + given().when().get().then().statusCode(404); + + // POST dataset endpoint + given().when().post().then().statusCode(404); + } +} diff --git a/src/test/java/org/damap/base/rest/InvenioDamapResourceEnabledTest.java b/src/test/java/org/damap/base/rest/InvenioDamapResourceEnabledTest.java new file mode 100644 index 00000000..f9d7ed9b --- /dev/null +++ b/src/test/java/org/damap/base/rest/InvenioDamapResourceEnabledTest.java @@ -0,0 +1,197 @@ +package org.damap.base.rest; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.not; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.TestProfile; +import io.restassured.http.Header; +import io.smallrye.jwt.build.Jwt; +import io.smallrye.jwt.build.JwtClaimsBuilder; +import jakarta.inject.Inject; +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.ws.rs.core.MediaType; +import java.util.List; +import org.damap.base.TestProfiles; +import org.damap.base.domain.Dmp; +import org.damap.base.repo.DmpRepo; +import org.damap.base.rest.dmp.domain.DmpDO; +import org.damap.base.rest.dmp.service.DmpService; +import org.damap.base.rest.invenio_damap.DMPPayload; +import org.damap.base.rest.invenio_damap.InvenioDAMAPResource; +import org.damap.base.rest.madmp.dto.Dataset; +import org.damap.base.util.TestDOFactory; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +// NOTE: +// We could put tests for the same resource into one file and annotate sub classes with @Nested. +// However, for some reason, this is really slow (3 Tests took 60 seconds). + +@QuarkusTest +@TestProfile(TestProfiles.InvenioDAMAPEnabledProfile.class) +@TestHTTPEndpoint(InvenioDAMAPResource.class) +class InvenioDamapResourceEnabledTest { + + @ConfigProperty(name = "invenio.shared-secret") + String sharedSecret; + + @Inject TestDOFactory testDOFactory; + + @Inject DmpRepo dmpRepo; + + @Inject DmpService dmpService; + + @BeforeEach + public void cleanup() { + List dmps = dmpRepo.getAll(); + for (Dmp dmp : dmps) dmpService.delete(dmp.id); + } + + private JsonObject createInvenioDamapClaim(String userId) { + return Json.createObjectBuilder() + .add("identifiers", Json.createObjectBuilder().add("personID", userId)) + .build(); + } + + private String generateJwt(long expiresIn) { + long currentTime = System.currentTimeMillis() / 1000; + long exp = currentTime + expiresIn; + String userId = getUserId(); + JwtClaimsBuilder claimsBuilder = Jwt.claims(); + + claimsBuilder + .subject(userId) + .expiresAt(exp) + .issuedAt(currentTime) + .claim("invenio-damap", createInvenioDamapClaim(userId)); + + return claimsBuilder.signWithSecret(sharedSecret); + } + + private String getUserId() { + return "012345"; + } + + @Test + void getDmpsForPersonThenUnauthorized() { + // case where no header is passed + given().when().get().then().statusCode(401); + + // case where empty value is included in the header + given().when().header("X-Auth", "").get().then().statusCode(401); + + // case where wrong token is included in the header + given().when().header("X-Auth", "wrong-credentials").get().then().statusCode(401); + + // case where token has expired + String jwtToken = generateJwt(0); + given().when().header("X-Auth", jwtToken).get().then().statusCode(401); + } + + @Test + void getDmpsForPersonThenValid() { + Header authHeader = new Header("X-Auth", generateJwt(600)); + + // case where user has no DMPs (which means identity is not present in the + // system or identity is invalid) + given().when().header(authHeader).get().then().statusCode(401); + + // case with DMP created + DmpDO dmpDO = testDOFactory.createDmp("invenio-damap", true); + given() + .when() + .header(authHeader) + .get() + .then() + .statusCode(200) + .body("", hasSize(1)) + .body("[0].title", equalTo(dmpDO.getTitle())); + } + + @Test + void addDataSetToDmpThenValid() throws JsonProcessingException { + Header authHeader = new Header("X-Auth", generateJwt(600)); + String datasetData = + """ + { + "dataset_id" : { + "type": "doi", + "identifier": "repository/123.456" + }, + "description" : "Dataset description", + "distribution" : [ { + "access_url" : "https://repository.tugraz.at/", + "byte_size" : 705032704, + "data_access" : "open", + "description" : "Distribution description", + "format" : [ "Standard office documents", "Images", "Raw data", "Scientific and statistical data formats", "Plain text" ], + "host" : { + "description" : "An institutional repository at Graz University of Technology to enable storing, sharing and publishing research data, publications and open educational resources. It provides open access services and follows the FAIR principles.", + "pid_system" : [ "doi" ], + "storage_type" : "institutional", + "support_versioning" : "yes", + "title" : "TU Graz Repository", + "url" : "https://repository.tugraz.at/" + }, + "license" : [ { + "license_ref" : "https://creativecommons.org/licenses/by-nc-sa/4.0/" + } ], + "title" : "Host title" + } ], + "personal_data" : "no", + "security_and_privacy" : [ ], + "sensitive_data" : "no", + "title" : "Dataset title", + "type" : "Standard office documents, Images, Raw data, Scientific and statistical data formats, Plain text" + }"""; + + DmpDO dmpDO = testDOFactory.createDmp("invenio-damap", true); + Dataset madmpDataset = (new ObjectMapper()).readValue(datasetData, Dataset.class); + + DMPPayload payload1 = new DMPPayload(); + payload1.setDmpId(dmpDO.getId()); + payload1.setDataset(madmpDataset); + + // case with dataset creation + given() + .when() + .contentType(MediaType.APPLICATION_JSON) + .header(authHeader) + .body(payload1) + .post() + .then() + .statusCode(200) + .body("", not(empty())) + .body("datasets", hasSize(3)) + .body("datasets[2].title", equalTo(madmpDataset.getTitle())); + + Dataset minimalDataset = new Dataset(); + minimalDataset.setTitle("minimal"); + + DMPPayload payload2 = new DMPPayload(); + payload2.setDmpId(dmpDO.getId()); + payload2.setDataset(minimalDataset); + + // case with minimal dataset creation + given() + .when() + .contentType(MediaType.APPLICATION_JSON) + .header(authHeader) + .body(payload2) + .post() + .then() + .statusCode(200) + .body("", not(empty())) + .body("datasets", hasSize(4)) + .body("datasets[3].title", equalTo(minimalDataset.getTitle())); + } +}