From 079e6004c85f6903fa1129d7be8406c45176ff02 Mon Sep 17 00:00:00 2001 From: southeo Date: Wed, 3 Jul 2024 02:05:07 +0200 Subject: [PATCH 01/15] Add mongo layer --- .gitignore | 2 +- pom.xml | 15 + .../configuration/AppConfig.java | 12 +- .../configuration/InstantDeserializer.java | 30 + .../configuration/InstantSerializer.java | 30 + .../configuration/MongoConfig.java | 23 + .../domain/fdo/DigitalSpecimenRequest.java | 8 +- .../domain/fdo/DoiRecordRequest.java | 2 - .../handlemanager/domain/fdo/FdoProfile.java | 6 +- .../handlemanager/domain/fdo/FdoType.java | 34 +- .../domain/fdo/MediaObjectRequest.java | 20 +- .../vocabulary/specimen/StructuralType.java | 2 +- .../repsitoryobjects/HandleAttributeJson.java | 42 + .../domain/repsitoryobjects/HandleData.java | 5 + .../properties/MongoProperties.java | 20 + .../repository/PidMongoRepository.java | 43 + .../handlemanager/service/DoiService.java | 3 +- .../service/FdoRecordService.java | 761 ++++++++++-- .../handlemanager/service/HandleService.java | 38 +- .../repository/BatchInserterIT.java | 6 +- .../repository/PidRepositoryIT.java | 1071 +++++++++-------- .../service/FdoRecordServiceTest.java | 3 +- .../handlemanager/testUtils/TestUtils.java | 20 +- 23 files changed, 1554 insertions(+), 642 deletions(-) create mode 100644 src/main/java/eu/dissco/core/handlemanager/configuration/InstantDeserializer.java create mode 100644 src/main/java/eu/dissco/core/handlemanager/configuration/InstantSerializer.java create mode 100644 src/main/java/eu/dissco/core/handlemanager/configuration/MongoConfig.java create mode 100644 src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/HandleAttributeJson.java create mode 100644 src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/HandleData.java create mode 100644 src/main/java/eu/dissco/core/handlemanager/properties/MongoProperties.java create mode 100644 src/main/java/eu/dissco/core/handlemanager/repository/PidMongoRepository.java diff --git a/.gitignore b/.gitignore index 844b0ba3..868ca982 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ target/ .idea -src/main/resources/* +src/main/resources/application.properties .mvn/ diff --git a/pom.xml b/pom.xml index c207c821..3361e2ea 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,7 @@ 1.17.6 4.3 5.2.0 + 4.11.1 4.10.0 ../app-it/target/site/jacoco-aggregate/jacoco.xml @@ -113,6 +114,15 @@ org.apache.commons commons-lang3 + + org.springframework.boot + spring-boot-starter-data-mongodb + + + org.mongodb + mongodb-driver-sync + ${mongodb-driver.version} + @@ -177,6 +187,11 @@ org.springframework.boot test + + org.testcontainers + mongodb + test + com.squareup.okhttp3 okhttp diff --git a/src/main/java/eu/dissco/core/handlemanager/configuration/AppConfig.java b/src/main/java/eu/dissco/core/handlemanager/configuration/AppConfig.java index 3059535e..1350c1d6 100644 --- a/src/main/java/eu/dissco/core/handlemanager/configuration/AppConfig.java +++ b/src/main/java/eu/dissco/core/handlemanager/configuration/AppConfig.java @@ -1,6 +1,8 @@ package eu.dissco.core.handlemanager.configuration; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.time.Instant; import java.util.Random; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilderFactory; @@ -13,6 +15,8 @@ @Configuration public class AppConfig { + public static final String DATE_STRING = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"; + @Bean public DocumentBuilderFactory documentBuilderFactory() throws ParserConfigurationException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); @@ -31,7 +35,13 @@ public TransformerFactory transformerFactory() throws TransformerConfigurationEx @Bean public ObjectMapper objectMapper() { - return new ObjectMapper().findAndRegisterModules(); + var mapper = new ObjectMapper() + .findAndRegisterModules(); + SimpleModule dateModule = new SimpleModule(); + dateModule.addSerializer(Instant.class, new InstantSerializer()); + dateModule.addDeserializer(Instant.class, new InstantDeserializer()); + mapper.registerModule(dateModule); + return mapper; } @Bean diff --git a/src/main/java/eu/dissco/core/handlemanager/configuration/InstantDeserializer.java b/src/main/java/eu/dissco/core/handlemanager/configuration/InstantDeserializer.java new file mode 100644 index 00000000..454fa05d --- /dev/null +++ b/src/main/java/eu/dissco/core/handlemanager/configuration/InstantDeserializer.java @@ -0,0 +1,30 @@ +package eu.dissco.core.handlemanager.configuration; + +import static eu.dissco.core.handlemanager.configuration.AppConfig.DATE_STRING; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import java.io.IOException; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class InstantDeserializer extends JsonDeserializer { + + private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_STRING).withZone( + ZoneOffset.UTC); + + @Override + public Instant deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) { + try { + return Instant.from(formatter.parse(jsonParser.getText())); + } catch (IOException e) { + log.error("An error has occurred deserializing a date. More information: {}", e.getMessage()); + return null; + } + } + +} diff --git a/src/main/java/eu/dissco/core/handlemanager/configuration/InstantSerializer.java b/src/main/java/eu/dissco/core/handlemanager/configuration/InstantSerializer.java new file mode 100644 index 00000000..fc2e9846 --- /dev/null +++ b/src/main/java/eu/dissco/core/handlemanager/configuration/InstantSerializer.java @@ -0,0 +1,30 @@ +package eu.dissco.core.handlemanager.configuration; + +import static eu.dissco.core.handlemanager.configuration.AppConfig.DATE_STRING; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import java.io.IOException; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class InstantSerializer extends JsonSerializer { + + private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_STRING).withZone( + ZoneOffset.UTC); + + @Override + public void serialize(Instant value, JsonGenerator jsonGenerator, + SerializerProvider serializerProvider) { + try { + jsonGenerator.writeString(formatter.format(value)); + } catch (IOException e) { + log.error("An error has occurred serializing a date. More information: {}", e.getMessage()); + } + } + +} diff --git a/src/main/java/eu/dissco/core/handlemanager/configuration/MongoConfig.java b/src/main/java/eu/dissco/core/handlemanager/configuration/MongoConfig.java new file mode 100644 index 00000000..e1b7b833 --- /dev/null +++ b/src/main/java/eu/dissco/core/handlemanager/configuration/MongoConfig.java @@ -0,0 +1,23 @@ +package eu.dissco.core.handlemanager.configuration; + +import com.mongodb.client.MongoClients; +import com.mongodb.client.MongoCollection; +import eu.dissco.core.handlemanager.properties.MongoProperties; +import lombok.RequiredArgsConstructor; +import org.bson.Document; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@RequiredArgsConstructor +public class MongoConfig { + + private final MongoProperties properties; + + @Bean + public MongoCollection getHandleCollection() { + var client = MongoClients.create(properties.getConnectionString()); + var database = client.getDatabase(properties.getDatabase()); + return database.getCollection("handles"); + } +} diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/DigitalSpecimenRequest.java b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/DigitalSpecimenRequest.java index 1c3fccdc..de6e3032 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/DigitalSpecimenRequest.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/DigitalSpecimenRequest.java @@ -43,7 +43,7 @@ public class DigitalSpecimenRequest extends DoiRecordRequest { @JsonProperty(required = true) private final String normalisedPrimarySpecimenObjectId; @Nullable - private final String primarySpecimenObjectIdAbsenceReason; + private final String specimenObjectIdAbsenceReason; @Nullable private final List otherSpecimenIds; @Nullable @@ -86,7 +86,7 @@ public DigitalSpecimenRequest( PrimarySpecimenObjectIdType primarySpecimenObjectIdType, String primarySpecimenObjectIdName, String normalisedPrimarySpecimenObjetId, - String primarySpecimenObjectIdAbsenceReason, + String specimenObjectIdAbsenceReason, List otherSpecimenIds, TopicOrigin topicOrigin, TopicDomain topicDomain, @@ -109,7 +109,7 @@ public DigitalSpecimenRequest( primarySpecimenObjectIdType == null ? LOCAL : primarySpecimenObjectIdType; this.primarySpecimenObjectIdName = primarySpecimenObjectIdName; this.normalisedPrimarySpecimenObjectId = normalisedPrimarySpecimenObjetId; - this.primarySpecimenObjectIdAbsenceReason = primarySpecimenObjectIdAbsenceReason; + this.specimenObjectIdAbsenceReason = specimenObjectIdAbsenceReason; this.otherSpecimenIds = otherSpecimenIds; this.topicOrigin = topicOrigin; this.topicDomain = topicDomain; @@ -130,7 +130,7 @@ public DigitalSpecimenRequest( } private void idXorAbsence() throws InvalidRequestException { - if ((this.primarySpecimenObjectId == null) == (this.primarySpecimenObjectIdAbsenceReason + if ((this.primarySpecimenObjectId == null) == (this.specimenObjectIdAbsenceReason == null)) { throw new InvalidRequestException( "Request must contain exactly one of: [primarySpecimenObjectId, primarySpecimenObjectIdAbsenceReason]"); diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/DoiRecordRequest.java b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/DoiRecordRequest.java index 28ecbec8..1ef5e92b 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/DoiRecordRequest.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/DoiRecordRequest.java @@ -11,8 +11,6 @@ @EqualsAndHashCode(callSuper = true) public class DoiRecordRequest extends HandleRecordRequest { - private static final String PLACEHOLDER = "{This value is a placeholder}"; - private final String referentType; @JsonPropertyDescription("Local name of the object (human-readable)") diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoProfile.java b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoProfile.java index 667d94d3..cd4e0aed 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoProfile.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoProfile.java @@ -55,7 +55,6 @@ public enum FdoProfile { // Media MEDIA_HOST("mediaHost", 400), MEDIA_HOST_NAME("mediaHostName", 401), - MEDIA_FORMAT("mediaFormat", 402), IS_DERIVED_FROM_SPECIMEN("isDerivedFromSpecimen", 403), LINKED_DO_PID("linkedDigitalObjectPid", 404), LINKED_DO_TYPE("linkedDigitalObjectType", 405), @@ -63,8 +62,9 @@ public enum FdoProfile { PRIMARY_MEDIA_ID("primaryMediaId", 407), PRIMARY_MO_ID_TYPE("primaryMediaObjectIdType", 408), PRIMARY_MO_ID_NAME("primaryMediaObjectIdName", 409), - DCTERMS_TYPE("dcterms:type", 411), - MEDIA_MIME_TYPE("mediaMimeType", 412), + DCTERMS_TYPE("dcterms:type", 410), + DCTERMS_SUBJECT("dcterms:subject", 411), + DCTERMS_FORMAT("dcterms:format", 412), DERIVED_FROM_ENTITY("derivedFromEntity", 413), LICENSE_NAME("licenseName", 414), LICENSE_URL("licenseUrl", 415), diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoType.java b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoType.java index 8e7e5991..e7c77b53 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoType.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoType.java @@ -10,53 +10,65 @@ public enum FdoType { @JsonProperty("https://hdl.handle.net/21.T11148/532ce6796e2828dd2be6") HANDLE( "Handle Kernel", "https://hdl.handle.net/21.T11148/532ce6796e2828dd2be6", - "https://hdl.handle.net/21.T11148/532ce6796e2828dd2be6"), + "https://hdl.handle.net/21.T11148/532ce6796e2828dd2be6", + "https://hdl.handle.net/"), @JsonProperty("https://hdl.handle.net/21.T11148/527856fd709ec8c5bc8c") DOI( "DOI Kernel", "https://hdl.handle.net/21.T11148/527856fd709ec8c5bc8c", - "https://hdl.handle.net/21.T11148/527856fd709ec8c5bc8c"), + "https://hdl.handle.net/21.T11148/527856fd709ec8c5bc8c", + "https://doi.org/"), @JsonProperty("https://hdl.handle.net/21.T11148/894b1e6cad57e921764e") DIGITAL_SPECIMEN( "DigitalSpecimen", "https://hdl.handle.net/21.T11148/894b1e6cad57e921764e", - "https://hdl.handle.net/21.T11148/894b1e6cad57e921764e"), + "https://hdl.handle.net/21.T11148/894b1e6cad57e921764e", + "https://doi.org/"), @JsonProperty("https://hdl.handle.net/21.T11148/bbad8c4e101e8af01115") MEDIA_OBJECT( "MediaObject", "https://hdl.handle.net/21.T11148/bbad8c4e101e8af01115", - "https://hdl.handle.net/21.T11148/bbad8c4e101e8af01115"), + "https://hdl.handle.net/21.T11148/bbad8c4e101e8af01115", + "https://doi.org/"), @JsonProperty("https://hdl.handle.net/21.T11148/cf458ca9ee1d44a5608f") ANNOTATION( "Annotation", "https://hdl.handle.net/21.T11148/cf458ca9ee1d44a5608f", - "https://hdl.handle.net/21.T11148/cf458ca9ee1d44a5608f"), + "https://hdl.handle.net/21.T11148/cf458ca9ee1d44a5608f", + "https://hdl.handle.net/"), @JsonProperty("https://hdl.handle.net/21.T11148/417a4f472f60f7974c12") SOURCE_SYSTEM( "sourceSystem", "https://hdl.handle.net/21.T11148/417a4f472f60f7974c12", - "https://hdl.handle.net/21.T11148/417a4f472f60f7974c12"), + "https://hdl.handle.net/21.T11148/417a4f472f60f7974c12", + "https://hdl.handle.net/"), @JsonProperty("https://hdl.handle.net/21.T11148/ce794a6f4df42eb7e77e") MAPPING( "Mapping", "https://hdl.handle.net/21.T11148/ce794a6f4df42eb7e77e", - "https://hdl.handle.net/21.T11148/ce794a6f4df42eb7e77e"), + "https://hdl.handle.net/21.T11148/ce794a6f4df42eb7e77e", + "https://hdl.handle.net/"), @JsonProperty("https://hdl.handle.net/21.T11148/413c00cbd83ae33d1ac0") ORGANISATION( "Organisation", "https://hdl.handle.net/21.T11148/413c00cbd83ae33d1ac0", - "https://hdl.handle.net/21.T11148/413c00cbd83ae33d1ac0"), + "https://hdl.handle.net/21.T11148/413c00cbd83ae33d1ac0", + "https://hdl.handle.net/"), @JsonProperty("https://hdl.handle.net/21.T11148/d7570227982f70256af3") TOMBSTONE( "Tombstone", "https://hdl.handle.net/21.T11148/d7570227982f70256af3", - "https://hdl.handle.net/21.T11148/d7570227982f70256af3"), + "https://hdl.handle.net/21.T11148/d7570227982f70256af3", + "https://hdl.handle.net/"), @JsonProperty("https://hdl.handle.net/21.T11148/22e71a0015cbcfba8ffa") MAS( "Machine Annotation Service", "https://hdl.handle.net/21.T11148/22e71a0015cbcfba8ffa", - "https://hdl.handle.net/21.T11148/22e71a0015cbcfba8ffa"); + "https://hdl.handle.net/21.T11148/22e71a0015cbcfba8ffa", + "https://hdl.handle.net/"); private final String digitalObjectName; private final String digitalObjectType; private final String fdoProfile; + private final String domain; FdoType(@JsonProperty("type") String digitalObjectName, String digitalObjectType, - String fdoProfile) { + String fdoProfile, String domain) { this.digitalObjectName = digitalObjectName; this.digitalObjectType = digitalObjectType; this.fdoProfile = fdoProfile; + this.domain = domain; } @Override diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/MediaObjectRequest.java b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/MediaObjectRequest.java index 6ff348fc..e0fe14c8 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/MediaObjectRequest.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/MediaObjectRequest.java @@ -23,7 +23,7 @@ public class MediaObjectRequest extends DoiRecordRequest { private final String mediaHostName; @Nullable @JsonProperty(value = "dcterms:format") - private final MediaFormat mediaFormat; + private final MediaFormat dctermsFormat; @JsonProperty(required = true) private final Boolean isDerivedFromSpecimen; @JsonProperty(required = true) @@ -32,6 +32,7 @@ public class MediaObjectRequest extends DoiRecordRequest { private final LinkedDigitalObjectType linkedDigitalObjectType; @Nullable private final String linkedAttribute; + @JsonProperty(required = true) private final String primaryMediaId; @Nullable private final PrimarySpecimenObjectIdType primaryMediaObjectIdType; @@ -41,13 +42,14 @@ public class MediaObjectRequest extends DoiRecordRequest { @JsonProperty(value = "dcterms:type") private final DcTermsType dcTermsType; @Nullable - private final String mediaMimeType; + @JsonProperty(value = "dcterms:subject") + private final String dctermsSubject; @Nullable private final String derivedFromEntity; @Nullable private final String licenseName; @Nullable - private final String license; + private final String licenseUrl; @Nullable private final String rightsholderName; @Nullable @@ -68,7 +70,7 @@ public MediaObjectRequest( // Media String mediaHost, String mediaHostName, - MediaFormat mediaFormat, + MediaFormat dctermsFormat, Boolean isDerivedFromSpecimen, String linkedDigitalObjectPid, LinkedDigitalObjectType linkedDigitalObjectType, @@ -77,10 +79,10 @@ public MediaObjectRequest( PrimarySpecimenObjectIdType primaryMediaObjectIdType, String primaryMediaObjectIdName, DcTermsType dcTermsType, - String mediaMimeType, + String dctermsSubject, String derivedFromEntity, String licenseName, - String license, + String licenseUrl, String rightsholderPid, String rightsholderName, PrimarySpecimenObjectIdType rightsholderPidType, @@ -91,7 +93,7 @@ public MediaObjectRequest( referentName, FdoType.MEDIA_OBJECT.getDigitalObjectName(), primaryReferentType); this.mediaHost = mediaHost; this.mediaHostName = mediaHostName; - this.mediaFormat = mediaFormat; + this.dctermsFormat = dctermsFormat; this.isDerivedFromSpecimen = isDerivedFromSpecimen; this.linkedDigitalObjectPid = linkedDigitalObjectPid; this.linkedDigitalObjectType = linkedDigitalObjectType; @@ -100,10 +102,10 @@ public MediaObjectRequest( this.primaryMediaObjectIdType = primaryMediaObjectIdType; this.primaryMediaObjectIdName = primaryMediaObjectIdName; this.dcTermsType = dcTermsType; - this.mediaMimeType = mediaMimeType; + this.dctermsSubject = dctermsSubject; this.derivedFromEntity = derivedFromEntity; this.licenseName = licenseName; - this.license = license; + this.licenseUrl = licenseUrl; this.rightsholderPid = rightsholderPid == null ? mediaHost : rightsholderPid; this.rightsholderName = rightsholderName; this.rightsholderPidType = rightsholderPidType; diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/specimen/StructuralType.java b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/specimen/StructuralType.java index 4be772fd..4c561aa8 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/specimen/StructuralType.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/specimen/StructuralType.java @@ -11,7 +11,7 @@ public enum StructuralType { private final String state; - private StructuralType(String state) { + StructuralType(String state) { this.state = state; } diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/HandleAttributeJson.java b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/HandleAttributeJson.java new file mode 100644 index 00000000..19ddf3f1 --- /dev/null +++ b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/HandleAttributeJson.java @@ -0,0 +1,42 @@ +package eu.dissco.core.handlemanager.domain.repsitoryobjects; + +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.HS_ADMIN; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import eu.dissco.core.handlemanager.domain.fdo.FdoProfile; +import java.time.Instant; +import lombok.Value; + +@Value +public class HandleAttributeJson { + + int index; + String type; + HandleData data; + int ttl = 86400; + Instant timestamp; + + public HandleAttributeJson(FdoProfile fdoAttribute, Instant timestamp, String value) { + this.index = fdoAttribute.index(); + this.type = fdoAttribute.get(); + this.timestamp = timestamp; + this.data = new StringHandleData(value); + } + + public HandleAttributeJson(Instant timestamp, String prefix) { + this.index = HS_ADMIN.index(); + this.type = HS_ADMIN.get(); + this.data = new AdminHandleData(prefix); + this.timestamp = timestamp; + } + + @JsonIgnore + public String getValue() { + if (data instanceof StringHandleData) { + return ((StringHandleData) data).getValue(); + } else { + return "HS_ADMIN"; + } + } + +} diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/HandleData.java b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/HandleData.java new file mode 100644 index 00000000..655fb0e3 --- /dev/null +++ b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/HandleData.java @@ -0,0 +1,5 @@ +package eu.dissco.core.handlemanager.domain.repsitoryobjects; + +public interface HandleData { + +} diff --git a/src/main/java/eu/dissco/core/handlemanager/properties/MongoProperties.java b/src/main/java/eu/dissco/core/handlemanager/properties/MongoProperties.java new file mode 100644 index 00000000..f2e21f1a --- /dev/null +++ b/src/main/java/eu/dissco/core/handlemanager/properties/MongoProperties.java @@ -0,0 +1,20 @@ +package eu.dissco.core.handlemanager.properties; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +@Data +@Validated +@ConfigurationProperties(prefix = "mongo") +public class MongoProperties { + + @NotBlank + private String connectionString; + + @NotBlank + private String database; + + +} diff --git a/src/main/java/eu/dissco/core/handlemanager/repository/PidMongoRepository.java b/src/main/java/eu/dissco/core/handlemanager/repository/PidMongoRepository.java new file mode 100644 index 00000000..6cd4d677 --- /dev/null +++ b/src/main/java/eu/dissco/core/handlemanager/repository/PidMongoRepository.java @@ -0,0 +1,43 @@ +package eu.dissco.core.handlemanager.repository; + +import static com.mongodb.client.model.Filters.eq; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mongodb.client.MongoCollection; +import eu.dissco.core.handlemanager.exceptions.PidResolutionException; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.bson.Document; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class PidMongoRepository { + + private final MongoCollection collection; + private final ObjectMapper mapper; + + public JsonNode getHandleRecord(String id) + throws PidResolutionException, JsonProcessingException { + var result = collection.find(eq("_id", id)); + if (result.first() == null) { + throw new PidResolutionException("Unable to find handle " + id); + } + return mapper.readValue(result.first().toJson(), JsonNode.class); + } + + public void postSingleHandleRecord(JsonNode handleRecord, String id) + throws JsonProcessingException { + var doc = new Document("_id", id) + .append("values", mapper.writeValueAsString(handleRecord)); + collection.insertOne(doc); + } + + public void postBatchHandleRecord(List handleRecords) { + collection.insertMany(handleRecords); + } + + +} diff --git a/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java b/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java index ea1dc889..58d19da5 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java @@ -97,7 +97,8 @@ private void publishToDataCite(List handleAttributes, EventType (key, value) -> { if (eventType.equals(EventType.UPDATE)) { value.add( - new HandleAttribute(FdoProfile.PID, key.getBytes(StandardCharsets.UTF_8), key)); + new HandleAttribute(FdoProfile.PID, key.getBytes(StandardCharsets.UTF_8), + key)); } eventList.add( new DataCiteEvent(jsonFormatSingleRecord(value), eventType)); diff --git a/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java b/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java index 6daab967..ee180df2 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java @@ -1,9 +1,12 @@ package eu.dissco.core.handlemanager.service; +import static eu.dissco.core.handlemanager.configuration.AppConfig.DATE_STRING; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.ANNOTATION_HASH; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.BASE_TYPE_OF_SPECIMEN; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.CATALOG_IDENTIFIER; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DCTERMS_FORMAT; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DCTERMS_SUBJECT; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DCTERMS_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DC_TERMS_CONFORMS; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DERIVED_FROM_ENTITY; @@ -27,10 +30,8 @@ import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MAS_NAME; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MATERIAL_OR_DIGITAL_ENTITY; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MATERIAL_SAMPLE_TYPE; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MEDIA_FORMAT; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MEDIA_HOST; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MEDIA_HOST_NAME; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MEDIA_MIME_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MOTIVATION; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.NORMALISED_SPECIMEN_OBJECT_ID; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.ORGANISATION_ID; @@ -70,7 +71,12 @@ import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOPIC_ORIGIN; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.WAS_DERIVED_FROM_ENTITY; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.DIGITAL_SPECIMEN; +import static eu.dissco.core.handlemanager.domain.fdo.FdoType.HANDLE; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.MAPPING; +import static eu.dissco.core.handlemanager.domain.fdo.FdoType.MAS; +import static eu.dissco.core.handlemanager.domain.fdo.FdoType.MEDIA_OBJECT; +import static eu.dissco.core.handlemanager.domain.fdo.FdoType.ORGANISATION; +import static eu.dissco.core.handlemanager.domain.fdo.FdoType.SOURCE_SYSTEM; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; @@ -91,12 +97,14 @@ import eu.dissco.core.handlemanager.domain.fdo.OrganisationRequest; import eu.dissco.core.handlemanager.domain.fdo.SourceSystemRequest; import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttributeJson; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.InvalidRequestRuntimeException; import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.properties.ApplicationProperties; import eu.dissco.core.handlemanager.properties.ProfileProperties; import eu.dissco.core.handlemanager.web.PidResolver; +import jakarta.annotation.Nullable; import java.io.IOException; import java.io.StringWriter; import java.nio.charset.StandardCharsets; @@ -119,8 +127,9 @@ import javax.xml.transform.stream.StreamResult; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.bson.Document; import org.springframework.stereotype.Service; -import org.w3c.dom.Document; + @RequiredArgsConstructor @Service @@ -155,13 +164,584 @@ public class FdoRecordService { RESOLVABLE_KEYS = Collections.unmodifiableMap(hashMap); } - private final DateTimeFormatter dt = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX") + private final DateTimeFormatter dt = DateTimeFormatter.ofPattern(DATE_STRING) .withZone(ZoneId.of("UTC")); + private final ApplicationProperties applicationProperties; public HandleAttribute genHsAdmin(byte[] handle) { return new HandleAttribute(HS_ADMIN.index(), handle, HS_ADMIN.get(), ADMIN_HEX); } + private Document toMongoDbDocument(List fdoRecord, String handle) + throws JsonProcessingException { + var arrayNode = mapper.createArrayNode(); + for (var attribute : fdoRecord) { + arrayNode.add(attribute); + } + var vals = mapper.createObjectNode() + .set("values", arrayNode); + return Document.parse(mapper.writeValueAsString(vals)) + .append("_id", handle); + } + + /* Handle Record Creation */ + + public Document prepareNewHandleDocument(HandleRecordRequest request, String handle, + FdoType fdoType, Instant timestamp) throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareHandleAttributesGenerated(handle, HANDLE, timestamp); + prepareNewHandleJsonNodeRecord(request, handle, fdoType, timestamp); + return toMongoDbDocument(fdoRecord, handle); + } + + public Document prepareUpdatedHandleDocument(HandleRecordRequest request, String handle, + FdoType fdoType, Instant timestamp, int issueNumber) + throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareUpdatedHandleJsonNodeRecord(request, handle, fdoType, timestamp, + issueNumber); + return toMongoDbDocument(fdoRecord, handle); + } + + private List prepareNewHandleJsonNodeRecord(HandleRecordRequest request, String handle, + FdoType fdoType, Instant timestamp) throws InvalidRequestException { + var handleAttributes = prepareHandleAttributesFromRequest(request, handle, fdoType, timestamp); + handleAttributes.addAll(prepareHandleAttributesGenerated(handle, fdoType, timestamp)); + return handleAttributes; + } + + private List prepareUpdatedHandleJsonNodeRecord(HandleRecordRequest request, + String handle, FdoType fdoType, Instant timestamp, int issueNumber) + throws InvalidRequestException { + var handleAttributes = prepareHandleAttributesFromRequest(request, handle, fdoType, timestamp); + handleAttributes.add(mapper.valueToTree( + new HandleAttributeJson(PID_RECORD_ISSUE_NUMBER, timestamp, String.valueOf(issueNumber)))); + return handleAttributes; + } + + // These attributes may change on an update + private ArrayList prepareHandleAttributesFromRequest(HandleRecordRequest request, + String handle, + FdoType fdoType, Instant timestamp) + throws InvalidRequestException { + var fdoProfile = new ArrayList(); + var handleAttributeList = new ArrayList(); + // 101: 10320/Loc + if (!fdoType.equals(ORGANISATION)) { + handleAttributeList.add(new HandleAttributeJson(LOC, timestamp, + setLocations(request.getLocations(), handle, fdoType))); + } + // 1: FDO Profile + handleAttributeList.add( + new HandleAttributeJson(FDO_PROFILE, timestamp, fdoType.getFdoProfile())); + // 3: Digital Object Type + handleAttributeList.add( + new HandleAttributeJson(DIGITAL_OBJECT_TYPE, timestamp, fdoType.getDigitalObjectType())); + // 4: Digital ObjectName + handleAttributeList.add( + new HandleAttributeJson(DIGITAL_OBJECT_NAME, timestamp, fdoType.getDigitalObjectType())); + // 6: PID Issuer + handleAttributeList.add(new HandleAttributeJson(PID_ISSUER, timestamp, request.getPidIssuer())); + // 7: PID Issuer Name + handleAttributeList.add(new HandleAttributeJson(PID_ISSUER_NAME, timestamp, + getObjectName(request.getPidIssuer(), null))); + // 8: Issued For Agent + handleAttributeList.add( + new HandleAttributeJson(ISSUED_FOR_AGENT, timestamp, request.getIssuedForAgent())); + // 9: Issued for Agent Name + handleAttributeList.add(new HandleAttributeJson(ISSUED_FOR_AGENT_NAME, timestamp, + getObjectName(request.getIssuedForAgent(), null))); + // 12: Structural Type + handleAttributeList.add( + new HandleAttributeJson(STRUCTURAL_TYPE, timestamp, + request.getStructuralType().toString())); + for (var attribute : handleAttributeList) { + fdoProfile.add(mapper.valueToTree(attribute)); + } + return fdoProfile; + } + + // These attributes do not change on an update (except issue number, which is dealt with separately) + private List prepareHandleAttributesGenerated(String handle, FdoType fdoType, + Instant timestamp) { + var fdoProfile = new ArrayList(); + var handleAttributeList = new ArrayList(); + // 2: FDO Record License + handleAttributeList.add( + new HandleAttributeJson(FDO_RECORD_LICENSE, timestamp, PID_KERNEL_METADATA_LICENSE)); + // 5: PID + handleAttributeList.add(new HandleAttributeJson(PID, timestamp, fdoType.getDomain() + handle)); + // 10: PID Record Issue Date + handleAttributeList.add( + new HandleAttributeJson(PID_RECORD_ISSUE_DATE, timestamp, getDate(timestamp))); + // 11: Pid Record Issue Number + handleAttributeList.add( + new HandleAttributeJson(PID_RECORD_ISSUE_NUMBER, timestamp, + "1")); // This gets replaced on an update + // 13: Pid Status + handleAttributeList.add(new HandleAttributeJson(PID_STATUS, timestamp, "TEST")); + // 100 HS Admin + handleAttributeList.add(new HandleAttributeJson(timestamp, applicationProperties.getPrefix())); + for (var attribute : handleAttributeList) { + fdoProfile.add(mapper.valueToTree(attribute)); + } + return fdoProfile; + } + + /* DOI Record Creation */ + public Document prepareNewDoiDocument(DoiRecordRequest request, String handle, + FdoType fdoType, Instant timestamp) throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareNewDoiJsonNodeRecord(request, handle, fdoType, timestamp); + return toMongoDbDocument(fdoRecord, handle); + } + + public Document prepareUpdatedDoiDocument(DoiRecordRequest request, String handle, + FdoType fdoType, Instant timestamp, int issueNum) + throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareUpdatedDoiJsonNodeRecord(request, handle, fdoType, timestamp, issueNum); + return toMongoDbDocument(fdoRecord, handle); + } + + private List prepareNewDoiJsonNodeRecord(DoiRecordRequest request, String handle, + FdoType fdoType, Instant timestamp) throws InvalidRequestException { + var fdoProfile = prepareNewHandleJsonNodeRecord(request, handle, fdoType, timestamp); + fdoProfile.addAll(prepareDoiAttributesFromRequest(request, handle, timestamp)); + return fdoProfile; + } + + private List prepareUpdatedDoiJsonNodeRecord(DoiRecordRequest request, String handle, + FdoType fdoType, Instant timestamp, int issueNumber) throws InvalidRequestException { + var fdoProfile = prepareUpdatedHandleJsonNodeRecord(request, handle, fdoType, timestamp, + issueNumber); + fdoProfile.addAll(prepareDoiAttributesFromRequest(request, handle, timestamp)); + return fdoProfile; + } + + private List prepareDoiAttributesFromRequest(DoiRecordRequest request, String handle, + Instant timestamp) { + var fdoProfile = new ArrayList(); + var handleAttributeList = new ArrayList(); + + // 40: Referent Type + handleAttributeList.add( + new HandleAttributeJson(REFERENT_TYPE, timestamp, request.getReferentType())); + // 41: Referent DOI Name + handleAttributeList.add(new HandleAttributeJson(REFERENT_DOI_NAME, timestamp, handle)); + // 42: Referent Name + handleAttributeList.add( + new HandleAttributeJson(REFERENT_NAME, timestamp, request.getReferentName())); + // 43: Primary Referent Type + handleAttributeList.add(new HandleAttributeJson(PRIMARY_REFERENT_TYPE, timestamp, + request.getPrimaryReferentType())); + for (var attribute : handleAttributeList) { + fdoProfile.add(mapper.valueToTree(attribute)); + } + return fdoProfile; + } + + /* Annotation Record Creation */ + public Document prepareNewAnnotationDocument(AnnotationRequest request, String handle, + Instant timestamp) + throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareNewHandleJsonNodeRecord(request, handle, FdoType.ANNOTATION, timestamp); + fdoRecord.addAll(prepareAnnotationAttributesFromRequest(request, timestamp)); + return toMongoDbDocument(fdoRecord, handle); + } + + public Document prepareUpdatedAnnotationDocument(AnnotationRequest request, String handle, + Instant timestamp, int issueNumber) + throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareUpdatedHandleJsonNodeRecord(request, handle, FdoType.ANNOTATION, + timestamp, issueNumber); + fdoRecord.addAll(prepareAnnotationAttributesFromRequest(request, timestamp)); + return toMongoDbDocument(fdoRecord, handle); + } + + private List prepareAnnotationAttributesFromRequest(AnnotationRequest request, + Instant timestamp) { + var fdoProfile = new ArrayList(); + var handleAttributeList = new ArrayList(); + // 500 Target PID + handleAttributeList.add(new HandleAttributeJson(TARGET_PID, timestamp, request.getTargetPid())); + // 501 Target Type + handleAttributeList.add( + new HandleAttributeJson(TARGET_TYPE, timestamp, request.getTargetType())); + // 502 Motivation + handleAttributeList.add(new HandleAttributeJson(MOTIVATION, timestamp, + request.getMotivation().toString())); + // 503 Annotation Hash + handleAttributeList.add(new HandleAttributeJson(PRIMARY_REFERENT_TYPE, timestamp, + request.getMotivation().toString())); + for (var attribute : handleAttributeList) { + fdoProfile.add(mapper.valueToTree(attribute)); + } + return fdoProfile; + } + + /* Data Mapping Record Creation */ + public Document prepareNewDataMappingDocument(MappingRequest request, String handle, + Instant timestamp) + throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareNewHandleJsonNodeRecord(request, handle, MAPPING, timestamp); + fdoRecord.addAll(prepareDataMappingAttributesFromRequest(request, timestamp)); + return toMongoDbDocument(fdoRecord, handle); + } + + public Document prepareUpdatedDataMappingDocument(MappingRequest request, String handle, + Instant timestamp, int issueNumber) + throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareUpdatedHandleJsonNodeRecord(request, handle, MAPPING, timestamp, + issueNumber); + fdoRecord.addAll(prepareDataMappingAttributesFromRequest(request, timestamp)); + return toMongoDbDocument(fdoRecord, handle); + } + + private List prepareDataMappingAttributesFromRequest(MappingRequest request, + Instant timestamp) { + var fdoProfile = new ArrayList(); + fdoProfile.add(mapper.valueToTree( + new HandleAttributeJson(SOURCE_DATA_STANDARD, timestamp, request.getSourceDataStandard()))); + return fdoProfile; + } + + /* Digital Specimen Record Creation */ + public Document prepareNewDigitalSpecimenRecord(DigitalSpecimenRequest request, + String handle, Instant timestamp) + throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareNewDoiJsonNodeRecord(request, handle, DIGITAL_SPECIMEN, timestamp); + fdoRecord.addAll(prepareDigitalSpecimenRecordAttributesFromRequest(request, timestamp)); + return toMongoDbDocument(fdoRecord, handle) + .append(NORMALISED_SPECIMEN_OBJECT_ID.get(), + request.getNormalisedPrimarySpecimenObjectId()); + } + + public List prepareUpdatedDigitalSpecimenRecord(DigitalSpecimenRequest request, + String handle, Instant timestamp, int issueNumber) + throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareUpdatedDoiJsonNodeRecord(request, handle, DIGITAL_SPECIMEN, timestamp, + issueNumber); + fdoRecord.addAll(prepareDigitalSpecimenRecordAttributesFromRequest(request, timestamp)); + return fdoRecord; + } + + private List prepareDigitalSpecimenRecordAttributesFromRequest( + DigitalSpecimenRequest request, Instant timestamp) + throws InvalidRequestException, JsonProcessingException { + var fdoProfile = new ArrayList(); + var handleAttributeList = new ArrayList(); + // 200 Specimen Host + handleAttributeList.add( + new HandleAttributeJson(SPECIMEN_HOST, timestamp, request.getSpecimenHost())); + // 201 Specimen Host Name + handleAttributeList.add(new HandleAttributeJson(SPECIMEN_HOST_NAME, timestamp, + getObjectName(request.getSpecimenHost(), request.getSpecimenHostName()))); + // 202 Primary Specimen Object ID + handleAttributeList.add(new HandleAttributeJson(PRIMARY_SPECIMEN_OBJECT_ID, timestamp, + getObjectName(request.getSpecimenHost(), request.getPrimarySpecimenObjectId()))); + // 203 Primary Specimen Object ID Type + handleAttributeList.add(new HandleAttributeJson(PRIMARY_SPECIMEN_OBJECT_ID_TYPE, timestamp, + getObjectName(request.getSpecimenHost(), + request.getPrimarySpecimenObjectIdType().toString()))); + // 204 Primary Specimen Object ID Name + handleAttributeList.add(new HandleAttributeJson(PRIMARY_SPECIMEN_OBJECT_ID_NAME, timestamp, + getObjectName(request.getSpecimenHost(), request.getPrimarySpecimenObjectIdName()))); + // 205 Normalised Specimen Object Id + handleAttributeList.add(new HandleAttributeJson(NORMALISED_SPECIMEN_OBJECT_ID, timestamp, + request.getNormalisedPrimarySpecimenObjectId())); + // 206 Specimen Object Id Absence Reason + handleAttributeList.add(new HandleAttributeJson(SPECIMEN_OBJECT_ID_ABSENCE_REASON, timestamp, + request.getSpecimenObjectIdAbsenceReason())); + // 206 Specimen Object Id Absence Reason + handleAttributeList.add(new HandleAttributeJson(SPECIMEN_OBJECT_ID_ABSENCE_REASON, timestamp, + request.getSpecimenObjectIdAbsenceReason())); + // 207 Other Specimen Ids + handleAttributeList.add(new HandleAttributeJson(OTHER_SPECIMEN_IDS, timestamp, + mapper.writeValueAsString(request.getOtherSpecimenIds()))); + // 208 Topic Origin + if (request.getTopicOrigin() != null) { + handleAttributeList.add( + new HandleAttributeJson(TOPIC_ORIGIN, timestamp, request.getTopicOrigin().toString())); + } else { + handleAttributeList.add(new HandleAttributeJson(TOPIC_ORIGIN, timestamp, null)); + } + // 209 Topic Domain + if (request.getTopicDomain() != null) { + handleAttributeList.add( + new HandleAttributeJson(TOPIC_DOMAIN, timestamp, request.getTopicDomain().toString())); + } else { + handleAttributeList.add(new HandleAttributeJson(TOPIC_DOMAIN, timestamp, null)); + } + // 210 Topic Discipline + if (request.getTopicDiscipline() != null) { + handleAttributeList.add(new HandleAttributeJson(TOPIC_DISCIPLINE, timestamp, + request.getTopicDiscipline().toString())); + } else { + handleAttributeList.add(new HandleAttributeJson(TOPIC_DISCIPLINE, timestamp, null)); + } + // 211 Topic Category + if (request.getTopicDiscipline() != null) { + handleAttributeList.add(new HandleAttributeJson(TOPIC_CATEGORY, timestamp, + request.getTopicDiscipline().toString())); + } else { + handleAttributeList.add(new HandleAttributeJson(TOPIC_CATEGORY, timestamp, null)); + } + // 212 Living or Preserved + if (request.getLivingOrPreserved() != null) { + handleAttributeList.add(new HandleAttributeJson(LIVING_OR_PRESERVED, timestamp, + request.getLivingOrPreserved().toString())); + } else { + handleAttributeList.add(new HandleAttributeJson(LIVING_OR_PRESERVED, timestamp, null)); + } + // 213 Base Type of Specimen + if (request.getBaseTypeOfSpecimen() != null) { + handleAttributeList.add(new HandleAttributeJson(BASE_TYPE_OF_SPECIMEN, timestamp, + request.getBaseTypeOfSpecimen().toString())); + } else { + handleAttributeList.add(new HandleAttributeJson(BASE_TYPE_OF_SPECIMEN, timestamp, null)); + } + // 214 Information Artefact Type + if (request.getInformationArtefactType() != null) { + handleAttributeList.add(new HandleAttributeJson(INFORMATION_ARTEFACT_TYPE, timestamp, + request.getInformationArtefactType().toString())); + } else { + handleAttributeList.add(new HandleAttributeJson(INFORMATION_ARTEFACT_TYPE, timestamp, null)); + } + // 215 Material Sample Type + if (request.getMaterialSampleType() != null) { + handleAttributeList.add(new HandleAttributeJson(MATERIAL_SAMPLE_TYPE, timestamp, + request.getMaterialSampleType().toString())); + } else { + handleAttributeList.add(new HandleAttributeJson(MATERIAL_SAMPLE_TYPE, timestamp, null)); + } + // 216 Material or Digital Entity + if (request.getMaterialOrDigitalEntity() != null) { + handleAttributeList.add(new HandleAttributeJson(MATERIAL_OR_DIGITAL_ENTITY, timestamp, + request.getMaterialOrDigitalEntity().toString())); + } else { + handleAttributeList.add(new HandleAttributeJson(MATERIAL_OR_DIGITAL_ENTITY, timestamp, null)); + } + // 217 Marked as Type + if (request.getMarkedAsType() != null) { + handleAttributeList.add(new HandleAttributeJson(MARKED_AS_TYPE, timestamp, + String.valueOf(request.getMarkedAsType()))); + } else { + handleAttributeList.add(new HandleAttributeJson(MARKED_AS_TYPE, timestamp, null)); + } + // 218 Derived From Entity + handleAttributeList.add( + new HandleAttributeJson(DERIVED_FROM_ENTITY, timestamp, request.getDerivedFromEntity())); + // 219 Catalog ID + handleAttributeList.add( + new HandleAttributeJson(CATALOG_IDENTIFIER, timestamp, request.getCatalogIdentifier())); + for (var attribute : handleAttributeList) { + fdoProfile.add(mapper.valueToTree(attribute)); + } + return fdoProfile; + } + + /* MAS Record Creation */ + public Document prepareMasDocument(MasRequest request, String handle, + Instant timestamp) + throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareNewHandleJsonNodeRecord(request, handle, MAS, timestamp); + fdoRecord.addAll(prepareMasAttributesFromRequest(request, timestamp)); + return toMongoDbDocument(fdoRecord, handle); + } + + public Document prepareUpdatedMasDocument(MasRequest request, String handle, + Instant timestamp, int issueNumber) + throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareUpdatedHandleJsonNodeRecord(request, handle, MAS, timestamp, + issueNumber); + fdoRecord.addAll(prepareMasAttributesFromRequest(request, timestamp)); + return toMongoDbDocument(fdoRecord, handle); + } + + private List prepareMasAttributesFromRequest(MasRequest request, + Instant timestamp) { + var fdoProfile = new ArrayList(); + fdoProfile.add(mapper.valueToTree( + new HandleAttributeJson(MAS_NAME, timestamp, request.getMachineAnnotationServiceName()))); + return fdoProfile; + } + + /* Media Object Record Creation */ + public Document prepareNewDigitalMediaRecord(MediaObjectRequest request, + String handle, Instant timestamp) + throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareNewDoiJsonNodeRecord(request, handle, MEDIA_OBJECT, timestamp); + fdoRecord.addAll(prepareDigitalMediaAttributesFromRequest(request, timestamp)); + return toMongoDbDocument(fdoRecord, handle) + .append(PRIMARY_MEDIA_ID.get(), + request.getPrimaryMediaId()); + } + + public List prepareUpdatedDigitalMediaRecord(MediaObjectRequest request, + String handle, Instant timestamp, int issueNumber) + throws InvalidRequestException { + var fdoRecord = prepareUpdatedDoiJsonNodeRecord(request, handle, MEDIA_OBJECT, timestamp, + issueNumber); + fdoRecord.addAll(prepareDigitalMediaAttributesFromRequest(request, timestamp)); + return fdoRecord; + } + + private List prepareDigitalMediaAttributesFromRequest( + MediaObjectRequest request, Instant timestamp) + throws InvalidRequestException { + var fdoProfile = new ArrayList(); + var handleAttributeList = new ArrayList(); + // 400 Media Host + handleAttributeList.add( + new HandleAttributeJson(MEDIA_HOST, timestamp, request.getMediaHost())); + // 401 MediaHostName + handleAttributeList.add( + new HandleAttributeJson(MEDIA_HOST_NAME, timestamp, + getObjectName(request.getMediaHost(), request.getMediaHostName()))); + // 403 Is Derived From Specimen + handleAttributeList.add( + new HandleAttributeJson(IS_DERIVED_FROM_SPECIMEN, timestamp, + String.valueOf(request.getIsDerivedFromSpecimen()))); + // 404 Linked Digital Object PID + handleAttributeList.add( + new HandleAttributeJson(LINKED_DO_PID, timestamp, request.getLinkedDigitalObjectPid())); + // 405 Linked Digital Object Type + handleAttributeList.add( + new HandleAttributeJson(LINKED_DO_TYPE, timestamp, + request.getLinkedDigitalObjectType().toString())); + // 406 Linked Attribute + handleAttributeList.add( + new HandleAttributeJson(LINKED_ATTRIBUTE, timestamp, request.getLinkedAttribute())); + // 407 Primary Media ID + handleAttributeList.add( + new HandleAttributeJson(PRIMARY_MEDIA_ID, timestamp, request.getPrimaryMediaId())); + // 408 Primary Media Object Id Type + if (request.getPrimaryMediaObjectIdType() != null) { + handleAttributeList.add( + new HandleAttributeJson(PRIMARY_MO_ID_TYPE, timestamp, + request.getPrimaryMediaObjectIdType().toString())); + } else { + handleAttributeList.add( + new HandleAttributeJson(PRIMARY_MO_ID_TYPE, timestamp, null)); + } + // 409 Primary Media Object Id Name + handleAttributeList.add( + new HandleAttributeJson(PRIMARY_MO_ID_NAME, timestamp, + request.getPrimaryMediaObjectIdName())); + // 410 dcterms:type + if (request.getDcTermsType() != null) { + handleAttributeList.add( + new HandleAttributeJson(DCTERMS_TYPE, timestamp, request.getDcTermsType().toString())); + } else { + handleAttributeList.add( + new HandleAttributeJson(DCTERMS_TYPE, timestamp, null)); + } + // 411 dcterms:subject + handleAttributeList.add( + new HandleAttributeJson(DCTERMS_SUBJECT, timestamp, request.getDctermsSubject())); + // 412 dcterms:format + if (request.getDctermsFormat() != null) { + handleAttributeList.add( + new HandleAttributeJson(DCTERMS_FORMAT, timestamp, + request.getDctermsFormat().toString())); + } else { + handleAttributeList.add( + new HandleAttributeJson(DCTERMS_FORMAT, timestamp, null)); + } + // 413 Derived from Entity + handleAttributeList.add( + new HandleAttributeJson(DERIVED_FROM_ENTITY, timestamp, request.getDerivedFromEntity())); + // 414 License Name + handleAttributeList.add( + new HandleAttributeJson(LICENSE_NAME, timestamp, request.getLicenseName())); + // 415 License URL + new HandleAttributeJson(LICENSE_URL, timestamp, request.getLicenseName()); + // 416 RightsholderName + new HandleAttributeJson(RIGHTSHOLDER_NAME, timestamp, request.getRightsholderName()); + // 417 Rightsholder PID + new HandleAttributeJson(RIGHTSHOLDER_PID, timestamp, request.getRightsholderPid()); + // 418 RightsholderPidType + if (request.getRightsholderPidType() != null) { + new HandleAttributeJson(RIGHTSHOLDER_PID_TYPE, timestamp, + request.getRightsholderPidType().toString()); + } else { + new HandleAttributeJson(RIGHTSHOLDER_PID_TYPE, timestamp, null); + } + // 419 dcterms:conformsTo + new HandleAttributeJson(DC_TERMS_CONFORMS, timestamp, request.getDctermsConformsTo()); + for (var attribute : handleAttributeList) { + fdoProfile.add(mapper.valueToTree(attribute)); + } + return fdoProfile; + } + + /* Organisation Record Creation */ + public Document prepareOrganisationDocument(OrganisationRequest request, String handle, + Instant timestamp) + throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareNewDoiJsonNodeRecord(request, handle, ORGANISATION, timestamp); + fdoRecord.addAll(prepareOrganisationAttributesFromRequest(request, handle, timestamp)); + return toMongoDbDocument(fdoRecord, handle); + } + + public Document prepareUpdatedOrganisationDocument(OrganisationRequest request, String handle, + Instant timestamp, int issueNumber) + throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareUpdatedDoiJsonNodeRecord(request, handle, ORGANISATION, timestamp, + issueNumber); + fdoRecord.addAll(prepareOrganisationAttributesFromRequest(request, handle, timestamp)); + return toMongoDbDocument(fdoRecord, handle); + } + + private List prepareOrganisationAttributesFromRequest(OrganisationRequest request, + String handle, + Instant timestamp) throws InvalidRequestException { + var fdoProfile = new ArrayList(); + var handleAttributeList = new ArrayList(); + // 101 10320/loc -> includes organisation ROR + var userLocations = concatLocations(request.getLocations(), + List.of(request.getOrganisationIdentifier())); + handleAttributeList.add( + new HandleAttributeJson(LOC, timestamp, setLocations(userLocations, handle, ORGANISATION))); + // 601 Organisation Identifier + handleAttributeList.add( + new HandleAttributeJson(ORGANISATION_ID, timestamp, request.getOrganisationIdentifier())); + // 602 Organisation Identifier type + handleAttributeList.add(new HandleAttributeJson(ORGANISATION_ID_TYPE, timestamp, + request.getOrganisationIdentifierType())); + // 603 Organisation Name + handleAttributeList.add(new HandleAttributeJson(ORGANISATION_NAME, timestamp, + getObjectName(request.getOrganisationIdentifier(), null))); + for (var attribute : handleAttributeList) { + fdoProfile.add(mapper.valueToTree(attribute)); + } + return fdoProfile; + } + + + /* Source System Record Creation */ + public Document prepareSourceSystemDocument(SourceSystemRequest request, String handle, + Instant timestamp) + throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareNewHandleJsonNodeRecord(request, handle, SOURCE_SYSTEM, timestamp); + fdoRecord.addAll(prepareSourceSystemAttributesFromRequest(request, timestamp)); + return toMongoDbDocument(fdoRecord, handle); + } + + public Document prepareUpdatedSourceSystemDocument(SourceSystemRequest request, String handle, + Instant timestamp, int issueNumber) + throws InvalidRequestException, JsonProcessingException { + var fdoRecord = prepareUpdatedHandleJsonNodeRecord(request, handle, SOURCE_SYSTEM, timestamp, + issueNumber); + fdoRecord.addAll(prepareSourceSystemAttributesFromRequest(request, timestamp)); + return toMongoDbDocument(fdoRecord, handle); + } + + private List prepareSourceSystemAttributesFromRequest(SourceSystemRequest request, + Instant timestamp) { + var fdoProfile = new ArrayList(); + fdoProfile.add(mapper.valueToTree( + new HandleAttributeJson(MAS_NAME, timestamp, request.getSourceSystemName()))); + return fdoProfile; + } + public List prepareHandleRecordAttributes(HandleRecordRequest request, byte[] handle, FdoType type) throws InvalidRequestException { List fdoRecord = new ArrayList<>(); @@ -171,7 +751,8 @@ public List prepareHandleRecordAttributes(HandleRecordRequest r // 101: 10320/loc if (type != FdoType.ORGANISATION) { - byte[] loc = setLocations(request.getLocations(), new String(handle, StandardCharsets.UTF_8), + byte[] loc = setLocationsByte(request.getLocations(), + new String(handle, StandardCharsets.UTF_8), type); fdoRecord.add(new HandleAttribute(LOC.index(), handle, LOC.get(), loc)); } @@ -196,7 +777,7 @@ public List prepareHandleRecordAttributes(HandleRecordRequest r fdoRecord.add(new HandleAttribute(PID_ISSUER, handle, request.getPidIssuer())); // 7: pidIssuerName - String pidIssuerName = getObjectName(request.getPidIssuer()); + String pidIssuerName = getObjectName(request.getPidIssuer(), null); fdoRecord.add(new HandleAttribute(PID_ISSUER_NAME, handle, pidIssuerName)); // 8: issuedForAgent @@ -208,7 +789,7 @@ public List prepareHandleRecordAttributes(HandleRecordRequest r fdoRecord.add(new HandleAttribute(ISSUED_FOR_AGENT_NAME, handle, issuedForAgentName)); // 10: pidRecordIssueDate - fdoRecord.add(new HandleAttribute(PID_RECORD_ISSUE_DATE, handle, getDate())); + fdoRecord.add(new HandleAttribute(PID_RECORD_ISSUE_DATE, handle, getDate(Instant.now()))); // 11: pidRecordIssueNumber fdoRecord.add(new HandleAttribute(PID_RECORD_ISSUE_NUMBER, handle, "1")); @@ -223,11 +804,17 @@ public List prepareHandleRecordAttributes(HandleRecordRequest r return fdoRecord; } - private String getObjectName(String url) throws InvalidRequestException { + + private String getObjectName(String url, String name) throws InvalidRequestException { + if (name != null) { + return name; + } if (url.contains(ROR_DOMAIN)) { return pidResolver.getObjectName(getRor(url)); } else if (url.contains(HANDLE_DOMAIN) || url.contains(DOI_DOMAIN)) { return pidResolver.getObjectName(url); + } else if (url.contains(WIKIDATA_DOMAIN)) { + return pidResolver.resolveQid(url.replace(WIKIDATA_DOMAIN, WIKIDATA_API)); } throw new InvalidRequestException(String.format(PROXY_ERROR, url, (ROR_DOMAIN + ", " + HANDLE_DOMAIN + ", or " + DOI_DOMAIN))); @@ -267,11 +854,13 @@ public List prepareMediaObjectAttributes(MediaObjectRequest req var fdoRecord = prepareDoiRecordAttributes(request, handle, FdoType.MEDIA_OBJECT); fdoRecord.add(new HandleAttribute(MEDIA_HOST, handle, request.getMediaHost())); - var mediaHostName = setHostName(request.getMediaHostName(), request.getMediaHost(), handle, + var mediaHostName = setHostNameHandleAttribute(request.getMediaHostName(), + request.getMediaHost(), handle, MEDIA_HOST_NAME); fdoRecord.add(mediaHostName); - if (request.getMediaFormat() != null) { - fdoRecord.add(new HandleAttribute(MEDIA_FORMAT, handle, request.getMediaFormat().toString())); + if (request.getDctermsFormat() != null) { + fdoRecord.add( + new HandleAttribute(DCTERMS_FORMAT, handle, request.getDctermsFormat().toString())); } fdoRecord.add(new HandleAttribute(IS_DERIVED_FROM_SPECIMEN, handle, request.getIsDerivedFromSpecimen().toString())); @@ -294,8 +883,9 @@ public List prepareMediaObjectAttributes(MediaObjectRequest req fdoRecord.add(new HandleAttribute(PRIMARY_MO_ID_TYPE, handle, request.getPrimaryMediaObjectIdType().toString())); } - if (request.getMediaMimeType() != null) { - fdoRecord.add(new HandleAttribute(MEDIA_MIME_TYPE, handle, request.getMediaMimeType())); + if (request.getDcTermsType() != null) { + fdoRecord.add( + new HandleAttribute(DCTERMS_SUBJECT, handle, request.getDcTermsType().toString())); } if (request.getDerivedFromEntity() != null) { fdoRecord.add( @@ -304,10 +894,11 @@ public List prepareMediaObjectAttributes(MediaObjectRequest req if (request.getLicenseName() != null) { fdoRecord.add(new HandleAttribute(LICENSE_NAME, handle, request.getLicenseName())); } - if (request.getLicense() != null) { - fdoRecord.add(new HandleAttribute(LICENSE_URL, handle, request.getLicense())); + if (request.getLicenseUrl() != null) { + fdoRecord.add(new HandleAttribute(LICENSE_URL, handle, request.getLicenseUrl())); } - var rightsholderName = setHostName(request.getRightsholderName(), request.getRightsholderPid(), + var rightsholderName = setHostNameHandleAttribute(request.getRightsholderName(), + request.getRightsholderPid(), handle, RIGHTSHOLDER_NAME); fdoRecord.add(rightsholderName); if (request.getRightsholderPid() != null) { @@ -371,7 +962,7 @@ public List prepareOrganisationAttributes(OrganisationRequest r if (request.getLocations() != null) { objectLocations.addAll(List.of(request.getLocations())); } - byte[] loc = setLocations(objectLocations.toArray(new String[0]), + byte[] loc = setLocationsByte(objectLocations.toArray(new String[0]), new String(handle, StandardCharsets.UTF_8), FdoType.ORGANISATION); fdoRecord.add(new HandleAttribute(LOC.index(), handle, LOC.get(), loc)); @@ -410,7 +1001,8 @@ public List prepareDigitalSpecimenRecordAttributes( fdoRecord.add(new HandleAttribute(SPECIMEN_HOST, handle, request.getSpecimenHost())); // 201: Specimen Host name - var specimenHostName = setHostName(request.getSpecimenHostName(), request.getSpecimenHost(), + var specimenHostName = setHostNameHandleAttribute(request.getSpecimenHostName(), + request.getSpecimenHost(), handle, SPECIMEN_HOST_NAME); fdoRecord.add(specimenHostName); @@ -435,9 +1027,9 @@ public List prepareDigitalSpecimenRecordAttributes( request.getNormalisedPrimarySpecimenObjectId())); // 206: specimenObjectIdAbsenceReason - if (request.getPrimarySpecimenObjectIdAbsenceReason() != null) { + if (request.getSpecimenObjectIdAbsenceReason() != null) { fdoRecord.add(new HandleAttribute(SPECIMEN_OBJECT_ID_ABSENCE_REASON, handle, - request.getPrimarySpecimenObjectIdAbsenceReason())); + request.getSpecimenObjectIdAbsenceReason())); } // 207: otherSpecimenIds @@ -524,7 +1116,7 @@ public List prepareDigitalSpecimenRecordAttributes( return fdoRecord; } - private HandleAttribute setHostName(String hostName, String hostId, byte[] handle, + private HandleAttribute setHostNameHandleAttribute(String hostName, String hostId, byte[] handle, FdoProfile targetAttribute) throws PidResolutionException { if (hostName != null) { return new HandleAttribute(targetAttribute, handle, hostName); @@ -590,7 +1182,7 @@ private List addResolvedNames(Map updateR ArrayList resolvedPidNameAttributes = new ArrayList<>(); for (var resolvableKey : resolvableKeys.entrySet()) { var targetAttribute = RESOLVABLE_KEYS.get(resolvableKey.getKey()); - var resolvedPid = getObjectName(resolvableKey.getValue().asText()); + var resolvedPid = getObjectName(resolvableKey.getValue().asText(), null); resolvedPidNameAttributes.add(new HandleAttribute(FdoProfile.retrieveIndex(targetAttribute), handle, targetAttribute, resolvedPid.getBytes(StandardCharsets.UTF_8))); } @@ -614,7 +1206,7 @@ public List prepareTombstoneAttributes(byte[] handle, private HandleAttribute genLandingPage(byte[] handle) throws InvalidRequestException { var landingPage = new String[]{"Placeholder landing page"}; - var data = setLocations(landingPage, new String(handle, StandardCharsets.UTF_8), + var data = setLocationsByte(landingPage, new String(handle, StandardCharsets.UTF_8), FdoType.TOMBSTONE); return new HandleAttribute(LOC.index(), handle, LOC.get(), data); } @@ -630,7 +1222,7 @@ private JsonNode setLocationXmlFromJson(JsonNode request, String handle, FdoType try { String[] locArr = mapper.treeToValue(locNode, String[].class); requestObjectNode.put(LOC.get(), - new String(setLocations(locArr, handle, type), StandardCharsets.UTF_8)); + new String(setLocationsByte(locArr, handle, type), StandardCharsets.UTF_8)); requestObjectNode.remove(LOC_REQUEST); } catch (IOException e) { throw new InvalidRequestException( @@ -639,11 +1231,82 @@ private JsonNode setLocationXmlFromJson(JsonNode request, String handle, FdoType return requestObjectNode; } - private String getDate() { - return dt.format(Instant.now()); + private String getDate(Instant timestamp) { + return dt.format(timestamp); + } + + private List defaultLocations(String handle, FdoType fdoType) { + switch (fdoType) { + case DIGITAL_SPECIMEN -> { + String api = appProperties.getApiUrl() + "/specimens/" + handle; + String ui = appProperties.getUiUrl() + "/ds/" + handle; + return List.of(api, ui); + } + case MAPPING -> { + return List.of(appProperties.getOrchestrationUrl() + "/mapping/" + handle); + } + case SOURCE_SYSTEM -> { + return List.of(appProperties.getOrchestrationUrl() + "/source-system/" + handle); + } + case MEDIA_OBJECT -> { + String api = appProperties.getApiUrl() + "/digitalMedia/" + handle; + String ui = appProperties.getUiUrl() + "/dm/" + handle; + return List.of(api, ui); + } + case ANNOTATION -> { + return List.of(appProperties.getApiUrl() + "/annotations/" + handle); + } + case MAS -> { + return List.of(appProperties.getOrchestrationUrl() + "/mas/" + handle); + } + default -> { + // Handle, DOI, Organisation (Org locations are all in userLocations) + return Collections.emptyList(); + } + } + } + + private String documentToString(org.w3c.dom.Document document) throws TransformerException { + var transformer = tf.newTransformer(); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + StringWriter writer = new StringWriter(); + transformer.transform(new DOMSource(document), new StreamResult(writer)); + return writer.getBuffer().toString(); } - public byte[] setLocations(String[] userLocations, String handle, FdoType type) + + private String setLocations(@Nullable String[] userLocations, String handle, FdoType type) + throws InvalidRequestException { + DocumentBuilder documentBuilder; + try { + documentBuilder = dbf.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new InvalidRequestException(e.getMessage()); + } + var doc = documentBuilder.newDocument(); + var locations = doc.createElement(LOC_REQUEST); + doc.appendChild(locations); + String[] objectLocations = concatLocations(userLocations, defaultLocations(handle, type)); + if (objectLocations.length == 0) { + return ""; + } + for (int i = 0; i < objectLocations.length; i++) { + var locs = doc.createElement("location"); + locs.setAttribute("id", String.valueOf(i)); + locs.setAttribute("href", objectLocations[i]); + String weight = i < 1 ? "1" : "0"; + locs.setAttribute("weight", weight); + locations.appendChild(locs); + } + try { + return documentToString(doc); + } catch (TransformerException e) { + throw new InvalidRequestException("An error has occurred parsing location data"); + } + } + + // Todo delete + public byte[] setLocationsByte(@Nullable String[] userLocations, String handle, FdoType type) throws InvalidRequestException { DocumentBuilder documentBuilder; @@ -656,7 +1319,7 @@ public byte[] setLocations(String[] userLocations, String handle, FdoType type) var doc = documentBuilder.newDocument(); var locations = doc.createElement(LOC_REQUEST); doc.appendChild(locations); - String[] objectLocations = concatLocations(userLocations, handle, type); + String[] objectLocations = concatLocations(userLocations, defaultLocations(handle, type)); for (int i = 0; i < objectLocations.length; i++) { var locs = doc.createElement("location"); @@ -673,50 +1336,12 @@ public byte[] setLocations(String[] userLocations, String handle, FdoType type) } } - private String[] concatLocations(String[] userLocations, String handle, FdoType type) { - var objectLocations = new ArrayList<>(List.of(defaultLocations(handle, type))); + private String[] concatLocations(String[] userLocations, List defaultLocations) { + var objectLocations = new ArrayList<>(defaultLocations); if (userLocations != null) { objectLocations.addAll(List.of(userLocations)); } return objectLocations.toArray(new String[0]); } - private String[] defaultLocations(String handle, FdoType type) { - switch (type) { - case DIGITAL_SPECIMEN -> { - String api = appProperties.getApiUrl() + "/specimens/" + handle; - String ui = appProperties.getUiUrl() + "/ds/" + handle; - return new String[]{ui, api}; - } - case MAPPING -> { - return new String[]{appProperties.getOrchestrationUrl() + "/mapping/" + handle}; - } - case SOURCE_SYSTEM -> { - return new String[]{appProperties.getOrchestrationUrl() + "/source-system/" + handle}; - } - case MEDIA_OBJECT -> { - String api = appProperties.getApiUrl() + "/digitalMedia/" + handle; - String ui = appProperties.getUiUrl() + "/dm/" + handle; - return new String[]{ui, api}; - } - case ANNOTATION -> { - return new String[]{appProperties.getApiUrl() + "/annotations/" + handle}; - } - case MAS -> { - return new String[]{appProperties.getOrchestrationUrl() + "/mas/" + handle}; - } - default -> { - // Handle, DOI, Organisation (organisation handled separately) - return new String[]{}; - } - } - } - - private String documentToString(Document document) throws TransformerException { - var transformer = tf.newTransformer(); - transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - StringWriter writer = new StringWriter(); - transformer.transform(new DOMSource(document), new StreamResult(writer)); - return writer.getBuffer().toString(); - } } diff --git a/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java b/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java index 8d628864..377a1a55 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java @@ -16,17 +16,21 @@ import eu.dissco.core.handlemanager.domain.fdo.MasRequest; import eu.dissco.core.handlemanager.domain.fdo.OrganisationRequest; import eu.dissco.core.handlemanager.domain.fdo.SourceSystemRequest; +import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiDataLinks; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperWrite; import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.properties.ProfileProperties; +import eu.dissco.core.handlemanager.repository.PidMongoRepository; import eu.dissco.core.handlemanager.repository.PidRepository; +import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import lombok.extern.slf4j.Slf4j; +import org.bson.Document; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; @@ -35,15 +39,22 @@ @Profile(Profiles.HANDLE) public class HandleService extends PidService { + private final PidMongoRepository mongoRepository; + public HandleService(PidRepository pidRepository, FdoRecordService fdoRecordService, - PidNameGeneratorService hf, ObjectMapper mapper, ProfileProperties profileProperties) { + PidNameGeneratorService hf, ObjectMapper mapper, ProfileProperties profileProperties, + PidMongoRepository mongoRepository) { super(pidRepository, fdoRecordService, hf, mapper, profileProperties); + this.mongoRepository = mongoRepository; } // Pid Record Creation @Override public JsonApiWrapperWrite createRecords(List requests) throws InvalidRequestException { - var handles = hf.genHandleList(requests.size()).iterator(); + var handlesBytes = hf.genHandleList(requests.size()); + var handles = handlesBytes.iterator(); + var handleStr = handlesBytes.stream().map(b -> new String(b, StandardCharsets.UTF_8)).toList() + .iterator(); var requestAttributes = requests.stream() .map(request -> request.get(NODE_DATA).get(NODE_ATTRIBUTES)).toList(); var type = getObjectTypeFromJsonNode(requests); @@ -54,7 +65,15 @@ public JsonApiWrapperWrite createRecords(List requests) throws Invalid case DIGITAL_SPECIMEN -> handleAttributes = createDigitalSpecimen(requestAttributes, handles); case DOI -> handleAttributes = createDoi(requestAttributes, handles); - case HANDLE -> handleAttributes = createHandle(requestAttributes, handles); + case HANDLE -> { + var documents = createHandleDocument(requestAttributes, handleStr); + mongoRepository.postBatchHandleRecord(documents); + return new JsonApiWrapperWrite(List.of(new JsonApiDataLinks( + new String(handlesBytes.get(0), StandardCharsets.UTF_8), + HANDLE.getDigitalObjectType(), + null, + null))); + } case MAPPING -> handleAttributes = createMapping(requestAttributes, handles); case MAS -> handleAttributes = createMas(requestAttributes, handles); case MEDIA_OBJECT -> handleAttributes = createMediaObject(requestAttributes, handles); @@ -98,6 +117,19 @@ private List createDoi(List requestAttributes, return handleAttributes; } + private List createHandleDocument(List requestAttributes, + Iterator handleIterator) + throws JsonProcessingException, InvalidRequestException { + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); + for (var request : requestAttributes) { + var requestObject = mapper.treeToValue(request, HandleRecordRequest.class); + fdoRecords.add(fdoRecordService.prepareNewHandleDocument(requestObject, handleIterator.next(), + HANDLE, timestamp)); + } + return fdoRecords; + } + private List createHandle(List requestAttributes, Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { diff --git a/src/test/java/eu/dissco/core/handlemanager/repository/BatchInserterIT.java b/src/test/java/eu/dissco/core/handlemanager/repository/BatchInserterIT.java index 92a52014..85a59c2f 100644 --- a/src/test/java/eu/dissco/core/handlemanager/repository/BatchInserterIT.java +++ b/src/test/java/eu/dissco/core/handlemanager/repository/BatchInserterIT.java @@ -56,7 +56,8 @@ void testBatchInsert() throws Exception { void testBatchInsertIllegalChar() { // Given var attributes = List.of( - new HandleAttribute(FdoProfile.SPECIMEN_HOST, HANDLE.getBytes(StandardCharsets.UTF_8), + new HandleAttribute(FdoProfile.SPECIMEN_HOST, + HANDLE.getBytes(StandardCharsets.UTF_8), "this is \n bad data") ); var created = CREATED.getEpochSecond(); @@ -69,7 +70,8 @@ void testBatchInsertIllegalChar() { @Test void testDelimiterInData() throws Exception { var attributes = List.of(new HandleAttribute( - FdoProfile.SPECIMEN_HOST, HANDLE.getBytes(StandardCharsets.UTF_8), "this, has a comma" + FdoProfile.SPECIMEN_HOST, HANDLE.getBytes(StandardCharsets.UTF_8), + "this, has a comma" )); // When diff --git a/src/test/java/eu/dissco/core/handlemanager/repository/PidRepositoryIT.java b/src/test/java/eu/dissco/core/handlemanager/repository/PidRepositoryIT.java index 73bc3fdd..8b822dd5 100644 --- a/src/test/java/eu/dissco/core/handlemanager/repository/PidRepositoryIT.java +++ b/src/test/java/eu/dissco/core/handlemanager/repository/PidRepositoryIT.java @@ -1,8 +1,40 @@ package eu.dissco.core.handlemanager.repository; +import static eu.dissco.core.handlemanager.database.jooq.Tables.HANDLES; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.HS_ADMIN; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MATERIAL_SAMPLE_TYPE; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.NORMALISED_SPECIMEN_OBJECT_ID; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PID_RECORD_ISSUE_NUMBER; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PID_STATUS; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PRIMARY_SPECIMEN_OBJECT_ID; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SPECIMEN_HOST; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.CREATED; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE_ALT; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.PID_STATUS_TESTVAL; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.SPECIMEN_HOST_TESTVAL; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.genDigitalSpecimenAttributes; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.genDoiRecordAttributes; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.genHandleRecordAttributes; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.genHandleRecordAttributesAltLoc; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.genTombstoneRecordFullAttributes; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.genUpdateRecordAttributesAltLoc; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.BDDMockito.then; + import eu.dissco.core.handlemanager.database.jooq.tables.Handles; import eu.dissco.core.handlemanager.domain.fdo.FdoType; import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.stream.Stream; import org.jooq.Query; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -11,571 +43,560 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.stream.Stream; - -import static eu.dissco.core.handlemanager.database.jooq.Tables.HANDLES; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.*; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.*; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.mockito.BDDMockito.then; - @ExtendWith(MockitoExtension.class) class PidRepositoryIT extends BaseRepositoryIT { - private PidRepository pidRepository; - @Mock - BatchInserter batchInserter; - - @BeforeEach - void setup() { - pidRepository = new PidRepository(context, batchInserter); - } - - @AfterEach - void destroy() { - context.truncate(HANDLES).execute(); - } - - @Test - void testCreateRecord() throws Exception { - // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - List attributesToPost = genHandleRecordAttributes(handle, FdoType.HANDLE); - - // When - pidRepository.postAttributesToDb(CREATED.getEpochSecond(), attributesToPost); - - // Then - then(batchInserter).should().batchCopy(CREATED.getEpochSecond(), attributesToPost); - } - - @Test - void testHandlesExistTrue() { - // Given - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - List rows = List.of(new HandleAttribute(1, handles.get(0), PID_STATUS.get(), - PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8)), - new HandleAttribute(1, handles.get(1), PID_STATUS.get(), - PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8))); - - postAttributes(rows); - - // When - List collisions = pidRepository.getHandlesExist(handles); - - // Then - assertThat(collisions).hasSize(handles.size()); - assert (byteArrListsAreEqual(handles, collisions)); - } - - @Test - void testHandlesExistFalse() { - // Given - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - - // When - List collisions = pidRepository.getHandlesExist(handles); - - // Then - assertThat(collisions).isEmpty(); - assertFalse(byteArrListsAreEqual(handles, collisions)); - } - - @Test - void testHandlesWritableTrue() { - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - List rows = List.of(new HandleAttribute(1, handles.get(0), PID_STATUS.get(), - PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8)), - new HandleAttribute(1, handles.get(1), PID_STATUS.get(), - PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8))); - - postAttributes(rows); - - // When - List collisions = pidRepository.checkHandlesWritable(handles); - - // Then - assertThat(collisions).hasSize(handles.size()); - assert (byteArrListsAreEqual(handles, collisions)); - } - - @Test - void testHandlesWritableFalse() { - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - List rows = List.of(new HandleAttribute(1, handles.get(0), PID_STATUS.get(), - "ARCHIVED".getBytes(StandardCharsets.UTF_8)), - new HandleAttribute(1, handles.get(1), PID_STATUS.get(), - "ARCHIVED".getBytes(StandardCharsets.UTF_8))); - - postAttributes(rows); - - // When - List collisions = pidRepository.checkHandlesWritable(handles); - - // Then - assertThat(collisions).isEmpty(); - assertFalse(byteArrListsAreEqual(handles, collisions)); + private PidRepository pidRepository; + @Mock + BatchInserter batchInserter; + + @BeforeEach + void setup() { + pidRepository = new PidRepository(context, batchInserter); + } + + @AfterEach + void destroy() { + context.truncate(HANDLES).execute(); + } + + @Test + void testCreateRecord() throws Exception { + // Given + byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); + List attributesToPost = genHandleRecordAttributes(handle, FdoType.HANDLE); + + // When + pidRepository.postAttributesToDb(CREATED.getEpochSecond(), attributesToPost); + + // Then + then(batchInserter).should().batchCopy(CREATED.getEpochSecond(), attributesToPost); + } + + @Test + void testHandlesExistTrue() { + // Given + List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), + HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); + List rows = List.of(new HandleAttribute(1, handles.get(0), PID_STATUS.get(), + PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8)), + new HandleAttribute(1, handles.get(1), PID_STATUS.get(), + PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8))); + + postAttributes(rows); + + // When + List collisions = pidRepository.getHandlesExist(handles); + + // Then + assertThat(collisions).hasSize(handles.size()); + assert (byteArrListsAreEqual(handles, collisions)); + } + + @Test + void testHandlesExistFalse() { + // Given + List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), + HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); + + // When + List collisions = pidRepository.getHandlesExist(handles); + + // Then + assertThat(collisions).isEmpty(); + assertFalse(byteArrListsAreEqual(handles, collisions)); + } + + @Test + void testHandlesWritableTrue() { + List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), + HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); + List rows = List.of(new HandleAttribute(1, handles.get(0), PID_STATUS.get(), + PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8)), + new HandleAttribute(1, handles.get(1), PID_STATUS.get(), + PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8))); + + postAttributes(rows); + + // When + List collisions = pidRepository.checkHandlesWritable(handles); + + // Then + assertThat(collisions).hasSize(handles.size()); + assert (byteArrListsAreEqual(handles, collisions)); + } + + @Test + void testHandlesWritableFalse() { + List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), + HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); + List rows = List.of(new HandleAttribute(1, handles.get(0), PID_STATUS.get(), + "ARCHIVED".getBytes(StandardCharsets.UTF_8)), + new HandleAttribute(1, handles.get(1), PID_STATUS.get(), + "ARCHIVED".getBytes(StandardCharsets.UTF_8))); + + postAttributes(rows); + + // When + List collisions = pidRepository.checkHandlesWritable(handles); + + // Then + assertThat(collisions).isEmpty(); + assertFalse(byteArrListsAreEqual(handles, collisions)); + } + + @Test + void testResolveSingleRecord() throws Exception { + // Given + byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); + List responseExpected = genHandleRecordAttributes(handle, FdoType.HANDLE); + postAttributes(responseExpected); + + // When + var responseReceived = pidRepository.resolveHandleAttributes(handle); + + // Then + assertThat(responseReceived).isEqualTo(responseExpected); + } + + @Test + void testGetPrimarySpecimenObjectIds() throws Exception { + // Given + var handle = HANDLE.getBytes(StandardCharsets.UTF_8); + var handleAlt = HANDLE_ALT.getBytes(StandardCharsets.UTF_8); + var postedAttributes = Stream.concat( + genHandleRecordAttributes(handle, FdoType.DIGITAL_SPECIMEN).stream(), + genHandleRecordAttributes(handleAlt, FdoType.DIGITAL_SPECIMEN).stream()).toList(); + postAttributes(postedAttributes); + List expected = new ArrayList<>(); + for (var row : postedAttributes) { + if (row.getType().equals(PRIMARY_SPECIMEN_OBJECT_ID.get())) { + expected.add(row); + } } - - @Test - void testResolveSingleRecord() throws Exception { - // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - List responseExpected = genHandleRecordAttributes(handle, FdoType.HANDLE); - postAttributes(responseExpected); - - // When - var responseReceived = pidRepository.resolveHandleAttributes(handle); - - // Then - assertThat(responseReceived).isEqualTo(responseExpected); + // When + var result = pidRepository.getPrimarySpecimenObjectId(List.of(handle, handleAlt)); + + // Then + assertThat(result).hasSameElementsAs(expected); + } + + @Test + void testResolveBatchRecord() throws Exception { + // Given + List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), + HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); + + List responseExpected = new ArrayList<>(); + for (byte[] handle : handles) { + responseExpected.addAll(genHandleRecordAttributes(handle, FdoType.HANDLE)); } - @Test - void testGetPrimarySpecimenObjectIds() throws Exception { - // Given - var handle = HANDLE.getBytes(StandardCharsets.UTF_8); - var handleAlt = HANDLE_ALT.getBytes(StandardCharsets.UTF_8); - var postedAttributes = Stream.concat( - genHandleRecordAttributes(handle, FdoType.DIGITAL_SPECIMEN).stream(), - genHandleRecordAttributes(handleAlt, FdoType.DIGITAL_SPECIMEN).stream()).toList(); - postAttributes(postedAttributes); - List expected = new ArrayList<>(); - for (var row : postedAttributes) { - if (row.getType().equals(PRIMARY_SPECIMEN_OBJECT_ID.get())) { - expected.add(row); - } - } - // When - var result = pidRepository.getPrimarySpecimenObjectId(List.of(handle, handleAlt)); - - // Then - assertThat(result).hasSameElementsAs(expected); - } + postAttributes(responseExpected); - @Test - void testResolveBatchRecord() throws Exception { - // Given - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); + // When + var responseReceived = pidRepository.resolveHandleAttributes(handles); - List responseExpected = new ArrayList<>(); - for (byte[] handle : handles) { - responseExpected.addAll(genHandleRecordAttributes(handle, FdoType.HANDLE)); - } + // Then + assertThat(responseReceived).isEqualTo(responseExpected); + } - postAttributes(responseExpected); + @Test + void testGetAllHandlesPaging() { + // Given + int pageNum = 1; + int pageSize = 5; - // When - var responseReceived = pidRepository.resolveHandleAttributes(handles); + List handles = genListofHandlesString(pageSize); + List rows = new ArrayList<>(); - // Then - assertThat(responseReceived).isEqualTo(responseExpected); + for (String handle : handles) { + rows.add(new HandleAttribute(1, handle.getBytes(StandardCharsets.UTF_8), PID_STATUS.get(), + PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8))); } + postAttributes(rows); - @Test - void testGetAllHandlesPaging() { - // Given - int pageNum = 1; - int pageSize = 5; + // When + List responseReceived = pidRepository.getAllHandles(pageNum, pageSize); - List handles = genListofHandlesString(pageSize); - List rows = new ArrayList<>(); + // Then + assertThat(responseReceived).hasSameElementsAs(handles); + } - for (String handle : handles) { - rows.add(new HandleAttribute(1, handle.getBytes(StandardCharsets.UTF_8), PID_STATUS.get(), - PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8))); - } - postAttributes(rows); + @Test + void testGetAllHandlesLastPage() { + // Given + int pageNum = 2; + int pageSize = 5; - // When - List responseReceived = pidRepository.getAllHandles(pageNum, pageSize); + List handles = genListofHandlesString(pageSize + 1); + List rows = new ArrayList<>(); - // Then - assertThat(responseReceived).hasSameElementsAs(handles); + for (String handle : handles) { + rows.add(new HandleAttribute(1, handle.getBytes(StandardCharsets.UTF_8), PID_STATUS.get(), + PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8))); } - - @Test - void testGetAllHandlesLastPage() { - // Given - int pageNum = 2; - int pageSize = 5; - - List handles = genListofHandlesString(pageSize + 1); - List rows = new ArrayList<>(); - - for (String handle : handles) { - rows.add(new HandleAttribute(1, handle.getBytes(StandardCharsets.UTF_8), PID_STATUS.get(), - PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8))); - } - postAttributes(rows); - - // When - List responseReceived = pidRepository.getAllHandles(pageNum, pageSize); - - // Then - assertThat(responseReceived).hasSize(1); + postAttributes(rows); + + // When + List responseReceived = pidRepository.getAllHandles(pageNum, pageSize); + + // Then + assertThat(responseReceived).hasSize(1); + } + + @Test + void testGetAllHandlesPagingByPidStatus() { + // Given + int pageNum = 1; + int pageSize = 5; + byte[] pidStatusTarget = "ARCHIVED".getBytes(StandardCharsets.UTF_8); + + List handles = genListofHandlesString(pageSize + 2); + List responseExpected = handles.subList(0, pageSize); + List extraHandles = handles.subList(pageSize, handles.size()); + List rows = new ArrayList<>(); + + for (String handle : responseExpected) { + rows.add(new HandleAttribute(1, handle.getBytes(StandardCharsets.UTF_8), PID_STATUS.get(), + pidStatusTarget)); } - - @Test - void testGetAllHandlesPagingByPidStatus() { - // Given - int pageNum = 1; - int pageSize = 5; - byte[] pidStatusTarget = "ARCHIVED".getBytes(StandardCharsets.UTF_8); - - List handles = genListofHandlesString(pageSize + 2); - List responseExpected = handles.subList(0, pageSize); - List extraHandles = handles.subList(pageSize, handles.size()); - List rows = new ArrayList<>(); - - for (String handle : responseExpected) { - rows.add(new HandleAttribute(1, handle.getBytes(StandardCharsets.UTF_8), PID_STATUS.get(), - pidStatusTarget)); - } - for (String handle : extraHandles) { - rows.add(new HandleAttribute(1, handle.getBytes(StandardCharsets.UTF_8), PID_STATUS.get(), - PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8))); - } - postAttributes(rows); - - // When - List responseReceived = pidRepository.getAllHandles(pidStatusTarget, pageNum, pageSize); - - // Then - assertThat(responseReceived).hasSameElementsAs(responseExpected); + for (String handle : extraHandles) { + rows.add(new HandleAttribute(1, handle.getBytes(StandardCharsets.UTF_8), PID_STATUS.get(), + PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8))); } - - @Test - void testSearchByPhysicalIdentifierFullRecord() { - // Given - var targetPhysicalIdentifier = NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL.getBytes( - StandardCharsets.UTF_8); - List responseExpected = new ArrayList<>(); - responseExpected.add(new HandleAttribute(1, HANDLE.getBytes(StandardCharsets.UTF_8), - NORMALISED_SPECIMEN_OBJECT_ID.get(), targetPhysicalIdentifier)); - responseExpected.add( - new HandleAttribute(2, HANDLE.getBytes(StandardCharsets.UTF_8), SPECIMEN_HOST.get(), - SPECIMEN_HOST_TESTVAL.getBytes( - StandardCharsets.UTF_8))); - - List nonTargetAttributes = new ArrayList<>(); - nonTargetAttributes.add(new HandleAttribute(1, HANDLE_ALT.getBytes(StandardCharsets.UTF_8), - NORMALISED_SPECIMEN_OBJECT_ID.get(), "A".getBytes( + postAttributes(rows); + + // When + List responseReceived = pidRepository.getAllHandles(pidStatusTarget, pageNum, pageSize); + + // Then + assertThat(responseReceived).hasSameElementsAs(responseExpected); + } + + @Test + void testSearchByPhysicalIdentifierFullRecord() { + // Given + var targetPhysicalIdentifier = NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL.getBytes( + StandardCharsets.UTF_8); + List responseExpected = new ArrayList<>(); + responseExpected.add(new HandleAttribute(1, HANDLE.getBytes(StandardCharsets.UTF_8), + NORMALISED_SPECIMEN_OBJECT_ID.get(), targetPhysicalIdentifier)); + responseExpected.add( + new HandleAttribute(2, HANDLE.getBytes(StandardCharsets.UTF_8), SPECIMEN_HOST.get(), + SPECIMEN_HOST_TESTVAL.getBytes( StandardCharsets.UTF_8))); - postAttributes(responseExpected); - postAttributes(nonTargetAttributes); - - // When - var responseReceived = pidRepository.searchByNormalisedPhysicalIdentifierFullRecord( - List.of(targetPhysicalIdentifier)); - - // Then - assertThat(responseReceived).hasSameElementsAs(responseExpected); + List nonTargetAttributes = new ArrayList<>(); + nonTargetAttributes.add(new HandleAttribute(1, HANDLE_ALT.getBytes(StandardCharsets.UTF_8), + NORMALISED_SPECIMEN_OBJECT_ID.get(), "A".getBytes( + StandardCharsets.UTF_8))); + + postAttributes(responseExpected); + postAttributes(nonTargetAttributes); + + // When + var responseReceived = pidRepository.searchByNormalisedPhysicalIdentifierFullRecord( + List.of(targetPhysicalIdentifier)); + + // Then + assertThat(responseReceived).hasSameElementsAs(responseExpected); + } + + @Test + void testSearchByPhysicalSpecimenId() throws Exception { + //Given + var handle = HANDLE.getBytes(StandardCharsets.UTF_8); + var dbRecord = List.of(new HandleAttribute(NORMALISED_SPECIMEN_OBJECT_ID.index(), handle, + NORMALISED_SPECIMEN_OBJECT_ID.get(), + NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL.getBytes(StandardCharsets.UTF_8))); + + postAttributes(genDoiRecordAttributes(handle, FdoType.DOI)); + postAttributes(dbRecord); + + // When + var response = pidRepository.searchByNormalisedPhysicalIdentifier( + List.of(NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL.getBytes(StandardCharsets.UTF_8))); + + // Then + assertThat(response).isEqualTo(dbRecord); + } + + @Test + void testSearchByPhysicalSpecimenIdIsArchived() { + //Given + var handle = HANDLE.getBytes(StandardCharsets.UTF_8); + var dbRecord = List.of(new HandleAttribute(NORMALISED_SPECIMEN_OBJECT_ID, handle, + NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL), + new HandleAttribute(PID_STATUS, handle, "ARCHIVED")); + + postAttributes(dbRecord); + + // When + var response = pidRepository.searchByNormalisedPhysicalIdentifier( + List.of(NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL.getBytes(StandardCharsets.UTF_8))); + + // Then + assertThat(response).isEmpty(); + } + + @Test + void testUpdateRecord() throws Exception { + // Given + byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); + List originalRecord = genHandleRecordAttributes(handle); + List recordUpdate = genUpdateRecordAttributesAltLoc(handle); + var responseExpected = incrementVersion(genHandleRecordAttributesAltLoc(handle), true); + postAttributes(originalRecord); + + // When + pidRepository.updateRecord(CREATED.getEpochSecond(), recordUpdate, true); + var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, + Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) + .where(Handles.HANDLES.HANDLE.eq(handle)).and(Handles.HANDLES.TYPE.notEqual( + HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN + .fetch(this::mapToAttribute); + + // Then + assertThat(responseReceived).hasSameElementsAs(responseExpected); + } + + @Test + void testUpdateRecordBatch() throws Exception { + + // Given + List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), + HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); + + List> updateAttributes = new ArrayList<>(); + List responseExpected = new ArrayList<>(); + for (byte[] handle : handles) { + postAttributes(genHandleRecordAttributes(handle)); + updateAttributes.add(genUpdateRecordAttributesAltLoc(handle)); + responseExpected.addAll(incrementVersion(genHandleRecordAttributesAltLoc(handle), true)); } - @Test - void testSearchByPhysicalSpecimenId() throws Exception { - //Given - var handle = HANDLE.getBytes(StandardCharsets.UTF_8); - var dbRecord = List.of(new HandleAttribute(NORMALISED_SPECIMEN_OBJECT_ID.index(), handle, - NORMALISED_SPECIMEN_OBJECT_ID.get(), - NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL.getBytes(StandardCharsets.UTF_8))); - - postAttributes(genDoiRecordAttributes(handle, FdoType.DOI)); - postAttributes(dbRecord); - - // When - var response = pidRepository.searchByNormalisedPhysicalIdentifier( - List.of(NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL.getBytes(StandardCharsets.UTF_8))); - - // Then - assertThat(response).isEqualTo(dbRecord); + // When + pidRepository.updateRecordBatch(CREATED.getEpochSecond(), updateAttributes, true); + var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, + Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) + .where(Handles.HANDLES.HANDLE.in(handles)).and(Handles.HANDLES.TYPE.notEqual( + HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN + .fetch(this::mapToAttribute); + + // Then + assertThat(responseReceived).hasSameElementsAs(responseExpected); + } + + @Test + void testUpdateRecordBatchNoIncrement() throws Exception { + + // Given + List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), + HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); + + List> updateAttributes = new ArrayList<>(); + List responseExpected = new ArrayList<>(); + for (byte[] handle : handles) { + postAttributes(genHandleRecordAttributes(handle)); + updateAttributes.add(genUpdateRecordAttributesAltLoc(handle)); + responseExpected.addAll(incrementVersion(genHandleRecordAttributesAltLoc(handle), false)); } - @Test - void testSearchByPhysicalSpecimenIdIsArchived() { - //Given - var handle = HANDLE.getBytes(StandardCharsets.UTF_8); - var dbRecord = List.of(new HandleAttribute(NORMALISED_SPECIMEN_OBJECT_ID, handle, - NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL), - new HandleAttribute(PID_STATUS, handle, "ARCHIVED")); - - postAttributes(dbRecord); - - // When - var response = pidRepository.searchByNormalisedPhysicalIdentifier( - List.of(NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL.getBytes(StandardCharsets.UTF_8))); - - // Then - assertThat(response).isEmpty(); - } - - @Test - void testUpdateRecord() throws Exception { - // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - List originalRecord = genHandleRecordAttributes(handle); - List recordUpdate = genUpdateRecordAttributesAltLoc(handle); - var responseExpected = incrementVersion(genHandleRecordAttributesAltLoc(handle), true); - postAttributes(originalRecord); - - // When - pidRepository.updateRecord(CREATED.getEpochSecond(), recordUpdate, true); - var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) - .where(Handles.HANDLES.HANDLE.eq(handle)).and(Handles.HANDLES.TYPE.notEqual( - HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN - .fetch(this::mapToAttribute); - - // Then - assertThat(responseReceived).hasSameElementsAs(responseExpected); - } - - @Test - void testUpdateRecordBatch() throws Exception { - - // Given - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - - List> updateAttributes = new ArrayList<>(); - List responseExpected = new ArrayList<>(); - for (byte[] handle : handles) { - postAttributes(genHandleRecordAttributes(handle)); - updateAttributes.add(genUpdateRecordAttributesAltLoc(handle)); - responseExpected.addAll(incrementVersion(genHandleRecordAttributesAltLoc(handle), true)); - } - - // When - pidRepository.updateRecordBatch(CREATED.getEpochSecond(), updateAttributes, true); - var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) - .where(Handles.HANDLES.HANDLE.in(handles)).and(Handles.HANDLES.TYPE.notEqual( - HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN - .fetch(this::mapToAttribute); - - // Then - assertThat(responseReceived).hasSameElementsAs(responseExpected); + // When + pidRepository.updateRecordBatch(CREATED.getEpochSecond(), updateAttributes, false); + var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, + Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) + .where(Handles.HANDLES.HANDLE.in(handles)).and(Handles.HANDLES.TYPE.notEqual( + HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN + .fetch(this::mapToAttribute); + + // Then + assertThat(responseReceived).hasSameElementsAs(responseExpected); + } + + @Test + void testArchiveRecordBatch() throws Exception { + + // Given + List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), + HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); + List handlesStr = List.of(HANDLE, HANDLE_ALT); + + List tombstoneAttributes = new ArrayList<>(); + for (var handle : handles) { + postAttributes(genDigitalSpecimenAttributes(handle)); + tombstoneAttributes.addAll(incrementVersion(genTombstoneRecordFullAttributes(handle), true)); } - @Test - void testUpdateRecordBatchNoIncrement() throws Exception { - - // Given - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - - List> updateAttributes = new ArrayList<>(); - List responseExpected = new ArrayList<>(); - for (byte[] handle : handles) { - postAttributes(genHandleRecordAttributes(handle)); - updateAttributes.add(genUpdateRecordAttributesAltLoc(handle)); - responseExpected.addAll(incrementVersion(genHandleRecordAttributesAltLoc(handle), false)); - } - - // When - pidRepository.updateRecordBatch(CREATED.getEpochSecond(), updateAttributes, false); - var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) - .where(Handles.HANDLES.HANDLE.in(handles)).and(Handles.HANDLES.TYPE.notEqual( - HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN - .fetch(this::mapToAttribute); - - // Then - assertThat(responseReceived).hasSameElementsAs(responseExpected); - } - - @Test - void testArchiveRecordBatch() throws Exception { - - // Given - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - List handlesStr = List.of(HANDLE, HANDLE_ALT); - - List tombstoneAttributes = new ArrayList<>(); - for (var handle : handles) { - postAttributes(genDigitalSpecimenAttributes(handle)); - tombstoneAttributes.addAll(incrementVersion(genTombstoneRecordFullAttributes(handle), true)); - } - - // When - pidRepository.archiveRecords(CREATED.getEpochSecond(), tombstoneAttributes, handlesStr); - var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) - .where(Handles.HANDLES.HANDLE.in(handles)).and(Handles.HANDLES.TYPE.notEqual( - HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN - .fetch(this::mapToAttribute); - - // Then - assertThat(responseReceived).hasSameElementsAs(tombstoneAttributes); - } - - @Test - void testArchiveRecord() throws Exception { - // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - List originalRecord = genDigitalSpecimenAttributes(handle); - var tombstoneAttributes = incrementVersion(genTombstoneRecordFullAttributes(handle), true); - - postAttributes(originalRecord); - - // When - pidRepository.archiveRecords(CREATED.getEpochSecond(), tombstoneAttributes, List.of(HANDLE)); - var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) - .where(Handles.HANDLES.HANDLE.eq(handle)).and(Handles.HANDLES.TYPE.notEqual( - HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN - .fetch(this::mapToAttribute); - - // Then - assertThat(responseReceived).hasSameElementsAs(tombstoneAttributes); - } - - @Test - void testRollbackHandleCreation() throws Exception { - // Given - var expected = genHandleRecordAttributes(HANDLE.getBytes(StandardCharsets.UTF_8)); - postAttributes(expected); - postAttributes(genHandleRecordAttributes(HANDLE_ALT.getBytes(StandardCharsets.UTF_8))); - - // When - pidRepository.rollbackHandles(List.of(HANDLE_ALT)); - var response = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(HANDLES).fetch(this::mapToAttribute); + // When + pidRepository.archiveRecords(CREATED.getEpochSecond(), tombstoneAttributes, handlesStr); + var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, + Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) + .where(Handles.HANDLES.HANDLE.in(handles)).and(Handles.HANDLES.TYPE.notEqual( + HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN + .fetch(this::mapToAttribute); + + // Then + assertThat(responseReceived).hasSameElementsAs(tombstoneAttributes); + } + + @Test + void testArchiveRecord() throws Exception { + // Given + byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); + List originalRecord = genDigitalSpecimenAttributes(handle); + var tombstoneAttributes = incrementVersion(genTombstoneRecordFullAttributes(handle), true); + + postAttributes(originalRecord); + + // When + pidRepository.archiveRecords(CREATED.getEpochSecond(), tombstoneAttributes, List.of(HANDLE)); + var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, + Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) + .where(Handles.HANDLES.HANDLE.eq(handle)).and(Handles.HANDLES.TYPE.notEqual( + HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN + .fetch(this::mapToAttribute); + + // Then + assertThat(responseReceived).hasSameElementsAs(tombstoneAttributes); + } + + @Test + void testRollbackHandleCreation() throws Exception { + // Given + var expected = genHandleRecordAttributes(HANDLE.getBytes(StandardCharsets.UTF_8)); + postAttributes(expected); + postAttributes(genHandleRecordAttributes(HANDLE_ALT.getBytes(StandardCharsets.UTF_8))); + + // When + pidRepository.rollbackHandles(List.of(HANDLE_ALT)); + var response = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, + Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(HANDLES).fetch(this::mapToAttribute); + + // Then + assertThat(response).hasSameElementsAs(expected); + } + + @Test + void testRollbackHandleUpdate() throws Exception { + // Given + byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); + List originalRecord = genHandleRecordAttributes(handle); + List recordUpdate = genUpdateRecordAttributesAltLoc(handle); + var responseExpected = incrementVersion(genHandleRecordAttributesAltLoc(handle), false); + postAttributes(originalRecord); + + // When + pidRepository.updateRecord(CREATED.getEpochSecond(), recordUpdate, false); + var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, + Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) + .where(Handles.HANDLES.HANDLE.eq(handle)).and(Handles.HANDLES.TYPE.notEqual( + HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN + .fetch(this::mapToAttribute); + + // Then + assertThat(responseReceived).hasSameElementsAs(responseExpected); + } + + @Test + void testPostAndUpdateHandles() throws Exception { + // Given + var handle = HANDLE.getBytes(StandardCharsets.UTF_8); + var handleAlt = HANDLE_ALT.getBytes(StandardCharsets.UTF_8); + var existingRecord = genHandleRecordAttributes(handle, FdoType.HANDLE); + postAttributes(existingRecord); + var updatedRecord = new ArrayList<>(existingRecord); + updatedRecord.add( + new HandleAttribute(MATERIAL_SAMPLE_TYPE.index(), handle, MATERIAL_SAMPLE_TYPE.get(), + "digital".getBytes(StandardCharsets.UTF_8))); + updatedRecord.add( + new HandleAttribute(PID_RECORD_ISSUE_NUMBER.index(), handle, PID_RECORD_ISSUE_NUMBER.get(), + String.valueOf(2).getBytes( + StandardCharsets.UTF_8))); + updatedRecord.remove( + new HandleAttribute(PID_RECORD_ISSUE_NUMBER.index(), handle, PID_RECORD_ISSUE_NUMBER.get(), + String.valueOf(1).getBytes( + StandardCharsets.UTF_8))); - // Then - assertThat(response).hasSameElementsAs(expected); + var newRecord = genHandleRecordAttributes(handleAlt, FdoType.HANDLE); + var expected = Stream.concat(updatedRecord.stream(), newRecord.stream()).toList(); + + // When + pidRepository.postAndUpdateHandles(CREATED.getEpochSecond(), newRecord, List.of(updatedRecord)); + var response = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, + Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(HANDLES).fetch(this::mapToAttribute); + + // Then + assertThat(response).hasSameElementsAs(expected); + } + + private void postAttributes(List rows) { + List queryList = new ArrayList<>(); + for (var handleAttribute : rows) { + var query = context.insertInto(Handles.HANDLES) + .set(Handles.HANDLES.HANDLE, handleAttribute.getHandle()) + .set(Handles.HANDLES.IDX, handleAttribute.getIndex()) + .set(Handles.HANDLES.TYPE, handleAttribute.getType().getBytes(StandardCharsets.UTF_8)) + .set(Handles.HANDLES.DATA, handleAttribute.getData()).set(Handles.HANDLES.TTL, 86400) + .set(Handles.HANDLES.TIMESTAMP, CREATED.getEpochSecond()) + .set(Handles.HANDLES.ADMIN_READ, true).set(Handles.HANDLES.ADMIN_WRITE, true) + .set(Handles.HANDLES.PUB_READ, true).set(Handles.HANDLES.PUB_WRITE, false); + queryList.add(query); } + context.batch(queryList).execute(); + } - @Test - void testRollbackHandleUpdate() throws Exception { - // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - List originalRecord = genHandleRecordAttributes(handle); - List recordUpdate = genUpdateRecordAttributesAltLoc(handle); - var responseExpected = incrementVersion(genHandleRecordAttributesAltLoc(handle), false); - postAttributes(originalRecord); - - // When - pidRepository.updateRecord(CREATED.getEpochSecond(), recordUpdate, false); - var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) - .where(Handles.HANDLES.HANDLE.eq(handle)).and(Handles.HANDLES.TYPE.notEqual( - HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN - .fetch(this::mapToAttribute); - - // Then - assertThat(responseReceived).hasSameElementsAs(responseExpected); + private boolean byteArrListsAreEqual(List a, List b) { + if (a.size() != b.size()) { + return false; } - @Test - void testPostAndUpdateHandles() throws Exception { - // Given - var handle = HANDLE.getBytes(StandardCharsets.UTF_8); - var handleAlt = HANDLE_ALT.getBytes(StandardCharsets.UTF_8); - var existingRecord = genHandleRecordAttributes(handle, FdoType.HANDLE); - postAttributes(existingRecord); - var updatedRecord = new ArrayList<>(existingRecord); - updatedRecord.add( - new HandleAttribute(MATERIAL_SAMPLE_TYPE.index(), handle, MATERIAL_SAMPLE_TYPE.get(), - "digital".getBytes(StandardCharsets.UTF_8))); - updatedRecord.add( - new HandleAttribute(PID_RECORD_ISSUE_NUMBER.index(), handle, PID_RECORD_ISSUE_NUMBER.get(), - String.valueOf(2).getBytes( - StandardCharsets.UTF_8))); - updatedRecord.remove( - new HandleAttribute(PID_RECORD_ISSUE_NUMBER.index(), handle, PID_RECORD_ISSUE_NUMBER.get(), - String.valueOf(1).getBytes( - StandardCharsets.UTF_8))); - - var newRecord = genHandleRecordAttributes(handleAlt, FdoType.HANDLE); - var expected = Stream.concat(updatedRecord.stream(), newRecord.stream()).toList(); - - // When - pidRepository.postAndUpdateHandles(CREATED.getEpochSecond(), newRecord, List.of(updatedRecord)); - var response = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(HANDLES).fetch(this::mapToAttribute); - - // Then - assertThat(response).hasSameElementsAs(expected); - } + List aStr = new ArrayList<>(); + List bStr = new ArrayList<>(); - private void postAttributes(List rows) { - List queryList = new ArrayList<>(); - for (var handleAttribute : rows) { - var query = context.insertInto(Handles.HANDLES) - .set(Handles.HANDLES.HANDLE, handleAttribute.getHandle()) - .set(Handles.HANDLES.IDX, handleAttribute.getIndex()) - .set(Handles.HANDLES.TYPE, handleAttribute.getType().getBytes(StandardCharsets.UTF_8)) - .set(Handles.HANDLES.DATA, handleAttribute.getData()).set(Handles.HANDLES.TTL, 86400) - .set(Handles.HANDLES.TIMESTAMP, CREATED.getEpochSecond()) - .set(Handles.HANDLES.ADMIN_READ, true).set(Handles.HANDLES.ADMIN_WRITE, true) - .set(Handles.HANDLES.PUB_READ, true).set(Handles.HANDLES.PUB_WRITE, false); - queryList.add(query); - } - context.batch(queryList).execute(); + for (int i = 0; i < a.size(); i++) { + aStr.add(new String(a.get(i))); + bStr.add(new String(b.get(i))); } - private boolean byteArrListsAreEqual(List a, List b) { - if (a.size() != b.size()) { - return false; - } + Collections.sort(aStr); + Collections.sort(bStr); - List aStr = new ArrayList<>(); - List bStr = new ArrayList<>(); + return aStr.equals(bStr); + } - for (int i = 0; i < a.size(); i++) { - aStr.add(new String(a.get(i))); - bStr.add(new String(b.get(i))); - } + private List genListofHandlesString(int numberOfHandles) { + Random random = new Random(); + int length = 3; + char[] buffer = new char[length]; + char[] symbols = "ABCDEFGHJKLMNPQRSTUVWXYZ1234567890".toCharArray(); + Set handles = new HashSet<>(); - Collections.sort(aStr); - Collections.sort(bStr); - - return aStr.equals(bStr); - } - - private List genListofHandlesString(int numberOfHandles) { - Random random = new Random(); - int length = 3; - char[] buffer = new char[length]; - char[] symbols = "ABCDEFGHJKLMNPQRSTUVWXYZ1234567890".toCharArray(); - Set handles = new HashSet<>(); - - while (handles.size() < numberOfHandles) { - for (int j = 0; j < length; j++) { - buffer[j] = symbols[random.nextInt(symbols.length)]; - } - handles.add(new String(buffer)); - } - return new ArrayList<>(handles); + while (handles.size() < numberOfHandles) { + for (int j = 0; j < length; j++) { + buffer[j] = symbols[random.nextInt(symbols.length)]; + } + handles.add(new String(buffer)); } - - private List incrementVersion(List handleAttributes, - boolean increaseVersionNum) { - for (int i = 0; i < handleAttributes.size(); i++) { - if (handleAttributes.get(i).getType().equals(PID_RECORD_ISSUE_NUMBER.get())) { - var removedRecord = handleAttributes.remove(i); - var currentVersion = Integer.parseInt(new String(removedRecord.getData())); - var newVersionNum = increaseVersionNum ? currentVersion + 1 : currentVersion; - byte[] issueNum = String.valueOf(newVersionNum).getBytes(StandardCharsets.UTF_8); - handleAttributes.add(i, - new HandleAttribute(removedRecord.getIndex(), removedRecord.getHandle(), - removedRecord.getType(), - issueNum)); - } - } - return handleAttributes; + return new ArrayList<>(handles); + } + + private List incrementVersion(List handleAttributes, + boolean increaseVersionNum) { + for (int i = 0; i < handleAttributes.size(); i++) { + if (handleAttributes.get(i).getType().equals(PID_RECORD_ISSUE_NUMBER.get())) { + var removedRecord = handleAttributes.remove(i); + var currentVersion = Integer.parseInt(new String(removedRecord.getData())); + var newVersionNum = increaseVersionNum ? currentVersion + 1 : currentVersion; + byte[] issueNum = String.valueOf(newVersionNum).getBytes(StandardCharsets.UTF_8); + handleAttributes.add(i, + new HandleAttribute(removedRecord.getIndex(), removedRecord.getHandle(), + removedRecord.getType(), + issueNum)); + } } + return handleAttributes; + } } diff --git a/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java index 433f092d..679a1ff1 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java @@ -647,7 +647,8 @@ void testUpdateAttributesOtherSpecimenIds() throws Exception { ] """)); var expected = List.of( - new HandleAttribute(FdoProfile.OTHER_SPECIMEN_IDS, HANDLE.getBytes(StandardCharsets.UTF_8), + new HandleAttribute(FdoProfile.OTHER_SPECIMEN_IDS, + HANDLE.getBytes(StandardCharsets.UTF_8), expectedStr)); // When diff --git a/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java b/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java index 45c46235..c9ded575 100644 --- a/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java +++ b/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java @@ -3,6 +3,8 @@ import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.ANNOTATION_HASH; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.BASE_TYPE_OF_SPECIMEN; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.CATALOG_IDENTIFIER; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DCTERMS_FORMAT; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DCTERMS_SUBJECT; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DCTERMS_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DC_TERMS_CONFORMS; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DERIVED_FROM_ENTITY; @@ -26,10 +28,8 @@ import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MAS_NAME; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MATERIAL_OR_DIGITAL_ENTITY; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MATERIAL_SAMPLE_TYPE; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MEDIA_FORMAT; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MEDIA_HOST; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MEDIA_HOST_NAME; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MEDIA_MIME_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MOTIVATION; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.NORMALISED_SPECIMEN_OBJECT_ID; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.ORGANISATION_ID; @@ -407,11 +407,11 @@ public static List genDigitalSpecimenAttributes(byte[] handle, request.getNormalisedPrimarySpecimenObjectId())); // 206: specimenObjectIdAbsenceReason - if (request.getPrimarySpecimenObjectIdAbsenceReason() != null) { + if (request.getSpecimenObjectIdAbsenceReason() != null) { fdoRecord.add( new HandleAttribute(SPECIMEN_OBJECT_ID_ABSENCE_REASON.index(), handle, SPECIMEN_OBJECT_ID_ABSENCE_REASON.get(), - request.getPrimarySpecimenObjectIdAbsenceReason() + request.getSpecimenObjectIdAbsenceReason() .getBytes(StandardCharsets.UTF_8))); } @@ -540,9 +540,9 @@ public static List genMediaObjectAttributes(byte[] handle, } else { fdoRecord.add(new HandleAttribute(MEDIA_HOST_NAME, handle, request.getMediaHostName())); } - if (request.getMediaFormat() != null) { + if (request.getDctermsFormat() != null) { fdoRecord.add( - new HandleAttribute(MEDIA_FORMAT, handle, request.getMediaFormat().toString())); + new HandleAttribute(DCTERMS_FORMAT, handle, request.getDctermsFormat().toString())); } fdoRecord.add(new HandleAttribute(IS_DERIVED_FROM_SPECIMEN, handle, request.getIsDerivedFromSpecimen().toString())); @@ -579,9 +579,9 @@ public static List genMediaObjectAttributes(byte[] handle, new HandleAttribute(DCTERMS_TYPE, handle, request.getDcTermsType().toString())); } - if (request.getMediaMimeType() != null) { + if (request.getDctermsSubject() != null) { fdoRecord.add( - new HandleAttribute(MEDIA_MIME_TYPE, handle, request.getMediaMimeType())); + new HandleAttribute(DCTERMS_SUBJECT, handle, request.getDctermsSubject())); } if (request.getDerivedFromEntity() != null) { fdoRecord.add( @@ -592,9 +592,9 @@ public static List genMediaObjectAttributes(byte[] handle, fdoRecord.add( new HandleAttribute(LICENSE_NAME, handle, request.getLicenseName())); } - if (request.getLicense() != null) { + if (request.getLicenseUrl() != null) { fdoRecord.add( - new HandleAttribute(LICENSE_URL, handle, request.getLicense())); + new HandleAttribute(LICENSE_URL, handle, request.getLicenseUrl())); } if (request.getRightsholderName() != null) { fdoRecord.add( From 3e9f6549a89368874a0601409429ba60edab9bcf Mon Sep 17 00:00:00 2001 From: southeo Date: Wed, 3 Jul 2024 02:05:15 +0200 Subject: [PATCH 02/15] Add mongo layer --- .../repsitoryobjects/AdminHandleData.java | 23 +++++++++++++++++++ .../repsitoryobjects/StringHandleData.java | 10 ++++++++ .../security/WebSecurityConfig.java | 2 +- 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/AdminHandleData.java create mode 100644 src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/StringHandleData.java diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/AdminHandleData.java b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/AdminHandleData.java new file mode 100644 index 00000000..9f1ed05e --- /dev/null +++ b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/AdminHandleData.java @@ -0,0 +1,23 @@ +package eu.dissco.core.handlemanager.domain.repsitoryobjects; + +import lombok.Value; + +@Value +public class AdminHandleData implements HandleData { + + String format = "admin"; + AdminValue value; + + public AdminHandleData(String prefix) { + this.value = new AdminValue("0.NA/" + prefix); + } + + @Value + protected static class AdminValue { + + String handle; + int index = 200; + String permissions = "011111110011"; + } + +} diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/StringHandleData.java b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/StringHandleData.java new file mode 100644 index 00000000..fa07a9eb --- /dev/null +++ b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/StringHandleData.java @@ -0,0 +1,10 @@ +package eu.dissco.core.handlemanager.domain.repsitoryobjects; + +import lombok.Value; + +@Value +public class StringHandleData implements HandleData { + + String format = "string"; + String value; +} diff --git a/src/main/java/eu/dissco/core/handlemanager/security/WebSecurityConfig.java b/src/main/java/eu/dissco/core/handlemanager/security/WebSecurityConfig.java index b4b3540f..e2488074 100644 --- a/src/main/java/eu/dissco/core/handlemanager/security/WebSecurityConfig.java +++ b/src/main/java/eu/dissco/core/handlemanager/security/WebSecurityConfig.java @@ -15,7 +15,7 @@ @RequiredArgsConstructor @Configuration @EnableWebSecurity -public class WebSecurityConfig { +public class WebSecurityConfig { private final JwtAuthConverter jwtAuthConverter; From e88d17adb0ccc6156ce124b0bfdb9e6b48a1e69e Mon Sep 17 00:00:00 2001 From: southeo Date: Wed, 3 Jul 2024 02:51:28 +0200 Subject: [PATCH 03/15] Update doi service --- .../handlemanager/domain/fdo/FdoType.java | 23 +- .../repository/PidRepository.java | 5 +- .../handlemanager/service/DoiService.java | 25 +- .../service/FdoRecordService.java | 405 +----------------- .../handlemanager/service/HandleService.java | 152 +++---- .../service/PidNameGeneratorService.java | 101 ++--- .../handlemanager/service/PidService.java | 47 +- .../handlemanager/service/DoiServiceTest.java | 5 +- 8 files changed, 181 insertions(+), 582 deletions(-) diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoType.java b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoType.java index e7c77b53..5273fd4e 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoType.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoType.java @@ -1,5 +1,8 @@ package eu.dissco.core.handlemanager.domain.fdo; +import static eu.dissco.core.handlemanager.service.FdoRecordService.DOI_DOMAIN; +import static eu.dissco.core.handlemanager.service.FdoRecordService.HANDLE_DOMAIN; + import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -11,52 +14,52 @@ public enum FdoType { "Handle Kernel", "https://hdl.handle.net/21.T11148/532ce6796e2828dd2be6", "https://hdl.handle.net/21.T11148/532ce6796e2828dd2be6", - "https://hdl.handle.net/"), + HANDLE_DOMAIN), @JsonProperty("https://hdl.handle.net/21.T11148/527856fd709ec8c5bc8c") DOI( "DOI Kernel", "https://hdl.handle.net/21.T11148/527856fd709ec8c5bc8c", "https://hdl.handle.net/21.T11148/527856fd709ec8c5bc8c", - "https://doi.org/"), + DOI_DOMAIN), @JsonProperty("https://hdl.handle.net/21.T11148/894b1e6cad57e921764e") DIGITAL_SPECIMEN( "DigitalSpecimen", "https://hdl.handle.net/21.T11148/894b1e6cad57e921764e", "https://hdl.handle.net/21.T11148/894b1e6cad57e921764e", - "https://doi.org/"), + DOI_DOMAIN), @JsonProperty("https://hdl.handle.net/21.T11148/bbad8c4e101e8af01115") MEDIA_OBJECT( "MediaObject", "https://hdl.handle.net/21.T11148/bbad8c4e101e8af01115", "https://hdl.handle.net/21.T11148/bbad8c4e101e8af01115", - "https://doi.org/"), + DOI_DOMAIN), @JsonProperty("https://hdl.handle.net/21.T11148/cf458ca9ee1d44a5608f") ANNOTATION( "Annotation", "https://hdl.handle.net/21.T11148/cf458ca9ee1d44a5608f", "https://hdl.handle.net/21.T11148/cf458ca9ee1d44a5608f", - "https://hdl.handle.net/"), + HANDLE_DOMAIN), @JsonProperty("https://hdl.handle.net/21.T11148/417a4f472f60f7974c12") SOURCE_SYSTEM( "sourceSystem", "https://hdl.handle.net/21.T11148/417a4f472f60f7974c12", "https://hdl.handle.net/21.T11148/417a4f472f60f7974c12", - "https://hdl.handle.net/"), + HANDLE_DOMAIN), @JsonProperty("https://hdl.handle.net/21.T11148/ce794a6f4df42eb7e77e") MAPPING( "Mapping", "https://hdl.handle.net/21.T11148/ce794a6f4df42eb7e77e", "https://hdl.handle.net/21.T11148/ce794a6f4df42eb7e77e", - "https://hdl.handle.net/"), + HANDLE_DOMAIN), @JsonProperty("https://hdl.handle.net/21.T11148/413c00cbd83ae33d1ac0") ORGANISATION( "Organisation", "https://hdl.handle.net/21.T11148/413c00cbd83ae33d1ac0", "https://hdl.handle.net/21.T11148/413c00cbd83ae33d1ac0", - "https://hdl.handle.net/"), + HANDLE_DOMAIN), @JsonProperty("https://hdl.handle.net/21.T11148/d7570227982f70256af3") TOMBSTONE( "Tombstone", "https://hdl.handle.net/21.T11148/d7570227982f70256af3", "https://hdl.handle.net/21.T11148/d7570227982f70256af3", - "https://hdl.handle.net/"), + HANDLE_DOMAIN), @JsonProperty("https://hdl.handle.net/21.T11148/22e71a0015cbcfba8ffa") MAS( "Machine Annotation Service", "https://hdl.handle.net/21.T11148/22e71a0015cbcfba8ffa", "https://hdl.handle.net/21.T11148/22e71a0015cbcfba8ffa", - "https://hdl.handle.net/"); + HANDLE_DOMAIN); private final String digitalObjectName; private final String digitalObjectType; diff --git a/src/main/java/eu/dissco/core/handlemanager/repository/PidRepository.java b/src/main/java/eu/dissco/core/handlemanager/repository/PidRepository.java index 96819a70..75da770b 100644 --- a/src/main/java/eu/dissco/core/handlemanager/repository/PidRepository.java +++ b/src/main/java/eu/dissco/core/handlemanager/repository/PidRepository.java @@ -33,9 +33,10 @@ public class PidRepository { private final BatchInserter batchInserter; // For Handle Name Generation - public List getHandlesExist(List handles) { + public List getHandlesExist(List handles) { + // todo this won't work return context.selectDistinct(HANDLES.HANDLE).from(HANDLES).where(HANDLES.HANDLE.in(handles)) - .fetch().getValues(HANDLES.HANDLE, byte[].class); + .fetch().getValues(HANDLES.HANDLE, String.class); } public List checkHandlesWritable(List handles) { diff --git a/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java b/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java index 58d19da5..e848e07d 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java @@ -20,12 +20,13 @@ import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.exceptions.UnprocessableEntityException; import eu.dissco.core.handlemanager.properties.ProfileProperties; +import eu.dissco.core.handlemanager.repository.PidMongoRepository; import eu.dissco.core.handlemanager.repository.PidRepository; import java.nio.charset.StandardCharsets; -import java.time.Instant; import java.util.ArrayList; import java.util.List; import lombok.extern.slf4j.Slf4j; +import org.bson.Document; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; @@ -39,8 +40,9 @@ public class DoiService extends PidService { public DoiService(PidRepository pidRepository, FdoRecordService fdoRecordService, PidNameGeneratorService pidNameGeneratorService, ObjectMapper mapper, ProfileProperties profileProperties, - DataCiteService dataCiteService) { - super(pidRepository, fdoRecordService, pidNameGeneratorService, mapper, profileProperties); + DataCiteService dataCiteService, PidMongoRepository mongoRepository) { + super(pidRepository, fdoRecordService, pidNameGeneratorService, mapper, profileProperties, + mongoRepository); this.dataCiteService = dataCiteService; } @@ -49,16 +51,15 @@ public DoiService(PidRepository pidRepository, @Override public JsonApiWrapperWrite createRecords(List requests) throws InvalidRequestException, UnprocessableEntityException { - var handles = hf.genHandleList(requests.size()).iterator(); + var handles = hf.genHandleListString(requests.size()).iterator(); var requestAttributes = requests.stream() .map(request -> request.get(NODE_DATA).get(NODE_ATTRIBUTES)).toList(); var type = getObjectTypeFromJsonNode(requests); - List handleAttributes; + List fdoRecords; try { switch (type) { - case DIGITAL_SPECIMEN -> - handleAttributes = createDigitalSpecimen(requestAttributes, handles); - case MEDIA_OBJECT -> handleAttributes = createMediaObject(requestAttributes, handles); + case DIGITAL_SPECIMEN -> fdoRecords = createDigitalSpecimen(requestAttributes, handles); + case MEDIA_OBJECT -> fdoRecords = createMediaObject(requestAttributes, handles); default -> throw new UnsupportedOperationException( type + " is not an appropriate Type for DOI endpoint."); } @@ -67,11 +68,11 @@ public JsonApiWrapperWrite createRecords(List requests) "An error has occurred parsing a record in request. More information: " + e.getMessage()); } - log.info("Persisting new dois to db"); - pidRepository.postAttributesToDb(Instant.now().getEpochSecond(), handleAttributes); + log.info("Persisting new DOIs to Document Store"); + mongoRepository.postBatchHandleRecord(fdoRecords); log.info("Publishing to DataCite"); - publishToDataCite(handleAttributes, EventType.CREATE, type); - return new JsonApiWrapperWrite(formatCreateRecords(handleAttributes, type)); + //todo publishToDataCite(fdoRecords, EventType.CREATE, type); + return new JsonApiWrapperWrite(formatCreateDocuments(fdoRecords, type)); } @Override diff --git a/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java b/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java index ee180df2..eec00491 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java @@ -2,7 +2,6 @@ import static eu.dissco.core.handlemanager.configuration.AppConfig.DATE_STRING; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.ANNOTATION_HASH; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.BASE_TYPE_OF_SPECIMEN; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.CATALOG_IDENTIFIER; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DCTERMS_FORMAT; @@ -58,7 +57,6 @@ import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.RIGHTSHOLDER_PID; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.RIGHTSHOLDER_PID_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SOURCE_DATA_STANDARD; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SOURCE_SYSTEM_NAME; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SPECIMEN_HOST; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SPECIMEN_HOST_NAME; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SPECIMEN_OBJECT_ID_ABSENCE_REASON; @@ -69,7 +67,6 @@ import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOPIC_DISCIPLINE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOPIC_DOMAIN; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOPIC_ORIGIN; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.WAS_DERIVED_FROM_ENTITY; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.DIGITAL_SPECIMEN; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.HANDLE; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.MAPPING; @@ -100,7 +97,6 @@ import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttributeJson; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.InvalidRequestRuntimeException; -import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.properties.ApplicationProperties; import eu.dissco.core.handlemanager.properties.ProfileProperties; import eu.dissco.core.handlemanager.web.PidResolver; @@ -140,10 +136,10 @@ public class FdoRecordService { private final DocumentBuilderFactory dbf; private final PidResolver pidResolver; private final ObjectMapper mapper; - private final ApplicationProperties appProperties; + private final ApplicationProperties applicationProperties; private final ProfileProperties profileProperties; - private static final String HANDLE_DOMAIN = "https://hdl.handle.net/"; - private static final String DOI_DOMAIN = "https://doi.org/"; + public static final String HANDLE_DOMAIN = "https://hdl.handle.net/"; + public static final String DOI_DOMAIN = "https://doi.org/"; private static final String ROR_API_DOMAIN = "https://api.ror.org/organizations/"; private static final String ROR_DOMAIN = "https://ror.org/"; private static final String WIKIDATA_DOMAIN = "https://www.wikidata.org/wiki/"; @@ -166,7 +162,6 @@ public class FdoRecordService { private final DateTimeFormatter dt = DateTimeFormatter.ofPattern(DATE_STRING) .withZone(ZoneId.of("UTC")); - private final ApplicationProperties applicationProperties; public HandleAttribute genHsAdmin(byte[] handle) { return new HandleAttribute(HS_ADMIN.index(), handle, HS_ADMIN.get(), ADMIN_HEX); @@ -538,7 +533,7 @@ private List prepareDigitalSpecimenRecordAttributesFromRequest( } /* MAS Record Creation */ - public Document prepareMasDocument(MasRequest request, String handle, + public Document prepareNewMasDocument(MasRequest request, String handle, Instant timestamp) throws InvalidRequestException, JsonProcessingException { var fdoRecord = prepareNewHandleJsonNodeRecord(request, handle, MAS, timestamp); @@ -673,7 +668,7 @@ private List prepareDigitalMediaAttributesFromRequest( } /* Organisation Record Creation */ - public Document prepareOrganisationDocument(OrganisationRequest request, String handle, + public Document prepareNewOrganisationDocument(OrganisationRequest request, String handle, Instant timestamp) throws InvalidRequestException, JsonProcessingException { var fdoRecord = prepareNewDoiJsonNodeRecord(request, handle, ORGANISATION, timestamp); @@ -715,9 +710,8 @@ private List prepareOrganisationAttributesFromRequest(OrganisationRequ return fdoProfile; } - /* Source System Record Creation */ - public Document prepareSourceSystemDocument(SourceSystemRequest request, String handle, + public Document prepareNewSourceSystemDocument(SourceSystemRequest request, String handle, Instant timestamp) throws InvalidRequestException, JsonProcessingException { var fdoRecord = prepareNewHandleJsonNodeRecord(request, handle, SOURCE_SYSTEM, timestamp); @@ -742,69 +736,6 @@ private List prepareSourceSystemAttributesFromRequest(SourceSystemRequ return fdoProfile; } - public List prepareHandleRecordAttributes(HandleRecordRequest request, - byte[] handle, FdoType type) throws InvalidRequestException { - List fdoRecord = new ArrayList<>(); - - // 100: Admin Handle - fdoRecord.add(genHsAdmin(handle)); - - // 101: 10320/loc - if (type != FdoType.ORGANISATION) { - byte[] loc = setLocationsByte(request.getLocations(), - new String(handle, StandardCharsets.UTF_8), - type); - fdoRecord.add(new HandleAttribute(LOC.index(), handle, LOC.get(), loc)); - } - - // 1: FDO Profile - fdoRecord.add(new HandleAttribute(FDO_PROFILE, handle, type.getFdoProfile())); - - // 2: FDO Record License - fdoRecord.add(new HandleAttribute(FDO_RECORD_LICENSE, handle, PID_KERNEL_METADATA_LICENSE)); - - // 3: DigitalObjectType - fdoRecord.add(new HandleAttribute(DIGITAL_OBJECT_TYPE, handle, type.getDigitalObjectType())); - - // 4: DigitalObjectName - fdoRecord.add(new HandleAttribute(DIGITAL_OBJECT_NAME, handle, type.getDigitalObjectName())); - - // 5: Pid - var pid = profileProperties.getDomain() + new String(handle, StandardCharsets.UTF_8); - fdoRecord.add(new HandleAttribute(PID, handle, pid)); - - // 6: PidIssuer - fdoRecord.add(new HandleAttribute(PID_ISSUER, handle, request.getPidIssuer())); - - // 7: pidIssuerName - String pidIssuerName = getObjectName(request.getPidIssuer(), null); - fdoRecord.add(new HandleAttribute(PID_ISSUER_NAME, handle, pidIssuerName)); - - // 8: issuedForAgent - fdoRecord.add(new HandleAttribute(ISSUED_FOR_AGENT, handle, request.getIssuedForAgent())); - - // 9: issuedForAgentName - var agentNameRor = getRor(request.getIssuedForAgent()); - var issuedForAgentName = pidResolver.getObjectName(agentNameRor); - fdoRecord.add(new HandleAttribute(ISSUED_FOR_AGENT_NAME, handle, issuedForAgentName)); - - // 10: pidRecordIssueDate - fdoRecord.add(new HandleAttribute(PID_RECORD_ISSUE_DATE, handle, getDate(Instant.now()))); - - // 11: pidRecordIssueNumber - fdoRecord.add(new HandleAttribute(PID_RECORD_ISSUE_NUMBER, handle, "1")); - - // 12: structuralType - fdoRecord.add( - new HandleAttribute(STRUCTURAL_TYPE, handle, request.getStructuralType().toString())); - - // 13: PidStatus - fdoRecord.add(new HandleAttribute(PID_STATUS, handle, "TEST")); - - return fdoRecord; - } - - private String getObjectName(String url, String name) throws InvalidRequestException { if (name != null) { return name; @@ -827,314 +758,6 @@ private static String getRor(String url) throws InvalidRequestException { return url.replace(ROR_DOMAIN, ROR_API_DOMAIN); } - public List prepareDoiRecordAttributes(DoiRecordRequest request, byte[] handle, - FdoType type) throws InvalidRequestException { - var fdoRecord = prepareHandleRecordAttributes(request, handle, type); - - // 40: referentType - fdoRecord.add(new HandleAttribute(REFERENT_TYPE, handle, request.getReferentType())); - - // 41: referentDoiName - fdoRecord.add( - new HandleAttribute(REFERENT_DOI_NAME.index(), handle, REFERENT_DOI_NAME.get(), handle)); - - // 42: referentName - if (request.getReferentName() != null) { - fdoRecord.add(new HandleAttribute(REFERENT_NAME, handle, request.getReferentName())); - } - // 43: primaryReferentType - fdoRecord.add( - new HandleAttribute(PRIMARY_REFERENT_TYPE, handle, request.getPrimaryReferentType())); - - return fdoRecord; - } - - public List prepareMediaObjectAttributes(MediaObjectRequest request, - byte[] handle) throws InvalidRequestException { - var fdoRecord = prepareDoiRecordAttributes(request, handle, FdoType.MEDIA_OBJECT); - - fdoRecord.add(new HandleAttribute(MEDIA_HOST, handle, request.getMediaHost())); - var mediaHostName = setHostNameHandleAttribute(request.getMediaHostName(), - request.getMediaHost(), handle, - MEDIA_HOST_NAME); - fdoRecord.add(mediaHostName); - if (request.getDctermsFormat() != null) { - fdoRecord.add( - new HandleAttribute(DCTERMS_FORMAT, handle, request.getDctermsFormat().toString())); - } - fdoRecord.add(new HandleAttribute(IS_DERIVED_FROM_SPECIMEN, handle, - request.getIsDerivedFromSpecimen().toString())); - fdoRecord.add(new HandleAttribute(LINKED_DO_PID, handle, request.getLinkedDigitalObjectPid())); - fdoRecord.add(new HandleAttribute(LINKED_DO_TYPE, handle, - request.getLinkedDigitalObjectType().toString())); - if (request.getLinkedAttribute() != null) { - fdoRecord.add(new HandleAttribute(LINKED_ATTRIBUTE, handle, request.getLinkedAttribute())); - } - fdoRecord.add(new HandleAttribute(PRIMARY_MEDIA_ID, handle, request.getPrimaryMediaId())); - - if (request.getDcTermsType() != null) { - fdoRecord.add(new HandleAttribute(DCTERMS_TYPE, handle, request.getDcTermsType().toString())); - } - if (request.getPrimaryMediaObjectIdName() != null) { - fdoRecord.add( - new HandleAttribute(PRIMARY_MO_ID_NAME, handle, request.getPrimaryMediaObjectIdName())); - } - if (request.getPrimaryMediaObjectIdType() != null) { - fdoRecord.add(new HandleAttribute(PRIMARY_MO_ID_TYPE, handle, - request.getPrimaryMediaObjectIdType().toString())); - } - if (request.getDcTermsType() != null) { - fdoRecord.add( - new HandleAttribute(DCTERMS_SUBJECT, handle, request.getDcTermsType().toString())); - } - if (request.getDerivedFromEntity() != null) { - fdoRecord.add( - new HandleAttribute(DERIVED_FROM_ENTITY, handle, request.getDerivedFromEntity())); - } - if (request.getLicenseName() != null) { - fdoRecord.add(new HandleAttribute(LICENSE_NAME, handle, request.getLicenseName())); - } - if (request.getLicenseUrl() != null) { - fdoRecord.add(new HandleAttribute(LICENSE_URL, handle, request.getLicenseUrl())); - } - var rightsholderName = setHostNameHandleAttribute(request.getRightsholderName(), - request.getRightsholderPid(), - handle, RIGHTSHOLDER_NAME); - fdoRecord.add(rightsholderName); - if (request.getRightsholderPid() != null) { - fdoRecord.add(new HandleAttribute(RIGHTSHOLDER_PID, handle, request.getRightsholderPid())); - } - if (request.getRightsholderPidType() != null) { - fdoRecord.add(new HandleAttribute(RIGHTSHOLDER_PID_TYPE, handle, - request.getRightsholderPidType().toString())); - } - if (request.getDctermsConformsTo() != null) { - fdoRecord.add(new HandleAttribute(DC_TERMS_CONFORMS, handle, request.getDctermsConformsTo())); - } - return fdoRecord; - } - - public List prepareAnnotationAttributes(AnnotationRequest request, - byte[] handle) throws InvalidRequestException { - var fdoRecord = prepareHandleRecordAttributes(request, handle, FdoType.ANNOTATION); - - // 500 TargetPid - fdoRecord.add(new HandleAttribute(TARGET_PID, handle, request.getTargetPid())); - - // 501 TargetType - fdoRecord.add(new HandleAttribute(TARGET_TYPE, handle, request.getTargetType())); - - // 502 motivation - fdoRecord.add(new HandleAttribute(MOTIVATION, handle, request.getMotivation().toString())); - - // 503 AnnotationHash - if (request.getAnnotationHash() != null) { - fdoRecord.add( - new HandleAttribute(ANNOTATION_HASH, handle, request.getAnnotationHash().toString())); - } - return fdoRecord; - } - - public List prepareMasRecordAttributes(MasRequest request, byte[] handle) - throws InvalidRequestException { - var fdoRecord = prepareHandleRecordAttributes(request, handle, FdoType.MAS); - fdoRecord.add(new HandleAttribute(MAS_NAME, handle, request.getMachineAnnotationServiceName())); - return fdoRecord; - } - - - public List prepareSourceSystemAttributes(SourceSystemRequest request, - byte[] handle) throws InvalidRequestException { - var fdoRecord = prepareHandleRecordAttributes(request, handle, FdoType.SOURCE_SYSTEM); - - // 600 sourceSystemName - fdoRecord.add(new HandleAttribute(SOURCE_SYSTEM_NAME, handle, request.getSourceSystemName())); - - return fdoRecord; - } - - public List prepareOrganisationAttributes(OrganisationRequest request, - byte[] handle) throws InvalidRequestException { - var fdoRecord = prepareDoiRecordAttributes(request, handle, FdoType.ORGANISATION); - - //101 10320/loc -> must contain ROR - var objectLocations = new ArrayList<>(List.of(request.getOrganisationIdentifier())); - if (request.getLocations() != null) { - objectLocations.addAll(List.of(request.getLocations())); - } - byte[] loc = setLocationsByte(objectLocations.toArray(new String[0]), - new String(handle, StandardCharsets.UTF_8), FdoType.ORGANISATION); - fdoRecord.add(new HandleAttribute(LOC.index(), handle, LOC.get(), loc)); - - // 601 OrganisationIdentifier - fdoRecord.add( - new HandleAttribute(ORGANISATION_ID, handle, request.getOrganisationIdentifier())); - - // 602 OrganisationIdentifierType - fdoRecord.add( - new HandleAttribute(ORGANISATION_ID_TYPE, handle, request.getOrganisationIdentifierType())); - - // 603 OrganisationName - var organisationName = pidResolver.getObjectName(getRor(request.getOrganisationIdentifier())); - fdoRecord.add(new HandleAttribute(ORGANISATION_NAME, handle, organisationName)); - - return fdoRecord; - } - - public List prepareMappingAttributes(MappingRequest request, byte[] handle) - throws InvalidRequestException { - var fdoRecord = prepareHandleRecordAttributes(request, handle, MAPPING); - - // 700 Source Data Standard - fdoRecord.add( - new HandleAttribute(SOURCE_DATA_STANDARD, handle, request.getSourceDataStandard())); - - return fdoRecord; - } - - public List prepareDigitalSpecimenRecordAttributes( - DigitalSpecimenRequest request, byte[] handle) - throws InvalidRequestException { - var fdoRecord = prepareDoiRecordAttributes(request, handle, DIGITAL_SPECIMEN); - - // 200: Specimen Host - fdoRecord.add(new HandleAttribute(SPECIMEN_HOST, handle, request.getSpecimenHost())); - - // 201: Specimen Host name - var specimenHostName = setHostNameHandleAttribute(request.getSpecimenHostName(), - request.getSpecimenHost(), - handle, SPECIMEN_HOST_NAME); - fdoRecord.add(specimenHostName); - - // 202: primarySpecimenObjectId - fdoRecord.add(new HandleAttribute(PRIMARY_SPECIMEN_OBJECT_ID, handle, - request.getPrimarySpecimenObjectId())); - - // 203: primarySpecimenObjectIdType - fdoRecord.add(new HandleAttribute(PRIMARY_SPECIMEN_OBJECT_ID_TYPE, handle, - request.getPrimarySpecimenObjectIdType().toString())); - - // 204-217 are optional - - // 204: primarySpecimenObjectIdName - if (request.getPrimarySpecimenObjectIdName() != null) { - fdoRecord.add(new HandleAttribute(PRIMARY_SPECIMEN_OBJECT_ID_NAME, handle, - request.getPrimarySpecimenObjectIdName())); - } - - // 205 normalisedSpecimenObjectId - fdoRecord.add(new HandleAttribute(NORMALISED_SPECIMEN_OBJECT_ID, handle, - request.getNormalisedPrimarySpecimenObjectId())); - - // 206: specimenObjectIdAbsenceReason - if (request.getSpecimenObjectIdAbsenceReason() != null) { - fdoRecord.add(new HandleAttribute(SPECIMEN_OBJECT_ID_ABSENCE_REASON, handle, - request.getSpecimenObjectIdAbsenceReason())); - } - - // 207: otherSpecimenIds - if (request.getOtherSpecimenIds() != null) { - try { - var otherSpecimenIds = mapper.writeValueAsString(request.getOtherSpecimenIds()); - fdoRecord.add(new HandleAttribute(OTHER_SPECIMEN_IDS, handle, otherSpecimenIds)); - } catch (JsonProcessingException e) { - log.warn("Unable to parse otherSpecimenIds {} to string", request.getOtherSpecimenIds(), e); - } - } - - // 208: topicOrigin - if (request.getTopicOrigin() != null) { - fdoRecord.add(new HandleAttribute(TOPIC_ORIGIN, handle, request.getTopicOrigin().toString())); - } - - // 209: topicDomain - var topicDomain = request.getTopicDomain(); - if (topicDomain != null) { - fdoRecord.add(new HandleAttribute(TOPIC_DOMAIN, handle, topicDomain.toString())); - } - - // 210: topicDiscipline - var topicDisc = request.getTopicDiscipline(); - if (topicDisc != null) { - fdoRecord.add(new HandleAttribute(TOPIC_DISCIPLINE, handle, topicDisc.toString())); - } - - // 211 topicCategory - var topicCategory = request.getTopicCategory(); - if (topicCategory != null) { - fdoRecord.add(new HandleAttribute(TOPIC_CATEGORY, handle, topicCategory.toString())); - } - - // 212: livingOrPreserved - var livingOrPres = request.getLivingOrPreserved(); - if (livingOrPres != null) { - fdoRecord.add(new HandleAttribute(LIVING_OR_PRESERVED, handle, livingOrPres.toString())); - } - - // 213 baseTypeOfSpecimen - var baseType = request.getBaseTypeOfSpecimen(); - if (baseType != null) { - fdoRecord.add(new HandleAttribute(BASE_TYPE_OF_SPECIMEN, handle, baseType.toString())); - } - - // 214: informationArtefactType - var artType = request.getInformationArtefactType(); - if (artType != null) { - fdoRecord.add(new HandleAttribute(INFORMATION_ARTEFACT_TYPE, handle, artType.toString())); - } - - // 215: materialSampleType - var matSamp = request.getMaterialSampleType(); - if (matSamp != null) { - fdoRecord.add(new HandleAttribute(MATERIAL_SAMPLE_TYPE, handle, matSamp.toString())); - } - - // 216: materialOrDigitalEntity - if (request.getMaterialOrDigitalEntity() != null) { - fdoRecord.add(new HandleAttribute(MATERIAL_OR_DIGITAL_ENTITY, handle, - request.getMaterialOrDigitalEntity().toString())); - } - - // 217: markedAsType - var markedAsType = request.getMarkedAsType(); - if (markedAsType != null) { - fdoRecord.add(new HandleAttribute(MARKED_AS_TYPE, handle, markedAsType.toString())); - } - - // 218: wasDerivedFromEntity - var wasDerivedFrom = request.getDerivedFromEntity(); - if (wasDerivedFrom != null) { - fdoRecord.add(new HandleAttribute(WAS_DERIVED_FROM_ENTITY, handle, wasDerivedFrom)); - } - - // 219 catalogId - var catId = request.getCatalogIdentifier(); - if (catId != null) { - fdoRecord.add(new HandleAttribute(CATALOG_IDENTIFIER, handle, catId)); - } - - return fdoRecord; - } - - private HandleAttribute setHostNameHandleAttribute(String hostName, String hostId, byte[] handle, - FdoProfile targetAttribute) throws PidResolutionException { - if (hostName != null) { - return new HandleAttribute(targetAttribute, handle, hostName); - } else { - String hostNameResolved; - if (hostId.contains(ROR_DOMAIN)) { - hostNameResolved = pidResolver.getObjectName(hostId.replace(ROR_DOMAIN, ROR_API_DOMAIN)); - return new HandleAttribute(targetAttribute, handle, hostNameResolved); - } else if (hostId.contains(WIKIDATA_DOMAIN)) { - hostNameResolved = pidResolver.resolveQid(hostId.replace(WIKIDATA_DOMAIN, WIKIDATA_API)); - } else { - log.error("Specimen host ID {} is neither QID nor ROR.", hostId); - throw new PidResolutionException("Invalid host id: " + hostId); - } - return new HandleAttribute(targetAttribute, handle, hostNameResolved); - } - } - public List prepareUpdateAttributes(byte[] handle, JsonNode requestAttributes, FdoType type) throws InvalidRequestException { requestAttributes = setLocationXmlFromJson(requestAttributes, @@ -1238,26 +861,26 @@ private String getDate(Instant timestamp) { private List defaultLocations(String handle, FdoType fdoType) { switch (fdoType) { case DIGITAL_SPECIMEN -> { - String api = appProperties.getApiUrl() + "/specimens/" + handle; - String ui = appProperties.getUiUrl() + "/ds/" + handle; + String api = applicationProperties.getApiUrl() + "/specimens/" + handle; + String ui = applicationProperties.getUiUrl() + "/ds/" + handle; return List.of(api, ui); } case MAPPING -> { - return List.of(appProperties.getOrchestrationUrl() + "/mapping/" + handle); + return List.of(applicationProperties.getOrchestrationUrl() + "/mapping/" + handle); } case SOURCE_SYSTEM -> { - return List.of(appProperties.getOrchestrationUrl() + "/source-system/" + handle); + return List.of(applicationProperties.getOrchestrationUrl() + "/source-system/" + handle); } case MEDIA_OBJECT -> { - String api = appProperties.getApiUrl() + "/digitalMedia/" + handle; - String ui = appProperties.getUiUrl() + "/dm/" + handle; + String api = applicationProperties.getApiUrl() + "/digitalMedia/" + handle; + String ui = applicationProperties.getUiUrl() + "/dm/" + handle; return List.of(api, ui); } case ANNOTATION -> { - return List.of(appProperties.getApiUrl() + "/annotations/" + handle); + return List.of(applicationProperties.getApiUrl() + "/annotations/" + handle); } case MAS -> { - return List.of(appProperties.getOrchestrationUrl() + "/mas/" + handle); + return List.of(applicationProperties.getOrchestrationUrl() + "/mas/" + handle); } default -> { // Handle, DOI, Organisation (Org locations are all in userLocations) diff --git a/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java b/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java index 377a1a55..b6d72b5c 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java @@ -16,15 +16,12 @@ import eu.dissco.core.handlemanager.domain.fdo.MasRequest; import eu.dissco.core.handlemanager.domain.fdo.OrganisationRequest; import eu.dissco.core.handlemanager.domain.fdo.SourceSystemRequest; -import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiDataLinks; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperWrite; -import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.properties.ProfileProperties; import eu.dissco.core.handlemanager.repository.PidMongoRepository; import eu.dissco.core.handlemanager.repository.PidRepository; -import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.ArrayList; import java.util.Iterator; @@ -39,46 +36,32 @@ @Profile(Profiles.HANDLE) public class HandleService extends PidService { - private final PidMongoRepository mongoRepository; - public HandleService(PidRepository pidRepository, FdoRecordService fdoRecordService, PidNameGeneratorService hf, ObjectMapper mapper, ProfileProperties profileProperties, PidMongoRepository mongoRepository) { - super(pidRepository, fdoRecordService, hf, mapper, profileProperties); - this.mongoRepository = mongoRepository; + super(pidRepository, fdoRecordService, hf, mapper, profileProperties, mongoRepository); } // Pid Record Creation @Override public JsonApiWrapperWrite createRecords(List requests) throws InvalidRequestException { - var handlesBytes = hf.genHandleList(requests.size()); - var handles = handlesBytes.iterator(); - var handleStr = handlesBytes.stream().map(b -> new String(b, StandardCharsets.UTF_8)).toList() - .iterator(); + var handles = hf.genHandleListString(requests.size()).iterator(); var requestAttributes = requests.stream() .map(request -> request.get(NODE_DATA).get(NODE_ATTRIBUTES)).toList(); var type = getObjectTypeFromJsonNode(requests); - List handleAttributes; + List fdoRecords; try { switch (type) { - case ANNOTATION -> handleAttributes = createAnnotation(requestAttributes, handles); - case DIGITAL_SPECIMEN -> - handleAttributes = createDigitalSpecimen(requestAttributes, handles); - case DOI -> handleAttributes = createDoi(requestAttributes, handles); - case HANDLE -> { - var documents = createHandleDocument(requestAttributes, handleStr); - mongoRepository.postBatchHandleRecord(documents); - return new JsonApiWrapperWrite(List.of(new JsonApiDataLinks( - new String(handlesBytes.get(0), StandardCharsets.UTF_8), - HANDLE.getDigitalObjectType(), - null, - null))); - } - case MAPPING -> handleAttributes = createMapping(requestAttributes, handles); - case MAS -> handleAttributes = createMas(requestAttributes, handles); - case MEDIA_OBJECT -> handleAttributes = createMediaObject(requestAttributes, handles); - case ORGANISATION -> handleAttributes = createOrganisation(requestAttributes, handles); - case SOURCE_SYSTEM -> handleAttributes = createSourceSystem(requestAttributes, handles); + case ANNOTATION -> fdoRecords = createAnnotation(requestAttributes, handles); + case DIGITAL_SPECIMEN -> fdoRecords = createDigitalSpecimen(requestAttributes, handles); + case DOI -> fdoRecords = createDoi(requestAttributes, handles); + case HANDLE -> fdoRecords = createHandle(requestAttributes, handles); + + case MAPPING -> fdoRecords = createMapping(requestAttributes, handles); + case MAS -> fdoRecords = createMas(requestAttributes, handles); + case MEDIA_OBJECT -> fdoRecords = createMediaObject(requestAttributes, handles); + case ORGANISATION -> fdoRecords = createOrganisation(requestAttributes, handles); + case SOURCE_SYSTEM -> fdoRecords = createSourceSystem(requestAttributes, handles); default -> throw new UnsupportedOperationException("Unrecognized type"); } } catch (JsonProcessingException | PidResolutionException e) { @@ -86,38 +69,39 @@ public JsonApiWrapperWrite createRecords(List requests) throws Invalid throw new InvalidRequestException( "An error has occurred parsing a record in request. More information: " + e.getMessage()); } - log.info("Persisting new handles to db"); - pidRepository.postAttributesToDb(Instant.now().getEpochSecond(), handleAttributes); - return new JsonApiWrapperWrite(formatCreateRecords(handleAttributes, type)); + log.info("Persisting new handles to Document Store"); + mongoRepository.postBatchHandleRecord(fdoRecords); + return new JsonApiWrapperWrite(formatCreateDocuments(fdoRecords, type)); } - private List createAnnotation(List requestAttributes, - Iterator handleIterator) + private List createAnnotation(List requestAttributes, + Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { - List handleAttributes = new ArrayList<>(); + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); for (var request : requestAttributes) { - var thisHandle = handleIterator.next(); var requestObject = mapper.treeToValue(request, AnnotationRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareAnnotationAttributes(requestObject, thisHandle)); + fdoRecords.add( + fdoRecordService.prepareNewAnnotationDocument(requestObject, handleIterator.next(), + timestamp)); } - return handleAttributes; + return fdoRecords; } - private List createDoi(List requestAttributes, - Iterator handleIterator) + private List createDoi(List requestAttributes, + Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { - List handleAttributes = new ArrayList<>(); + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); for (var request : requestAttributes) { - var thisHandle = handleIterator.next(); var requestObject = mapper.treeToValue(request, DoiRecordRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareDoiRecordAttributes(requestObject, thisHandle, DOI)); + fdoRecords.add(fdoRecordService.prepareNewDoiDocument(requestObject, handleIterator.next(), + DOI, timestamp)); } - return handleAttributes; + return fdoRecords; } - private List createHandleDocument(List requestAttributes, + private List createHandle(List requestAttributes, Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { List fdoRecords = new ArrayList<>(); @@ -130,64 +114,56 @@ private List createHandleDocument(List requestAttributes, return fdoRecords; } - private List createHandle(List requestAttributes, - Iterator handleIterator) - throws JsonProcessingException, InvalidRequestException { - List handleAttributes = new ArrayList<>(); - for (var request : requestAttributes) { - var thisHandle = handleIterator.next(); - var requestObject = mapper.treeToValue(request, HandleRecordRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareHandleRecordAttributes(requestObject, thisHandle, HANDLE)); - } - return handleAttributes; - } - - private List createMapping(List requestAttributes, - Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { - List handleAttributes = new ArrayList<>(); + private List createMapping(List requestAttributes, + Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); for (var request : requestAttributes) { - var thisHandle = handleIterator.next(); var requestObject = mapper.treeToValue(request, MappingRequest.class); - handleAttributes.addAll(fdoRecordService.prepareMappingAttributes(requestObject, thisHandle)); + fdoRecords.add( + fdoRecordService.prepareNewDataMappingDocument(requestObject, handleIterator.next(), + timestamp)); } - return handleAttributes; + return fdoRecords; } - private List createMas(List requestAttributes, - Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { - List handleAttributes = new ArrayList<>(); + private List createMas(List requestAttributes, + Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); for (var request : requestAttributes) { - var thisHandle = handleIterator.next(); var requestObject = mapper.treeToValue(request, MasRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareMasRecordAttributes(requestObject, thisHandle)); + fdoRecords.add(fdoRecordService.prepareNewMasDocument(requestObject, handleIterator.next(), + timestamp)); } - return handleAttributes; + return fdoRecords; } - private List createOrganisation(List requestAttributes, - Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { - List handleAttributes = new ArrayList<>(); + private List createOrganisation(List requestAttributes, + Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); for (var request : requestAttributes) { - var thisHandle = handleIterator.next(); var requestObject = mapper.treeToValue(request, OrganisationRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareOrganisationAttributes(requestObject, thisHandle)); + fdoRecords.add( + fdoRecordService.prepareNewOrganisationDocument(requestObject, handleIterator.next(), + timestamp)); } - return handleAttributes; + return fdoRecords; } - private List createSourceSystem(List requestAttributes, - Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { - List handleAttributes = new ArrayList<>(); + private List createSourceSystem(List requestAttributes, + Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); for (var request : requestAttributes) { - var thisHandle = handleIterator.next(); var requestObject = mapper.treeToValue(request, SourceSystemRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareSourceSystemAttributes(requestObject, thisHandle)); + fdoRecords.add( + fdoRecordService.prepareNewSourceSystemDocument(requestObject, handleIterator.next(), + timestamp)); } - return handleAttributes; + return fdoRecords; + } } diff --git a/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java b/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java index addae022..ec3d97b0 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java @@ -2,9 +2,9 @@ import eu.dissco.core.handlemanager.properties.ApplicationProperties; import eu.dissco.core.handlemanager.repository.PidRepository; -import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Random; @@ -30,19 +30,16 @@ public class PidNameGeneratorService { private final Random random; - public List genHandleList(int h) { - return unwrapBytes((HashSet) genHandleHash(h)); + public List genHandleListString(int h) { + return new ArrayList<>(genHandleHashSet(h)); } - private List unwrapBytes(HashSet handleHash) { - List handleList = new ArrayList<>(); - for (ByteBuffer hash : handleHash) { - handleList.add(hash.array()); - } - return handleList; + public List genHandleList(int h) { + var list = genHandleListString(h); + return list.stream().map(s -> s.getBytes(StandardCharsets.UTF_8)).toList(); } - private Set genHandleHash(int h) { + private Set genHandleHashSet(int h) { /* * Generates a HashSet of minted handles of size h Calls the handlefactory @@ -52,19 +49,18 @@ private Set genHandleHash(int h) { */ // Generate h number of bytes and wrap it into a HashSet - List handleList = newHandle(h); - - HashSet handleHash = wrapBytes(handleList); + var handleList = newHandles(h); + var handleSet = new HashSet<>(handleList); // Check for duplicates from repository and wrap the duplicates - HashSet duplicates = wrapBytes(pidRepository.getHandlesExist(handleList)); + var duplicates = new HashSet<>(pidRepository.getHandlesExist(handleList)); // If a duplicate was found, recursively call this function // Generate new handles for every duplicate found and add it to our hash list if (!duplicates.isEmpty()) { - handleHash.removeAll(duplicates); - handleHash.addAll(genHandleHash(duplicates.size())); + handleSet.removeAll(duplicates); + handleSet.addAll(genHandleHashSet(duplicates.size())); } /* @@ -72,50 +68,15 @@ private Set genHandleHash(int h) { * recursive cal)ls to this function, we generate the same If this occurs, we * will not have our expected number of handles */ - while (h > handleHash.size()) { - handleHash.addAll(genHandleHash(h - handleHash.size())); + while (h > handleSet.size()) { + handleSet.addAll(genHandleHashSet(h - handleSet.size())); } - return handleHash; + return handleSet; } - // Converting between List - /* - * List <----> HashSet HashSets are useful for preventing - * collisions within the list List is used to interface with repository - * layer - */ - - // Converts List --> HashSet - private HashSet wrapBytes(List byteList) { - HashSet byteHash = new HashSet<>(); - for (byte[] bytes : byteList) { - byteHash.add(ByteBuffer.wrap(bytes)); - } - return byteHash; - } - - private String newSuffix() { - for (int idx = 0; idx < buf.length; ++idx) { - if (idx == 3 || idx == 7) { // - buf[idx] = '-'; // Sneak a lil dash in the middle - } else { - buf[idx] = symbols[random.nextInt(symbols.length)]; - } - } - return new String(buf); - } - - private String newHandle() { - return applicationProperties.getPrefix() + "/" + newSuffix(); - } - - private byte[] newHandleBytes() { - return newHandle().getBytes(StandardCharsets.UTF_8); - } - - private List newHandle(int numberOfHandles) { // Generates h number of handles + private List newHandles(int numberOfHandles) { // Generates h number of handles if (numberOfHandles < 1) { - return new ArrayList<>(); + return Collections.emptyList(); } if (numberOfHandles > applicationProperties.getMaxHandles()) { log.warn("Max number of handles exceeded. Generating maximum {} handles instead", @@ -126,19 +87,35 @@ private List newHandle(int numberOfHandles) { // Generates h number of h // We'll use this to make sure we're not duplicating results // It's of type ByteBuffer and not byte[] because ByteBuffer has equality testing // byte[] is too primitive for our needs - HashSet handleHash = new HashSet<>(); + HashSet handleHash = new HashSet<>(); // This is the object we'll actually return - List handleList = new ArrayList<>(); - byte[] hdl; + var handleList = new ArrayList(); + String hdl; for (int i = 0; i < numberOfHandles; i++) { - hdl = newHandleBytes(); - while (!handleHash.add(ByteBuffer.wrap(hdl))) { - hdl = newHandleBytes(); + hdl = newHandle(); + while (!handleHash.add(hdl)) { + hdl = newHandle(); } handleList.add(hdl); } return handleList; } + + private String newHandle() { + return applicationProperties.getPrefix() + "/" + newSuffix(); + } + + + private String newSuffix() { + for (int idx = 0; idx < buf.length; ++idx) { + if (idx == 3 || idx == 7) { // + buf[idx] = '-'; // Sneak a lil dash in the middle + } else { + buf[idx] = symbols[random.nextInt(symbols.length)]; + } + } + return new String(buf); + } } diff --git a/src/main/java/eu/dissco/core/handlemanager/service/PidService.java b/src/main/java/eu/dissco/core/handlemanager/service/PidService.java index 01138a85..bb26185c 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/PidService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/PidService.java @@ -32,10 +32,12 @@ import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.exceptions.UnprocessableEntityException; import eu.dissco.core.handlemanager.properties.ProfileProperties; +import eu.dissco.core.handlemanager.repository.PidMongoRepository; import eu.dissco.core.handlemanager.repository.PidRepository; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -44,6 +46,7 @@ import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.bson.Document; @Slf4j @RequiredArgsConstructor @@ -54,6 +57,7 @@ public abstract class PidService { protected final PidNameGeneratorService hf; protected final ObjectMapper mapper; protected final ProfileProperties profileProperties; + protected final PidMongoRepository mongoRepository; private List formatRecords(List dbRecord) { var handleMap = mapRecords(dbRecord); @@ -111,6 +115,16 @@ protected List formatCreateRecords(List dbRec } } + protected List formatCreateDocuments(List fdoRecords, FdoType type) { + var dataList = new ArrayList(); + for (var fdoRecord : fdoRecords) { + dataList.add( + new JsonApiDataLinks(fdoRecord.get("_id").toString(), type.getDigitalObjectName(), null, + null)); + } + return dataList; + } + private List formatCreateRecordsAnnotation( Map> handleMap) { List dataLinksList = new ArrayList<>(); @@ -265,23 +279,24 @@ protected FdoType getObjectTypeFromJsonNode(List requests) { return FdoType.fromString(type.get()); } - protected List createDigitalSpecimen(List requestAttributes, - Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { + protected List createDigitalSpecimen(List requestAttributes, + Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { var specimenRequests = new ArrayList(); for (var request : requestAttributes) { specimenRequests.add(mapper.treeToValue(request, DigitalSpecimenRequest.class)); } if (specimenRequests.isEmpty()) { - return new ArrayList<>(); + return Collections.emptyList(); } verifySpecimensAreNew(specimenRequests); - var handleAttributes = new ArrayList(); + var fdoRecords = new ArrayList(); + var timestamp = Instant.now(); for (var request : specimenRequests) { - var thisHandle = handleIterator.next(); - handleAttributes.addAll( - fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, thisHandle)); + fdoRecords.add( + fdoRecordService.prepareNewDigitalSpecimenRecord(request, handleIterator.next(), + timestamp)); } - return handleAttributes; + return fdoRecords; } private void verifySpecimensAreNew(List requests) @@ -303,18 +318,18 @@ private void verifySpecimensAreNew(List requests) } } - - protected List createMediaObject(List requestAttributes, - Iterator handleIterator) + protected List createMediaObject(List requestAttributes, + Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { - List handleAttributes = new ArrayList<>(); + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); for (var request : requestAttributes) { - var thisHandle = handleIterator.next(); var requestObject = mapper.treeToValue(request, MediaObjectRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareMediaObjectAttributes(requestObject, thisHandle)); + fdoRecords.add( + fdoRecordService.prepareNewDigitalMediaRecord(requestObject, handleIterator.next(), + timestamp)); } - return handleAttributes; + return fdoRecords; } // Update diff --git a/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java index 3aa00f04..d1b2e7e8 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java @@ -38,6 +38,7 @@ import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.UnprocessableEntityException; import eu.dissco.core.handlemanager.properties.ProfileProperties; +import eu.dissco.core.handlemanager.repository.PidMongoRepository; import eu.dissco.core.handlemanager.repository.PidRepository; import java.nio.charset.StandardCharsets; import java.time.Clock; @@ -68,6 +69,8 @@ class DoiServiceTest { private ProfileProperties profileProperties; @Mock private DataCiteService dataCiteService; + @Mock + private PidMongoRepository mongoRepository; private PidService service; private MockedStatic mockedStatic; private MockedStatic mockedClock; @@ -76,7 +79,7 @@ class DoiServiceTest { void setup() { initTime(); service = new DoiService(pidRepository, fdoRecordService, pidNameGeneratorService, MAPPER, - profileProperties, dataCiteService); + profileProperties, dataCiteService, mongoRepository); } private void initTime() { From d535264d4a0ff077f5665697c34c80d70a2f2d20 Mon Sep 17 00:00:00 2001 From: southeo Date: Wed, 10 Jul 2024 06:21:40 +0200 Subject: [PATCH 04/15] updates --- ...leAttributeJson.java => FdoAttribute.java} | 6 +- .../domain/repsitoryobjects/FdoRecord.java | 12 + .../exceptions/InvalidRequestException.java | 4 + .../repository/PidMongoRepository.java | 76 +- .../handlemanager/service/DoiService.java | 53 +- .../service/FdoRecordService.java | 585 +++++++------- .../handlemanager/service/HandleService.java | 195 ++++- .../service/PidNameGeneratorService.java | 8 +- .../handlemanager/service/PidService.java | 297 ++++--- .../controller/PidControllerTest.java | 78 +- .../domain/TopicDisciplineTest.java | 104 --- .../repository/BatchInserterIT.java | 2 +- .../handlemanager/service/DoiServiceTest.java | 85 +- .../service/HandleServiceTest.java | 5 +- .../handlemanager/testUtils/TestUtils.java | 747 +++++++----------- 15 files changed, 1159 insertions(+), 1098 deletions(-) rename src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/{HandleAttributeJson.java => FdoAttribute.java} (82%) create mode 100644 src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoRecord.java delete mode 100644 src/test/java/eu/dissco/core/handlemanager/domain/TopicDisciplineTest.java diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/HandleAttributeJson.java b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoAttribute.java similarity index 82% rename from src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/HandleAttributeJson.java rename to src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoAttribute.java index 19ddf3f1..2ce08475 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/HandleAttributeJson.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoAttribute.java @@ -8,7 +8,7 @@ import lombok.Value; @Value -public class HandleAttributeJson { +public class FdoAttribute { int index; String type; @@ -16,14 +16,14 @@ public class HandleAttributeJson { int ttl = 86400; Instant timestamp; - public HandleAttributeJson(FdoProfile fdoAttribute, Instant timestamp, String value) { + public FdoAttribute(FdoProfile fdoAttribute, Instant timestamp, String value) { this.index = fdoAttribute.index(); this.type = fdoAttribute.get(); this.timestamp = timestamp; this.data = new StringHandleData(value); } - public HandleAttributeJson(Instant timestamp, String prefix) { + public FdoAttribute(Instant timestamp, String prefix) { this.index = HS_ADMIN.index(); this.type = HS_ADMIN.get(); this.data = new AdminHandleData(prefix); diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoRecord.java b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoRecord.java new file mode 100644 index 00000000..a902d640 --- /dev/null +++ b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoRecord.java @@ -0,0 +1,12 @@ +package eu.dissco.core.handlemanager.domain.repsitoryobjects; + +import eu.dissco.core.handlemanager.domain.fdo.FdoType; +import java.util.List; + +public record FdoRecord( + String handle, + FdoType fdoType, + List attributes, + String primaryLocalId) { + +} diff --git a/src/main/java/eu/dissco/core/handlemanager/exceptions/InvalidRequestException.java b/src/main/java/eu/dissco/core/handlemanager/exceptions/InvalidRequestException.java index 061409cb..1d82a299 100644 --- a/src/main/java/eu/dissco/core/handlemanager/exceptions/InvalidRequestException.java +++ b/src/main/java/eu/dissco/core/handlemanager/exceptions/InvalidRequestException.java @@ -8,4 +8,8 @@ public InvalidRequestException(String s) { super(s); } + public InvalidRequestException() { + super(); + } + } diff --git a/src/main/java/eu/dissco/core/handlemanager/repository/PidMongoRepository.java b/src/main/java/eu/dissco/core/handlemanager/repository/PidMongoRepository.java index 6cd4d677..482ba134 100644 --- a/src/main/java/eu/dissco/core/handlemanager/repository/PidMongoRepository.java +++ b/src/main/java/eu/dissco/core/handlemanager/repository/PidMongoRepository.java @@ -1,43 +1,105 @@ package eu.dissco.core.handlemanager.repository; import static com.mongodb.client.model.Filters.eq; +import static com.mongodb.client.model.Filters.in; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DIGITAL_OBJECT_TYPE; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.NORMALISED_SPECIMEN_OBJECT_ID; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PRIMARY_MEDIA_ID; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.mongodb.client.FindIterable; import com.mongodb.client.MongoCollection; +import com.mongodb.client.model.ReplaceOneModel; +import eu.dissco.core.handlemanager.domain.fdo.FdoType; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoAttribute; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoRecord; import eu.dissco.core.handlemanager.exceptions.PidResolutionException; +import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.bson.Document; import org.springframework.stereotype.Repository; @Repository @RequiredArgsConstructor +@Slf4j public class PidMongoRepository { private final MongoCollection collection; private final ObjectMapper mapper; + private static String ID = "_id"; public JsonNode getHandleRecord(String id) throws PidResolutionException, JsonProcessingException { - var result = collection.find(eq("_id", id)); + var result = collection.find(eq(ID, id)); if (result.first() == null) { throw new PidResolutionException("Unable to find handle " + id); } return mapper.readValue(result.first().toJson(), JsonNode.class); } - public void postSingleHandleRecord(JsonNode handleRecord, String id) + public void postBatchHandleRecord(List handleRecords) { + collection.insertMany(handleRecords); + } + + public void updateHandleRecord(List handleRecords, List ids) { + var filter = in(ID, ids); + var queryList = handleRecords.stream().map(doc -> new ReplaceOneModel<>(filter, doc)).toList(); + collection.bulkWrite(queryList); + } + + public List getHandleRecords(List ids) throws JsonProcessingException { + var results = collection.find(in(ID, ids)); + return formatResults(results); + } + + public List searchByPrimaryLocalId(String localIdField, List localIds) throws JsonProcessingException { - var doc = new Document("_id", id) - .append("values", mapper.writeValueAsString(handleRecord)); - collection.insertOne(doc); + var results = collection.find(in(localIdField, localIds)); + return formatResults(results); } - public void postBatchHandleRecord(List handleRecords) { - collection.insertMany(handleRecords); + private List formatResults(FindIterable results) + throws JsonProcessingException { + var handleRecords = new ArrayList(); + for (var result : results) { + var jsonRecord = mapper.readValue(result.toJson(), JsonNode.class); + if (jsonRecord.get("values") == null) { + log.warn("Unable to read handle record values \n {}", + mapper.writeValueAsString(jsonRecord)); + } else { + var attributes = mapper.convertValue(jsonRecord.get("values"), + new TypeReference>() { + }); + var fdoType = getFdoType(attributes, jsonRecord.get("_id").asText()); + handleRecords.add(new FdoRecord(jsonRecord.get("_id").asText(), + fdoType, attributes, getLocalId(jsonRecord, fdoType))); + } + } + return handleRecords; } + private FdoType getFdoType(List fdoAttributes, String id) { + for (var fdoAttribute : fdoAttributes) { + if (DIGITAL_OBJECT_TYPE.index() == fdoAttribute.getIndex()) { + return FdoType.fromString(fdoAttribute.getValue()); + } + } + log.error("Unable to determine Fdo Type for record of handle {}", id); + throw new IllegalStateException(); + } + private String getLocalId(JsonNode jsonRecord, FdoType fdoType) { + if (FdoType.DIGITAL_SPECIMEN.equals(fdoType)) { + return jsonRecord.get(NORMALISED_SPECIMEN_OBJECT_ID.get()).asText(); + } + if (FdoType.MEDIA_OBJECT.equals(fdoType)) { + return jsonRecord.get(PRIMARY_MEDIA_ID.get()).asText(); + } + return null; + } } diff --git a/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java b/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java index e848e07d..e81139ac 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java @@ -5,6 +5,7 @@ import static eu.dissco.core.handlemanager.domain.fdo.FdoType.MEDIA_OBJECT; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ATTRIBUTES; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_DATA; +import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ID; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -15,6 +16,7 @@ import eu.dissco.core.handlemanager.domain.fdo.FdoProfile; import eu.dissco.core.handlemanager.domain.fdo.FdoType; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperWrite; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoRecord; import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.PidResolutionException; @@ -25,6 +27,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.bson.Document; import org.springframework.context.annotation.Profile; @@ -51,28 +54,63 @@ public DoiService(PidRepository pidRepository, @Override public JsonApiWrapperWrite createRecords(List requests) throws InvalidRequestException, UnprocessableEntityException { - var handles = hf.genHandleListString(requests.size()).iterator(); + var handles = hf.genHandleList(requests.size()).iterator(); var requestAttributes = requests.stream() .map(request -> request.get(NODE_DATA).get(NODE_ATTRIBUTES)).toList(); var type = getObjectTypeFromJsonNode(requests); - List fdoRecords; + List fdoDocuments; + List fdoRecords; try { switch (type) { case DIGITAL_SPECIMEN -> fdoRecords = createDigitalSpecimen(requestAttributes, handles); case MEDIA_OBJECT -> fdoRecords = createMediaObject(requestAttributes, handles); default -> throw new UnsupportedOperationException( - type + " is not an appropriate Type for DOI endpoint."); + String.format(TYPE_ERROR_MESSAGE, type.getDigitalObjectName())); } + fdoDocuments = fdoRecordService.toMongoDbDocument(fdoRecords); } catch (JsonProcessingException | PidResolutionException e) { throw new InvalidRequestException( "An error has occurred parsing a record in request. More information: " + e.getMessage()); } log.info("Persisting new DOIs to Document Store"); - mongoRepository.postBatchHandleRecord(fdoRecords); + mongoRepository.postBatchHandleRecord(fdoDocuments); log.info("Publishing to DataCite"); //todo publishToDataCite(fdoRecords, EventType.CREATE, type); - return new JsonApiWrapperWrite(formatCreateDocuments(fdoRecords, type)); + return new JsonApiWrapperWrite(formatCreateRecord(fdoRecords, type)); + } + + @Override + public JsonApiWrapperWrite updateRecords(List requests) + throws InvalidRequestException, UnprocessableEntityException { + var updateRequests = requests.stream() + .map(request -> request.get(NODE_DATA)).toList(); + var handles = updateRequests.stream() + .map(request -> request.get(NODE_ID).asText()).toList(); + var previousVersions = getPreviousVersions(handles); + checkInternalDuplicates(handles); + checkHandlesWritableFullRecord(previousVersions); + var fdoRecordMap = previousVersions.stream() + .collect(Collectors.toMap(FdoRecord::handle, f -> f)); + var type = getObjectTypeFromJsonNode(requests); + List fdoRecords; + List fdoDocuments; + try { + switch (type) { + case DIGITAL_SPECIMEN -> fdoRecords = updateDigitalSpecimen(updateRequests, fdoRecordMap); + case MEDIA_OBJECT -> fdoRecords = updateDigitalMedia(updateRequests, fdoRecordMap); + default -> throw new UnsupportedOperationException( + String.format(TYPE_ERROR_MESSAGE, type.getDigitalObjectName())); + } + fdoDocuments = fdoRecordService.toMongoDbDocument(fdoRecords); + var fdoType = getObjectTypeFromJsonNode(requests); + mongoRepository.updateHandleRecord(fdoDocuments, handles); + // todo figure out response + return new JsonApiWrapperWrite(formatCreateRecord(fdoRecords, fdoType)); + } catch (JsonProcessingException e) { + log.error("An error has occurred processing JSON data", e); + throw new UnprocessableEntityException("Json Processing Error"); + } } @Override @@ -80,7 +118,8 @@ public JsonApiWrapperWrite updateRecords(List requests, boolean increm throws InvalidRequestException, UnprocessableEntityException { var type = getObjectTypeFromJsonNode(requests); if (!DIGITAL_SPECIMEN.equals(type) && !MEDIA_OBJECT.equals(type)) { - throw new InvalidRequestException(TYPE_ERROR_MESSAGE); + throw new InvalidRequestException( + String.format(TYPE_ERROR_MESSAGE, type.getDigitalObjectName())); } var attributesToUpdate = getAttributesToUpdate(requests); var response = updateRecords(attributesToUpdate, incrementVersion, type); @@ -102,7 +141,7 @@ private void publishToDataCite(List handleAttributes, EventType key)); } eventList.add( - new DataCiteEvent(jsonFormatSingleRecord(value), eventType)); + new DataCiteEvent(jsonFormatSingleRecordOld(value), eventType)); }); for (var event : eventList) { diff --git a/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java b/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java index eec00491..b5d57557 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java @@ -2,6 +2,7 @@ import static eu.dissco.core.handlemanager.configuration.AppConfig.DATE_STRING; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.ANNOTATION_HASH; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.BASE_TYPE_OF_SPECIMEN; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.CATALOG_IDENTIFIER; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DCTERMS_FORMAT; @@ -67,8 +68,8 @@ import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOPIC_DISCIPLINE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOPIC_DOMAIN; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOPIC_ORIGIN; +import static eu.dissco.core.handlemanager.domain.fdo.FdoType.ANNOTATION; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.DIGITAL_SPECIMEN; -import static eu.dissco.core.handlemanager.domain.fdo.FdoType.HANDLE; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.MAPPING; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.MAS; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.MEDIA_OBJECT; @@ -93,12 +94,12 @@ import eu.dissco.core.handlemanager.domain.fdo.MediaObjectRequest; import eu.dissco.core.handlemanager.domain.fdo.OrganisationRequest; import eu.dissco.core.handlemanager.domain.fdo.SourceSystemRequest; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoAttribute; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoRecord; import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; -import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttributeJson; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.InvalidRequestRuntimeException; import eu.dissco.core.handlemanager.properties.ApplicationProperties; -import eu.dissco.core.handlemanager.properties.ProfileProperties; import eu.dissco.core.handlemanager.web.PidResolver; import jakarta.annotation.Nullable; import java.io.IOException; @@ -137,7 +138,6 @@ public class FdoRecordService { private final PidResolver pidResolver; private final ObjectMapper mapper; private final ApplicationProperties applicationProperties; - private final ProfileProperties profileProperties; public static final String HANDLE_DOMAIN = "https://hdl.handle.net/"; public static final String DOI_DOMAIN = "https://doi.org/"; private static final String ROR_API_DOMAIN = "https://api.ror.org/organizations/"; @@ -146,10 +146,14 @@ public class FdoRecordService { private static final String WIKIDATA_API = "https://wikidata.org/w/rest.php/wikibase/v0/entities/items/"; private static final String PROXY_ERROR = "Invalid attribute: %s must contain proxy: %s"; private static final String PID_KERNEL_METADATA_LICENSE = "https://creativecommons.org/publicdomain/zero/1.0/"; - private static final byte[] ADMIN_HEX = "\\\\x0FFF000000153330303A302E4E412F32302E353030302E31303235000000C8".getBytes( - StandardCharsets.UTF_8); private static final String LOC_REQUEST = "locations"; public static final Map RESOLVABLE_KEYS; + public static final List GENERATED_KEYS; + + static { + GENERATED_KEYS = List.of(FDO_RECORD_LICENSE.index(), PID.index(), PID_RECORD_ISSUE_DATE.index(), + PID_STATUS.index(), HS_ADMIN.index()); + } static { HashMap hashMap = new HashMap<>(); @@ -163,577 +167,578 @@ public class FdoRecordService { private final DateTimeFormatter dt = DateTimeFormatter.ofPattern(DATE_STRING) .withZone(ZoneId.of("UTC")); - public HandleAttribute genHsAdmin(byte[] handle) { - return new HandleAttribute(HS_ADMIN.index(), handle, HS_ADMIN.get(), ADMIN_HEX); - } - - private Document toMongoDbDocument(List fdoRecord, String handle) + public List toMongoDbDocument(List fdoRecords) throws JsonProcessingException { - var arrayNode = mapper.createArrayNode(); - for (var attribute : fdoRecord) { - arrayNode.add(attribute); + var documentList = new ArrayList(); + for (var fdoRecord : fdoRecords) { + var arrayNode = mapper.createArrayNode(); + for (var attribute : fdoRecord.attributes()) { + arrayNode.add(mapper.valueToTree(attribute)); + } + var vals = mapper.createObjectNode() + .set("values", arrayNode); + var doc = Document.parse(mapper.writeValueAsString(vals)) + .append("_id", fdoRecord.handle()); + if (DIGITAL_SPECIMEN.equals(fdoRecord.fdoType())) { + doc.append(NORMALISED_SPECIMEN_OBJECT_ID.get(), fdoRecord.primaryLocalId()); + } else if (MEDIA_OBJECT.equals(fdoRecord.fdoType())) { + doc.append(PRIMARY_MEDIA_ID.get(), fdoRecord.primaryLocalId()); + } else if (ANNOTATION.equals(fdoRecord.fdoType())) { + doc.append(ANNOTATION_HASH.get(), fdoRecord.primaryLocalId()); + } + documentList.add(doc); } - var vals = mapper.createObjectNode() - .set("values", arrayNode); - return Document.parse(mapper.writeValueAsString(vals)) - .append("_id", handle); + return documentList; } /* Handle Record Creation */ - - public Document prepareNewHandleDocument(HandleRecordRequest request, String handle, - FdoType fdoType, Instant timestamp) throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareHandleAttributesGenerated(handle, HANDLE, timestamp); - prepareNewHandleJsonNodeRecord(request, handle, fdoType, timestamp); - return toMongoDbDocument(fdoRecord, handle); + public FdoRecord prepareNewHandleRecord(HandleRecordRequest request, String handle, + FdoType fdoType, Instant timestamp) throws InvalidRequestException { + var fdoAttributes = prepareNewHandleAttributeList(request, handle, fdoType, timestamp); + return new FdoRecord(handle, fdoType, fdoAttributes, null); } - public Document prepareUpdatedHandleDocument(HandleRecordRequest request, String handle, - FdoType fdoType, Instant timestamp, int issueNumber) - throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareUpdatedHandleJsonNodeRecord(request, handle, fdoType, timestamp, - issueNumber); - return toMongoDbDocument(fdoRecord, handle); + public FdoRecord prepareUpdatedHandleRecord(HandleRecordRequest recordRequest, + FdoType fdoType, Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) + throws InvalidRequestException { + var fdoAttributes = prepareUpdatedHandleAttributesRecord(recordRequest, + previousVersion.handle(), fdoType, + timestamp, previousVersion, incrementVersion); + return new FdoRecord(previousVersion.handle(), fdoType, fdoAttributes, null); } - private List prepareNewHandleJsonNodeRecord(HandleRecordRequest request, String handle, + private List prepareNewHandleAttributeList(HandleRecordRequest request, + String handle, FdoType fdoType, Instant timestamp) throws InvalidRequestException { var handleAttributes = prepareHandleAttributesFromRequest(request, handle, fdoType, timestamp); handleAttributes.addAll(prepareHandleAttributesGenerated(handle, fdoType, timestamp)); return handleAttributes; } - private List prepareUpdatedHandleJsonNodeRecord(HandleRecordRequest request, - String handle, FdoType fdoType, Instant timestamp, int issueNumber) + private List prepareUpdatedHandleAttributesRecord(HandleRecordRequest request, + String handle, FdoType fdoType, Instant timestamp, FdoRecord previousVersion, + boolean incrementVersion) throws InvalidRequestException { - var handleAttributes = prepareHandleAttributesFromRequest(request, handle, fdoType, timestamp); - handleAttributes.add(mapper.valueToTree( - new HandleAttributeJson(PID_RECORD_ISSUE_NUMBER, timestamp, String.valueOf(issueNumber)))); - return handleAttributes; + var previousAttributes = new ArrayList<>(previousVersion.attributes()); + var newAttributes = prepareHandleAttributesFromRequest(request, handle, fdoType, timestamp); + var generatedAttributes = new ArrayList(); + for (var prevAttribute : previousAttributes) { + if (GENERATED_KEYS.contains(prevAttribute.getIndex())) { + generatedAttributes.add(prevAttribute); + } else if (PID_RECORD_ISSUE_NUMBER.index() == prevAttribute.getIndex()) { + var prevIssueNumber = Integer.parseInt(prevAttribute.getValue()); + var issueNumber = incrementVersion ? prevIssueNumber + 1 : prevIssueNumber; + generatedAttributes.add( + new FdoAttribute(PID_RECORD_ISSUE_NUMBER, timestamp, String.valueOf(issueNumber))); + } + } + newAttributes.addAll(generatedAttributes); + return newAttributes; } + // These attributes may change on an update - private ArrayList prepareHandleAttributesFromRequest(HandleRecordRequest request, + private ArrayList prepareHandleAttributesFromRequest( + HandleRecordRequest request, String handle, FdoType fdoType, Instant timestamp) throws InvalidRequestException { - var fdoProfile = new ArrayList(); - var handleAttributeList = new ArrayList(); + var handleAttributeList = new ArrayList(); // 101: 10320/Loc if (!fdoType.equals(ORGANISATION)) { - handleAttributeList.add(new HandleAttributeJson(LOC, timestamp, + handleAttributeList.add(new FdoAttribute(LOC, timestamp, setLocations(request.getLocations(), handle, fdoType))); } // 1: FDO Profile handleAttributeList.add( - new HandleAttributeJson(FDO_PROFILE, timestamp, fdoType.getFdoProfile())); + new FdoAttribute(FDO_PROFILE, timestamp, fdoType.getFdoProfile())); // 3: Digital Object Type handleAttributeList.add( - new HandleAttributeJson(DIGITAL_OBJECT_TYPE, timestamp, fdoType.getDigitalObjectType())); + new FdoAttribute(DIGITAL_OBJECT_TYPE, timestamp, fdoType.getDigitalObjectType())); // 4: Digital ObjectName handleAttributeList.add( - new HandleAttributeJson(DIGITAL_OBJECT_NAME, timestamp, fdoType.getDigitalObjectType())); + new FdoAttribute(DIGITAL_OBJECT_NAME, timestamp, fdoType.getDigitalObjectType())); // 6: PID Issuer - handleAttributeList.add(new HandleAttributeJson(PID_ISSUER, timestamp, request.getPidIssuer())); + handleAttributeList.add(new FdoAttribute(PID_ISSUER, timestamp, request.getPidIssuer())); // 7: PID Issuer Name - handleAttributeList.add(new HandleAttributeJson(PID_ISSUER_NAME, timestamp, + handleAttributeList.add(new FdoAttribute(PID_ISSUER_NAME, timestamp, getObjectName(request.getPidIssuer(), null))); // 8: Issued For Agent handleAttributeList.add( - new HandleAttributeJson(ISSUED_FOR_AGENT, timestamp, request.getIssuedForAgent())); + new FdoAttribute(ISSUED_FOR_AGENT, timestamp, request.getIssuedForAgent())); // 9: Issued for Agent Name - handleAttributeList.add(new HandleAttributeJson(ISSUED_FOR_AGENT_NAME, timestamp, + handleAttributeList.add(new FdoAttribute(ISSUED_FOR_AGENT_NAME, timestamp, getObjectName(request.getIssuedForAgent(), null))); // 12: Structural Type handleAttributeList.add( - new HandleAttributeJson(STRUCTURAL_TYPE, timestamp, + new FdoAttribute(STRUCTURAL_TYPE, timestamp, request.getStructuralType().toString())); - for (var attribute : handleAttributeList) { - fdoProfile.add(mapper.valueToTree(attribute)); - } - return fdoProfile; + return handleAttributeList; } - // These attributes do not change on an update (except issue number, which is dealt with separately) - private List prepareHandleAttributesGenerated(String handle, FdoType fdoType, + // These attributes do not depend on the request and do not change on an update (except issue number) + private List prepareHandleAttributesGenerated(String handle, FdoType fdoType, Instant timestamp) { - var fdoProfile = new ArrayList(); - var handleAttributeList = new ArrayList(); + var handleAttributeList = new ArrayList(); // 2: FDO Record License handleAttributeList.add( - new HandleAttributeJson(FDO_RECORD_LICENSE, timestamp, PID_KERNEL_METADATA_LICENSE)); + new FdoAttribute(FDO_RECORD_LICENSE, timestamp, PID_KERNEL_METADATA_LICENSE)); // 5: PID - handleAttributeList.add(new HandleAttributeJson(PID, timestamp, fdoType.getDomain() + handle)); + handleAttributeList.add(new FdoAttribute(PID, timestamp, fdoType.getDomain() + handle)); // 10: PID Record Issue Date handleAttributeList.add( - new HandleAttributeJson(PID_RECORD_ISSUE_DATE, timestamp, getDate(timestamp))); + new FdoAttribute(PID_RECORD_ISSUE_DATE, timestamp, getDate(timestamp))); // 11: Pid Record Issue Number handleAttributeList.add( - new HandleAttributeJson(PID_RECORD_ISSUE_NUMBER, timestamp, + new FdoAttribute(PID_RECORD_ISSUE_NUMBER, timestamp, "1")); // This gets replaced on an update // 13: Pid Status - handleAttributeList.add(new HandleAttributeJson(PID_STATUS, timestamp, "TEST")); + handleAttributeList.add(new FdoAttribute(PID_STATUS, timestamp, "TEST")); // 100 HS Admin - handleAttributeList.add(new HandleAttributeJson(timestamp, applicationProperties.getPrefix())); - for (var attribute : handleAttributeList) { - fdoProfile.add(mapper.valueToTree(attribute)); - } - return fdoProfile; + handleAttributeList.add(new FdoAttribute(timestamp, applicationProperties.getPrefix())); + return handleAttributeList; } /* DOI Record Creation */ - public Document prepareNewDoiDocument(DoiRecordRequest request, String handle, + public FdoRecord prepareNewDoiDocument(DoiRecordRequest request, String handle, FdoType fdoType, Instant timestamp) throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareNewDoiJsonNodeRecord(request, handle, fdoType, timestamp); - return toMongoDbDocument(fdoRecord, handle); + var fdoAttributes = prepareNewDoiFdoAttributes(request, handle, fdoType, timestamp); + return new FdoRecord(handle, fdoType, fdoAttributes, null); } - public Document prepareUpdatedDoiDocument(DoiRecordRequest request, String handle, - FdoType fdoType, Instant timestamp, int issueNum) - throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareUpdatedDoiJsonNodeRecord(request, handle, fdoType, timestamp, issueNum); - return toMongoDbDocument(fdoRecord, handle); + public FdoRecord prepareUpdatedDoiFdoRecord(DoiRecordRequest request, + FdoType fdoType, Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) + throws InvalidRequestException { + var fdoAttributes = prepareUpdatedDoiFdoAttributes(request, previousVersion.handle(), fdoType, + timestamp, + previousVersion, incrementVersion); + return new FdoRecord(previousVersion.handle(), fdoType, fdoAttributes, null); } - private List prepareNewDoiJsonNodeRecord(DoiRecordRequest request, String handle, + private List prepareNewDoiFdoAttributes(DoiRecordRequest request, + String handle, FdoType fdoType, Instant timestamp) throws InvalidRequestException { - var fdoProfile = prepareNewHandleJsonNodeRecord(request, handle, fdoType, timestamp); - fdoProfile.addAll(prepareDoiAttributesFromRequest(request, handle, timestamp)); - return fdoProfile; + var fdoAttributes = prepareNewHandleAttributeList(request, handle, fdoType, timestamp); + fdoAttributes.addAll(prepareDoiAttributesFromRequest(request, handle, timestamp)); + return fdoAttributes; } - private List prepareUpdatedDoiJsonNodeRecord(DoiRecordRequest request, String handle, - FdoType fdoType, Instant timestamp, int issueNumber) throws InvalidRequestException { - var fdoProfile = prepareUpdatedHandleJsonNodeRecord(request, handle, fdoType, timestamp, - issueNumber); - fdoProfile.addAll(prepareDoiAttributesFromRequest(request, handle, timestamp)); - return fdoProfile; + private List prepareUpdatedDoiFdoAttributes(DoiRecordRequest request, + String handle, + FdoType fdoType, Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) + throws InvalidRequestException { + var fdoAttributes = prepareUpdatedHandleAttributesRecord(request, handle, fdoType, timestamp, + previousVersion, + incrementVersion); + fdoAttributes.addAll(prepareDoiAttributesFromRequest(request, handle, timestamp)); + return fdoAttributes; } - private List prepareDoiAttributesFromRequest(DoiRecordRequest request, String handle, + private List prepareDoiAttributesFromRequest(DoiRecordRequest request, + String handle, Instant timestamp) { - var fdoProfile = new ArrayList(); - var handleAttributeList = new ArrayList(); - + var handleAttributeList = new ArrayList(); // 40: Referent Type handleAttributeList.add( - new HandleAttributeJson(REFERENT_TYPE, timestamp, request.getReferentType())); + new FdoAttribute(REFERENT_TYPE, timestamp, request.getReferentType())); // 41: Referent DOI Name - handleAttributeList.add(new HandleAttributeJson(REFERENT_DOI_NAME, timestamp, handle)); + handleAttributeList.add(new FdoAttribute(REFERENT_DOI_NAME, timestamp, handle)); // 42: Referent Name handleAttributeList.add( - new HandleAttributeJson(REFERENT_NAME, timestamp, request.getReferentName())); + new FdoAttribute(REFERENT_NAME, timestamp, request.getReferentName())); // 43: Primary Referent Type - handleAttributeList.add(new HandleAttributeJson(PRIMARY_REFERENT_TYPE, timestamp, + handleAttributeList.add(new FdoAttribute(PRIMARY_REFERENT_TYPE, timestamp, request.getPrimaryReferentType())); - for (var attribute : handleAttributeList) { - fdoProfile.add(mapper.valueToTree(attribute)); - } - return fdoProfile; + return handleAttributeList; } /* Annotation Record Creation */ - public Document prepareNewAnnotationDocument(AnnotationRequest request, String handle, - Instant timestamp) - throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareNewHandleJsonNodeRecord(request, handle, FdoType.ANNOTATION, timestamp); - fdoRecord.addAll(prepareAnnotationAttributesFromRequest(request, timestamp)); - return toMongoDbDocument(fdoRecord, handle); + public FdoRecord prepareNewAnnotationRecord(AnnotationRequest request, String handle, + Instant timestamp) throws InvalidRequestException { + var fdoAttributes = prepareNewHandleAttributeList(request, handle, ANNOTATION, + timestamp); + fdoAttributes.addAll(prepareAnnotationAttributesFromRequest(request, timestamp)); + return new FdoRecord(handle, ANNOTATION, fdoAttributes, request.getAnnotationHash().toString()); } - public Document prepareUpdatedAnnotationDocument(AnnotationRequest request, String handle, - Instant timestamp, int issueNumber) - throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareUpdatedHandleJsonNodeRecord(request, handle, FdoType.ANNOTATION, - timestamp, issueNumber); - fdoRecord.addAll(prepareAnnotationAttributesFromRequest(request, timestamp)); - return toMongoDbDocument(fdoRecord, handle); + public FdoRecord prepareUpdatedAnnotationFdoRecord(AnnotationRequest request, + Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) + throws InvalidRequestException { + var fdoAttributes = prepareUpdatedHandleAttributesRecord(request, previousVersion.handle(), + ANNOTATION, + timestamp, previousVersion, incrementVersion); + fdoAttributes.addAll(prepareAnnotationAttributesFromRequest(request, timestamp)); + return new FdoRecord(previousVersion.handle(), ANNOTATION, fdoAttributes, + request.getAnnotationHash().toString()); } - private List prepareAnnotationAttributesFromRequest(AnnotationRequest request, + private List prepareAnnotationAttributesFromRequest( + AnnotationRequest request, Instant timestamp) { - var fdoProfile = new ArrayList(); - var handleAttributeList = new ArrayList(); + var handleAttributeList = new ArrayList(); // 500 Target PID - handleAttributeList.add(new HandleAttributeJson(TARGET_PID, timestamp, request.getTargetPid())); + handleAttributeList.add(new FdoAttribute(TARGET_PID, timestamp, request.getTargetPid())); // 501 Target Type handleAttributeList.add( - new HandleAttributeJson(TARGET_TYPE, timestamp, request.getTargetType())); + new FdoAttribute(TARGET_TYPE, timestamp, request.getTargetType())); // 502 Motivation - handleAttributeList.add(new HandleAttributeJson(MOTIVATION, timestamp, + handleAttributeList.add(new FdoAttribute(MOTIVATION, timestamp, request.getMotivation().toString())); // 503 Annotation Hash - handleAttributeList.add(new HandleAttributeJson(PRIMARY_REFERENT_TYPE, timestamp, + handleAttributeList.add(new FdoAttribute(PRIMARY_REFERENT_TYPE, timestamp, request.getMotivation().toString())); - for (var attribute : handleAttributeList) { - fdoProfile.add(mapper.valueToTree(attribute)); - } - return fdoProfile; + return handleAttributeList; } /* Data Mapping Record Creation */ - public Document prepareNewDataMappingDocument(MappingRequest request, String handle, - Instant timestamp) - throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareNewHandleJsonNodeRecord(request, handle, MAPPING, timestamp); - fdoRecord.addAll(prepareDataMappingAttributesFromRequest(request, timestamp)); - return toMongoDbDocument(fdoRecord, handle); + public FdoRecord prepareNewDataMappingRecord(MappingRequest request, String handle, + Instant timestamp) throws InvalidRequestException { + var fdoAttributes = prepareNewHandleAttributeList(request, handle, MAPPING, timestamp); + fdoAttributes.addAll(prepareDataMappingAttributesFromRequest(request, timestamp)); + return new FdoRecord(handle, MAPPING, fdoAttributes, null); } - public Document prepareUpdatedDataMappingDocument(MappingRequest request, String handle, - Instant timestamp, int issueNumber) - throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareUpdatedHandleJsonNodeRecord(request, handle, MAPPING, timestamp, - issueNumber); - fdoRecord.addAll(prepareDataMappingAttributesFromRequest(request, timestamp)); - return toMongoDbDocument(fdoRecord, handle); + public FdoRecord prepareUpdatedDataMappingRecord(MappingRequest request, + Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) + throws InvalidRequestException { + var fdoAttributes = prepareUpdatedHandleAttributesRecord(request, previousVersion.handle(), + MAPPING, timestamp, + previousVersion, incrementVersion); + fdoAttributes.addAll(prepareDataMappingAttributesFromRequest(request, timestamp)); + return new FdoRecord(previousVersion.handle(), MAPPING, fdoAttributes, null); } - private List prepareDataMappingAttributesFromRequest(MappingRequest request, + private List prepareDataMappingAttributesFromRequest(MappingRequest request, Instant timestamp) { - var fdoProfile = new ArrayList(); - fdoProfile.add(mapper.valueToTree( - new HandleAttributeJson(SOURCE_DATA_STANDARD, timestamp, request.getSourceDataStandard()))); - return fdoProfile; + return List.of( + new FdoAttribute(SOURCE_DATA_STANDARD, timestamp, request.getSourceDataStandard())); } /* Digital Specimen Record Creation */ - public Document prepareNewDigitalSpecimenRecord(DigitalSpecimenRequest request, + public FdoRecord prepareNewDigitalSpecimenRecord(DigitalSpecimenRequest request, String handle, Instant timestamp) throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareNewDoiJsonNodeRecord(request, handle, DIGITAL_SPECIMEN, timestamp); - fdoRecord.addAll(prepareDigitalSpecimenRecordAttributesFromRequest(request, timestamp)); - return toMongoDbDocument(fdoRecord, handle) - .append(NORMALISED_SPECIMEN_OBJECT_ID.get(), - request.getNormalisedPrimarySpecimenObjectId()); + var fdoAttributes = prepareNewDoiFdoAttributes(request, handle, DIGITAL_SPECIMEN, timestamp); + fdoAttributes.addAll(prepareDigitalSpecimenRecordAttributesFromRequest(request, timestamp)); + return new FdoRecord(handle, DIGITAL_SPECIMEN, fdoAttributes, + request.getNormalisedPrimarySpecimenObjectId()); } - public List prepareUpdatedDigitalSpecimenRecord(DigitalSpecimenRequest request, - String handle, Instant timestamp, int issueNumber) + public FdoRecord prepareUpdatedDigitalSpecimenRecord( + DigitalSpecimenRequest request, Instant timestamp, FdoRecord previousVersion, + boolean incrementVersion) throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareUpdatedDoiJsonNodeRecord(request, handle, DIGITAL_SPECIMEN, timestamp, - issueNumber); - fdoRecord.addAll(prepareDigitalSpecimenRecordAttributesFromRequest(request, timestamp)); - return fdoRecord; + var fdoAttributes = prepareUpdatedDoiFdoAttributes(request, previousVersion.handle(), + DIGITAL_SPECIMEN, timestamp, previousVersion, incrementVersion); + fdoAttributes.addAll(prepareDigitalSpecimenRecordAttributesFromRequest(request, timestamp)); + return new FdoRecord(previousVersion.handle(), DIGITAL_SPECIMEN, fdoAttributes, + request.getNormalisedPrimarySpecimenObjectId()); } - private List prepareDigitalSpecimenRecordAttributesFromRequest( + private List prepareDigitalSpecimenRecordAttributesFromRequest( DigitalSpecimenRequest request, Instant timestamp) throws InvalidRequestException, JsonProcessingException { - var fdoProfile = new ArrayList(); - var handleAttributeList = new ArrayList(); + var handleAttributeList = new ArrayList(); // 200 Specimen Host handleAttributeList.add( - new HandleAttributeJson(SPECIMEN_HOST, timestamp, request.getSpecimenHost())); + new FdoAttribute(SPECIMEN_HOST, timestamp, request.getSpecimenHost())); // 201 Specimen Host Name - handleAttributeList.add(new HandleAttributeJson(SPECIMEN_HOST_NAME, timestamp, + handleAttributeList.add(new FdoAttribute(SPECIMEN_HOST_NAME, timestamp, getObjectName(request.getSpecimenHost(), request.getSpecimenHostName()))); // 202 Primary Specimen Object ID - handleAttributeList.add(new HandleAttributeJson(PRIMARY_SPECIMEN_OBJECT_ID, timestamp, + handleAttributeList.add(new FdoAttribute(PRIMARY_SPECIMEN_OBJECT_ID, timestamp, getObjectName(request.getSpecimenHost(), request.getPrimarySpecimenObjectId()))); // 203 Primary Specimen Object ID Type - handleAttributeList.add(new HandleAttributeJson(PRIMARY_SPECIMEN_OBJECT_ID_TYPE, timestamp, + handleAttributeList.add(new FdoAttribute(PRIMARY_SPECIMEN_OBJECT_ID_TYPE, timestamp, getObjectName(request.getSpecimenHost(), request.getPrimarySpecimenObjectIdType().toString()))); // 204 Primary Specimen Object ID Name - handleAttributeList.add(new HandleAttributeJson(PRIMARY_SPECIMEN_OBJECT_ID_NAME, timestamp, + handleAttributeList.add(new FdoAttribute(PRIMARY_SPECIMEN_OBJECT_ID_NAME, timestamp, getObjectName(request.getSpecimenHost(), request.getPrimarySpecimenObjectIdName()))); // 205 Normalised Specimen Object Id - handleAttributeList.add(new HandleAttributeJson(NORMALISED_SPECIMEN_OBJECT_ID, timestamp, + handleAttributeList.add(new FdoAttribute(NORMALISED_SPECIMEN_OBJECT_ID, timestamp, request.getNormalisedPrimarySpecimenObjectId())); // 206 Specimen Object Id Absence Reason - handleAttributeList.add(new HandleAttributeJson(SPECIMEN_OBJECT_ID_ABSENCE_REASON, timestamp, + handleAttributeList.add(new FdoAttribute(SPECIMEN_OBJECT_ID_ABSENCE_REASON, timestamp, request.getSpecimenObjectIdAbsenceReason())); // 206 Specimen Object Id Absence Reason - handleAttributeList.add(new HandleAttributeJson(SPECIMEN_OBJECT_ID_ABSENCE_REASON, timestamp, + handleAttributeList.add(new FdoAttribute(SPECIMEN_OBJECT_ID_ABSENCE_REASON, timestamp, request.getSpecimenObjectIdAbsenceReason())); // 207 Other Specimen Ids - handleAttributeList.add(new HandleAttributeJson(OTHER_SPECIMEN_IDS, timestamp, + handleAttributeList.add(new FdoAttribute(OTHER_SPECIMEN_IDS, timestamp, mapper.writeValueAsString(request.getOtherSpecimenIds()))); // 208 Topic Origin if (request.getTopicOrigin() != null) { handleAttributeList.add( - new HandleAttributeJson(TOPIC_ORIGIN, timestamp, request.getTopicOrigin().toString())); + new FdoAttribute(TOPIC_ORIGIN, timestamp, request.getTopicOrigin().toString())); } else { - handleAttributeList.add(new HandleAttributeJson(TOPIC_ORIGIN, timestamp, null)); + handleAttributeList.add(new FdoAttribute(TOPIC_ORIGIN, timestamp, null)); } // 209 Topic Domain if (request.getTopicDomain() != null) { handleAttributeList.add( - new HandleAttributeJson(TOPIC_DOMAIN, timestamp, request.getTopicDomain().toString())); + new FdoAttribute(TOPIC_DOMAIN, timestamp, request.getTopicDomain().toString())); } else { - handleAttributeList.add(new HandleAttributeJson(TOPIC_DOMAIN, timestamp, null)); + handleAttributeList.add(new FdoAttribute(TOPIC_DOMAIN, timestamp, null)); } // 210 Topic Discipline if (request.getTopicDiscipline() != null) { - handleAttributeList.add(new HandleAttributeJson(TOPIC_DISCIPLINE, timestamp, + handleAttributeList.add(new FdoAttribute(TOPIC_DISCIPLINE, timestamp, request.getTopicDiscipline().toString())); } else { - handleAttributeList.add(new HandleAttributeJson(TOPIC_DISCIPLINE, timestamp, null)); + handleAttributeList.add(new FdoAttribute(TOPIC_DISCIPLINE, timestamp, null)); } // 211 Topic Category if (request.getTopicDiscipline() != null) { - handleAttributeList.add(new HandleAttributeJson(TOPIC_CATEGORY, timestamp, + handleAttributeList.add(new FdoAttribute(TOPIC_CATEGORY, timestamp, request.getTopicDiscipline().toString())); } else { - handleAttributeList.add(new HandleAttributeJson(TOPIC_CATEGORY, timestamp, null)); + handleAttributeList.add(new FdoAttribute(TOPIC_CATEGORY, timestamp, null)); } // 212 Living or Preserved if (request.getLivingOrPreserved() != null) { - handleAttributeList.add(new HandleAttributeJson(LIVING_OR_PRESERVED, timestamp, + handleAttributeList.add(new FdoAttribute(LIVING_OR_PRESERVED, timestamp, request.getLivingOrPreserved().toString())); } else { - handleAttributeList.add(new HandleAttributeJson(LIVING_OR_PRESERVED, timestamp, null)); + handleAttributeList.add(new FdoAttribute(LIVING_OR_PRESERVED, timestamp, null)); } // 213 Base Type of Specimen if (request.getBaseTypeOfSpecimen() != null) { - handleAttributeList.add(new HandleAttributeJson(BASE_TYPE_OF_SPECIMEN, timestamp, + handleAttributeList.add(new FdoAttribute(BASE_TYPE_OF_SPECIMEN, timestamp, request.getBaseTypeOfSpecimen().toString())); } else { - handleAttributeList.add(new HandleAttributeJson(BASE_TYPE_OF_SPECIMEN, timestamp, null)); + handleAttributeList.add(new FdoAttribute(BASE_TYPE_OF_SPECIMEN, timestamp, null)); } // 214 Information Artefact Type if (request.getInformationArtefactType() != null) { - handleAttributeList.add(new HandleAttributeJson(INFORMATION_ARTEFACT_TYPE, timestamp, + handleAttributeList.add(new FdoAttribute(INFORMATION_ARTEFACT_TYPE, timestamp, request.getInformationArtefactType().toString())); } else { - handleAttributeList.add(new HandleAttributeJson(INFORMATION_ARTEFACT_TYPE, timestamp, null)); + handleAttributeList.add(new FdoAttribute(INFORMATION_ARTEFACT_TYPE, timestamp, null)); } // 215 Material Sample Type if (request.getMaterialSampleType() != null) { - handleAttributeList.add(new HandleAttributeJson(MATERIAL_SAMPLE_TYPE, timestamp, + handleAttributeList.add(new FdoAttribute(MATERIAL_SAMPLE_TYPE, timestamp, request.getMaterialSampleType().toString())); } else { - handleAttributeList.add(new HandleAttributeJson(MATERIAL_SAMPLE_TYPE, timestamp, null)); + handleAttributeList.add(new FdoAttribute(MATERIAL_SAMPLE_TYPE, timestamp, null)); } // 216 Material or Digital Entity if (request.getMaterialOrDigitalEntity() != null) { - handleAttributeList.add(new HandleAttributeJson(MATERIAL_OR_DIGITAL_ENTITY, timestamp, + handleAttributeList.add(new FdoAttribute(MATERIAL_OR_DIGITAL_ENTITY, timestamp, request.getMaterialOrDigitalEntity().toString())); } else { - handleAttributeList.add(new HandleAttributeJson(MATERIAL_OR_DIGITAL_ENTITY, timestamp, null)); + handleAttributeList.add(new FdoAttribute(MATERIAL_OR_DIGITAL_ENTITY, timestamp, null)); } // 217 Marked as Type if (request.getMarkedAsType() != null) { - handleAttributeList.add(new HandleAttributeJson(MARKED_AS_TYPE, timestamp, + handleAttributeList.add(new FdoAttribute(MARKED_AS_TYPE, timestamp, String.valueOf(request.getMarkedAsType()))); } else { - handleAttributeList.add(new HandleAttributeJson(MARKED_AS_TYPE, timestamp, null)); + handleAttributeList.add(new FdoAttribute(MARKED_AS_TYPE, timestamp, null)); } // 218 Derived From Entity handleAttributeList.add( - new HandleAttributeJson(DERIVED_FROM_ENTITY, timestamp, request.getDerivedFromEntity())); + new FdoAttribute(DERIVED_FROM_ENTITY, timestamp, request.getDerivedFromEntity())); // 219 Catalog ID handleAttributeList.add( - new HandleAttributeJson(CATALOG_IDENTIFIER, timestamp, request.getCatalogIdentifier())); - for (var attribute : handleAttributeList) { - fdoProfile.add(mapper.valueToTree(attribute)); - } - return fdoProfile; + new FdoAttribute(CATALOG_IDENTIFIER, timestamp, request.getCatalogIdentifier())); + return handleAttributeList; } /* MAS Record Creation */ - public Document prepareNewMasDocument(MasRequest request, String handle, + public FdoRecord prepareNewMasRecord(MasRequest request, String handle, Instant timestamp) - throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareNewHandleJsonNodeRecord(request, handle, MAS, timestamp); - fdoRecord.addAll(prepareMasAttributesFromRequest(request, timestamp)); - return toMongoDbDocument(fdoRecord, handle); + throws InvalidRequestException { + var fdoAttributes = prepareNewHandleAttributeList(request, handle, MAS, timestamp); + fdoAttributes.addAll(prepareMasAttributesFromRequest(request, timestamp)); + return new FdoRecord(handle, MAS, fdoAttributes, null); } - public Document prepareUpdatedMasDocument(MasRequest request, String handle, - Instant timestamp, int issueNumber) - throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareUpdatedHandleJsonNodeRecord(request, handle, MAS, timestamp, - issueNumber); - fdoRecord.addAll(prepareMasAttributesFromRequest(request, timestamp)); - return toMongoDbDocument(fdoRecord, handle); + public FdoRecord prepareUpdatedMasRecord(MasRequest request, + Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) + throws InvalidRequestException { + var fdoAttributes = prepareUpdatedHandleAttributesRecord(request, previousVersion.handle(), MAS, + timestamp, + previousVersion, incrementVersion); + fdoAttributes.addAll(prepareMasAttributesFromRequest(request, timestamp)); + return new FdoRecord(previousVersion.handle(), MAS, fdoAttributes, null); } - private List prepareMasAttributesFromRequest(MasRequest request, + private List prepareMasAttributesFromRequest(MasRequest request, Instant timestamp) { - var fdoProfile = new ArrayList(); - fdoProfile.add(mapper.valueToTree( - new HandleAttributeJson(MAS_NAME, timestamp, request.getMachineAnnotationServiceName()))); - return fdoProfile; + return List.of( + new FdoAttribute(MAS_NAME, timestamp, request.getMachineAnnotationServiceName())); } /* Media Object Record Creation */ - public Document prepareNewDigitalMediaRecord(MediaObjectRequest request, + public FdoRecord prepareNewDigitalMediaRecord(MediaObjectRequest request, String handle, Instant timestamp) throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareNewDoiJsonNodeRecord(request, handle, MEDIA_OBJECT, timestamp); - fdoRecord.addAll(prepareDigitalMediaAttributesFromRequest(request, timestamp)); - return toMongoDbDocument(fdoRecord, handle) - .append(PRIMARY_MEDIA_ID.get(), - request.getPrimaryMediaId()); + var fdoAttributes = prepareNewDoiFdoAttributes(request, handle, MEDIA_OBJECT, timestamp); + fdoAttributes.addAll(prepareDigitalMediaAttributesFromRequest(request, timestamp)); + return new FdoRecord(handle, MEDIA_OBJECT, fdoAttributes, request.getPrimaryMediaId()); + } - public List prepareUpdatedDigitalMediaRecord(MediaObjectRequest request, - String handle, Instant timestamp, int issueNumber) + public FdoRecord prepareUpdatedDigitalMediaRecord(MediaObjectRequest request, + Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) throws InvalidRequestException { - var fdoRecord = prepareUpdatedDoiJsonNodeRecord(request, handle, MEDIA_OBJECT, timestamp, - issueNumber); - fdoRecord.addAll(prepareDigitalMediaAttributesFromRequest(request, timestamp)); - return fdoRecord; + var fdoAttributes = prepareUpdatedDoiFdoAttributes(request, previousVersion.handle(), + MEDIA_OBJECT, timestamp, + previousVersion, incrementVersion); + fdoAttributes.addAll(prepareDigitalMediaAttributesFromRequest(request, timestamp)); + return new FdoRecord(previousVersion.handle(), MEDIA_OBJECT, fdoAttributes, + request.getPrimaryMediaId()); } - private List prepareDigitalMediaAttributesFromRequest( + private List prepareDigitalMediaAttributesFromRequest( MediaObjectRequest request, Instant timestamp) throws InvalidRequestException { - var fdoProfile = new ArrayList(); - var handleAttributeList = new ArrayList(); + var handleAttributeList = new ArrayList(); // 400 Media Host handleAttributeList.add( - new HandleAttributeJson(MEDIA_HOST, timestamp, request.getMediaHost())); + new FdoAttribute(MEDIA_HOST, timestamp, request.getMediaHost())); // 401 MediaHostName handleAttributeList.add( - new HandleAttributeJson(MEDIA_HOST_NAME, timestamp, + new FdoAttribute(MEDIA_HOST_NAME, timestamp, getObjectName(request.getMediaHost(), request.getMediaHostName()))); // 403 Is Derived From Specimen handleAttributeList.add( - new HandleAttributeJson(IS_DERIVED_FROM_SPECIMEN, timestamp, + new FdoAttribute(IS_DERIVED_FROM_SPECIMEN, timestamp, String.valueOf(request.getIsDerivedFromSpecimen()))); // 404 Linked Digital Object PID handleAttributeList.add( - new HandleAttributeJson(LINKED_DO_PID, timestamp, request.getLinkedDigitalObjectPid())); + new FdoAttribute(LINKED_DO_PID, timestamp, request.getLinkedDigitalObjectPid())); // 405 Linked Digital Object Type handleAttributeList.add( - new HandleAttributeJson(LINKED_DO_TYPE, timestamp, + new FdoAttribute(LINKED_DO_TYPE, timestamp, request.getLinkedDigitalObjectType().toString())); // 406 Linked Attribute handleAttributeList.add( - new HandleAttributeJson(LINKED_ATTRIBUTE, timestamp, request.getLinkedAttribute())); + new FdoAttribute(LINKED_ATTRIBUTE, timestamp, request.getLinkedAttribute())); // 407 Primary Media ID handleAttributeList.add( - new HandleAttributeJson(PRIMARY_MEDIA_ID, timestamp, request.getPrimaryMediaId())); + new FdoAttribute(PRIMARY_MEDIA_ID, timestamp, request.getPrimaryMediaId())); // 408 Primary Media Object Id Type if (request.getPrimaryMediaObjectIdType() != null) { handleAttributeList.add( - new HandleAttributeJson(PRIMARY_MO_ID_TYPE, timestamp, + new FdoAttribute(PRIMARY_MO_ID_TYPE, timestamp, request.getPrimaryMediaObjectIdType().toString())); } else { handleAttributeList.add( - new HandleAttributeJson(PRIMARY_MO_ID_TYPE, timestamp, null)); + new FdoAttribute(PRIMARY_MO_ID_TYPE, timestamp, null)); } // 409 Primary Media Object Id Name handleAttributeList.add( - new HandleAttributeJson(PRIMARY_MO_ID_NAME, timestamp, + new FdoAttribute(PRIMARY_MO_ID_NAME, timestamp, request.getPrimaryMediaObjectIdName())); // 410 dcterms:type if (request.getDcTermsType() != null) { handleAttributeList.add( - new HandleAttributeJson(DCTERMS_TYPE, timestamp, request.getDcTermsType().toString())); + new FdoAttribute(DCTERMS_TYPE, timestamp, request.getDcTermsType().toString())); } else { handleAttributeList.add( - new HandleAttributeJson(DCTERMS_TYPE, timestamp, null)); + new FdoAttribute(DCTERMS_TYPE, timestamp, null)); } // 411 dcterms:subject handleAttributeList.add( - new HandleAttributeJson(DCTERMS_SUBJECT, timestamp, request.getDctermsSubject())); + new FdoAttribute(DCTERMS_SUBJECT, timestamp, request.getDctermsSubject())); // 412 dcterms:format if (request.getDctermsFormat() != null) { handleAttributeList.add( - new HandleAttributeJson(DCTERMS_FORMAT, timestamp, + new FdoAttribute(DCTERMS_FORMAT, timestamp, request.getDctermsFormat().toString())); } else { handleAttributeList.add( - new HandleAttributeJson(DCTERMS_FORMAT, timestamp, null)); + new FdoAttribute(DCTERMS_FORMAT, timestamp, null)); } // 413 Derived from Entity handleAttributeList.add( - new HandleAttributeJson(DERIVED_FROM_ENTITY, timestamp, request.getDerivedFromEntity())); + new FdoAttribute(DERIVED_FROM_ENTITY, timestamp, request.getDerivedFromEntity())); // 414 License Name handleAttributeList.add( - new HandleAttributeJson(LICENSE_NAME, timestamp, request.getLicenseName())); + new FdoAttribute(LICENSE_NAME, timestamp, request.getLicenseName())); // 415 License URL - new HandleAttributeJson(LICENSE_URL, timestamp, request.getLicenseName()); + new FdoAttribute(LICENSE_URL, timestamp, request.getLicenseName()); // 416 RightsholderName - new HandleAttributeJson(RIGHTSHOLDER_NAME, timestamp, request.getRightsholderName()); + new FdoAttribute(RIGHTSHOLDER_NAME, timestamp, request.getRightsholderName()); // 417 Rightsholder PID - new HandleAttributeJson(RIGHTSHOLDER_PID, timestamp, request.getRightsholderPid()); + new FdoAttribute(RIGHTSHOLDER_PID, timestamp, request.getRightsholderPid()); // 418 RightsholderPidType if (request.getRightsholderPidType() != null) { - new HandleAttributeJson(RIGHTSHOLDER_PID_TYPE, timestamp, + new FdoAttribute(RIGHTSHOLDER_PID_TYPE, timestamp, request.getRightsholderPidType().toString()); } else { - new HandleAttributeJson(RIGHTSHOLDER_PID_TYPE, timestamp, null); + new FdoAttribute(RIGHTSHOLDER_PID_TYPE, timestamp, null); } // 419 dcterms:conformsTo - new HandleAttributeJson(DC_TERMS_CONFORMS, timestamp, request.getDctermsConformsTo()); - for (var attribute : handleAttributeList) { - fdoProfile.add(mapper.valueToTree(attribute)); - } - return fdoProfile; + new FdoAttribute(DC_TERMS_CONFORMS, timestamp, request.getDctermsConformsTo()); + return handleAttributeList; } /* Organisation Record Creation */ - public Document prepareNewOrganisationDocument(OrganisationRequest request, String handle, - Instant timestamp) - throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareNewDoiJsonNodeRecord(request, handle, ORGANISATION, timestamp); - fdoRecord.addAll(prepareOrganisationAttributesFromRequest(request, handle, timestamp)); - return toMongoDbDocument(fdoRecord, handle); + public FdoRecord prepareNewOrganisationRecord(OrganisationRequest request, String handle, + Instant timestamp) throws InvalidRequestException { + var fdoAttributes = prepareNewDoiFdoAttributes(request, handle, ORGANISATION, timestamp); + fdoAttributes.addAll(prepareOrganisationAttributesFromRequest(request, handle, timestamp)); + return new FdoRecord(handle, ORGANISATION, fdoAttributes, null); } - public Document prepareUpdatedOrganisationDocument(OrganisationRequest request, String handle, - Instant timestamp, int issueNumber) - throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareUpdatedDoiJsonNodeRecord(request, handle, ORGANISATION, timestamp, - issueNumber); - fdoRecord.addAll(prepareOrganisationAttributesFromRequest(request, handle, timestamp)); - return toMongoDbDocument(fdoRecord, handle); + public FdoRecord prepareUpdatedOrganisationRecord(OrganisationRequest request, + Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) + throws InvalidRequestException { + var fdoAttributes = prepareUpdatedDoiFdoAttributes(request, previousVersion.handle(), + ORGANISATION, timestamp, previousVersion, incrementVersion); + fdoAttributes.addAll( + prepareOrganisationAttributesFromRequest(request, previousVersion.handle(), timestamp)); + return new FdoRecord(previousVersion.handle(), ORGANISATION, fdoAttributes, null); } - private List prepareOrganisationAttributesFromRequest(OrganisationRequest request, + private List prepareOrganisationAttributesFromRequest( + OrganisationRequest request, String handle, Instant timestamp) throws InvalidRequestException { - var fdoProfile = new ArrayList(); - var handleAttributeList = new ArrayList(); + var handleAttributeList = new ArrayList(); // 101 10320/loc -> includes organisation ROR var userLocations = concatLocations(request.getLocations(), List.of(request.getOrganisationIdentifier())); handleAttributeList.add( - new HandleAttributeJson(LOC, timestamp, setLocations(userLocations, handle, ORGANISATION))); + new FdoAttribute(LOC, timestamp, setLocations(userLocations, handle, ORGANISATION))); // 601 Organisation Identifier handleAttributeList.add( - new HandleAttributeJson(ORGANISATION_ID, timestamp, request.getOrganisationIdentifier())); + new FdoAttribute(ORGANISATION_ID, timestamp, request.getOrganisationIdentifier())); // 602 Organisation Identifier type - handleAttributeList.add(new HandleAttributeJson(ORGANISATION_ID_TYPE, timestamp, + handleAttributeList.add(new FdoAttribute(ORGANISATION_ID_TYPE, timestamp, request.getOrganisationIdentifierType())); // 603 Organisation Name - handleAttributeList.add(new HandleAttributeJson(ORGANISATION_NAME, timestamp, + handleAttributeList.add(new FdoAttribute(ORGANISATION_NAME, timestamp, getObjectName(request.getOrganisationIdentifier(), null))); - for (var attribute : handleAttributeList) { - fdoProfile.add(mapper.valueToTree(attribute)); - } - return fdoProfile; + return handleAttributeList; } /* Source System Record Creation */ - public Document prepareNewSourceSystemDocument(SourceSystemRequest request, String handle, - Instant timestamp) - throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareNewHandleJsonNodeRecord(request, handle, SOURCE_SYSTEM, timestamp); - fdoRecord.addAll(prepareSourceSystemAttributesFromRequest(request, timestamp)); - return toMongoDbDocument(fdoRecord, handle); + public FdoRecord prepareNewSourceSystemRecord(SourceSystemRequest request, String handle, + Instant timestamp) throws InvalidRequestException { + var fdoAttributes = prepareNewHandleAttributeList(request, handle, SOURCE_SYSTEM, timestamp); + fdoAttributes.addAll(prepareSourceSystemAttributesFromRequest(request, timestamp)); + return new FdoRecord(handle, SOURCE_SYSTEM, fdoAttributes, null); } - public Document prepareUpdatedSourceSystemDocument(SourceSystemRequest request, String handle, - Instant timestamp, int issueNumber) - throws InvalidRequestException, JsonProcessingException { - var fdoRecord = prepareUpdatedHandleJsonNodeRecord(request, handle, SOURCE_SYSTEM, timestamp, - issueNumber); - fdoRecord.addAll(prepareSourceSystemAttributesFromRequest(request, timestamp)); - return toMongoDbDocument(fdoRecord, handle); + public FdoRecord prepareUpdatedSourceSystemRecord(SourceSystemRequest request, Instant timestamp, + FdoRecord previousVersion, boolean incrementVersion) throws InvalidRequestException { + var fdoAttributes = prepareUpdatedHandleAttributesRecord(request, previousVersion.handle(), + SOURCE_SYSTEM, + timestamp, previousVersion, incrementVersion); + fdoAttributes.addAll(prepareSourceSystemAttributesFromRequest(request, timestamp)); + return new FdoRecord(previousVersion.handle(), ORGANISATION, fdoAttributes, null); } - private List prepareSourceSystemAttributesFromRequest(SourceSystemRequest request, + private List prepareSourceSystemAttributesFromRequest( + SourceSystemRequest request, Instant timestamp) { - var fdoProfile = new ArrayList(); - fdoProfile.add(mapper.valueToTree( - new HandleAttributeJson(MAS_NAME, timestamp, request.getSourceSystemName()))); - return fdoProfile; + return List.of( + new FdoAttribute(MAS_NAME, timestamp, request.getSourceSystemName())); } private String getObjectName(String url, String name) throws InvalidRequestException { diff --git a/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java b/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java index b6d72b5c..d0e251c6 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java @@ -4,6 +4,7 @@ import static eu.dissco.core.handlemanager.domain.fdo.FdoType.HANDLE; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ATTRIBUTES; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_DATA; +import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ID; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -17,8 +18,10 @@ import eu.dissco.core.handlemanager.domain.fdo.OrganisationRequest; import eu.dissco.core.handlemanager.domain.fdo.SourceSystemRequest; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperWrite; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoRecord; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.PidResolutionException; +import eu.dissco.core.handlemanager.exceptions.UnprocessableEntityException; import eu.dissco.core.handlemanager.properties.ProfileProperties; import eu.dissco.core.handlemanager.repository.PidMongoRepository; import eu.dissco.core.handlemanager.repository.PidRepository; @@ -26,6 +29,8 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.bson.Document; import org.springframework.context.annotation.Profile; @@ -45,18 +50,18 @@ public HandleService(PidRepository pidRepository, FdoRecordService fdoRecordServ // Pid Record Creation @Override public JsonApiWrapperWrite createRecords(List requests) throws InvalidRequestException { - var handles = hf.genHandleListString(requests.size()).iterator(); + var handles = hf.genHandleList(requests.size()).iterator(); var requestAttributes = requests.stream() .map(request -> request.get(NODE_DATA).get(NODE_ATTRIBUTES)).toList(); var type = getObjectTypeFromJsonNode(requests); - List fdoRecords; + List fdoRecords; + List fdoDocuments; try { switch (type) { case ANNOTATION -> fdoRecords = createAnnotation(requestAttributes, handles); case DIGITAL_SPECIMEN -> fdoRecords = createDigitalSpecimen(requestAttributes, handles); case DOI -> fdoRecords = createDoi(requestAttributes, handles); case HANDLE -> fdoRecords = createHandle(requestAttributes, handles); - case MAPPING -> fdoRecords = createMapping(requestAttributes, handles); case MAS -> fdoRecords = createMas(requestAttributes, handles); case MEDIA_OBJECT -> fdoRecords = createMediaObject(requestAttributes, handles); @@ -64,34 +69,88 @@ public JsonApiWrapperWrite createRecords(List requests) throws Invalid case SOURCE_SYSTEM -> fdoRecords = createSourceSystem(requestAttributes, handles); default -> throw new UnsupportedOperationException("Unrecognized type"); } + fdoDocuments = fdoRecordService.toMongoDbDocument(fdoRecords); } catch (JsonProcessingException | PidResolutionException e) { log.error("An error has occurred in processing request", e); throw new InvalidRequestException( "An error has occurred parsing a record in request. More information: " + e.getMessage()); } log.info("Persisting new handles to Document Store"); - mongoRepository.postBatchHandleRecord(fdoRecords); - return new JsonApiWrapperWrite(formatCreateDocuments(fdoRecords, type)); + mongoRepository.postBatchHandleRecord(fdoDocuments); + return new JsonApiWrapperWrite(formatCreateRecord(fdoRecords, type)); } - private List createAnnotation(List requestAttributes, + @Override + public JsonApiWrapperWrite updateRecords(List requests) + throws UnprocessableEntityException, InvalidRequestException { + var updateRequests = requests.stream() + .map(request -> request.get(NODE_DATA)).toList(); + var handles = updateRequests.stream() + .map(request -> request.get(NODE_ID).asText()).toList(); + var previousVersions = getPreviousVersions(handles); + checkInternalDuplicates(handles); + checkHandlesWritableFullRecord(previousVersions); + var fdoRecordMap = previousVersions.stream() + .collect(Collectors.toMap(FdoRecord::handle, f -> f)); + var type = getObjectTypeFromJsonNode(requests); + List fdoRecords; + List fdoDocuments; + try { + switch (type) { + case ANNOTATION -> fdoRecords = updateAnnotation(updateRequests, fdoRecordMap); + case DIGITAL_SPECIMEN -> fdoRecords = updateDigitalSpecimen(updateRequests, fdoRecordMap); + case DOI -> fdoRecords = updateDoi(updateRequests, fdoRecordMap); + case HANDLE -> fdoRecords = updateHandle(updateRequests, fdoRecordMap); + case MAPPING -> fdoRecords = updateDataMapping(updateRequests, fdoRecordMap); + case MAS -> fdoRecords = updateMas(updateRequests, fdoRecordMap); + case MEDIA_OBJECT -> fdoRecords = updateDigitalMedia(updateRequests, fdoRecordMap); + case ORGANISATION -> fdoRecords = updateOrganisation(updateRequests, fdoRecordMap); + case SOURCE_SYSTEM -> fdoRecords = updateSourceSystem(updateRequests, fdoRecordMap); + default -> throw new UnsupportedOperationException("Unrecognized type"); + } + fdoDocuments = fdoRecordService.toMongoDbDocument(fdoRecords); + var fdoType = getObjectTypeFromJsonNode(requests); + mongoRepository.updateHandleRecord(fdoDocuments, handles); + // todo figure out response + return new JsonApiWrapperWrite(formatCreateRecord(fdoRecords, fdoType)); + } catch (JsonProcessingException e) { + log.error("An error has occurred processing JSON data", e); + throw new InvalidRequestException(); + } + } + + private List createAnnotation(List requestAttributes, Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { - List fdoRecords = new ArrayList<>(); + List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); for (var request : requestAttributes) { var requestObject = mapper.treeToValue(request, AnnotationRequest.class); fdoRecords.add( - fdoRecordService.prepareNewAnnotationDocument(requestObject, handleIterator.next(), + fdoRecordService.prepareNewAnnotationRecord(requestObject, handleIterator.next(), timestamp)); } return fdoRecords; } - private List createDoi(List requestAttributes, + private List updateAnnotation(List updateRequests, + Map previousVersionMap) + throws JsonProcessingException, InvalidRequestException { + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); + for (var request : updateRequests) { + var requestObject = mapper.treeToValue(request.get(NODE_ATTRIBUTES), AnnotationRequest.class); + fdoRecords.add( + fdoRecordService.prepareUpdatedAnnotationFdoRecord(requestObject, timestamp, + previousVersionMap.get(request.get(NODE_ID).asText()), true)); + } + return fdoRecords; + } + + private List createDoi(List requestAttributes, Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { - List fdoRecords = new ArrayList<>(); + List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); for (var request : requestAttributes) { var requestObject = mapper.treeToValue(request, DoiRecordRequest.class); @@ -101,69 +160,155 @@ private List createDoi(List requestAttributes, return fdoRecords; } - private List createHandle(List requestAttributes, + private List updateDoi(List updateRequests, + Map previousVersionMap) + throws JsonProcessingException, InvalidRequestException { + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); + for (var request : updateRequests) { + var requestObject = mapper.treeToValue(request.get(NODE_ATTRIBUTES), DoiRecordRequest.class); + fdoRecords.add( + fdoRecordService.prepareUpdatedDoiFdoRecord(requestObject, DOI, timestamp, + previousVersionMap.get(request.get(NODE_ID).asText()), true)); + } + return fdoRecords; + } + + private List createHandle(List requestAttributes, Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { - List fdoRecords = new ArrayList<>(); + List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); for (var request : requestAttributes) { var requestObject = mapper.treeToValue(request, HandleRecordRequest.class); - fdoRecords.add(fdoRecordService.prepareNewHandleDocument(requestObject, handleIterator.next(), + fdoRecords.add(fdoRecordService.prepareNewHandleRecord(requestObject, handleIterator.next(), HANDLE, timestamp)); } return fdoRecords; } - private List createMapping(List requestAttributes, + private List updateHandle(List updateRequests, + Map previousVersionMap) + throws JsonProcessingException, InvalidRequestException { + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); + for (var request : updateRequests) { + var requestObject = mapper.treeToValue(request.get(NODE_ATTRIBUTES), + HandleRecordRequest.class); + fdoRecords.add( + fdoRecordService.prepareUpdatedHandleRecord(requestObject, HANDLE, timestamp, + previousVersionMap.get(request.get(NODE_ID).asText()), true)); + } + return fdoRecords; + } + + private List createMapping(List requestAttributes, Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { - List fdoRecords = new ArrayList<>(); + List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); for (var request : requestAttributes) { var requestObject = mapper.treeToValue(request, MappingRequest.class); fdoRecords.add( - fdoRecordService.prepareNewDataMappingDocument(requestObject, handleIterator.next(), + fdoRecordService.prepareNewDataMappingRecord(requestObject, handleIterator.next(), timestamp)); } return fdoRecords; } - private List createMas(List requestAttributes, + private List updateDataMapping(List updateRequests, + Map previousVersionMap) + throws JsonProcessingException, InvalidRequestException { + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); + for (var request : updateRequests) { + var requestObject = mapper.treeToValue(request.get(NODE_ATTRIBUTES), MappingRequest.class); + fdoRecords.add( + fdoRecordService.prepareUpdatedDataMappingRecord(requestObject, timestamp, + previousVersionMap.get(request.get(NODE_ID).asText()), true)); + } + return fdoRecords; + } + + private List createMas(List requestAttributes, Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { - List fdoRecords = new ArrayList<>(); + List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); for (var request : requestAttributes) { var requestObject = mapper.treeToValue(request, MasRequest.class); - fdoRecords.add(fdoRecordService.prepareNewMasDocument(requestObject, handleIterator.next(), + fdoRecords.add(fdoRecordService.prepareNewMasRecord(requestObject, handleIterator.next(), timestamp)); } return fdoRecords; } - private List createOrganisation(List requestAttributes, + private List updateMas(List updateRequests, + Map previousVersionMap) + throws JsonProcessingException, InvalidRequestException { + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); + for (var request : updateRequests) { + var requestObject = mapper.treeToValue(request.get(NODE_ATTRIBUTES), MasRequest.class); + fdoRecords.add( + fdoRecordService.prepareUpdatedMasRecord(requestObject, timestamp, + previousVersionMap.get(request.get(NODE_ID).asText()), true)); + } + return fdoRecords; + } + + private List createOrganisation(List requestAttributes, Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { - List fdoRecords = new ArrayList<>(); + List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); for (var request : requestAttributes) { var requestObject = mapper.treeToValue(request, OrganisationRequest.class); fdoRecords.add( - fdoRecordService.prepareNewOrganisationDocument(requestObject, handleIterator.next(), + fdoRecordService.prepareNewOrganisationRecord(requestObject, handleIterator.next(), timestamp)); } return fdoRecords; } - private List createSourceSystem(List requestAttributes, + private List updateOrganisation(List updateRequests, + Map previousVersionMap) + throws JsonProcessingException, InvalidRequestException { + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); + for (var request : updateRequests) { + var requestObject = mapper.treeToValue(request.get(NODE_ATTRIBUTES), + OrganisationRequest.class); + fdoRecords.add( + fdoRecordService.prepareUpdatedOrganisationRecord(requestObject, timestamp, + previousVersionMap.get(request.get(NODE_ID).asText()), true)); + } + return fdoRecords; + } + + private List createSourceSystem(List requestAttributes, Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { - List fdoRecords = new ArrayList<>(); + List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); for (var request : requestAttributes) { var requestObject = mapper.treeToValue(request, SourceSystemRequest.class); fdoRecords.add( - fdoRecordService.prepareNewSourceSystemDocument(requestObject, handleIterator.next(), + fdoRecordService.prepareNewSourceSystemRecord(requestObject, handleIterator.next(), timestamp)); } return fdoRecords; + } + private List updateSourceSystem(List updateRequests, + Map previousVersionMap) + throws JsonProcessingException, InvalidRequestException { + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); + for (var request : updateRequests) { + var requestObject = mapper.treeToValue(request.get(NODE_ATTRIBUTES), + SourceSystemRequest.class); + fdoRecords.add( + fdoRecordService.prepareUpdatedSourceSystemRecord(requestObject, timestamp, + previousVersionMap.get(request.get(NODE_ID).asText()), true)); + } + return fdoRecords; } } diff --git a/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java b/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java index ec3d97b0..b827a9da 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java @@ -2,7 +2,6 @@ import eu.dissco.core.handlemanager.properties.ApplicationProperties; import eu.dissco.core.handlemanager.repository.PidRepository; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -30,15 +29,10 @@ public class PidNameGeneratorService { private final Random random; - public List genHandleListString(int h) { + public List genHandleList(int h) { return new ArrayList<>(genHandleHashSet(h)); } - public List genHandleList(int h) { - var list = genHandleListString(h); - return list.stream().map(s -> s.getBytes(StandardCharsets.UTF_8)).toList(); - } - private Set genHandleHashSet(int h) { /* diff --git a/src/main/java/eu/dissco/core/handlemanager/service/PidService.java b/src/main/java/eu/dissco/core/handlemanager/service/PidService.java index bb26185c..2806f0a0 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/PidService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/PidService.java @@ -4,12 +4,12 @@ import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DIGITAL_OBJECT_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.HS_ADMIN; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.LINKED_DO_PID; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.NORMALISED_SPECIMEN_OBJECT_ID; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PID; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PRIMARY_MEDIA_ID; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PID_STATUS; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PRIMARY_SPECIMEN_OBJECT_ID; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.ANNOTATION; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.DIGITAL_SPECIMEN; -import static eu.dissco.core.handlemanager.domain.fdo.FdoType.MEDIA_OBJECT; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ATTRIBUTES; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_DATA; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ID; @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import eu.dissco.core.handlemanager.domain.fdo.DigitalSpecimenRequest; +import eu.dissco.core.handlemanager.domain.fdo.FdoProfile; import eu.dissco.core.handlemanager.domain.fdo.FdoType; import eu.dissco.core.handlemanager.domain.fdo.MediaObjectRequest; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiDataLinks; @@ -27,6 +28,8 @@ import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperRead; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperReadSingle; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperWrite; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoAttribute; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoRecord; import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.PidResolutionException; @@ -46,7 +49,6 @@ import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.bson.Document; @Slf4j @RequiredArgsConstructor @@ -61,10 +63,11 @@ public abstract class PidService { private List formatRecords(List dbRecord) { var handleMap = mapRecords(dbRecord); - return handleMap.values().stream().map(this::jsonFormatSingleRecord).toList(); + return handleMap.values().stream().map(this::jsonFormatSingleRecordOld).toList(); } - protected JsonNode jsonFormatSingleRecord(List dbRecord) { + // todo Delete + protected JsonNode jsonFormatSingleRecordOld(List dbRecord) { ObjectNode rootNode = mapper.createObjectNode(); for (var row : dbRecord) { if (row.getIndex() != HS_ADMIN.index()) { @@ -80,6 +83,39 @@ protected JsonNode jsonFormatSingleRecord(List dbRecord) { return rootNode; } + protected JsonNode jsonFormatSingleRecord(List dbRecord) { + ObjectNode rootNode = mapper.createObjectNode(); + for (var row : dbRecord) { + if (row.getIndex() != HS_ADMIN.index()) { + try { + var nodeData = mapper.readTree(row.getValue()); + rootNode.set(row.getType(), nodeData); + } catch (JsonProcessingException ignored) { + rootNode.put(row.getType(), row.getValue()); + } + } + } + return rootNode; + } + + protected JsonNode jsonFormatSingleRecord(List dbRecord, + List keyAttributes) { + ObjectNode rootNode = mapper.createObjectNode(); + var indexList = keyAttributes.stream().map(FdoProfile::index).toList(); + for (var row : dbRecord) { + if (indexList.contains(row.getIndex())) { + try { + var nodeData = mapper.readTree(row.getValue()); + rootNode.set(row.getType(), nodeData); + } catch (JsonProcessingException ignored) { + rootNode.put(row.getType(), row.getValue()); + } + } + } + return rootNode; + } + + protected Map> mapRecords(List flatList) { return flatList.stream() .collect(Collectors.groupingBy(row -> new String(row.getHandle(), StandardCharsets.UTF_8))); @@ -96,90 +132,72 @@ private String getPidName(String pidLink) { return pidLink.substring(profileProperties.getDomain().length()); } - protected List formatCreateRecords(List dbRecord, - FdoType objectType) { - var handleMap = mapRecords(dbRecord); - switch (objectType) { + protected List formatCreateRecord(List fdoRecords, + FdoType fdoType) { + switch (fdoType) { case ANNOTATION -> { - return formatCreateRecordsAnnotation(handleMap); + return formatCreateRecordsAnnotation(fdoRecords); } case DIGITAL_SPECIMEN -> { - return formatCreateRecordsSpecimen(handleMap); + return formatCreateRecordsSpecimen(fdoRecords); } case MEDIA_OBJECT -> { - return formatCreateRecordsMedia(handleMap); + return formatCreateRecordsMedia(fdoRecords); } default -> { - return formatCreateRecordsDefault(handleMap, objectType); + return formatCreateRecordsDefault(fdoRecords, fdoType); } } } - protected List formatCreateDocuments(List fdoRecords, FdoType type) { - var dataList = new ArrayList(); - for (var fdoRecord : fdoRecords) { - dataList.add( - new JsonApiDataLinks(fdoRecord.get("_id").toString(), type.getDigitalObjectName(), null, - null)); - } - return dataList; - } - - private List formatCreateRecordsAnnotation( - Map> handleMap) { + private List formatCreateRecordsAnnotation(List fdoRecords) { List dataLinksList = new ArrayList<>(); - for (var handleRecord : handleMap.entrySet()) { - var hashRow = handleRecord.getValue().stream() - .filter(row -> row.getType().equals(ANNOTATION_HASH.get())).findFirst(); - var subRecord = hashRow.map(List::of).orElse(handleRecord.getValue()); - var rootNode = jsonFormatSingleRecord(subRecord); - String pidLink = profileProperties.getDomain() + handleRecord.getKey(); + for (var handleRecord : fdoRecords) { + var attributeNode = jsonFormatSingleRecord(handleRecord.attributes(), + List.of(ANNOTATION_HASH)); + String pidLink = profileProperties.getDomain() + handleRecord.handle(); dataLinksList.add( - new JsonApiDataLinks(handleRecord.getKey(), ANNOTATION.getDigitalObjectType(), rootNode, + new JsonApiDataLinks(handleRecord.handle(), ANNOTATION.getDigitalObjectType(), + attributeNode, new JsonApiLinks(pidLink))); } return dataLinksList; } - private List formatCreateRecordsSpecimen( - Map> handleMap) { + private List formatCreateRecordsSpecimen(List fdoRecords) { List dataLinksList = new ArrayList<>(); - for (var handleRecord : handleMap.entrySet()) { - var subRecord = handleRecord.getValue().stream() - .filter(row -> row.getType().equals(PRIMARY_SPECIMEN_OBJECT_ID.get())).toList(); - var rootNode = jsonFormatSingleRecord(subRecord); - String pidLink = profileProperties.getDomain() + handleRecord.getKey(); + for (var handleRecord : fdoRecords) { + var attributeNode = jsonFormatSingleRecord(handleRecord.attributes(), + List.of(PRIMARY_SPECIMEN_OBJECT_ID)); + String pidLink = profileProperties.getDomain() + handleRecord.handle(); dataLinksList.add( - new JsonApiDataLinks(handleRecord.getKey(), DIGITAL_SPECIMEN.getDigitalObjectType(), - rootNode, new JsonApiLinks(pidLink))); + new JsonApiDataLinks(handleRecord.handle(), DIGITAL_SPECIMEN.getDigitalObjectType(), + attributeNode, new JsonApiLinks(pidLink))); } return dataLinksList; } - private List formatCreateRecordsMedia( - Map> handleMap) { + private List formatCreateRecordsMedia(List fdoRecords) { List dataLinksList = new ArrayList<>(); - for (var handleRecord : handleMap.entrySet()) { - var subRecord = handleRecord.getValue().stream().filter( - row -> row.getType().equals(PRIMARY_MEDIA_ID.get()) || row.getType() - .equals(LINKED_DO_PID.get())).toList(); - var rootNode = jsonFormatSingleRecord(subRecord); - String pidLink = profileProperties.getDomain() + handleRecord.getKey(); + for (var handleRecord : fdoRecords) { + var attributeNode = jsonFormatSingleRecord(handleRecord.attributes(), + List.of(PRIMARY_SPECIMEN_OBJECT_ID, LINKED_DO_PID)); + String pidLink = profileProperties.getDomain() + handleRecord.handle(); dataLinksList.add( - new JsonApiDataLinks(handleRecord.getKey(), MEDIA_OBJECT.getDigitalObjectType(), rootNode, - new JsonApiLinks(pidLink))); + new JsonApiDataLinks(handleRecord.handle(), DIGITAL_SPECIMEN.getDigitalObjectType(), + attributeNode, new JsonApiLinks(pidLink))); } return dataLinksList; } - private List formatCreateRecordsDefault( - Map> handleMap, FdoType objectType) { + private List formatCreateRecordsDefault(List fdoRecords, + FdoType fdoType) { List dataLinksList = new ArrayList<>(); - for (var handleRecord : handleMap.entrySet()) { - var rootNode = jsonFormatSingleRecord(handleRecord.getValue()); - String pidLink = profileProperties.getDomain() + handleRecord.getKey(); + for (var handleRecord : fdoRecords) { + var rootNode = jsonFormatSingleRecord(handleRecord.attributes()); + String pidLink = profileProperties.getDomain() + handleRecord.handle(); dataLinksList.add( - new JsonApiDataLinks(handleRecord.getKey(), objectType.getDigitalObjectType(), rootNode, + new JsonApiDataLinks(handleRecord.handle(), fdoType.getDigitalObjectType(), rootNode, new JsonApiLinks(pidLink))); } return dataLinksList; @@ -189,7 +207,7 @@ private JsonApiWrapperWrite formatArchives(List> archiveRe List dataList = new ArrayList<>(); for (var archiveRecord : archiveRecords) { String handle = new String(archiveRecord.get(0).getHandle(), StandardCharsets.UTF_8); - var attributeNode = jsonFormatSingleRecord(archiveRecord); + var attributeNode = jsonFormatSingleRecordOld(archiveRecord); dataList.add( new JsonApiDataLinks(handle, FdoType.TOMBSTONE.getDigitalObjectType(), attributeNode, new JsonApiLinks(profileProperties.getDomain() + handle))); @@ -260,7 +278,7 @@ public JsonApiWrapperWrite searchByPhysicalSpecimenId(String normalisedPhysicalI } List dataNode = new ArrayList<>(); - var jsonFormattedRecord = jsonFormatSingleRecord(returnedRows); + var jsonFormattedRecord = jsonFormatSingleRecordOld(returnedRows); dataNode.add(wrapResolvedData(jsonFormattedRecord, DIGITAL_SPECIMEN.getDigitalObjectType())); return new JsonApiWrapperWrite(dataNode); } @@ -269,6 +287,45 @@ public JsonApiWrapperWrite searchByPhysicalSpecimenId(String normalisedPhysicalI public abstract JsonApiWrapperWrite createRecords(List requests) throws InvalidRequestException, UnprocessableEntityException; + protected List getPreviousVersions(List handles) + throws InvalidRequestException { + List previousVersions; + try { + previousVersions = mongoRepository.getHandleRecords(handles); + } catch (JsonProcessingException e) { + throw new InvalidRequestException("Unable to process handles resolution"); + } + if (previousVersions.size() < handles.size()) { + throw new InvalidRequestException("Unable to resolve all handles"); + } + return previousVersions; + } + + // Update + public abstract JsonApiWrapperWrite updateRecords(List requests) + throws InvalidRequestException, UnprocessableEntityException; + + protected void checkHandlesWritableFullRecord(List previousVersions) + throws InvalidRequestException { + for (var fdoRecord : previousVersions) { + var pidStatus = getField(fdoRecord.attributes(), PID_STATUS); + if ("ARCHIVED".equals(pidStatus)) { + log.error("Attempting to update a FDO record that has been archived"); + throw new InvalidRequestException(); + } + } + } + + protected static FdoAttribute getField(List fdoAttributes, FdoProfile targetField) { + for (var attribute : fdoAttributes) { + if (attribute.getIndex() == targetField.index()) { + return attribute; + } + } + log.error("Unable to find field {} in record {}", targetField, fdoAttributes); + throw new IllegalStateException(); + } + protected FdoType getObjectTypeFromJsonNode(List requests) { var types = requests.stream().map(request -> request.get(NODE_DATA).get(NODE_TYPE).asText()) .collect(Collectors.toSet()); @@ -279,7 +336,7 @@ protected FdoType getObjectTypeFromJsonNode(List requests) { return FdoType.fromString(type.get()); } - protected List createDigitalSpecimen(List requestAttributes, + protected List createDigitalSpecimen(List requestAttributes, Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { var specimenRequests = new ArrayList(); for (var request : requestAttributes) { @@ -288,8 +345,11 @@ protected List createDigitalSpecimen(List requestAttributes, if (specimenRequests.isEmpty()) { return Collections.emptyList(); } - verifySpecimensAreNew(specimenRequests); - var fdoRecords = new ArrayList(); + verifyObjectsAreNew(specimenRequests + .stream() + .map(DigitalSpecimenRequest::getNormalisedPrimarySpecimenObjectId) + .toList()); + var fdoRecords = new ArrayList(); var timestamp = Instant.now(); for (var request : specimenRequests) { fdoRecords.add( @@ -299,47 +359,87 @@ protected List createDigitalSpecimen(List requestAttributes, return fdoRecords; } - private void verifySpecimensAreNew(List requests) - throws InvalidRequestException { - var normalisedIds = requests.stream().map( - request -> request.getNormalisedPrimarySpecimenObjectId().getBytes(StandardCharsets.UTF_8)) - .toList(); - var existingHandles = pidRepository.searchByNormalisedPhysicalIdentifier(normalisedIds); - if (!existingHandles.isEmpty()) { - log.error("Unable to create new handles, as "); - var handleMap = existingHandles.stream().collect( - Collectors.toMap(ha -> new String(ha.getHandle(), StandardCharsets.UTF_8), - ha -> new String(ha.getData(), StandardCharsets.UTF_8))); - log.error( - "Unable to create new handles, as they already exist. Verify the following identifiers: {}", - handleMap); - throw new InvalidRequestException( - "Attempting to create handle records for specimens already in system"); + protected List updateDigitalSpecimen(List updateRequests, + Map previousVersionMap) + throws JsonProcessingException, InvalidRequestException { + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); + for (var request : updateRequests) { + var requestObject = mapper.treeToValue(request.get(NODE_ATTRIBUTES), + DigitalSpecimenRequest.class); + fdoRecords.add( + fdoRecordService.prepareUpdatedDigitalSpecimenRecord(requestObject, timestamp, + previousVersionMap.get(request.get(NODE_ID).asText()), true)); } + return fdoRecords; } - protected List createMediaObject(List requestAttributes, + protected List createMediaObject(List requestAttributes, Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { - List fdoRecords = new ArrayList<>(); + List fdoRecords = new ArrayList<>(); + List mediaRequests = new ArrayList<>(); var timestamp = Instant.now(); for (var request : requestAttributes) { - var requestObject = mapper.treeToValue(request, MediaObjectRequest.class); + mediaRequests.add(mapper.treeToValue(request, MediaObjectRequest.class)); + } + if (mediaRequests.isEmpty()) { + return Collections.emptyList(); + } + verifyObjectsAreNew(mediaRequests + .stream() + .map(MediaObjectRequest::getPrimaryMediaId) + .toList()); + for (var mediaRequest : mediaRequests) { fdoRecords.add( - fdoRecordService.prepareNewDigitalMediaRecord(requestObject, handleIterator.next(), + fdoRecordService.prepareNewDigitalMediaRecord(mediaRequest, handleIterator.next(), timestamp)); } return fdoRecords; } + + protected List updateDigitalMedia(List updateRequests, + Map previousVersionMap) + throws JsonProcessingException, InvalidRequestException { + List fdoRecords = new ArrayList<>(); + var timestamp = Instant.now(); + for (var request : updateRequests) { + var requestObject = mapper.treeToValue(request.get(NODE_ATTRIBUTES), + MediaObjectRequest.class); + fdoRecords.add( + fdoRecordService.prepareUpdatedDigitalMediaRecord(requestObject, timestamp, + previousVersionMap.get(request.get(NODE_ID).asText()), true)); + } + return fdoRecords; + } + + private void verifyObjectsAreNew(List normalisedIds) + throws InvalidRequestException, JsonProcessingException { + var existingHandles = mongoRepository + .searchByPrimaryLocalId(NORMALISED_SPECIMEN_OBJECT_ID.get(), normalisedIds); + if (!existingHandles.isEmpty()) { + log.error("Unable to create new handles, as "); + var handleMap = existingHandles.stream() + .collect(Collectors.toMap( + FdoRecord::handle, + FdoRecord::primaryLocalId)); + log.error( + "Unable to create new handles, as they already exist. Verify the following identifiers: {}", + handleMap); + throw new InvalidRequestException( + "Attempting to create handle records for specimens already in system"); + } + } + // Update public JsonApiWrapperWrite updateRecords(List> attributesToUpdate, boolean incrementVersion, FdoType recordType) throws InvalidRequestException { var recordTimestamp = Instant.now().getEpochSecond(); var handles = attributesToUpdate.stream().map(pidRecord -> pidRecord.get(0).getHandle()) .toList(); - checkInternalDuplicates(handles); - checkHandlesWritable(handles); + //checkInternalDuplicates(handles); + //checkHandlesWritable(handles); log.info("Writing updates to db"); pidRepository.updateRecordBatch(recordTimestamp, attributesToUpdate, incrementVersion); return formatUpdates(handles.stream().map(h -> new String(h, StandardCharsets.UTF_8)).toList(), @@ -367,22 +467,21 @@ protected List> getAttributesToUpdate(List reque return attributesToUpdate; } - protected void checkInternalDuplicates(List handles) throws InvalidRequestException { - Set handlesToUpdateStr = handles.stream() - .map(h -> new String(h, StandardCharsets.UTF_8)).collect(Collectors.toSet()); - if (handlesToUpdateStr.size() < handles.size()) { - Set duplicateHandles = findDuplicates(handles, handlesToUpdateStr); + protected void checkInternalDuplicates(List handles) throws InvalidRequestException { + Set handlesToUpdate = new HashSet<>(handles); + if (handlesToUpdate.size() < handles.size()) { + Set duplicateHandles = findDuplicates(handles, handlesToUpdate); throw new InvalidRequestException( "INVALID INPUT. Attempting to update the same record multiple times in one request. " + "The following handles are duplicated in the request: " + duplicateHandles); } } - private Set findDuplicates(List handles, Set handlesToUpdate) { + private Set findDuplicates(List handles, Set handlesToUpdate) { Set duplicateHandles = new HashSet<>(); - for (byte[] handle : handles) { - if (!handlesToUpdate.add(new String(handle, StandardCharsets.UTF_8))) { - duplicateHandles.add(new String(handle, StandardCharsets.UTF_8)); + for (var handle : handles) { + if (!handlesToUpdate.add(handle)) { + duplicateHandles.add(handle); } } return duplicateHandles; @@ -414,25 +513,25 @@ protected void checkHandlesWritable(List handles) throws PidResolutionEx public JsonApiWrapperWrite archiveRecordBatch(List requests) throws InvalidRequestException { var recordTimestamp = Instant.now().getEpochSecond(); - List handles = new ArrayList<>(); + var handles = new ArrayList(); var archiveAttributesFlat = new ArrayList(); var archiveAttributes = new ArrayList>(); for (JsonNode root : requests) { JsonNode data = root.get(NODE_DATA); JsonNode requestAttributes = data.get(NODE_ATTRIBUTES); - var handle = data.get(NODE_ID).asText().getBytes(StandardCharsets.UTF_8); + var handle = data.get(NODE_ID).asText(); handles.add(handle); - var recordAttributes = fdoRecordService.prepareTombstoneAttributes(handle, requestAttributes); + var recordAttributes = fdoRecordService.prepareTombstoneAttributes(handle.getBytes( + StandardCharsets.UTF_8), requestAttributes); archiveAttributesFlat.addAll(recordAttributes); archiveAttributes.add(recordAttributes); } checkInternalDuplicates(handles); - checkHandlesWritable(handles); +// checkHandlesWritable(handles); - pidRepository.archiveRecords(recordTimestamp, archiveAttributesFlat, - handles.stream().map(h -> new String(h, StandardCharsets.UTF_8)).toList()); + pidRepository.archiveRecords(recordTimestamp, archiveAttributesFlat, handles); return formatArchives(archiveAttributes); } diff --git a/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java b/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java index bbc55d7c..917b2ba8 100644 --- a/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java @@ -121,7 +121,7 @@ void testSearchByPhysicalId() throws Exception { // Given var responseExpected = givenRecordResponseWriteGeneric( - List.of(HANDLE.getBytes(StandardCharsets.UTF_8)), FdoType.DIGITAL_SPECIMEN); + List.of(HANDLE), FdoType.DIGITAL_SPECIMEN); given( service.searchByPhysicalSpecimenId(PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL )).willReturn(responseExpected); @@ -140,7 +140,7 @@ void testSearchByPhysicalIdCombined() throws Exception { // Given String physicalId = PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL; var responseExpected = givenRecordResponseWriteGeneric( - List.of(HANDLE.getBytes(StandardCharsets.UTF_8)), FdoType.DIGITAL_SPECIMEN); + List.of(HANDLE), FdoType.DIGITAL_SPECIMEN); given( service.searchByPhysicalSpecimenId(physicalId)).willReturn( responseExpected); @@ -161,10 +161,8 @@ void testResolveBatchHandle() throws Exception { r.setRequestURI("view"); List handleString = List.of(HANDLE, HANDLE_ALT); - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - var responseExpected = givenRecordResponseRead(handles, path, FdoType.HANDLE); + var responseExpected = givenRecordResponseRead(handleString, path, FdoType.HANDLE); given(applicationProperties.getUiUrl()).willReturn(SANDBOX_URI); given(applicationProperties.getMaxHandles()).willReturn(1000); given(service.resolveBatchRecord(anyList(), eq(path))).willReturn(responseExpected); @@ -201,10 +199,9 @@ void testResolveBatchHandleExceedsMax() { @Test void testCreateHandleRecord() throws Exception { // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); HandleRecordRequest requestObject = givenHandleRecordRequestObject(); ObjectNode requestNode = genCreateRecordRequest(requestObject, FdoType.HANDLE); - JsonApiWrapperWrite responseExpected = givenRecordResponseWrite(List.of(handle), + JsonApiWrapperWrite responseExpected = givenRecordResponseWrite(List.of(HANDLE), FdoType.HANDLE); given(service.createRecords(List.of(requestNode))).willReturn(responseExpected); @@ -220,10 +217,9 @@ void testCreateHandleRecord() throws Exception { @Test void testCreateDoiRecord() throws Exception { // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); HandleRecordRequest requestObject = givenDoiRecordRequestObject(); ObjectNode requestNode = genCreateRecordRequest(requestObject, FdoType.DOI); - JsonApiWrapperWrite responseExpected = givenRecordResponseWrite(List.of(handle), + JsonApiWrapperWrite responseExpected = givenRecordResponseWrite(List.of(HANDLE), FdoType.DOI); given(service.createRecords(List.of(requestNode))).willReturn(responseExpected); @@ -239,10 +235,9 @@ void testCreateDoiRecord() throws Exception { @Test void testCreateDigitalSpecimenRecord() throws Exception { // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); DigitalSpecimenRequest requestObject = givenDigitalSpecimenRequestObjectNullOptionals(); ObjectNode requestNode = genCreateRecordRequest(requestObject, FdoType.DIGITAL_SPECIMEN); - JsonApiWrapperWrite responseExpected = givenRecordResponseWrite(List.of(handle), + JsonApiWrapperWrite responseExpected = givenRecordResponseWrite(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN); given(service.createRecords(List.of(requestNode))).willReturn(responseExpected); @@ -258,10 +253,9 @@ void testCreateDigitalSpecimenRecord() throws Exception { @Test void testCreateMediaObjectRecord() throws Exception { // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); HandleRecordRequest requestObject = givenMediaRequestObject(); ObjectNode requestNode = genCreateRecordRequest(requestObject, FdoType.MEDIA_OBJECT); - JsonApiWrapperWrite responseExpected = givenRecordResponseWrite(List.of(handle), + JsonApiWrapperWrite responseExpected = givenRecordResponseWrite(List.of(HANDLE), FdoType.MEDIA_OBJECT); given(service.createRecords(List.of(requestNode))).willReturn(responseExpected); @@ -277,9 +271,7 @@ void testCreateMediaObjectRecord() throws Exception { @Test void testCreateHandleRecordBatch() throws Exception { // Given - List handles = List.of( - HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); + var handles = List.of(HANDLE, HANDLE_ALT); List requests = new ArrayList<>(); @@ -300,9 +292,7 @@ void testCreateHandleRecordBatch() throws Exception { @Test void testCreateDoiRecordBatch() throws Exception { // Given - List handles = List.of( - HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); + var handles = List.of(HANDLE, HANDLE_ALT); List requests = new ArrayList<>(); handles.forEach( @@ -322,12 +312,8 @@ void testCreateDoiRecordBatch() throws Exception { @Test void testCreateDigitalSpecimenBatch() throws Exception { // Given - List handles = List.of( - HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - + var handles = List.of(HANDLE, HANDLE_ALT); List requests = new ArrayList<>(); - handles.forEach(handle -> requests.add( genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), @@ -347,15 +333,11 @@ void testCreateDigitalSpecimenBatch() throws Exception { @Test void testCreateMediaRecordBatch() throws Exception { // Given - List handles = List.of( - HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - + var handles = List.of(HANDLE, HANDLE_ALT); List requests = new ArrayList<>(); for (int i = 0; i < handles.size(); i++) { requests.add(genCreateRecordRequest(givenMediaRequestObject(), FdoType.MEDIA_OBJECT)); } - var responseExpected = givenRecordResponseWrite(handles, FdoType.DOI); given(service.createRecords(requests)).willReturn(responseExpected); @@ -370,10 +352,7 @@ void testCreateMediaRecordBatch() throws Exception { @Test void testCreateSourceSystemsBatch() throws Exception { // Given - List handles = List.of( - HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - + var handles = List.of(HANDLE, HANDLE_ALT); List requests = new ArrayList<>(); handles.forEach(handle -> requests.add( genCreateRecordRequest(givenSourceSystemRequestObject(), FdoType.SOURCE_SYSTEM))); @@ -392,14 +371,10 @@ void testCreateSourceSystemsBatch() throws Exception { @Test void testCreateAnnotationsBatch() throws Exception { // Given - List handles = List.of( - HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - + var handles = List.of(HANDLE, HANDLE_ALT); List requests = new ArrayList<>(); handles.forEach(handle -> requests.add( genCreateRecordRequest(givenAnnotationRequestObject(), FdoType.ANNOTATION))); - var responseExpected = givenRecordResponseWrite(handles, FdoType.ANNOTATION); given(service.createRecords(requests)).willReturn(responseExpected); @@ -414,10 +389,7 @@ void testCreateAnnotationsBatch() throws Exception { @Test void testCreateMappingBatch() throws Exception { // Given - List handles = List.of( - HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - + var handles = List.of(HANDLE, HANDLE_ALT); List requests = new ArrayList<>(); handles.forEach(handle -> requests.add( genCreateRecordRequest(givenMappingRequestObject(), FdoType.MAPPING))); @@ -444,13 +416,12 @@ private JsonNode givenJsonNode(String id, String type, JsonNode attributes) { @Test void testUpdateRecord() throws Exception { // Given - byte[] handle = HANDLE.getBytes(); var updateAttributes = genUpdateRequestAltLoc(); ObjectNode updateRequestNode = mapper.createObjectNode(); updateRequestNode.set(NODE_DATA, givenJsonNode(HANDLE, FdoType.HANDLE.getDigitalObjectType(), updateAttributes)); - var responseExpected = givenRecordResponseWriteAltLoc(List.of(handle)); + var responseExpected = givenRecordResponseWriteAltLoc(List.of(HANDLE)); given(service.updateRecords(List.of(updateRequestNode), true)).willReturn( responseExpected); @@ -480,10 +451,7 @@ void testUpdateRecordBadRequest() { @Test void testUpdateRecordBatch() throws Exception { // Given - List handles = List.of( - HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - + var handles = List.of(HANDLE, HANDLE_ALT); List updateRequestList = new ArrayList<>(); var responseExpected = givenRecordResponseWriteAltLoc(handles); handles.forEach(h -> { @@ -506,10 +474,7 @@ void testUpdateRecordBatch() throws Exception { @Test void testRollbackUpdate() throws Exception { // Given - List handles = List.of( - HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - + var handles = List.of(HANDLE, HANDLE_ALT); List updateRequestList = new ArrayList<>(); var responseExpected = givenRecordResponseWriteAltLoc(handles); handles.forEach(h -> { @@ -575,10 +540,7 @@ void testRollbackHandlesBadRequest() { @Test void testArchiveRecord() throws Exception { // Given - - byte[] handle = HANDLE.getBytes(); - - var responseExpected = givenRecordResponseWriteArchive(List.of(handle)); + var responseExpected = givenRecordResponseWriteArchive(List.of(HANDLE)); var archiveRequest = givenArchiveRequest(); given(service.archiveRecordBatch(List.of(archiveRequest))).willReturn( responseExpected); @@ -605,9 +567,7 @@ void testArchiveRecordBadHandle() { @Test void testArchiveRecordBatch() throws Exception { // Given - List handles = List.of( - HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); + var handles = List.of(HANDLE, HANDLE_ALT); List archiveRequestList = new ArrayList<>(); handles.forEach(h -> archiveRequestList.add(givenArchiveRequest())); var responseExpected = givenRecordResponseWriteArchive(handles); diff --git a/src/test/java/eu/dissco/core/handlemanager/domain/TopicDisciplineTest.java b/src/test/java/eu/dissco/core/handlemanager/domain/TopicDisciplineTest.java deleted file mode 100644 index 475efee6..00000000 --- a/src/test/java/eu/dissco/core/handlemanager/domain/TopicDisciplineTest.java +++ /dev/null @@ -1,104 +0,0 @@ -package eu.dissco.core.handlemanager.domain; - -import static org.assertj.core.api.Assertions.assertThat; - -import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.TopicCategory; -import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.TopicDiscipline; -import org.junit.jupiter.api.Test; - -class TopicDisciplineTest { - - @Test - void testAnthro() { - // Given - var targetDisc = TopicDiscipline.ANTHRO; - var targetCategory = TopicCategory.HUMAN; - var notTargetCategory = TopicCategory.ALGAE; - - // When - assertThat(targetDisc.isCorrectCategory(targetCategory)).isTrue(); - assertThat(targetDisc.isCorrectCategory(notTargetCategory)).isFalse(); - } - - @Test - void testBotany() { - // Given - var targetDisc = TopicDiscipline.BOTANY; - var targetCategory = TopicCategory.MYCOLOGY; - var notTargetCategory = TopicCategory.FINDS; - - // When - assertThat(targetDisc.isCorrectCategory(targetCategory)).isTrue(); - assertThat(targetDisc.isCorrectCategory(notTargetCategory)).isFalse(); - } - - @Test - void testGeology() { - // Given - var targetDisc = TopicDiscipline.GEOLOGY; - var targetCategory = TopicCategory.MINERALS; - var notTargetCategory = TopicCategory.FINDS; - - // When - assertThat(targetDisc.isCorrectCategory(targetCategory)).isTrue(); - assertThat(targetDisc.isCorrectCategory(notTargetCategory)).isFalse(); - } - - @Test - void testZoology() { - // Given - var targetDisc = TopicDiscipline.ZOO; - var targetCategory = TopicCategory.AMPHIBIANS; - var notTargetCategory = TopicCategory.FINDS; - - // When - assertThat(targetDisc.isCorrectCategory(targetCategory)).isTrue(); - assertThat(targetDisc.isCorrectCategory(notTargetCategory)).isFalse(); - } - - @Test - void testMicro() { - // Given - var targetDisc = TopicDiscipline.MICRO; - var targetCategory = TopicCategory.PHAGES; - var notTargetCategory = TopicCategory.FINDS; - - // When - assertThat(targetDisc.isCorrectCategory(targetCategory)).isTrue(); - assertThat(targetDisc.isCorrectCategory(notTargetCategory)).isFalse(); - } - - @Test - void testPaleo() { - // Given - var targetDisc = TopicDiscipline.PALEO; - var targetCategory = TopicCategory.BOTANY_FOSSILS; - var notTargetCategory = TopicCategory.FINDS; - - // When - assertThat(targetDisc.isCorrectCategory(targetCategory)).isTrue(); - assertThat(targetDisc.isCorrectCategory(notTargetCategory)).isFalse(); - } - - @Test - void testAstro() { - // Given - var targetDisc = TopicDiscipline.ASTRO; - var targetCategory = TopicCategory.FINDS; - var notTargetCategory = TopicCategory.LOOSE_SEDIMENT; - - // When - assertThat(targetDisc.isCorrectCategory(targetCategory)).isTrue(); - assertThat(targetDisc.isCorrectCategory(notTargetCategory)).isFalse(); - } - - @Test - void testDefault() { - // Given - var targetDisc = TopicDiscipline.ECO; - - // When - assertThat(targetDisc.getTopicCategories()).isEmpty(); - } - -} diff --git a/src/test/java/eu/dissco/core/handlemanager/repository/BatchInserterIT.java b/src/test/java/eu/dissco/core/handlemanager/repository/BatchInserterIT.java index 85a59c2f..f6f9ea92 100644 --- a/src/test/java/eu/dissco/core/handlemanager/repository/BatchInserterIT.java +++ b/src/test/java/eu/dissco/core/handlemanager/repository/BatchInserterIT.java @@ -41,7 +41,7 @@ void destroy() { @Test void testBatchInsert() throws Exception { // Given - var attributes = genDigitalSpecimenAttributes(HANDLE.getBytes(StandardCharsets.UTF_8)); + var attributes = genDigitalSpecimenAttributes(HANDLE); // When batchInserter.batchCopy(CREATED.getEpochSecond(), attributes); diff --git a/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java index d1b2e7e8..dd7cb6fe 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java @@ -1,18 +1,15 @@ package eu.dissco.core.handlemanager.service; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.LINKED_DO_PID; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PRIMARY_MEDIA_ID; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PRIMARY_SPECIMEN_OBJECT_ID; import static eu.dissco.core.handlemanager.testUtils.TestUtils.CREATED; import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE; import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE_DOMAIN; import static eu.dissco.core.handlemanager.testUtils.TestUtils.MAPPER; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genCreateRecordRequest; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genDigitalSpecimenAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genMediaObjectAttributes; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genObjectNodeAttributeRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genUpdateRecordAttributesAltLoc; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genUpdateRequestBatch; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaFdoRecord; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenRequestObjectNullOptionals; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMediaRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseNullAttributes; @@ -34,7 +31,6 @@ import eu.dissco.core.handlemanager.domain.datacite.DataCiteEvent; import eu.dissco.core.handlemanager.domain.datacite.EventType; import eu.dissco.core.handlemanager.domain.fdo.FdoType; -import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.UnprocessableEntityException; import eu.dissco.core.handlemanager.properties.ProfileProperties; @@ -44,7 +40,6 @@ import java.time.Clock; import java.time.Instant; import java.time.ZoneOffset; -import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -101,19 +96,16 @@ void destroy() { @Test void testCreateDigitalSpecimen() throws Exception { // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); var request = genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), FdoType.DIGITAL_SPECIMEN); - List digitalSpecimen = genDigitalSpecimenAttributes(handle); - var digitalSpecimenSublist = digitalSpecimen.stream() - .filter(row -> row.getType().equals(PRIMARY_SPECIMEN_OBJECT_ID.get())).toList(); - - var responseExpected = givenRecordResponseWriteSmallResponse(digitalSpecimenSublist, - List.of(handle), FdoType.DIGITAL_SPECIMEN); - var dataCiteEvent = new DataCiteEvent(genObjectNodeAttributeRecord(digitalSpecimen), + var digitalSpecimen = givenDigitalSpecimenFdoRecord(HANDLE); + var responseExpected = givenRecordResponseWriteSmallResponse(List.of(digitalSpecimen), + FdoType.DIGITAL_SPECIMEN); + var dataCiteEvent = new DataCiteEvent( + genObjectNodeAttributeRecord(digitalSpecimen.attributes()), EventType.CREATE); - given(pidNameGeneratorService.genHandleList(1)).willReturn(new ArrayList<>(List.of(handle))); - given(fdoRecordService.prepareDigitalSpecimenRecordAttributes(any(), any())).willReturn( + given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(fdoRecordService.prepareNewDigitalSpecimenRecord(any(), any(), any())).willReturn( digitalSpecimen); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -122,24 +114,22 @@ void testCreateDigitalSpecimen() throws Exception { // Then assertThat(responseReceived).isEqualTo(responseExpected); - then(dataCiteService).should().publishToDataCite(dataCiteEvent, FdoType.DIGITAL_SPECIMEN); + // Todo + //then(dataCiteService).should().publishToDataCite(dataCiteEvent, FdoType.DIGITAL_SPECIMEN); } @Test void testCreateMediaObject() throws Exception { // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); var request = genCreateRecordRequest(givenMediaRequestObject(), FdoType.MEDIA_OBJECT); - List mediaObject = genMediaObjectAttributes(handle); - var mediaSublist = mediaObject.stream().filter( - row -> row.getType().equals(PRIMARY_MEDIA_ID.get()) || row.getType() - .equals(LINKED_DO_PID.get())).toList(); - var responseExpected = givenRecordResponseWriteSmallResponse(mediaSublist, List.of(handle), + var mediaObject = givenDigitalMediaFdoRecord(HANDLE); + var responseExpected = givenRecordResponseWriteSmallResponse(List.of(mediaObject), FdoType.MEDIA_OBJECT); - var dataCiteEvent = new DataCiteEvent(genObjectNodeAttributeRecord(mediaObject), + var dataCiteEvent = new DataCiteEvent(genObjectNodeAttributeRecord(mediaObject.attributes()), EventType.CREATE); - given(pidNameGeneratorService.genHandleList(1)).willReturn(new ArrayList<>(List.of(handle))); - given(fdoRecordService.prepareMediaObjectAttributes(any(), any())).willReturn(mediaObject); + given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(fdoRecordService.prepareNewDigitalMediaRecord(any(), any(), any())).willReturn( + mediaObject); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -147,43 +137,45 @@ void testCreateMediaObject() throws Exception { // Then assertThat(responseReceived).isEqualTo(responseExpected); - then(dataCiteService).should().publishToDataCite(dataCiteEvent, FdoType.MEDIA_OBJECT); + // todo + //then(dataCiteService).should().publishToDataCite(dataCiteEvent, FdoType.MEDIA_OBJECT); } @Test void testCreateDigitalSpecimenDataCiteFails() throws Exception { + // todo // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); var request = List.of( (JsonNode) genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), FdoType.DIGITAL_SPECIMEN)); - List digitalSpecimen = genDigitalSpecimenAttributes(handle); - given(pidNameGeneratorService.genHandleList(1)).willReturn(new ArrayList<>(List.of(handle))); - given(fdoRecordService.prepareDigitalSpecimenRecordAttributes(any(), any())).willReturn( + var digitalSpecimen = givenDigitalSpecimenFdoRecord(HANDLE); + given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(fdoRecordService.prepareNewDigitalSpecimenRecord(any(), any(), any())).willReturn( digitalSpecimen); doThrow(JsonProcessingException.class).when(dataCiteService).publishToDataCite(any(), any()); // When - assertThrows(UnprocessableEntityException.class, () -> service.createRecords(request)); + //assertThrows(UnprocessableEntityException.class, () -> service.createRecords(request)); // Then - then(pidRepository).should().rollbackHandles(List.of(HANDLE)); + //then(pidRepository).should().rollbackHandles(List.of(HANDLE)); } @Test void testUpdateRecordLocation() throws Exception { // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - var updateRequest = genUpdateRequestBatch(List.of(handle), FdoType.DIGITAL_SPECIMEN); - var updatedAttributeRecord = genUpdateRecordAttributesAltLoc(handle); - var responseExpected = givenRecordResponseNullAttributes(List.of(handle), + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN); + var updatedAttributeRecord = genUpdateRecordAttributesAltLoc(HANDLE); + var responseExpected = givenRecordResponseNullAttributes(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN); var expectedEvent = new DataCiteEvent( ((ObjectNode) genObjectNodeAttributeRecord(updatedAttributeRecord)) .put("pid", HANDLE), EventType.UPDATE); - given(pidRepository.checkHandlesWritable(anyList())).willReturn(List.of(handle)); - given(fdoRecordService.prepareUpdateAttributes(any(), any(), any())).willReturn( + given(pidRepository.checkHandlesWritable(anyList())).willReturn(List.of(HANDLE.getBytes( + StandardCharsets.UTF_8))); + given(fdoRecordService.prepareUpdatedDigitalSpecimenRecord(any(), any(), any(), + any())).willReturn( updatedAttributeRecord); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -192,15 +184,15 @@ void testUpdateRecordLocation() throws Exception { // Then assertThat(responseReceived).isEqualTo(responseExpected); - then(pidRepository).should() - .updateRecordBatch(CREATED.getEpochSecond(), List.of(updatedAttributeRecord), true); + //then(pidRepository).should() + // .updateRecordBatch(CREATED.getEpochSecond(), List.of(updatedAttributeRecord), true); then(dataCiteService).should().publishToDataCite(expectedEvent, FdoType.DIGITAL_SPECIMEN); } @Test void testUpdateInvalidType() { // Given - var updateRequest = genUpdateRequestBatch(List.of(HANDLE.getBytes(StandardCharsets.UTF_8)), + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.HANDLE); // When Then @@ -212,11 +204,10 @@ void testUpdateInvalidType() { @Test void testUpdateRecordLocationDataCiteFails() throws Exception { // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - var updateRequest = genUpdateRequestBatch(List.of(handle), FdoType.DIGITAL_SPECIMEN); - var updatedAttributeRecord = genUpdateRecordAttributesAltLoc(handle); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN); + var updatedAttributeRecord = genUpdateRecordAttributesAltLoc(HANDLE); - given(pidRepository.checkHandlesWritable(anyList())).willReturn(List.of(handle)); + given(pidRepository.checkHandlesWritable(anyList())).willReturn(List.of(HANDLE)); given(fdoRecordService.prepareUpdateAttributes(any(), any(), any())).willReturn( updatedAttributeRecord); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); diff --git a/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java index b3753b5b..ff8efef4 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java @@ -62,6 +62,7 @@ import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.properties.ProfileProperties; +import eu.dissco.core.handlemanager.repository.PidMongoRepository; import eu.dissco.core.handlemanager.repository.PidRepository; import java.nio.charset.StandardCharsets; import java.time.Clock; @@ -91,6 +92,8 @@ class HandleServiceTest { private PidNameGeneratorService pidNameGeneratorService; @Mock private ProfileProperties profileProperties; + @Mock + PidMongoRepository mongoRepository; private PidService service; private List handles; private MockedStatic mockedStatic; @@ -101,7 +104,7 @@ void setup() { initTime(); initHandleList(); service = new HandleService(pidRepository, fdoRecordService, pidNameGeneratorService, MAPPER, - profileProperties); + profileProperties, mongoRepository); } private void initTime() { diff --git a/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java b/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java index c9ded575..ebbfde1a 100644 --- a/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java +++ b/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java @@ -81,6 +81,7 @@ import eu.dissco.core.handlemanager.domain.fdo.AnnotationRequest; import eu.dissco.core.handlemanager.domain.fdo.DigitalSpecimenRequest; import eu.dissco.core.handlemanager.domain.fdo.DoiRecordRequest; +import eu.dissco.core.handlemanager.domain.fdo.FdoProfile; import eu.dissco.core.handlemanager.domain.fdo.FdoType; import eu.dissco.core.handlemanager.domain.fdo.HandleRecordRequest; import eu.dissco.core.handlemanager.domain.fdo.MappingRequest; @@ -98,7 +99,8 @@ import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperRead; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperReadSingle; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperWrite; -import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoAttribute; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoRecord; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import java.io.IOException; import java.io.StringWriter; @@ -125,7 +127,6 @@ public class TestUtils { public static final String ISSUE_DATE_TESTVAL = "2022-11-01T09:59:24.000Z"; public static final Instant CREATED = Instant.parse(ISSUE_DATE_TESTVAL); - public final static String HANDLE_URI = "https://hdl.handle.net/"; public static final String PREFIX = "20.5000.1025"; public static final String HANDLE = PREFIX + "/QRS-321-ABC"; public static final String SUFFIX = "QRS-321-ABC"; @@ -138,8 +139,7 @@ public class TestUtils { public static final String ISSUED_FOR_AGENT_TESTVAL = ROR_DOMAIN + "0566bfb96"; public static final String PID_ISSUER_TESTVAL_OTHER = HANDLE_DOMAIN + "20.5000.1025/PID-ISSUER"; public static final StructuralType STRUCTURAL_TYPE_TESTVAL = StructuralType.DIGITAL; - public static final String[] LOC_TESTVAL = {"https://sandbox.dissco.tech/", - "https://dissco.eu"}; + public static final String[] LOC_TESTVAL = {"https://sandbox.dissco.tech/", "https://dissco.eu"}; public static final String[] LOC_ALT_TESTVAL = {"naturalis.nl"}; // DOI Request Attributes @@ -157,7 +157,7 @@ public class TestUtils { public static final String SPECIMEN_HOST_TESTVAL = ROR_DOMAIN + ROR_IDENTIFIER; public static final String SPECIMEN_HOST_NAME_TESTVAL = "Naturalis"; // Annotations - public static final String TARGET_DOI_TESTVAL = HANDLE_URI + PREFIX + "/111"; + public static final String TARGET_DOI_TESTVAL = HANDLE_DOMAIN + PREFIX + "/111"; public static final String TARGET_TYPE_TESTVAL = "digitalSpecimen"; public static final Motivation MOTIVATION_TESTVAL = Motivation.EDITING; public static final UUID ANNOTATION_HASH_TESTVAL = UUID.fromString( @@ -169,6 +169,7 @@ public class TestUtils { public static final LinkedDigitalObjectType LINKED_DIGITAL_OBJECT_TYPE_TESTVAL = LinkedDigitalObjectType.SPECIMEN; public static final String LINKED_DO_PID_TESTVAL = HANDLE; public static final String LICENSE_NAME_TESTVAL = "CC0 1.0 Universal (CC0 1.0) Public Domain Dedication"; + public static final String PRIMARY_MEDIA_ID_TESTVAL = "https://images.com/ABC"; // Mappings public static final String SOURCE_DATA_STANDARD_TESTVAL = "dwc"; // MAS @@ -182,7 +183,6 @@ public class TestUtils { public final static String NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL = PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL + ":" + ROR_IDENTIFIER; public final static String EXTERNAL_PID = "21.T11148/d8de0819e144e4096645"; - public static final String DIGITAL_OBJECT_NAME_TESTVAL = "digitalSpecimen"; // Tombstone Record vals public final static String TOMBSTONE_TEXT_TESTVAL = "pid was deleted"; @@ -201,502 +201,411 @@ private TestUtils() { } // Single Handle Attribute Lists + public static FdoRecord givenHandleFdoRecord(String handle) throws Exception { + return new FdoRecord(handle, FdoType.HANDLE, genHandleRecordAttributes(handle), null); + } - public static List genHandleRecordAttributes(byte[] handle) throws Exception { + public static List genHandleRecordAttributes(String handle) throws Exception { return genHandleRecordAttributes(handle, FdoType.HANDLE); } - public static List genHandleRecordAttributes(byte[] handle, FdoType type) - throws Exception { - List fdoRecord = new ArrayList<>(); + public static List genHandleRecordAttributes(String handle, FdoType fdoType) + throws Exception { + List fdoAttributes = new ArrayList<>(); var request = givenHandleRecordRequestObject(); - byte[] loc = setLocations(request.getLocations(), - new String(handle, StandardCharsets.UTF_8), - type, false); - fdoRecord.add(new HandleAttribute(LOC.index(), handle, LOC.get(), loc)); + var loc = setLocations(request.getLocations(), handle, fdoType, false); + fdoAttributes.add(new FdoAttribute(LOC, CREATED, loc)); // 1: FDO Profile - fdoRecord.add(new HandleAttribute(FDO_PROFILE.index(), handle, FDO_PROFILE.get(), - type.getFdoProfile().getBytes(StandardCharsets.UTF_8))); + fdoAttributes.add(new FdoAttribute(FDO_PROFILE, CREATED, fdoType.getFdoProfile())); // 2: FDO Record License - byte[] pidKernelMetadataLicense = "https://creativecommons.org/publicdomain/zero/1.0/".getBytes( - StandardCharsets.UTF_8); - fdoRecord.add(new HandleAttribute(FDO_RECORD_LICENSE.index(), handle, - FDO_RECORD_LICENSE.get(), pidKernelMetadataLicense)); + fdoAttributes.add(new FdoAttribute(FDO_RECORD_LICENSE, CREATED, + "https://creativecommons.org/publicdomain/zero/1.0/")); // 3: DigitalObjectType - fdoRecord.add( - new HandleAttribute(DIGITAL_OBJECT_TYPE, handle, - type.getDigitalObjectType())); + fdoAttributes.add( + new FdoAttribute(DIGITAL_OBJECT_TYPE, CREATED, fdoType.getDigitalObjectType())); // 4: DigitalObjectName - fdoRecord.add( - new HandleAttribute(DIGITAL_OBJECT_NAME, handle, type.getDigitalObjectName())); + fdoAttributes.add( + new FdoAttribute(DIGITAL_OBJECT_NAME, CREATED, fdoType.getDigitalObjectName())); // 5: Pid - byte[] pid = ("https://hdl.handle.net/" + new String(handle, - StandardCharsets.UTF_8)).getBytes( - StandardCharsets.UTF_8); - fdoRecord.add(new HandleAttribute(PID.index(), handle, PID.get(), pid)); + fdoAttributes.add(new FdoAttribute(PID, CREATED, HANDLE_DOMAIN + handle)); // 6: PidIssuer - fdoRecord.add(new HandleAttribute(PID_ISSUER.index(), handle, PID_ISSUER.get(), - request.getPidIssuer().getBytes(StandardCharsets.UTF_8))); + fdoAttributes.add(new FdoAttribute(PID_ISSUER, CREATED, request.getPidIssuer())); // 7: pidIssuerName - fdoRecord.add(new HandleAttribute(PID_ISSUER_NAME.index(), handle, PID_ISSUER_NAME.get(), - PID_ISSUER_TESTVAL_OTHER.getBytes(StandardCharsets.UTF_8))); + fdoAttributes.add(new FdoAttribute(PID_ISSUER_NAME, CREATED, PID_ISSUER_TESTVAL_OTHER)); // 8: issuedForAgent - fdoRecord.add(new HandleAttribute(ISSUED_FOR_AGENT.index(), handle, ISSUED_FOR_AGENT.get(), - request.getIssuedForAgent().getBytes(StandardCharsets.UTF_8))); + fdoAttributes.add(new FdoAttribute(ISSUED_FOR_AGENT, CREATED, request.getIssuedForAgent())); // 9: issuedForAgentName - fdoRecord.add( - new HandleAttribute(ISSUED_FOR_AGENT_NAME.index(), handle, - ISSUED_FOR_AGENT_NAME.get(), - ISSUED_FOR_AGENT_TESTVAL.getBytes(StandardCharsets.UTF_8))); + fdoAttributes.add(new FdoAttribute(ISSUED_FOR_AGENT_NAME, CREATED, ISSUED_FOR_AGENT_TESTVAL)); // 10: pidRecordIssueDate - fdoRecord.add(new HandleAttribute(PID_RECORD_ISSUE_DATE.index(), handle, - PID_RECORD_ISSUE_DATE.get(), ISSUE_DATE_TESTVAL.getBytes(StandardCharsets.UTF_8))); + fdoAttributes.add(new FdoAttribute(PID_RECORD_ISSUE_DATE, CREATED, ISSUE_DATE_TESTVAL)); // 11: pidRecordIssueNumber - fdoRecord.add(new HandleAttribute(PID_RECORD_ISSUE_NUMBER.index(), handle, - PID_RECORD_ISSUE_NUMBER.get(), "1".getBytes(StandardCharsets.UTF_8))); + fdoAttributes.add(new FdoAttribute(PID_RECORD_ISSUE_NUMBER, CREATED, "1")); // 12: structuralType - fdoRecord.add(new HandleAttribute(STRUCTURAL_TYPE.index(), handle, - STRUCTURAL_TYPE.get(), - STRUCTURAL_TYPE_TESTVAL.toString().getBytes(StandardCharsets.UTF_8))); + fdoAttributes.add( + new FdoAttribute(STRUCTURAL_TYPE, CREATED, STRUCTURAL_TYPE_TESTVAL.toString())); // 13: PidStatus - fdoRecord.add(new HandleAttribute(PID_STATUS.index(), handle, PID_STATUS.get(), - "TEST".getBytes(StandardCharsets.UTF_8))); + fdoAttributes.add(new FdoAttribute(PID_STATUS, CREATED, PID_STATUS_TESTVAL)); - return fdoRecord; + return fdoAttributes; } - public static List genHandleRecordAttributesAltLoc(byte[] handle) - throws Exception { - List attributes = genHandleRecordAttributes(handle, FdoType.HANDLE); - - byte[] locOriginal = setLocations(LOC_TESTVAL, new String(handle, StandardCharsets.UTF_8), - FdoType.HANDLE, false); - var locOriginalAttr = new HandleAttribute(LOC.index(), handle, LOC.get(), locOriginal); - - byte[] locAlt = setLocations(LOC_ALT_TESTVAL, new String(handle, StandardCharsets.UTF_8), - FdoType.HANDLE, false); - var locAltAttr = new HandleAttribute(LOC.index(), handle, LOC.get(), locAlt); - + public static List genHandleRecordAttributesAltLoc(String handle) throws Exception { + var attributes = genHandleRecordAttributes(handle, FdoType.HANDLE); + var locOriginal = setLocations(LOC_TESTVAL, handle, FdoType.HANDLE, false); + var locOriginalAttr = new FdoAttribute(LOC, CREATED, locOriginal); + var locAlt = setLocations(LOC_ALT_TESTVAL, handle, FdoType.HANDLE, false); + var locAltAttr = new FdoAttribute(LOC, CREATED, locAlt); attributes.set(attributes.indexOf(locOriginalAttr), locAltAttr); - return attributes; } - public static List genTombstoneRecordFullAttributes(byte[] handle) + public static List genTombstoneRecordFullAttributes(String handle) throws Exception { - List attributes = genHandleRecordAttributes(handle, FdoType.TOMBSTONE); - HandleAttribute oldPidStatus = new HandleAttribute(PID_STATUS.index(), handle, - PID_STATUS.get(), PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8)); - attributes.addAll(genHandleRecordAttributes(handle, FdoType.TOMBSTONE)); - attributes.remove(oldPidStatus); - attributes = new ArrayList<>( - (attributes.stream().filter(row -> row.getIndex() != LOC.index())).toList()); - attributes.add(givenLandingPageAttribute(handle)); - return attributes; + var fdoAttributes = genHandleRecordAttributes(handle, FdoType.TOMBSTONE); + var oldPidStatus = new FdoAttribute(PID_STATUS, CREATED, PID_STATUS_TESTVAL); + fdoAttributes.addAll(genHandleRecordAttributes(handle, FdoType.TOMBSTONE)); + fdoAttributes.remove(oldPidStatus); + fdoAttributes = new ArrayList<>( + (fdoAttributes.stream().filter(row -> row.getIndex() != LOC.index())).toList()); + fdoAttributes.add(givenLandingPageAttribute(handle)); + return fdoAttributes; } - public static List genUpdateRecordAttributesAltLoc(byte[] handle) + public static List genUpdateRecordAttributesAltLoc(String handle) throws ParserConfigurationException, TransformerException { - byte[] locAlt = setLocations(LOC_ALT_TESTVAL, new String(handle, StandardCharsets.UTF_8), - FdoType.HANDLE, false); - return List.of(new HandleAttribute(LOC.index(), handle, LOC.get(), locAlt)); + var locAlt = setLocations(LOC_ALT_TESTVAL, handle, FdoType.HANDLE, false); + return List.of(new FdoAttribute(LOC, CREATED, locAlt)); } - public static List genTombstoneRecordRequestAttributes(byte[] handle) + public static List genTombstoneRecordRequestAttributes(String handle) throws Exception { - List tombstoneAttributes = new ArrayList<>(); - tombstoneAttributes.add( - new HandleAttribute(TOMBSTONE_TEXT.index(), handle, TOMBSTONE_TEXT.get(), - TOMBSTONE_TEXT_TESTVAL.getBytes(StandardCharsets.UTF_8))); - tombstoneAttributes.add(new HandleAttribute(PID_STATUS.index(), handle, PID_STATUS.get(), - "ARCHIVED".getBytes(StandardCharsets.UTF_8))); + var tombstoneAttributes = new ArrayList(); + tombstoneAttributes.add(new FdoAttribute(TOMBSTONE_TEXT, CREATED, TOMBSTONE_TEXT_TESTVAL)); + tombstoneAttributes.add(new FdoAttribute(PID_STATUS, CREATED, "ARCHIVED")); tombstoneAttributes.add(givenLandingPageAttribute(handle)); return tombstoneAttributes; } - public static List genDoiRecordAttributes(byte[] handle, FdoType type) + public static FdoRecord givenDoiFdoRecord(String handle) throws Exception { + return new FdoRecord(handle, FdoType.DOI, genDoiRecordAttributes(handle, FdoType.DOI), null); + } + + public static List genDoiRecordAttributes(String handle, FdoType type) throws Exception { return genDoiRecordAttributes(handle, type, givenDoiRecordRequestObject()); } - public static List genDoiRecordAttributes(byte[] handle, FdoType type, - DoiRecordRequest request) - throws Exception { - List fdoRecord = genHandleRecordAttributes(handle, type); + public static List genDoiRecordAttributes(String handle, FdoType type, + DoiRecordRequest request) throws Exception { + var fdoRecord = genHandleRecordAttributes(handle, type); // 40: referentType - fdoRecord.add( - new HandleAttribute(REFERENT_TYPE.index(), handle, REFERENT_TYPE.get(), - request.getReferentType().getBytes(StandardCharsets.UTF_8))); + fdoRecord.add(new FdoAttribute(REFERENT_TYPE, CREATED, request.getReferentType())); // 41: referentDoiName - fdoRecord.add( - new HandleAttribute(REFERENT_DOI_NAME.index(), handle, REFERENT_DOI_NAME.get(), - REFERENT_DOI_NAME_TESTVAL.getBytes(StandardCharsets.UTF_8))); + fdoRecord.add(new FdoAttribute(REFERENT_DOI_NAME, CREATED, REFERENT_DOI_NAME_TESTVAL)); // 42: referentName - fdoRecord.add( - new HandleAttribute(REFERENT_NAME.index(), handle, REFERENT_NAME.get(), - request.getReferentName().getBytes(StandardCharsets.UTF_8))); + fdoRecord.add(new FdoAttribute(REFERENT_NAME, CREATED, request.getReferentName())); // 43: primaryReferentType fdoRecord.add( - new HandleAttribute(PRIMARY_REFERENT_TYPE.index(), handle, - PRIMARY_REFERENT_TYPE.get(), - request.getPrimaryReferentType().getBytes(StandardCharsets.UTF_8))); - + new FdoAttribute(PRIMARY_REFERENT_TYPE, CREATED, request.getPrimaryReferentType())); return fdoRecord; } - public static List genDigitalSpecimenAttributes(byte[] handle, + public static FdoRecord givenDigitalSpecimenFdoRecord(String handle) throws Exception { + return new FdoRecord(handle, FdoType.DIGITAL_SPECIMEN, genDigitalSpecimenAttributes(handle), + NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL); + } + + public static List genDigitalSpecimenAttributes(String handle, DigitalSpecimenRequest request) throws Exception { - List fdoRecord = genDoiRecordAttributes(handle, FdoType.DIGITAL_SPECIMEN, + List fdoRecord = genDoiRecordAttributes(handle, FdoType.DIGITAL_SPECIMEN, request); // 200: Specimen Host - fdoRecord.add( - new HandleAttribute(SPECIMEN_HOST.index(), handle, - SPECIMEN_HOST.get(), - request.getSpecimenHost().getBytes(StandardCharsets.UTF_8))); + fdoRecord.add(new FdoAttribute(SPECIMEN_HOST, CREATED, request.getSpecimenHost())); // 201: Specimen Host name - fdoRecord.add( - new HandleAttribute(SPECIMEN_HOST_NAME.index(), handle, - SPECIMEN_HOST_NAME.get(), - SPECIMEN_HOST_NAME_TESTVAL.getBytes(StandardCharsets.UTF_8))); + fdoRecord.add(new FdoAttribute(SPECIMEN_HOST_NAME, CREATED, SPECIMEN_HOST_NAME_TESTVAL)); // 202: primarySpecimenObjectId - fdoRecord.add( - new HandleAttribute(PRIMARY_SPECIMEN_OBJECT_ID.index(), handle, - PRIMARY_SPECIMEN_OBJECT_ID.get(), - request.getPrimarySpecimenObjectId().getBytes(StandardCharsets.UTF_8))); + fdoRecord.add(new FdoAttribute(PRIMARY_SPECIMEN_OBJECT_ID, CREATED, + request.getPrimarySpecimenObjectId())); // 203: primarySpecimenObjectIdType - fdoRecord.add( - new HandleAttribute(PRIMARY_SPECIMEN_OBJECT_ID_TYPE.index(), handle, - PRIMARY_SPECIMEN_OBJECT_ID_TYPE.get(), - request.getPrimarySpecimenObjectIdType().getBytes())); + fdoRecord.add(new FdoAttribute(PRIMARY_SPECIMEN_OBJECT_ID_TYPE, CREATED, + request.getPrimarySpecimenObjectIdType().toString())); // 204-217 are optional // 204: primarySpecimenObjectIdName if (request.getPrimarySpecimenObjectIdName() != null) { - fdoRecord.add( - new HandleAttribute(PRIMARY_SPECIMEN_OBJECT_ID_NAME.index(), handle, - PRIMARY_SPECIMEN_OBJECT_ID_NAME.get(), - request.getPrimarySpecimenObjectIdName() - .getBytes(StandardCharsets.UTF_8))); + fdoRecord.add(new FdoAttribute(PRIMARY_SPECIMEN_OBJECT_ID_NAME, CREATED, + request.getPrimarySpecimenObjectIdName())); } // 205: normalisedSpecimenObjectId - fdoRecord.add( - new HandleAttribute(NORMALISED_SPECIMEN_OBJECT_ID, handle, - request.getNormalisedPrimarySpecimenObjectId())); + fdoRecord.add(new FdoAttribute(NORMALISED_SPECIMEN_OBJECT_ID, CREATED, + request.getNormalisedPrimarySpecimenObjectId())); // 206: specimenObjectIdAbsenceReason if (request.getSpecimenObjectIdAbsenceReason() != null) { - fdoRecord.add( - new HandleAttribute(SPECIMEN_OBJECT_ID_ABSENCE_REASON.index(), handle, - SPECIMEN_OBJECT_ID_ABSENCE_REASON.get(), - request.getSpecimenObjectIdAbsenceReason() - .getBytes(StandardCharsets.UTF_8))); + fdoRecord.add(new FdoAttribute(SPECIMEN_OBJECT_ID_ABSENCE_REASON, CREATED, + request.getSpecimenObjectIdAbsenceReason())); } // 207: otherSpecimenIds if (request.getOtherSpecimenIds() != null && !request.getOtherSpecimenIds().isEmpty()) { var otherSpecimenIds = MAPPER.writeValueAsString(request.getOtherSpecimenIds()); - fdoRecord.add(new HandleAttribute(OTHER_SPECIMEN_IDS, handle, otherSpecimenIds)); + fdoRecord.add(new FdoAttribute(OTHER_SPECIMEN_IDS, CREATED, otherSpecimenIds)); } // 208: topicOrigin if (request.getTopicOrigin() != null) { - fdoRecord.add( - new HandleAttribute(TOPIC_ORIGIN.index(), handle, - TOPIC_ORIGIN.get(), - request.getTopicOrigin().toString().getBytes(StandardCharsets.UTF_8))); + fdoRecord.add(new FdoAttribute(TOPIC_ORIGIN, CREATED, request.getTopicOrigin().toString())); } // 209: topicDomain if (request.getTopicDomain() != null) { - fdoRecord.add( - new HandleAttribute(TOPIC_DOMAIN.index(), handle, - TOPIC_DOMAIN.get(), - request.getTopicDomain().toString().getBytes(StandardCharsets.UTF_8))); + fdoRecord.add(new FdoAttribute(TOPIC_DOMAIN, CREATED, request.getTopicDomain().toString())); } // 210: topicDiscipline if (request.getTopicDiscipline() != null) { fdoRecord.add( - new HandleAttribute(TOPIC_DISCIPLINE.index(), handle, - TOPIC_DISCIPLINE.get(), - request.getTopicDiscipline().toString() - .getBytes(StandardCharsets.UTF_8))); + new FdoAttribute(TOPIC_DISCIPLINE, CREATED, request.getTopicDiscipline().toString())); } // 211: topicCategory if (request.getTopicCategory() != null) { fdoRecord.add( - new HandleAttribute(TOPIC_CATEGORY, handle, - request.getTopicCategory().toString())); + new FdoAttribute(TOPIC_CATEGORY, CREATED, request.getTopicCategory().toString())); } // 212: livingOrPreserved if (request.getLivingOrPreserved() != null) { - fdoRecord.add( - new HandleAttribute(LIVING_OR_PRESERVED.index(), handle, - LIVING_OR_PRESERVED.get(), - request.getLivingOrPreserved().getBytes())); + fdoRecord.add(new FdoAttribute(LIVING_OR_PRESERVED, CREATED, + request.getLivingOrPreserved().toString())); } // 213: baseTypeOfSpecimen if (request.getBaseTypeOfSpecimen() != null) { - fdoRecord.add( - new HandleAttribute(BASE_TYPE_OF_SPECIMEN.index(), handle, - BASE_TYPE_OF_SPECIMEN.get(), - request.getBaseTypeOfSpecimen().toString() - .getBytes(StandardCharsets.UTF_8))); + fdoRecord.add(new FdoAttribute(BASE_TYPE_OF_SPECIMEN, CREATED, + request.getBaseTypeOfSpecimen().toString())); } // 214: informationArtefactType if (request.getInformationArtefactType() != null) { - fdoRecord.add( - new HandleAttribute(INFORMATION_ARTEFACT_TYPE.index(), handle, - INFORMATION_ARTEFACT_TYPE.get(), - request.getInformationArtefactType().toString() - .getBytes(StandardCharsets.UTF_8))); + fdoRecord.add(new FdoAttribute(INFORMATION_ARTEFACT_TYPE, CREATED, + request.getInformationArtefactType().toString())); } // 215: materialSampleType if (request.getMaterialSampleType() != null) { - fdoRecord.add( - new HandleAttribute(MATERIAL_SAMPLE_TYPE.index(), handle, - MATERIAL_SAMPLE_TYPE.get(), - request.getMaterialSampleType().toString() - .getBytes(StandardCharsets.UTF_8))); + fdoRecord.add(new FdoAttribute(MATERIAL_SAMPLE_TYPE, CREATED, + request.getMaterialSampleType().toString())); } // 216: materialOrDigitalEntity if (request.getMaterialSampleType() != null) { - fdoRecord.add( - new HandleAttribute(MATERIAL_OR_DIGITAL_ENTITY, handle, - request.getMaterialOrDigitalEntity().toString())); + fdoRecord.add(new FdoAttribute(MATERIAL_OR_DIGITAL_ENTITY, CREATED, + request.getMaterialOrDigitalEntity().toString())); } // 217: markedAsType if (request.getMarkedAsType() != null) { fdoRecord.add( - new HandleAttribute(MARKED_AS_TYPE.index(), handle, - MARKED_AS_TYPE.get(), - request.getMarkedAsType().toString().getBytes(StandardCharsets.UTF_8))); + new FdoAttribute(MARKED_AS_TYPE, CREATED, request.getMarkedAsType().toString())); } // 218: wasDerivedFromEntity if (request.getDerivedFromEntity() != null) { fdoRecord.add( - new HandleAttribute(WAS_DERIVED_FROM_ENTITY.index(), handle, - WAS_DERIVED_FROM_ENTITY.get(), - request.getDerivedFromEntity().getBytes(StandardCharsets.UTF_8))); + new FdoAttribute(WAS_DERIVED_FROM_ENTITY, CREATED, request.getDerivedFromEntity())); } var catId = request.getCatalogIdentifier(); if (catId != null) { - fdoRecord.add(new HandleAttribute(CATALOG_IDENTIFIER, handle, catId)); + fdoRecord.add(new FdoAttribute(CATALOG_IDENTIFIER, CREATED, catId)); } return fdoRecord; } - public static List genDigitalSpecimenAttributes(byte[] handle) - throws Exception { + public static List genDigitalSpecimenAttributes(String handle) throws Exception { var request = givenDigitalSpecimenRequestObjectNullOptionals(); return genDigitalSpecimenAttributes(handle, request); } - public static List genMediaObjectAttributes(byte[] handle) - throws Exception { + public static FdoRecord givenDigitalMediaFdoRecord(String handle) throws Exception { + return new FdoRecord(handle, FdoType.DIGITAL_SPECIMEN, genMediaObjectAttributes(handle), + PRIMARY_MEDIA_ID_TESTVAL); + } + + public static List genMediaObjectAttributes(String handle) throws Exception { var request = givenMediaRequestObject(); return genMediaObjectAttributes(handle, request); } - public static List genMediaObjectAttributes(byte[] handle, + public static List genMediaObjectAttributes(String handle, MediaObjectRequest request) throws Exception { - List fdoRecord = genDoiRecordAttributes(handle, FdoType.MEDIA_OBJECT); - fdoRecord.add(new HandleAttribute(MEDIA_HOST, handle, request.getMediaHost())); + var fdoRecord = genDoiRecordAttributes(handle, FdoType.MEDIA_OBJECT); + fdoRecord.add(new FdoAttribute(MEDIA_HOST, CREATED, request.getMediaHost())); if (request.getMediaHostName() == null) { - fdoRecord.add(new HandleAttribute(MEDIA_HOST_NAME, handle, MEDIA_HOST_NAME_TESTVAL)); + fdoRecord.add(new FdoAttribute(MEDIA_HOST_NAME, CREATED, MEDIA_HOST_NAME_TESTVAL)); } else { - fdoRecord.add(new HandleAttribute(MEDIA_HOST_NAME, handle, request.getMediaHostName())); + fdoRecord.add(new FdoAttribute(MEDIA_HOST_NAME, CREATED, request.getMediaHostName())); } if (request.getDctermsFormat() != null) { fdoRecord.add( - new HandleAttribute(DCTERMS_FORMAT, handle, request.getDctermsFormat().toString())); + new FdoAttribute(DCTERMS_FORMAT, CREATED, request.getDctermsFormat().toString())); } - fdoRecord.add(new HandleAttribute(IS_DERIVED_FROM_SPECIMEN, handle, + fdoRecord.add(new FdoAttribute(IS_DERIVED_FROM_SPECIMEN, CREATED, request.getIsDerivedFromSpecimen().toString())); if (request.getLinkedDigitalObjectPid() != null) { fdoRecord.add( - new HandleAttribute(LINKED_DO_PID, handle, - request.getLinkedDigitalObjectPid())); + new FdoAttribute(LINKED_DO_PID, CREATED, request.getLinkedDigitalObjectPid())); } if (request.getLinkedDigitalObjectType() != null) { - fdoRecord.add( - new HandleAttribute(LINKED_DO_TYPE, handle, request.getLinkedDigitalObjectType() - .toString())); + fdoRecord.add(new FdoAttribute(LINKED_DO_TYPE, CREATED, + request.getLinkedDigitalObjectType().toString())); } if (request.getLinkedAttribute() != null) { - fdoRecord.add( - new HandleAttribute(LINKED_ATTRIBUTE, handle, request.getLinkedAttribute())); + fdoRecord.add(new FdoAttribute(LINKED_ATTRIBUTE, CREATED, request.getLinkedAttribute())); } if (request.getPrimaryMediaId() != null) { - fdoRecord.add( - new HandleAttribute(PRIMARY_MEDIA_ID, handle, request.getPrimaryMediaId())); + fdoRecord.add(new FdoAttribute(PRIMARY_MEDIA_ID, CREATED, request.getPrimaryMediaId())); } if (request.getPrimaryMediaObjectIdType() != null) { - fdoRecord.add( - new HandleAttribute(PRIMARY_MO_ID_TYPE, handle, - request.getPrimaryMediaObjectIdType().toString())); + fdoRecord.add(new FdoAttribute(PRIMARY_MO_ID_TYPE, CREATED, + request.getPrimaryMediaObjectIdType().toString())); } if (request.getPrimaryMediaObjectIdName() != null) { fdoRecord.add( - new HandleAttribute(PRIMARY_MO_ID_NAME, handle, - request.getPrimaryMediaObjectIdName())); + new FdoAttribute(PRIMARY_MO_ID_NAME, CREATED, request.getPrimaryMediaObjectIdName())); } if (request.getDcTermsType() != null) { - fdoRecord.add( - new HandleAttribute(DCTERMS_TYPE, handle, - request.getDcTermsType().toString())); + fdoRecord.add(new FdoAttribute(DCTERMS_TYPE, CREATED, request.getDcTermsType().toString())); } if (request.getDctermsSubject() != null) { - fdoRecord.add( - new HandleAttribute(DCTERMS_SUBJECT, handle, request.getDctermsSubject())); + fdoRecord.add(new FdoAttribute(DCTERMS_SUBJECT, CREATED, request.getDctermsSubject())); } if (request.getDerivedFromEntity() != null) { fdoRecord.add( - new HandleAttribute(DERIVED_FROM_ENTITY, handle, - request.getDerivedFromEntity())); + new FdoAttribute(DERIVED_FROM_ENTITY, CREATED, request.getDerivedFromEntity())); } if (request.getLicenseName() != null) { - fdoRecord.add( - new HandleAttribute(LICENSE_NAME, handle, request.getLicenseName())); + fdoRecord.add(new FdoAttribute(LICENSE_NAME, CREATED, request.getLicenseName())); } if (request.getLicenseUrl() != null) { - fdoRecord.add( - new HandleAttribute(LICENSE_URL, handle, request.getLicenseUrl())); + fdoRecord.add(new FdoAttribute(LICENSE_URL, CREATED, request.getLicenseUrl())); } if (request.getRightsholderName() != null) { - fdoRecord.add( - new HandleAttribute(RIGHTSHOLDER_NAME, handle, request.getRightsholderName())); + fdoRecord.add(new FdoAttribute(RIGHTSHOLDER_NAME, CREATED, request.getRightsholderName())); } if (request.getRightsholderPid() != null) { - fdoRecord.add( - new HandleAttribute(RIGHTSHOLDER_PID, handle, request.getRightsholderPid())); + fdoRecord.add(new FdoAttribute(RIGHTSHOLDER_PID, CREATED, request.getRightsholderPid())); } if (request.getRightsholderPidType() != null) { - fdoRecord.add( - new HandleAttribute(RIGHTSHOLDER_PID_TYPE, handle, - request.getRightsholderPidType().toString())); + fdoRecord.add(new FdoAttribute(RIGHTSHOLDER_PID_TYPE, CREATED, + request.getRightsholderPidType().toString())); } if (request.getDctermsConformsTo() != null) { - fdoRecord.add( - new HandleAttribute(DC_TERMS_CONFORMS, handle, request.getDctermsConformsTo())); + fdoRecord.add(new FdoAttribute(DC_TERMS_CONFORMS, CREATED, request.getDctermsConformsTo())); } - return fdoRecord; } - public static List genAnnotationAttributes(byte[] handle, boolean includeHash) + public static FdoRecord givenAnnotationFdoRecord(String handle, boolean includeHash) + throws Exception { + return new FdoRecord(handle, FdoType.ANNOTATION, genAnnotationAttributes(handle, includeHash), + null); + } + + public static List genAnnotationAttributes(String handle, boolean includeHash) throws Exception { var fdoRecord = genHandleRecordAttributes(handle, FdoType.ANNOTATION); // 500 TargetPid - fdoRecord.add(new HandleAttribute(TARGET_PID, handle, TARGET_DOI_TESTVAL)); + fdoRecord.add(new FdoAttribute(TARGET_PID, CREATED, TARGET_DOI_TESTVAL)); // 501 TargetType - fdoRecord.add(new HandleAttribute(TARGET_TYPE, handle, TARGET_TYPE_TESTVAL)); + fdoRecord.add(new FdoAttribute(TARGET_TYPE, CREATED, TARGET_TYPE_TESTVAL)); // 502 motivation - fdoRecord.add( - new HandleAttribute(MOTIVATION, handle, MOTIVATION_TESTVAL.toString())); + fdoRecord.add(new FdoAttribute(MOTIVATION, CREATED, MOTIVATION_TESTVAL.toString())); // 503 AnnotationHash if (includeHash) { fdoRecord.add( - new HandleAttribute(ANNOTATION_HASH, handle, - ANNOTATION_HASH_TESTVAL.toString())); + new FdoAttribute(ANNOTATION_HASH, CREATED, ANNOTATION_HASH_TESTVAL.toString())); } return fdoRecord; } - public static List genMasAttributes(byte[] handle) - throws Exception { + public static List genMasAttributes(String handle) throws Exception { var fdoRecord = genHandleRecordAttributes(handle, FdoType.MAS); - - fdoRecord.add(new HandleAttribute(MAS_NAME.index(), handle, MAS_NAME.get(), - (MAS_NAME_TESTVAL).getBytes(StandardCharsets.UTF_8))); - + fdoRecord.add(new FdoAttribute(MAS_NAME, CREATED, (MAS_NAME_TESTVAL))); return fdoRecord; } - public static List genMappingAttributes(byte[] handle) - throws Exception { + public static List genMappingAttributes(String handle) throws Exception { var fdoRecord = genHandleRecordAttributes(handle, FdoType.MAPPING); // 500 subjectDigitalObjectId - fdoRecord.add(new HandleAttribute(SOURCE_DATA_STANDARD.index(), handle, - SOURCE_DATA_STANDARD.get(), - SOURCE_DATA_STANDARD_TESTVAL.getBytes(StandardCharsets.UTF_8))); + fdoRecord.add( + new FdoAttribute(SOURCE_DATA_STANDARD, CREATED, + SOURCE_DATA_STANDARD_TESTVAL)); return fdoRecord; } - public static List genSourceSystemAttributes(byte[] handle) - throws Exception { + public static List genSourceSystemAttributes(String handle) throws Exception { var fdoRecord = genHandleRecordAttributes(handle, FdoType.SOURCE_SYSTEM); // 600 hostInstitution - fdoRecord.add(new HandleAttribute(SOURCE_SYSTEM_NAME.index(), handle, - SOURCE_SYSTEM_NAME.get(), SPECIMEN_HOST_TESTVAL.getBytes(StandardCharsets.UTF_8))); + fdoRecord.add(new FdoAttribute(SOURCE_SYSTEM_NAME, CREATED, + SPECIMEN_HOST_TESTVAL)); return fdoRecord; } - public static List genOrganisationAttributes(byte[] handle, - OrganisationRequest request) - throws Exception { + public static List genOrganisationAttributes(String handle, + OrganisationRequest request) throws Exception { var fdoRecord = genDoiRecordAttributes(handle, FdoType.ORGANISATION, request); // 800 OrganisationIdentifier - fdoRecord.add(new HandleAttribute(ORGANISATION_ID.index(), handle, - ORGANISATION_ID.get(), SPECIMEN_HOST_TESTVAL.getBytes(StandardCharsets.UTF_8))); + fdoRecord.add(new FdoAttribute(ORGANISATION_ID, CREATED, + SPECIMEN_HOST_TESTVAL)); // 801 OrganisationIdentifier - fdoRecord.add(new HandleAttribute(ORGANISATION_ID_TYPE.index(), handle, - ORGANISATION_ID_TYPE.get(), PTR_TYPE_DOI.getBytes(StandardCharsets.UTF_8))); + fdoRecord.add( + new FdoAttribute(ORGANISATION_ID_TYPE, CREATED, + PTR_TYPE_DOI)); // 802 OrganisationName - fdoRecord.add( - new HandleAttribute(ORGANISATION_NAME.index(), handle, ORGANISATION_NAME.get(), - SPECIMEN_HOST_NAME_TESTVAL.getBytes( - StandardCharsets.UTF_8))); + fdoRecord.add(new FdoAttribute(ORGANISATION_NAME, CREATED, + SPECIMEN_HOST_NAME_TESTVAL)); return fdoRecord; } - public static List genOrganisationAttributes(byte[] handle) - throws Exception { + public static List genOrganisationAttributes(String handle) throws Exception { return genOrganisationAttributes(handle, givenOrganisationRequestObject()); } @@ -721,24 +630,14 @@ public static ObjectNode genCreateRecordRequest( // Single Requests public static HandleRecordRequest givenHandleRecordRequestObject() { - return new HandleRecordRequest( - ISSUED_FOR_AGENT_TESTVAL, - PID_ISSUER_TESTVAL_OTHER, - STRUCTURAL_TYPE_TESTVAL, - LOC_TESTVAL - ); + return new HandleRecordRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, + STRUCTURAL_TYPE_TESTVAL, LOC_TESTVAL); } public static DoiRecordRequest givenDoiRecordRequestObject() { - return new DoiRecordRequest( - ISSUED_FOR_AGENT_TESTVAL, - PID_ISSUER_TESTVAL_OTHER, - STRUCTURAL_TYPE_TESTVAL, - LOC_TESTVAL, - REFERENT_NAME_TESTVAL, - FdoType.MEDIA_OBJECT.getDigitalObjectName(), - PRIMARY_REFERENT_TYPE_TESTVAL - ); + return new DoiRecordRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, + STRUCTURAL_TYPE_TESTVAL, LOC_TESTVAL, REFERENT_NAME_TESTVAL, + FdoType.MEDIA_OBJECT.getDigitalObjectName(), PRIMARY_REFERENT_TYPE_TESTVAL); } public static DigitalSpecimenRequest givenDigitalSpecimenRequestObjectNullOptionals() { @@ -748,123 +647,70 @@ public static DigitalSpecimenRequest givenDigitalSpecimenRequestObjectNullOption public static DigitalSpecimenRequest givenDigitalSpecimenRequestObjectNullOptionals( String primarySpecimenObjectId) { try { - return new DigitalSpecimenRequest( - ISSUED_FOR_AGENT_TESTVAL, - PID_ISSUER_TESTVAL_OTHER, - LOC_TESTVAL, - REFERENT_NAME_TESTVAL, - PRIMARY_REFERENT_TYPE_TESTVAL, - SPECIMEN_HOST_TESTVAL, - SPECIMEN_HOST_NAME_TESTVAL, - primarySpecimenObjectId, - PrimarySpecimenObjectIdType.GLOBAL, null, primarySpecimenObjectId, - null, null, null, null, null, null, null, - null, - null, - null, null, null, - null, null); + return new DigitalSpecimenRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, + LOC_TESTVAL, REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, SPECIMEN_HOST_TESTVAL, + SPECIMEN_HOST_NAME_TESTVAL, primarySpecimenObjectId, PrimarySpecimenObjectIdType.GLOBAL, + null, primarySpecimenObjectId, null, null, null, null, null, null, null, null, null, null, + null, null, null, null); } catch (InvalidRequestException e) { throw new RuntimeException(e.getMessage()); } } public static MediaObjectRequest givenMediaRequestObject() throws InvalidRequestException { - return new MediaObjectRequest( - ISSUED_FOR_AGENT_TESTVAL, - PID_ISSUER_TESTVAL_OTHER, - LOC_TESTVAL, - REFERENT_NAME_TESTVAL, - PRIMARY_REFERENT_TYPE_TESTVAL, - MEDIA_HOST_TESTVAL, MEDIA_HOST_NAME_TESTVAL, null, Boolean.TRUE, - LINKED_DO_PID_TESTVAL, - LINKED_DIGITAL_OBJECT_TYPE_TESTVAL, null, HANDLE, null, null, null, null, null, - null, - null, SPECIMEN_HOST_TESTVAL, SPECIMEN_HOST_NAME_TESTVAL, null, null - ); + return new MediaObjectRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, LOC_TESTVAL, + REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, MEDIA_HOST_TESTVAL, + MEDIA_HOST_NAME_TESTVAL, null, Boolean.TRUE, LINKED_DO_PID_TESTVAL, + LINKED_DIGITAL_OBJECT_TYPE_TESTVAL, null, PRIMARY_MEDIA_ID_TESTVAL, null, null, null, null, + null, null, null, + SPECIMEN_HOST_TESTVAL, SPECIMEN_HOST_NAME_TESTVAL, null, null); } public static AnnotationRequest givenAnnotationRequestObject() { - return new AnnotationRequest( - ISSUED_FOR_AGENT_TESTVAL, - PID_ISSUER_TESTVAL_OTHER, - LOC_TESTVAL, - TARGET_DOI_TESTVAL, - TARGET_TYPE_TESTVAL, - MOTIVATION_TESTVAL, - ANNOTATION_HASH_TESTVAL - ); + return new AnnotationRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, LOC_TESTVAL, + TARGET_DOI_TESTVAL, TARGET_TYPE_TESTVAL, MOTIVATION_TESTVAL, ANNOTATION_HASH_TESTVAL); } public static AnnotationRequest givenAnnotationRequestObjectNoHash() { - return new AnnotationRequest( - ISSUED_FOR_AGENT_TESTVAL, - PID_ISSUER_TESTVAL_OTHER, - LOC_TESTVAL, - TARGET_DOI_TESTVAL, - TARGET_TYPE_TESTVAL, - MOTIVATION_TESTVAL, - null - ); + return new AnnotationRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, LOC_TESTVAL, + TARGET_DOI_TESTVAL, TARGET_TYPE_TESTVAL, MOTIVATION_TESTVAL, null); } public static MappingRequest givenMappingRequestObject() { - return new MappingRequest( - ISSUED_FOR_AGENT_TESTVAL, - PID_ISSUER_TESTVAL_OTHER, - LOC_TESTVAL, - SOURCE_DATA_STANDARD_TESTVAL - ); + return new MappingRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, LOC_TESTVAL, + SOURCE_DATA_STANDARD_TESTVAL); } public static SourceSystemRequest givenSourceSystemRequestObject() { - return new SourceSystemRequest( - ISSUED_FOR_AGENT_TESTVAL, - PID_ISSUER_TESTVAL_OTHER, - LOC_TESTVAL, - SPECIMEN_HOST_TESTVAL - ); + return new SourceSystemRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, LOC_TESTVAL, + SPECIMEN_HOST_TESTVAL); } public static OrganisationRequest givenOrganisationRequestObject() { - return new OrganisationRequest( - ISSUED_FOR_AGENT_TESTVAL, - PID_ISSUER_TESTVAL_OTHER, - LOC_TESTVAL, - REFERENT_NAME_TESTVAL, - PRIMARY_REFERENT_TYPE_TESTVAL, - SPECIMEN_HOST_TESTVAL, - PTR_TYPE_DOI - ); + return new OrganisationRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, LOC_TESTVAL, + REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, SPECIMEN_HOST_TESTVAL, PTR_TYPE_DOI); } public static MasRequest givenMasRecordRequestObject() { - return new MasRequest( - ISSUED_FOR_AGENT_TESTVAL, - PID_ISSUER_TESTVAL_OTHER, - LOC_TESTVAL, - MAS_NAME_TESTVAL - ); + return new MasRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, LOC_TESTVAL, + MAS_NAME_TESTVAL); } public static TombstoneRecordRequest genTombstoneRecordRequestObject() { - return new TombstoneRecordRequest( - TOMBSTONE_TEXT_TESTVAL - ); + return new TombstoneRecordRequest(TOMBSTONE_TEXT_TESTVAL); } - public static JsonApiWrapperRead givenRecordResponseRead(List handles, String path, - FdoType recordType) - throws Exception { + public static JsonApiWrapperRead givenRecordResponseRead(List handles, String path, + FdoType recordType) throws Exception { List dataNodes = new ArrayList<>(); - for (byte[] handle : handles) { + for (String handle : handles) { var testDbRecord = genAttributes(recordType, handle); JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); - var pidLink = new JsonApiLinks(HANDLE_URI + new String(handle, StandardCharsets.UTF_8)); - dataNodes.add(new JsonApiDataLinks(new String(handle, StandardCharsets.UTF_8), - recordType.getDigitalObjectType(), - recordAttributes, pidLink)); + var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); + dataNodes.add(new JsonApiDataLinks(handle, + recordType.getDigitalObjectType(), recordAttributes, pidLink)); } var responseLink = new JsonApiLinks(path); @@ -873,153 +719,166 @@ public static JsonApiWrapperRead givenRecordResponseRead(List handles, S public static JsonApiWrapperReadSingle givenRecordResponseReadSingle(String handle, String path, FdoType type, JsonNode attributes) { - return new JsonApiWrapperReadSingle( - new JsonApiLinks(path), + return new JsonApiWrapperReadSingle(new JsonApiLinks(path), new JsonApiDataLinks(handle, type.getDigitalObjectType(), attributes, new JsonApiLinks("https://hdl.handle.net/" + handle))); } - public static JsonApiWrapperWrite givenRecordResponseWrite(List handles, - FdoType recordType) - throws Exception { + public static JsonApiWrapperWrite givenRecordResponseWrite(List handles, + FdoType recordType) throws Exception { List dataNodes = new ArrayList<>(); - for (byte[] handle : handles) { + for (var handle : handles) { var testDbRecord = genAttributes(recordType, handle); JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); - var pidLink = new JsonApiLinks(HANDLE_URI + new String(handle, StandardCharsets.UTF_8)); - dataNodes.add(new JsonApiDataLinks(new String(handle, StandardCharsets.UTF_8), - recordType.getDigitalObjectType(), - recordAttributes, pidLink)); + var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); + dataNodes.add( + new JsonApiDataLinks(handle, recordType.getDigitalObjectType(), recordAttributes, + pidLink)); } return new JsonApiWrapperWrite(dataNodes); } - public static JsonApiWrapperWrite givenAnnotationResponseWrite(List handles) { + public static JsonApiWrapperWrite givenAnnotationResponseWrite(List handles) { List dataNodes = new ArrayList<>(); - for (byte[] handle : handles) { + for (var handle : handles) { var testDbRecord = List.of( - new HandleAttribute(ANNOTATION_HASH, handle, - ANNOTATION_HASH_TESTVAL.toString())); + new FdoAttribute(ANNOTATION_HASH, CREATED, ANNOTATION_HASH_TESTVAL.toString())); JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); - var pidLink = new JsonApiLinks(HANDLE_URI + new String(handle, StandardCharsets.UTF_8)); - dataNodes.add(new JsonApiDataLinks(new String(handle, StandardCharsets.UTF_8), - FdoType.ANNOTATION.getDigitalObjectType(), - recordAttributes, pidLink)); + var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); + dataNodes.add( + new JsonApiDataLinks(handle, FdoType.ANNOTATION.getDigitalObjectType(), recordAttributes, + pidLink)); } return new JsonApiWrapperWrite(dataNodes); } public static JsonApiWrapperWrite givenRecordResponseWriteSmallResponse( - List testDbRecord, List handles, FdoType type) { + List fdoRecords, FdoType fdoType) { List dataNodes = new ArrayList<>(); - for (var handle : handles) { - JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); - var pidLink = new JsonApiLinks(HANDLE_URI + new String(handle, StandardCharsets.UTF_8)); + List fdoSublist; + for (var fdoRecord : fdoRecords) { + switch (fdoType) { + case ANNOTATION -> { + fdoSublist = List.of(getField(fdoRecord.attributes(), ANNOTATION_HASH)); + } + case DIGITAL_SPECIMEN -> { + fdoSublist = List.of(getField(fdoRecord.attributes(), NORMALISED_SPECIMEN_OBJECT_ID)); + } + case MEDIA_OBJECT -> { + fdoSublist = List.of(getField(fdoRecord.attributes(), PRIMARY_MEDIA_ID), + getField(fdoRecord.attributes(), LINKED_DO_PID)); + } + default -> { + fdoSublist = fdoRecord.attributes(); + } + } + var recordAttributes = genObjectNodeAttributeRecord(fdoSublist); + var pidLink = new JsonApiLinks(HANDLE_DOMAIN + fdoRecord.handle()); dataNodes.add( - new JsonApiDataLinks(new String(handle, StandardCharsets.UTF_8), - type.getDigitalObjectType(), - recordAttributes, pidLink)); + new JsonApiDataLinks(fdoRecord.handle(), fdoType.getDigitalObjectType(), recordAttributes, + pidLink)); } return new JsonApiWrapperWrite(dataNodes); } + public static FdoAttribute getField(List fdoAttributes, FdoProfile targetField) { + for (var attribute : fdoAttributes) { + if (attribute.getIndex() == targetField.index()) { + return attribute; + } + } + log.error("Unable to find field {} in record {}", targetField, fdoAttributes); + throw new IllegalStateException(); + } + - public static JsonApiWrapperWrite givenRecordResponseWriteGeneric(List handles, - FdoType recordType) - throws Exception { + public static JsonApiWrapperWrite givenRecordResponseWriteGeneric(List handles, + FdoType fdoType) throws Exception { List dataNodes = new ArrayList<>(); - for (byte[] handle : handles) { - var testDbRecord = genAttributes(recordType, handle); + for (var handle : handles) { + var testDbRecord = genAttributes(fdoType, handle); JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); - var pidLink = new JsonApiLinks(HANDLE_URI + new String(handle, StandardCharsets.UTF_8)); - dataNodes.add(new JsonApiDataLinks(new String(handle, StandardCharsets.UTF_8), "PID", - recordAttributes, pidLink)); + var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); + dataNodes.add( + new JsonApiDataLinks(handle, "PID", recordAttributes, + pidLink)); } return new JsonApiWrapperWrite(dataNodes); } - public static JsonApiWrapperWrite givenRecordResponseWrite(List handles, - FdoType attributeType, String recordType) - throws Exception { + public static JsonApiWrapperWrite givenRecordResponseWrite(List handles, + FdoType attributeType, String recordType) throws Exception { List dataNodes = new ArrayList<>(); - for (byte[] handle : handles) { + for (var handle : handles) { var testDbRecord = genAttributes(attributeType, handle); JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); - var pidLink = new JsonApiLinks(HANDLE_URI + new String(handle, StandardCharsets.UTF_8)); - dataNodes.add( - new JsonApiDataLinks(new String(handle, StandardCharsets.UTF_8), recordType, - recordAttributes, pidLink)); + var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); + dataNodes.add(new JsonApiDataLinks(handle, recordType, + recordAttributes, pidLink)); } return new JsonApiWrapperWrite(dataNodes); } - public static JsonApiWrapperWrite givenRecordResponseWriteAltLoc(List handles) + public static JsonApiWrapperWrite givenRecordResponseWriteAltLoc(List handles) throws Exception { return givenRecordResponseWriteAltLoc(handles, FdoType.HANDLE); } - public static JsonApiWrapperWrite givenRecordResponseWriteAltLoc(List handles, - FdoType recordType) - throws Exception { + public static JsonApiWrapperWrite givenRecordResponseWriteAltLoc(List handles, + FdoType recordType) throws Exception { List dataNodes = new ArrayList<>(); - for (byte[] handle : handles) { + for (var handle : handles) { var testDbRecord = genUpdateRecordAttributesAltLoc(handle); JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); - var pidLink = new JsonApiLinks(HANDLE_URI + new String(handle, StandardCharsets.UTF_8)); - dataNodes.add( - new JsonApiDataLinks(new String(handle, StandardCharsets.UTF_8), - recordType.getDigitalObjectType(), - recordAttributes, pidLink)); + var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); + dataNodes.add(new JsonApiDataLinks(handle, + recordType.getDigitalObjectType(), recordAttributes, pidLink)); } return new JsonApiWrapperWrite(dataNodes); } - public static JsonApiWrapperWrite givenRecordResponseNullAttributes(List handles) { + public static JsonApiWrapperWrite givenRecordResponseNullAttributes(List handles) { return givenRecordResponseNullAttributes(handles, FdoType.HANDLE); } - public static JsonApiWrapperWrite givenRecordResponseNullAttributes(List handles, + public static JsonApiWrapperWrite givenRecordResponseNullAttributes(List handles, FdoType type) { List dataNodes = new ArrayList<>(); - for (byte[] handle : handles) { - var pidLink = new JsonApiLinks(HANDLE_URI + new String(handle, StandardCharsets.UTF_8)); - dataNodes.add( - new JsonApiDataLinks(new String(handle, StandardCharsets.UTF_8), - type.getDigitalObjectType(), null, - pidLink)); + for (var handle : handles) { + var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); + dataNodes.add(new JsonApiDataLinks(handle, + type.getDigitalObjectType(), null, pidLink)); } return new JsonApiWrapperWrite(dataNodes); } - public static JsonApiWrapperWrite givenRecordResponseWriteArchive(List handles) + public static JsonApiWrapperWrite givenRecordResponseWriteArchive(List handles) throws Exception { List dataNodes = new ArrayList<>(); - for (byte[] handle : handles) { + for (var handle : handles) { var testDbRecord = genTombstoneRecordRequestAttributes(handle); JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); - var pidLink = new JsonApiLinks(HANDLE_URI + new String(handle, StandardCharsets.UTF_8)); - dataNodes.add( - new JsonApiDataLinks(new String(handle, StandardCharsets.UTF_8), - FdoType.TOMBSTONE.getDigitalObjectType(), - recordAttributes, pidLink)); + var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); + dataNodes.add(new JsonApiDataLinks(handle, + FdoType.TOMBSTONE.getDigitalObjectType(), recordAttributes, pidLink)); } return new JsonApiWrapperWrite(dataNodes); } - public static List genAttributes(FdoType recordType, byte[] handle) + public static List genAttributes(FdoType recordType, String handle) throws Exception { switch (recordType) { case DOI -> { @@ -1053,15 +912,15 @@ public static List genAttributes(FdoType recordType, byte[] han } } - public static List genUpdateRequestBatch(List handles, FdoType type) { + public static List genUpdateRequestBatch(List handles, FdoType type) { ObjectMapper mapper = new ObjectMapper(); ObjectNode requestNodeRoot = mapper.createObjectNode(); ObjectNode requestNodeData = mapper.createObjectNode(); List requestNodeList = new ArrayList<>(); - for (byte[] handle : handles) { + for (var handle : handles) { requestNodeData.put("type", type.getDigitalObjectType()); - requestNodeData.put("id", new String(handle, StandardCharsets.UTF_8)); + requestNodeData.put("id", handle); requestNodeData.set("attributes", genUpdateRequestAltLoc()); requestNodeRoot.set("data", requestNodeData); @@ -1073,7 +932,7 @@ public static List genUpdateRequestBatch(List handles, FdoType return requestNodeList; } - public static List genUpdateRequestBatch(List handles) { + public static List genUpdateRequestBatch(List handles) { return genUpdateRequestBatch(handles, FdoType.HANDLE); } @@ -1113,13 +972,12 @@ public static JsonNode genTombstoneRequest() { } // Handle Attributes as ObjectNode - public static JsonNode genObjectNodeAttributeRecord(List dbRecord) { + public static JsonNode genObjectNodeAttributeRecord(List dbRecord) { ObjectMapper mapper = new ObjectMapper(); ObjectNode rootNode = mapper.createObjectNode(); - - for (HandleAttribute row : dbRecord) { + for (var row : dbRecord) { if (row.getIndex() != HS_ADMIN.index()) { - var rowData = new String(row.getData(), StandardCharsets.UTF_8); + var rowData = row.getValue(); try { var nodeData = mapper.readTree(rowData); rootNode.set(row.getType(), nodeData); @@ -1132,30 +990,23 @@ public static JsonNode genObjectNodeAttributeRecord(List dbReco } // Other Functions - - public static byte[] givenLandingPage(String handle) throws Exception { + public static FdoAttribute givenLandingPageAttribute(String handle) throws Exception { var landingPage = new String[]{"Placeholder landing page"}; - return setLocations(landingPage, handle, FdoType.TOMBSTONE, false); + var locations = setLocations(landingPage, handle, FdoType.TOMBSTONE, false); + return new FdoAttribute(LOC, CREATED, locations); } - public static HandleAttribute givenLandingPageAttribute(byte[] handle) throws Exception { - var data = givenLandingPage(new String(handle, StandardCharsets.UTF_8)); - return new HandleAttribute(LOC.index(), handle, LOC.get(), data); - } - - public static byte[] setLocations(String[] userLocations, String handle, FdoType type, - boolean isDoiProfileTest) - throws TransformerException, ParserConfigurationException { - DOC_BUILDER_FACTORY.setFeature("http://apache.org/xml/features/disallow-doctype-decl", - true); + public static String setLocations(String[] userLocations, String handle, FdoType type, + boolean isDoiProfileTest) throws TransformerException, ParserConfigurationException { + DOC_BUILDER_FACTORY.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); DocumentBuilder documentBuilder = DOC_BUILDER_FACTORY.newDocumentBuilder(); var doc = documentBuilder.newDocument(); var locations = doc.createElement("locations"); doc.appendChild(locations); - String[] objectLocations = isDoiProfileTest - ? userLocations : concatLocations(userLocations, handle, type); + String[] objectLocations = + isDoiProfileTest ? userLocations : concatLocations(userLocations, handle, type); for (int i = 0; i < objectLocations.length; i++) { var locs = doc.createElement("location"); @@ -1165,7 +1016,7 @@ public static byte[] setLocations(String[] userLocations, String handle, FdoType locs.setAttribute("weight", weight); locations.appendChild(locs); } - return documentToString(doc).getBytes(StandardCharsets.UTF_8); + return documentToString(doc); } private static String[] concatLocations(String[] userLocations, String handle, FdoType type) { @@ -1222,8 +1073,8 @@ private static String documentToString(Document document) throws TransformerExce } public static String loadResourceFile(String fileName) throws IOException { - return new String(new ClassPathResource(fileName).getInputStream() - .readAllBytes(), StandardCharsets.UTF_8); + return new String(new ClassPathResource(fileName).getInputStream().readAllBytes(), + StandardCharsets.UTF_8); } } From 8db01e653064eeb0a48dc0db50b2d8ec9ab6e96f Mon Sep 17 00:00:00 2001 From: southeo Date: Tue, 16 Jul 2024 14:59:30 +0200 Subject: [PATCH 05/15] test --- pom.xml | 13 - .../configuration/BatchInserterConfig.java | 25 - .../controller/PidController.java | 15 +- .../database/jooq/DefaultCatalog.java | 43 - .../handlemanager/database/jooq/Indexes.java | 28 - .../handlemanager/database/jooq/Keys.java | 28 - .../handlemanager/database/jooq/Public.java | 51 -- .../handlemanager/database/jooq/Tables.java | 20 - .../database/jooq/tables/Handles.java | 209 ----- .../jooq/tables/records/HandlesRecord.java | 518 ----------- ...ngRequest.java => DataMappingRequest.java} | 4 +- ...tRequest.java => DigitalMediaRequest.java} | 6 +- .../handlemanager/domain/fdo/FdoProfile.java | 17 +- .../handlemanager/domain/fdo/FdoType.java | 4 +- .../domain/fdo/TombstoneRecordRequest.java | 21 +- .../domain/fdo/vocabulary/PidStatus.java | 8 + .../vocabulary/tombstone/HasRelatedPid.java | 12 + .../domain/repsitoryobjects/FdoRecord.java | 6 + .../validation/JsonSchemaValidator.java | 40 +- .../repository/BatchInserter.java | 51 -- .../repository/PidMongoRepository.java | 105 --- .../repository/PidRepository.java | 308 ++----- .../handlemanager/service/DoiService.java | 95 +- .../service/FdoRecordService.java | 406 ++++----- .../handlemanager/service/HandleService.java | 105 ++- .../service/PidNameGeneratorService.java | 24 +- .../handlemanager/service/PidService.java | 397 +++----- .../handlemanager/service/ServiceUtils.java | 25 + .../controller/PidControllerTest.java | 71 +- .../handlemanager/domain/FdoProfileTest.java | 25 - .../domain/JsonSchemaValidatorTest.java | 32 +- .../repository/BaseRepositoryIT.java | 52 -- .../repository/BatchInserterIT.java | 86 -- .../repository/MongoRepositoryIT.java | 43 + .../repository/PidRepositoryIT.java | 602 ------------- .../service/DataCiteServiceTest.java | 4 +- .../handlemanager/service/DoiServiceTest.java | 170 ++-- .../service/FdoRecordServiceTest.java | 764 +++++++--------- .../service/HandleServiceTest.java | 847 ++++++++---------- .../service/PidNameGeneratorServiceTest.java | 44 +- .../handlemanager/testUtils/TestUtils.java | 725 ++++++++------- 41 files changed, 1859 insertions(+), 4190 deletions(-) delete mode 100644 src/main/java/eu/dissco/core/handlemanager/configuration/BatchInserterConfig.java delete mode 100644 src/main/java/eu/dissco/core/handlemanager/database/jooq/DefaultCatalog.java delete mode 100644 src/main/java/eu/dissco/core/handlemanager/database/jooq/Indexes.java delete mode 100644 src/main/java/eu/dissco/core/handlemanager/database/jooq/Keys.java delete mode 100644 src/main/java/eu/dissco/core/handlemanager/database/jooq/Public.java delete mode 100644 src/main/java/eu/dissco/core/handlemanager/database/jooq/Tables.java delete mode 100644 src/main/java/eu/dissco/core/handlemanager/database/jooq/tables/Handles.java delete mode 100644 src/main/java/eu/dissco/core/handlemanager/database/jooq/tables/records/HandlesRecord.java rename src/main/java/eu/dissco/core/handlemanager/domain/fdo/{MappingRequest.java => DataMappingRequest.java} (84%) rename src/main/java/eu/dissco/core/handlemanager/domain/fdo/{MediaObjectRequest.java => DigitalMediaRequest.java} (96%) create mode 100644 src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/PidStatus.java create mode 100644 src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/tombstone/HasRelatedPid.java delete mode 100644 src/main/java/eu/dissco/core/handlemanager/repository/BatchInserter.java delete mode 100644 src/main/java/eu/dissco/core/handlemanager/repository/PidMongoRepository.java create mode 100644 src/main/java/eu/dissco/core/handlemanager/service/ServiceUtils.java delete mode 100644 src/test/java/eu/dissco/core/handlemanager/repository/BaseRepositoryIT.java delete mode 100644 src/test/java/eu/dissco/core/handlemanager/repository/BatchInserterIT.java create mode 100644 src/test/java/eu/dissco/core/handlemanager/repository/MongoRepositoryIT.java delete mode 100644 src/test/java/eu/dissco/core/handlemanager/repository/PidRepositoryIT.java diff --git a/pom.xml b/pom.xml index 3361e2ea..ff4abb49 100644 --- a/pom.xml +++ b/pom.xml @@ -97,19 +97,6 @@ - - org.postgresql - postgresql - ${postgresql.version} - - - org.jooq - jooq - - - org.springframework - spring-jdbc - org.apache.commons commons-lang3 diff --git a/src/main/java/eu/dissco/core/handlemanager/configuration/BatchInserterConfig.java b/src/main/java/eu/dissco/core/handlemanager/configuration/BatchInserterConfig.java deleted file mode 100644 index 8b641eb8..00000000 --- a/src/main/java/eu/dissco/core/handlemanager/configuration/BatchInserterConfig.java +++ /dev/null @@ -1,25 +0,0 @@ -package eu.dissco.core.handlemanager.configuration; - -import java.sql.DriverManager; -import java.sql.SQLException; -import lombok.RequiredArgsConstructor; -import org.postgresql.copy.CopyManager; -import org.postgresql.core.BaseConnection; -import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -@RequiredArgsConstructor -public class BatchInserterConfig { - - private final DataSourceProperties properties; - - @Bean - public CopyManager copyManager() throws SQLException { - var connection = DriverManager.getConnection(properties.getUrl(), properties.getUsername(), - properties.getPassword()); - return new CopyManager((BaseConnection) connection); - } - -} diff --git a/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java b/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java index 074f6d11..d808562e 100644 --- a/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java +++ b/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java @@ -6,7 +6,6 @@ import com.fasterxml.jackson.databind.JsonNode; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperRead; -import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperReadSingle; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperWrite; import eu.dissco.core.handlemanager.domain.requests.RollbackRequest; import eu.dissco.core.handlemanager.domain.validation.JsonSchemaValidator; @@ -18,7 +17,6 @@ import io.swagger.v3.oas.annotations.Operation; import jakarta.servlet.http.HttpServletRequest; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import lombok.RequiredArgsConstructor; @@ -54,13 +52,13 @@ public class PidController { // Getters @Operation(summary = "Resolve single PID record") @GetMapping("/{prefix}/{suffix}") - public ResponseEntity resolvePid(@PathVariable("prefix") String prefix, + public ResponseEntity resolvePid(@PathVariable("prefix") String prefix, @PathVariable("suffix") String suffix, HttpServletRequest r) throws PidResolutionException { String path = applicationProperties.getUiUrl() + r.getRequestURI(); String handle = prefix + "/" + suffix; if (prefix.equals(applicationProperties.getPrefix())) { - var node = service.resolveSingleRecord(handle.getBytes(StandardCharsets.UTF_8), path); + var node = service.resolveSingleRecord(handle, path); return ResponseEntity.status(HttpStatus.OK).body(node); } throw new PidResolutionException( @@ -80,10 +78,8 @@ public ResponseEntity resolvePids( "Attempting to resolve more than maximum permitted PIDs in a single request. Maximum handles: " + applicationProperties.getMaxHandles()); } - List handleBytes = new ArrayList<>(); - handles.forEach(h -> handleBytes.add(h.getBytes(StandardCharsets.UTF_8))); - return ResponseEntity.status(HttpStatus.OK).body(service.resolveBatchRecord(handleBytes, path)); + return ResponseEntity.status(HttpStatus.OK).body(service.resolveBatchRecord(handles, path)); } @Operation(summary = "Given a physical identifier (i.e. local identifier), resolve PID record") @@ -171,7 +167,8 @@ public ResponseEntity archiveRecord(@PathVariable("prefix") + new String(handle, StandardCharsets.UTF_8) + ". Body: " + new String(handleRequest, StandardCharsets.UTF_8)); } - return ResponseEntity.status(HttpStatus.OK).body(service.archiveRecordBatch(List.of(request))); + return ResponseEntity.status(HttpStatus.OK) + .body(service.tombstoneRecordBatch(List.of(request))); } @Operation(summary = "rollback handle creation") @@ -227,7 +224,7 @@ public ResponseEntity archiveRecords(@RequestBody ListDEFAULT_CATALOG - */ - public static final DefaultCatalog DEFAULT_CATALOG = new DefaultCatalog(); - - /** - * The schema public. - */ - public final Public PUBLIC = Public.PUBLIC; - - /** - * No further instances allowed - */ - private DefaultCatalog() { - super(""); - } - - @Override - public final List getSchemas() { - return Arrays.asList( - Public.PUBLIC); - } -} diff --git a/src/main/java/eu/dissco/core/handlemanager/database/jooq/Indexes.java b/src/main/java/eu/dissco/core/handlemanager/database/jooq/Indexes.java deleted file mode 100644 index 9bcf7052..00000000 --- a/src/main/java/eu/dissco/core/handlemanager/database/jooq/Indexes.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package eu.dissco.core.handlemanager.database.jooq; - - -import eu.dissco.core.handlemanager.database.jooq.tables.Handles; -import org.jooq.Index; -import org.jooq.OrderField; -import org.jooq.impl.DSL; -import org.jooq.impl.Internal; - - -/** - * A class modelling indexes of tables in public. - */ -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class Indexes { - - // ------------------------------------------------------------------------- - // INDEX definitions - // ------------------------------------------------------------------------- - - public static final Index DATAINDEX = Internal.createIndex(DSL.name("dataindex"), Handles.HANDLES, - new OrderField[]{Handles.HANDLES.DATA}, false); - public static final Index HANDLEINDEX = Internal.createIndex(DSL.name("handleindex"), - Handles.HANDLES, new OrderField[]{Handles.HANDLES.HANDLE}, false); -} diff --git a/src/main/java/eu/dissco/core/handlemanager/database/jooq/Keys.java b/src/main/java/eu/dissco/core/handlemanager/database/jooq/Keys.java deleted file mode 100644 index ad4139e1..00000000 --- a/src/main/java/eu/dissco/core/handlemanager/database/jooq/Keys.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package eu.dissco.core.handlemanager.database.jooq; - - -import eu.dissco.core.handlemanager.database.jooq.tables.Handles; -import eu.dissco.core.handlemanager.database.jooq.tables.records.HandlesRecord; -import org.jooq.TableField; -import org.jooq.UniqueKey; -import org.jooq.impl.DSL; -import org.jooq.impl.Internal; - - -/** - * A class modelling foreign key relationships and constraints of tables in public. - */ -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class Keys { - - // ------------------------------------------------------------------------- - // UNIQUE and PRIMARY KEY definitions - // ------------------------------------------------------------------------- - - public static final UniqueKey HANDLES_PKEY = Internal.createUniqueKey( - Handles.HANDLES, DSL.name("handles_pkey"), - new TableField[]{Handles.HANDLES.HANDLE, Handles.HANDLES.IDX}, true); -} diff --git a/src/main/java/eu/dissco/core/handlemanager/database/jooq/Public.java b/src/main/java/eu/dissco/core/handlemanager/database/jooq/Public.java deleted file mode 100644 index fe5ae76b..00000000 --- a/src/main/java/eu/dissco/core/handlemanager/database/jooq/Public.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package eu.dissco.core.handlemanager.database.jooq; - - -import eu.dissco.core.handlemanager.database.jooq.tables.Handles; -import java.util.Arrays; -import java.util.List; -import org.jooq.Catalog; -import org.jooq.Table; -import org.jooq.impl.SchemaImpl; - - -/** - * This class is generated by jOOQ. - */ -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class Public extends SchemaImpl { - - private static final long serialVersionUID = 1L; - - /** - * The reference instance of public - */ - public static final Public PUBLIC = new Public(); - - /** - * The table public.handles. - */ - public final Handles HANDLES = Handles.HANDLES; - - /** - * No further instances allowed - */ - private Public() { - super("public", null); - } - - - @Override - public Catalog getCatalog() { - return DefaultCatalog.DEFAULT_CATALOG; - } - - @Override - public final List> getTables() { - return Arrays.>asList( - Handles.HANDLES); - } -} diff --git a/src/main/java/eu/dissco/core/handlemanager/database/jooq/Tables.java b/src/main/java/eu/dissco/core/handlemanager/database/jooq/Tables.java deleted file mode 100644 index 488e0242..00000000 --- a/src/main/java/eu/dissco/core/handlemanager/database/jooq/Tables.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package eu.dissco.core.handlemanager.database.jooq; - - -import eu.dissco.core.handlemanager.database.jooq.tables.Handles; - - -/** - * Convenience access to all tables in public. - */ -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class Tables { - - /** - * The table public.handles. - */ - public static final Handles HANDLES = Handles.HANDLES; -} diff --git a/src/main/java/eu/dissco/core/handlemanager/database/jooq/tables/Handles.java b/src/main/java/eu/dissco/core/handlemanager/database/jooq/tables/Handles.java deleted file mode 100644 index 7772dabf..00000000 --- a/src/main/java/eu/dissco/core/handlemanager/database/jooq/tables/Handles.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package eu.dissco.core.handlemanager.database.jooq.tables; - - -import eu.dissco.core.handlemanager.database.jooq.Indexes; -import eu.dissco.core.handlemanager.database.jooq.Keys; -import eu.dissco.core.handlemanager.database.jooq.Public; -import eu.dissco.core.handlemanager.database.jooq.tables.records.HandlesRecord; -import java.util.Arrays; -import java.util.List; -import org.jooq.Field; -import org.jooq.ForeignKey; -import org.jooq.Index; -import org.jooq.Name; -import org.jooq.Record; -import org.jooq.Row12; -import org.jooq.Schema; -import org.jooq.Table; -import org.jooq.TableField; -import org.jooq.TableOptions; -import org.jooq.UniqueKey; -import org.jooq.impl.DSL; -import org.jooq.impl.SQLDataType; -import org.jooq.impl.TableImpl; - - -/** - * This class is generated by jOOQ. - */ -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class Handles extends TableImpl { - - private static final long serialVersionUID = 1L; - - /** - * The reference instance of public.handles - */ - public static final Handles HANDLES = new Handles(); - - /** - * The class holding records for this type - */ - @Override - public Class getRecordType() { - return HandlesRecord.class; - } - - /** - * The column public.handles.handle. - */ - public final TableField HANDLE = createField(DSL.name("handle"), - SQLDataType.BLOB.nullable(false), this, ""); - - /** - * The column public.handles.idx. - */ - public final TableField IDX = createField(DSL.name("idx"), - SQLDataType.INTEGER.nullable(false), this, ""); - - /** - * The column public.handles.type. - */ - public final TableField TYPE = createField(DSL.name("type"), - SQLDataType.BLOB, this, ""); - - /** - * The column public.handles.data. - */ - public final TableField DATA = createField(DSL.name("data"), - SQLDataType.BLOB, this, ""); - - /** - * The column public.handles.ttl_type. - */ - public final TableField TTL_TYPE = createField(DSL.name("ttl_type"), - SQLDataType.SMALLINT, this, ""); - - /** - * The column public.handles.ttl. - */ - public final TableField TTL = createField(DSL.name("ttl"), - SQLDataType.INTEGER, this, ""); - - /** - * The column public.handles.timestamp. - */ - public final TableField TIMESTAMP = createField(DSL.name("timestamp"), - SQLDataType.BIGINT, this, ""); - - /** - * The column public.handles.refs. - */ - public final TableField REFS = createField(DSL.name("refs"), - SQLDataType.CLOB, this, ""); - - /** - * The column public.handles.admin_read. - */ - public final TableField ADMIN_READ = createField(DSL.name("admin_read"), - SQLDataType.BOOLEAN, this, ""); - - /** - * The column public.handles.admin_write. - */ - public final TableField ADMIN_WRITE = createField(DSL.name("admin_write"), - SQLDataType.BOOLEAN, this, ""); - - /** - * The column public.handles.pub_read. - */ - public final TableField PUB_READ = createField(DSL.name("pub_read"), - SQLDataType.BOOLEAN, this, ""); - - /** - * The column public.handles.pub_write. - */ - public final TableField PUB_WRITE = createField(DSL.name("pub_write"), - SQLDataType.BOOLEAN, this, ""); - - private Handles(Name alias, Table aliased) { - this(alias, aliased, null); - } - - private Handles(Name alias, Table aliased, Field[] parameters) { - super(alias, null, aliased, parameters, DSL.comment(""), TableOptions.table()); - } - - /** - * Create an aliased public.handles table reference - */ - public Handles(String alias) { - this(DSL.name(alias), HANDLES); - } - - /** - * Create an aliased public.handles table reference - */ - public Handles(Name alias) { - this(alias, HANDLES); - } - - /** - * Create a public.handles table reference - */ - public Handles() { - this(DSL.name("handles"), null); - } - - public Handles(Table child, ForeignKey key) { - super(child, key, HANDLES); - } - - @Override - public Schema getSchema() { - return Public.PUBLIC; - } - - @Override - public List getIndexes() { - return Arrays.asList(Indexes.DATAINDEX, Indexes.HANDLEINDEX); - } - - @Override - public UniqueKey getPrimaryKey() { - return Keys.HANDLES_PKEY; - } - - @Override - public List> getKeys() { - return Arrays.>asList(Keys.HANDLES_PKEY); - } - - @Override - public Handles as(String alias) { - return new Handles(DSL.name(alias), this); - } - - @Override - public Handles as(Name alias) { - return new Handles(alias, this); - } - - /** - * Rename this table - */ - @Override - public Handles rename(String name) { - return new Handles(DSL.name(name), null); - } - - /** - * Rename this table - */ - @Override - public Handles rename(Name name) { - return new Handles(name, null); - } - - // ------------------------------------------------------------------------- - // Row12 type methods - // ------------------------------------------------------------------------- - - @Override - public Row12 fieldsRow() { - return (Row12) super.fieldsRow(); - } -} diff --git a/src/main/java/eu/dissco/core/handlemanager/database/jooq/tables/records/HandlesRecord.java b/src/main/java/eu/dissco/core/handlemanager/database/jooq/tables/records/HandlesRecord.java deleted file mode 100644 index d0a1aea8..00000000 --- a/src/main/java/eu/dissco/core/handlemanager/database/jooq/tables/records/HandlesRecord.java +++ /dev/null @@ -1,518 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package eu.dissco.core.handlemanager.database.jooq.tables.records; - - -import eu.dissco.core.handlemanager.database.jooq.tables.Handles; -import org.jooq.Field; -import org.jooq.Record12; -import org.jooq.Record2; -import org.jooq.Row12; -import org.jooq.impl.UpdatableRecordImpl; - - -/** - * This class is generated by jOOQ. - */ -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class HandlesRecord extends UpdatableRecordImpl implements - Record12 { - - private static final long serialVersionUID = 1L; - - /** - * Setter for public.handles.handle. - */ - public void setHandle(byte[] value) { - set(0, value); - } - - /** - * Getter for public.handles.handle. - */ - public byte[] getHandle() { - return (byte[]) get(0); - } - - /** - * Setter for public.handles.idx. - */ - public void setIdx(Integer value) { - set(1, value); - } - - /** - * Getter for public.handles.idx. - */ - public Integer getIdx() { - return (Integer) get(1); - } - - /** - * Setter for public.handles.type. - */ - public void setType(byte[] value) { - set(2, value); - } - - /** - * Getter for public.handles.type. - */ - public byte[] getType() { - return (byte[]) get(2); - } - - /** - * Setter for public.handles.data. - */ - public void setData(byte[] value) { - set(3, value); - } - - /** - * Getter for public.handles.data. - */ - public byte[] getData() { - return (byte[]) get(3); - } - - /** - * Setter for public.handles.ttl_type. - */ - public void setTtlType(Short value) { - set(4, value); - } - - /** - * Getter for public.handles.ttl_type. - */ - public Short getTtlType() { - return (Short) get(4); - } - - /** - * Setter for public.handles.ttl. - */ - public void setTtl(Integer value) { - set(5, value); - } - - /** - * Getter for public.handles.ttl. - */ - public Integer getTtl() { - return (Integer) get(5); - } - - /** - * Setter for public.handles.timestamp. - */ - public void setTimestamp(Long value) { - set(6, value); - } - - /** - * Getter for public.handles.timestamp. - */ - public Long getTimestamp() { - return (Long) get(6); - } - - /** - * Setter for public.handles.refs. - */ - public void setRefs(String value) { - set(7, value); - } - - /** - * Getter for public.handles.refs. - */ - public String getRefs() { - return (String) get(7); - } - - /** - * Setter for public.handles.admin_read. - */ - public void setAdminRead(Boolean value) { - set(8, value); - } - - /** - * Getter for public.handles.admin_read. - */ - public Boolean getAdminRead() { - return (Boolean) get(8); - } - - /** - * Setter for public.handles.admin_write. - */ - public void setAdminWrite(Boolean value) { - set(9, value); - } - - /** - * Getter for public.handles.admin_write. - */ - public Boolean getAdminWrite() { - return (Boolean) get(9); - } - - /** - * Setter for public.handles.pub_read. - */ - public void setPubRead(Boolean value) { - set(10, value); - } - - /** - * Getter for public.handles.pub_read. - */ - public Boolean getPubRead() { - return (Boolean) get(10); - } - - /** - * Setter for public.handles.pub_write. - */ - public void setPubWrite(Boolean value) { - set(11, value); - } - - /** - * Getter for public.handles.pub_write. - */ - public Boolean getPubWrite() { - return (Boolean) get(11); - } - - // ------------------------------------------------------------------------- - // Primary key information - // ------------------------------------------------------------------------- - - @Override - public Record2 key() { - return (Record2) super.key(); - } - - // ------------------------------------------------------------------------- - // Record12 type implementation - // ------------------------------------------------------------------------- - - @Override - public Row12 fieldsRow() { - return (Row12) super.fieldsRow(); - } - - @Override - public Row12 valuesRow() { - return (Row12) super.valuesRow(); - } - - @Override - public Field field1() { - return Handles.HANDLES.HANDLE; - } - - @Override - public Field field2() { - return Handles.HANDLES.IDX; - } - - @Override - public Field field3() { - return Handles.HANDLES.TYPE; - } - - @Override - public Field field4() { - return Handles.HANDLES.DATA; - } - - @Override - public Field field5() { - return Handles.HANDLES.TTL_TYPE; - } - - @Override - public Field field6() { - return Handles.HANDLES.TTL; - } - - @Override - public Field field7() { - return Handles.HANDLES.TIMESTAMP; - } - - @Override - public Field field8() { - return Handles.HANDLES.REFS; - } - - @Override - public Field field9() { - return Handles.HANDLES.ADMIN_READ; - } - - @Override - public Field field10() { - return Handles.HANDLES.ADMIN_WRITE; - } - - @Override - public Field field11() { - return Handles.HANDLES.PUB_READ; - } - - @Override - public Field field12() { - return Handles.HANDLES.PUB_WRITE; - } - - @Override - public byte[] component1() { - return getHandle(); - } - - @Override - public Integer component2() { - return getIdx(); - } - - @Override - public byte[] component3() { - return getType(); - } - - @Override - public byte[] component4() { - return getData(); - } - - @Override - public Short component5() { - return getTtlType(); - } - - @Override - public Integer component6() { - return getTtl(); - } - - @Override - public Long component7() { - return getTimestamp(); - } - - @Override - public String component8() { - return getRefs(); - } - - @Override - public Boolean component9() { - return getAdminRead(); - } - - @Override - public Boolean component10() { - return getAdminWrite(); - } - - @Override - public Boolean component11() { - return getPubRead(); - } - - @Override - public Boolean component12() { - return getPubWrite(); - } - - @Override - public byte[] value1() { - return getHandle(); - } - - @Override - public Integer value2() { - return getIdx(); - } - - @Override - public byte[] value3() { - return getType(); - } - - @Override - public byte[] value4() { - return getData(); - } - - @Override - public Short value5() { - return getTtlType(); - } - - @Override - public Integer value6() { - return getTtl(); - } - - @Override - public Long value7() { - return getTimestamp(); - } - - @Override - public String value8() { - return getRefs(); - } - - @Override - public Boolean value9() { - return getAdminRead(); - } - - @Override - public Boolean value10() { - return getAdminWrite(); - } - - @Override - public Boolean value11() { - return getPubRead(); - } - - @Override - public Boolean value12() { - return getPubWrite(); - } - - @Override - public HandlesRecord value1(byte[] value) { - setHandle(value); - return this; - } - - @Override - public HandlesRecord value2(Integer value) { - setIdx(value); - return this; - } - - @Override - public HandlesRecord value3(byte[] value) { - setType(value); - return this; - } - - @Override - public HandlesRecord value4(byte[] value) { - setData(value); - return this; - } - - @Override - public HandlesRecord value5(Short value) { - setTtlType(value); - return this; - } - - @Override - public HandlesRecord value6(Integer value) { - setTtl(value); - return this; - } - - @Override - public HandlesRecord value7(Long value) { - setTimestamp(value); - return this; - } - - @Override - public HandlesRecord value8(String value) { - setRefs(value); - return this; - } - - @Override - public HandlesRecord value9(Boolean value) { - setAdminRead(value); - return this; - } - - @Override - public HandlesRecord value10(Boolean value) { - setAdminWrite(value); - return this; - } - - @Override - public HandlesRecord value11(Boolean value) { - setPubRead(value); - return this; - } - - @Override - public HandlesRecord value12(Boolean value) { - setPubWrite(value); - return this; - } - - @Override - public HandlesRecord values(byte[] value1, Integer value2, byte[] value3, byte[] value4, - Short value5, Integer value6, Long value7, String value8, Boolean value9, Boolean value10, - Boolean value11, Boolean value12) { - value1(value1); - value2(value2); - value3(value3); - value4(value4); - value5(value5); - value6(value6); - value7(value7); - value8(value8); - value9(value9); - value10(value10); - value11(value11); - value12(value12); - return this; - } - - // ------------------------------------------------------------------------- - // Constructors - // ------------------------------------------------------------------------- - - /** - * Create a detached HandlesRecord - */ - public HandlesRecord() { - super(Handles.HANDLES); - } - - /** - * Create a detached, initialised HandlesRecord - */ - public HandlesRecord(byte[] handle, Integer idx, byte[] type, byte[] data, Short ttlType, - Integer ttl, Long timestamp, String refs, Boolean adminRead, Boolean adminWrite, - Boolean pubRead, Boolean pubWrite) { - super(Handles.HANDLES); - - setHandle(handle); - setIdx(idx); - setType(type); - setData(data); - setTtlType(ttlType); - setTtl(ttl); - setTimestamp(timestamp); - setRefs(refs); - setAdminRead(adminRead); - setAdminWrite(adminWrite); - setPubRead(pubRead); - setPubWrite(pubWrite); - } -} diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/MappingRequest.java b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/DataMappingRequest.java similarity index 84% rename from src/main/java/eu/dissco/core/handlemanager/domain/fdo/MappingRequest.java rename to src/main/java/eu/dissco/core/handlemanager/domain/fdo/DataMappingRequest.java index 8dfdbc1d..3c6b6808 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/MappingRequest.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/DataMappingRequest.java @@ -9,12 +9,12 @@ @Getter @ToString @EqualsAndHashCode(callSuper = true) -public class MappingRequest extends HandleRecordRequest { +public class DataMappingRequest extends HandleRecordRequest { @JsonProperty(required = true) private final String sourceDataStandard; - public MappingRequest(String issuedForAgent, + public DataMappingRequest(String issuedForAgent, String pidIssuer, String[] locations, String sourceDataStandard) { super(issuedForAgent, pidIssuer, StructuralType.DIGITAL, locations); diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/MediaObjectRequest.java b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/DigitalMediaRequest.java similarity index 96% rename from src/main/java/eu/dissco/core/handlemanager/domain/fdo/MediaObjectRequest.java rename to src/main/java/eu/dissco/core/handlemanager/domain/fdo/DigitalMediaRequest.java index e0fe14c8..30dacf94 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/MediaObjectRequest.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/DigitalMediaRequest.java @@ -15,7 +15,7 @@ @Getter @ToString @EqualsAndHashCode(callSuper = true) -public class MediaObjectRequest extends DoiRecordRequest { +public class DigitalMediaRequest extends DoiRecordRequest { @JsonProperty(required = true) private final String mediaHost; @@ -60,7 +60,7 @@ public class MediaObjectRequest extends DoiRecordRequest { @JsonProperty(value = "dcterms:conformsTo") private final String dctermsConformsTo; - public MediaObjectRequest( + public DigitalMediaRequest( String issuedForAgent, String pidIssuer, String[] locations, @@ -90,7 +90,7 @@ public MediaObjectRequest( ) throws InvalidRequestException { super(issuedForAgent, pidIssuer, StructuralType.DIGITAL, locations, - referentName, FdoType.MEDIA_OBJECT.getDigitalObjectName(), primaryReferentType); + referentName, FdoType.DIGITAL_MEDIA.getDigitalObjectName(), primaryReferentType); this.mediaHost = mediaHost; this.mediaHostName = mediaHostName; this.dctermsFormat = dctermsFormat; diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoProfile.java b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoProfile.java index cd4e0aed..f389c9da 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoProfile.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoProfile.java @@ -1,6 +1,5 @@ package eu.dissco.core.handlemanager.domain.fdo; -import java.util.Arrays; import lombok.extern.slf4j.Slf4j; @Slf4j @@ -21,8 +20,9 @@ public enum FdoProfile { PID_STATUS("pidStatus", 13), // Tombstone - TOMBSTONE_TEXT("tombstoneText", 30), - TOMBSTONE_PIDS("tombstonePids", 31), + TOMBSTONED_TEXT("ods:tombstonedText", 30), + HAS_RELATED_PID("hasRelatedPID", 31), + TOMBSTONED_DATE("tombstonedDate", 32), // DOI REFERENT_TYPE("referentType", 40), @@ -111,15 +111,4 @@ public int index() { return this.index; } - public static int retrieveIndex(String searchAttribute) { - var fdoProfile = Arrays.stream(FdoProfile.values()) - .filter(fdoRow -> fdoRow.attribute.equals(searchAttribute)) - .findFirst(); - if (fdoProfile.isPresent()) { - return fdoProfile.get().index; - } - log.error("Unable to locate index for requested attribute {}", searchAttribute); - throw new IllegalStateException(searchAttribute + " not valid fdo attribute"); - } - } diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoType.java b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoType.java index 5273fd4e..d821850a 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoType.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/FdoType.java @@ -25,7 +25,7 @@ public enum FdoType { "https://hdl.handle.net/21.T11148/894b1e6cad57e921764e", "https://hdl.handle.net/21.T11148/894b1e6cad57e921764e", DOI_DOMAIN), - @JsonProperty("https://hdl.handle.net/21.T11148/bbad8c4e101e8af01115") MEDIA_OBJECT( + @JsonProperty("https://hdl.handle.net/21.T11148/bbad8c4e101e8af01115") DIGITAL_MEDIA( "MediaObject", "https://hdl.handle.net/21.T11148/bbad8c4e101e8af01115", "https://hdl.handle.net/21.T11148/bbad8c4e101e8af01115", @@ -40,7 +40,7 @@ public enum FdoType { "https://hdl.handle.net/21.T11148/417a4f472f60f7974c12", "https://hdl.handle.net/21.T11148/417a4f472f60f7974c12", HANDLE_DOMAIN), - @JsonProperty("https://hdl.handle.net/21.T11148/ce794a6f4df42eb7e77e") MAPPING( + @JsonProperty("https://hdl.handle.net/21.T11148/ce794a6f4df42eb7e77e") DATA_MAPPING( "Mapping", "https://hdl.handle.net/21.T11148/ce794a6f4df42eb7e77e", "https://hdl.handle.net/21.T11148/ce794a6f4df42eb7e77e", diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/TombstoneRecordRequest.java b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/TombstoneRecordRequest.java index 5d23044f..6340d9e4 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/TombstoneRecordRequest.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/TombstoneRecordRequest.java @@ -1,6 +1,8 @@ package eu.dissco.core.handlemanager.domain.fdo; import com.fasterxml.jackson.annotation.JsonProperty; +import eu.dissco.core.handlemanager.domain.fdo.vocabulary.tombstone.HasRelatedPid; +import java.util.List; import lombok.Getter; import lombok.ToString; import org.springframework.lang.Nullable; @@ -9,20 +11,15 @@ @ToString public class TombstoneRecordRequest { - @JsonProperty(required = true) - private final String tombstoneText; - + @JsonProperty(required = true, value = "ods:tombstonedText") + private final String tombstonedText; @Nullable - private final String[] tombstonePids; - - public TombstoneRecordRequest(String tombstoneText) { - this.tombstoneText = tombstoneText; - this.tombstonePids = new String[]{}; - } + @JsonProperty("ods:hasRelatedPID") + private final List hasRelatedPID; - public TombstoneRecordRequest(String tombstoneText, String[] tombstonePids) { - this.tombstoneText = tombstoneText; - this.tombstonePids = tombstonePids; + public TombstoneRecordRequest(String tombstoneText, List hasRelatedPID) { + this.tombstonedText = tombstoneText; + this.hasRelatedPID = hasRelatedPID; } } diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/PidStatus.java b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/PidStatus.java new file mode 100644 index 00000000..37e16293 --- /dev/null +++ b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/PidStatus.java @@ -0,0 +1,8 @@ +package eu.dissco.core.handlemanager.domain.fdo.vocabulary; + +public enum PidStatus { + TOMBSTONED, + ACTIVE, + DRAFT, + TEST +} diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/tombstone/HasRelatedPid.java b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/tombstone/HasRelatedPid.java new file mode 100644 index 00000000..9018d6f4 --- /dev/null +++ b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/tombstone/HasRelatedPid.java @@ -0,0 +1,12 @@ +package eu.dissco.core.handlemanager.domain.fdo.vocabulary.tombstone; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record HasRelatedPid( + @JsonProperty("ods:ID") + String odsId, + @JsonProperty("ods:relationshipType") + String odsRelationshipType +) { + +} diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoRecord.java b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoRecord.java index a902d640..c6b5a55f 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoRecord.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoRecord.java @@ -1,12 +1,18 @@ package eu.dissco.core.handlemanager.domain.repsitoryobjects; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import eu.dissco.core.handlemanager.domain.fdo.FdoType; import java.util.List; public record FdoRecord( + @JsonProperty("_id") String handle, + @JsonIgnore FdoType fdoType, + @JsonProperty("values") List attributes, + @JsonIgnore String primaryLocalId) { } diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/validation/JsonSchemaValidator.java b/src/main/java/eu/dissco/core/handlemanager/domain/validation/JsonSchemaValidator.java index 7cd4f38e..a04c3fc7 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/validation/JsonSchemaValidator.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/validation/JsonSchemaValidator.java @@ -17,20 +17,20 @@ import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.SpecVersion.VersionFlag; import com.networknt.schema.ValidationMessage; -import eu.dissco.core.handlemanager.domain.fdo.FdoType; -import eu.dissco.core.handlemanager.domain.requests.PatchRequest; -import eu.dissco.core.handlemanager.domain.requests.PostRequest; -import eu.dissco.core.handlemanager.domain.requests.PutRequest; import eu.dissco.core.handlemanager.domain.fdo.AnnotationRequest; +import eu.dissco.core.handlemanager.domain.fdo.DataMappingRequest; +import eu.dissco.core.handlemanager.domain.fdo.DigitalMediaRequest; import eu.dissco.core.handlemanager.domain.fdo.DigitalSpecimenRequest; import eu.dissco.core.handlemanager.domain.fdo.DoiRecordRequest; +import eu.dissco.core.handlemanager.domain.fdo.FdoType; import eu.dissco.core.handlemanager.domain.fdo.HandleRecordRequest; -import eu.dissco.core.handlemanager.domain.fdo.MappingRequest; import eu.dissco.core.handlemanager.domain.fdo.MasRequest; -import eu.dissco.core.handlemanager.domain.fdo.MediaObjectRequest; import eu.dissco.core.handlemanager.domain.fdo.OrganisationRequest; import eu.dissco.core.handlemanager.domain.fdo.SourceSystemRequest; import eu.dissco.core.handlemanager.domain.fdo.TombstoneRecordRequest; +import eu.dissco.core.handlemanager.domain.requests.PatchRequest; +import eu.dissco.core.handlemanager.domain.requests.PostRequest; +import eu.dissco.core.handlemanager.domain.requests.PutRequest; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import jakarta.validation.constraints.NotEmpty; import java.util.Arrays; @@ -55,8 +55,8 @@ public class JsonSchemaValidator { private JsonNode doiPatchReqJsonNode; private JsonNode digitalSpecimenPostReqJsonNode; private JsonNode digitalSpecimenPatchReqJsonNode; - private JsonNode mediaObjectPostReqJsonNode; - private JsonNode mediaObjectPatchReqJsonNode; + private JsonNode digitalMediaPostReqJsonNode; + private JsonNode digitalMediaPatchReqJsonNode; private JsonNode tombstoneReqJsonNode; private JsonNode annotationPostReqJsonNode; private JsonNode annotationPatchReqJsonNode; @@ -79,8 +79,8 @@ public class JsonSchemaValidator { private JsonSchema doiPatchReqSchema; private JsonSchema digitalSpecimenPostReqSchema; private JsonSchema digitalSpecimenPatchReqSchema; - private JsonSchema mediaObjectPostReqSchema; - private JsonSchema mediaObjectPatchReqSchema; + private JsonSchema digitalMediaPostReqSchema; + private JsonSchema digitalMediaPatchReqSchema; private JsonSchema tombstoneReqSchema; private JsonSchema annotationPostReqSchema; private JsonSchema annotationPatchReqSchema; @@ -105,10 +105,10 @@ private void setPostRequestAttributesJsonNodes() { handlePostReqJsonNode = schemaGenerator.generateSchema(HandleRecordRequest.class); doiPostReqJsonNode = schemaGenerator.generateSchema(DoiRecordRequest.class); digitalSpecimenPostReqJsonNode = schemaGenerator.generateSchema(DigitalSpecimenRequest.class); - mediaObjectPostReqJsonNode = schemaGenerator.generateSchema(MediaObjectRequest.class); + digitalMediaPostReqJsonNode = schemaGenerator.generateSchema(DigitalMediaRequest.class); tombstoneReqJsonNode = schemaGenerator.generateSchema(TombstoneRecordRequest.class); annotationPostReqJsonNode = schemaGenerator.generateSchema(AnnotationRequest.class); - mappingPostReqJsonNode = schemaGenerator.generateSchema(MappingRequest.class); + mappingPostReqJsonNode = schemaGenerator.generateSchema(DataMappingRequest.class); sourceSystemPostReqJsonNode = schemaGenerator.generateSchema(SourceSystemRequest.class); organisationPostReqJsonNode = schemaGenerator.generateSchema(OrganisationRequest.class); masPostReqJsonNode = schemaGenerator.generateSchema(MasRequest.class); @@ -156,9 +156,9 @@ private void setPatchRequestAttributesJsonNodes() { handlePatchReqJsonNode = schemaGenerator.generateSchema(HandleRecordRequest.class); doiPatchReqJsonNode = schemaGenerator.generateSchema(DoiRecordRequest.class); digitalSpecimenPatchReqJsonNode = schemaGenerator.generateSchema(DigitalSpecimenRequest.class); - mediaObjectPatchReqJsonNode = schemaGenerator.generateSchema(MediaObjectRequest.class); + digitalMediaPatchReqJsonNode = schemaGenerator.generateSchema(DigitalMediaRequest.class); annotationPatchReqJsonNode = schemaGenerator.generateSchema(AnnotationRequest.class); - mappingPatchReqJsonNode = schemaGenerator.generateSchema(MappingRequest.class); + mappingPatchReqJsonNode = schemaGenerator.generateSchema(DataMappingRequest.class); sourceSystemPatchReqJsonNode = schemaGenerator.generateSchema(SourceSystemRequest.class); organisationPatchReqJsonNode = schemaGenerator.generateSchema(OrganisationRequest.class); masPatchReqJsonNode = schemaGenerator.generateSchema(MasRequest.class); @@ -232,7 +232,7 @@ private void setJsonSchemas() { handlePostReqSchema = factory.getSchema(handlePostReqJsonNode); doiPostReqSchema = factory.getSchema(doiPostReqJsonNode); digitalSpecimenPostReqSchema = factory.getSchema(digitalSpecimenPostReqJsonNode); - mediaObjectPostReqSchema = factory.getSchema(mediaObjectPostReqJsonNode); + digitalMediaPostReqSchema = factory.getSchema(digitalMediaPostReqJsonNode); annotationPostReqSchema = factory.getSchema(annotationPostReqJsonNode); mappingPostReqSchema = factory.getSchema(mappingPostReqJsonNode); sourceSystemPostReqSchema = factory.getSchema(sourceSystemPostReqJsonNode); @@ -242,7 +242,7 @@ private void setJsonSchemas() { handlePatchReqSchema = factory.getSchema(handlePatchReqJsonNode); doiPatchReqSchema = factory.getSchema(doiPatchReqJsonNode); digitalSpecimenPatchReqSchema = factory.getSchema(digitalSpecimenPatchReqJsonNode); - mediaObjectPatchReqSchema = factory.getSchema(mediaObjectPatchReqJsonNode); + digitalMediaPatchReqSchema = factory.getSchema(digitalMediaPatchReqJsonNode); annotationPatchReqSchema = factory.getSchema(annotationPatchReqJsonNode); mappingPatchReqSchema = factory.getSchema(mappingPatchReqJsonNode); sourceSystemPatchReqSchema = factory.getSchema(sourceSystemPatchReqJsonNode); @@ -266,9 +266,9 @@ public void validatePostRequest(JsonNode requestRoot) throws InvalidRequestExcep case DOI -> validateRequestAttributes(attributes, doiPostReqSchema, type); case DIGITAL_SPECIMEN -> validateRequestAttributes(attributes, digitalSpecimenPostReqSchema, type); - case MEDIA_OBJECT -> validateRequestAttributes(attributes, mediaObjectPostReqSchema, type); + case DIGITAL_MEDIA -> validateRequestAttributes(attributes, digitalMediaPostReqSchema, type); case ANNOTATION -> validateRequestAttributes(attributes, annotationPostReqSchema, type); - case MAPPING -> validateRequestAttributes(attributes, mappingPostReqSchema, type); + case DATA_MAPPING -> validateRequestAttributes(attributes, mappingPostReqSchema, type); case SOURCE_SYSTEM -> validateRequestAttributes(attributes, sourceSystemPostReqSchema, type); case ORGANISATION -> validateRequestAttributes(attributes, organisationPostReqSchema, type); case MAS -> validateRequestAttributes(attributes, masPostReqSchema, type); @@ -290,9 +290,9 @@ public void validatePatchRequest(JsonNode requestRoot) throws InvalidRequestExce case DOI -> validateRequestAttributes(attributes, doiPatchReqSchema, type); case DIGITAL_SPECIMEN -> validateRequestAttributes(attributes, digitalSpecimenPatchReqSchema, type); - case MEDIA_OBJECT -> validateRequestAttributes(attributes, mediaObjectPatchReqSchema, type); + case DIGITAL_MEDIA -> validateRequestAttributes(attributes, digitalMediaPatchReqSchema, type); case ANNOTATION -> validateRequestAttributes(attributes, annotationPatchReqSchema, type); - case MAPPING -> validateRequestAttributes(attributes, mappingPatchReqSchema, type); + case DATA_MAPPING -> validateRequestAttributes(attributes, mappingPatchReqSchema, type); case SOURCE_SYSTEM -> validateRequestAttributes(attributes, sourceSystemPatchReqSchema, type); case ORGANISATION -> validateRequestAttributes(attributes, organisationPatchReqSchema, type); case MAS -> validateRequestAttributes(attributes, masPatchReqSchema, type); diff --git a/src/main/java/eu/dissco/core/handlemanager/repository/BatchInserter.java b/src/main/java/eu/dissco/core/handlemanager/repository/BatchInserter.java deleted file mode 100644 index 485590c4..00000000 --- a/src/main/java/eu/dissco/core/handlemanager/repository/BatchInserter.java +++ /dev/null @@ -1,51 +0,0 @@ -package eu.dissco.core.handlemanager.repository; - -import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; -import eu.dissco.core.handlemanager.exceptions.DatabaseCopyException; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.sql.SQLException; -import java.util.List; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.postgresql.copy.CopyManager; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -@Slf4j -public class BatchInserter { - - private final CopyManager copyManager; - - public void batchCopy(long recordTimestamp, List handleAttributes) { - try (var outputStream = new ByteArrayOutputStream()) { - for (var row : handleAttributes) { - outputStream.write(getCsvRow(recordTimestamp, row)); - } - var inputStream = new ByteArrayInputStream(outputStream.toByteArray()); - copyManager.copyIn("COPY handles FROM stdin DELIMITER ','", inputStream); - } catch (IOException | SQLException e) { - log.error("Sql error: ", e); - throw new DatabaseCopyException("Unable to insert new handles into database."); - } - } - - private static byte[] getCsvRow(Long recordTimestamp, HandleAttribute handleAttribute) { - return (new String(handleAttribute.getHandle(), StandardCharsets.UTF_8) + "," - + handleAttribute.getIndex() + "," - + handleAttribute.getType() + "," - + new String(handleAttribute.getData(), StandardCharsets.UTF_8).replace(",", "\\,") - + "," - + "0," - + "86400," - + recordTimestamp + "," - + "\\N," // Leave refs null - + true + "," - + true + "," - + true + "," - + false + "\n").getBytes(StandardCharsets.UTF_8); - } -} diff --git a/src/main/java/eu/dissco/core/handlemanager/repository/PidMongoRepository.java b/src/main/java/eu/dissco/core/handlemanager/repository/PidMongoRepository.java deleted file mode 100644 index 482ba134..00000000 --- a/src/main/java/eu/dissco/core/handlemanager/repository/PidMongoRepository.java +++ /dev/null @@ -1,105 +0,0 @@ -package eu.dissco.core.handlemanager.repository; - -import static com.mongodb.client.model.Filters.eq; -import static com.mongodb.client.model.Filters.in; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DIGITAL_OBJECT_TYPE; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.NORMALISED_SPECIMEN_OBJECT_ID; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PRIMARY_MEDIA_ID; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.mongodb.client.FindIterable; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.model.ReplaceOneModel; -import eu.dissco.core.handlemanager.domain.fdo.FdoType; -import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoAttribute; -import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoRecord; -import eu.dissco.core.handlemanager.exceptions.PidResolutionException; -import java.util.ArrayList; -import java.util.List; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.bson.Document; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -@Slf4j -public class PidMongoRepository { - - private final MongoCollection collection; - private final ObjectMapper mapper; - private static String ID = "_id"; - - public JsonNode getHandleRecord(String id) - throws PidResolutionException, JsonProcessingException { - var result = collection.find(eq(ID, id)); - if (result.first() == null) { - throw new PidResolutionException("Unable to find handle " + id); - } - return mapper.readValue(result.first().toJson(), JsonNode.class); - } - - public void postBatchHandleRecord(List handleRecords) { - collection.insertMany(handleRecords); - } - - public void updateHandleRecord(List handleRecords, List ids) { - var filter = in(ID, ids); - var queryList = handleRecords.stream().map(doc -> new ReplaceOneModel<>(filter, doc)).toList(); - collection.bulkWrite(queryList); - } - - public List getHandleRecords(List ids) throws JsonProcessingException { - var results = collection.find(in(ID, ids)); - return formatResults(results); - } - - public List searchByPrimaryLocalId(String localIdField, List localIds) - throws JsonProcessingException { - var results = collection.find(in(localIdField, localIds)); - return formatResults(results); - } - - private List formatResults(FindIterable results) - throws JsonProcessingException { - var handleRecords = new ArrayList(); - for (var result : results) { - var jsonRecord = mapper.readValue(result.toJson(), JsonNode.class); - if (jsonRecord.get("values") == null) { - log.warn("Unable to read handle record values \n {}", - mapper.writeValueAsString(jsonRecord)); - } else { - var attributes = mapper.convertValue(jsonRecord.get("values"), - new TypeReference>() { - }); - var fdoType = getFdoType(attributes, jsonRecord.get("_id").asText()); - handleRecords.add(new FdoRecord(jsonRecord.get("_id").asText(), - fdoType, attributes, getLocalId(jsonRecord, fdoType))); - } - } - return handleRecords; - } - - private FdoType getFdoType(List fdoAttributes, String id) { - for (var fdoAttribute : fdoAttributes) { - if (DIGITAL_OBJECT_TYPE.index() == fdoAttribute.getIndex()) { - return FdoType.fromString(fdoAttribute.getValue()); - } - } - log.error("Unable to determine Fdo Type for record of handle {}", id); - throw new IllegalStateException(); - } - - private String getLocalId(JsonNode jsonRecord, FdoType fdoType) { - if (FdoType.DIGITAL_SPECIMEN.equals(fdoType)) { - return jsonRecord.get(NORMALISED_SPECIMEN_OBJECT_ID.get()).asText(); - } - if (FdoType.MEDIA_OBJECT.equals(fdoType)) { - return jsonRecord.get(PRIMARY_MEDIA_ID.get()).asText(); - } - return null; - } -} diff --git a/src/main/java/eu/dissco/core/handlemanager/repository/PidRepository.java b/src/main/java/eu/dissco/core/handlemanager/repository/PidRepository.java index 75da770b..987ebafe 100644 --- a/src/main/java/eu/dissco/core/handlemanager/repository/PidRepository.java +++ b/src/main/java/eu/dissco/core/handlemanager/repository/PidRepository.java @@ -1,269 +1,117 @@ package eu.dissco.core.handlemanager.repository; -import static eu.dissco.core.handlemanager.database.jooq.tables.Handles.HANDLES; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.HS_ADMIN; +import static com.mongodb.client.model.Filters.in; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DIGITAL_OBJECT_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.NORMALISED_SPECIMEN_OBJECT_ID; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PID_RECORD_ISSUE_NUMBER; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PID_STATUS; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PRIMARY_SPECIMEN_OBJECT_ID; -import static org.jooq.impl.DSL.select; - -import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; -import java.nio.charset.StandardCharsets; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PRIMARY_MEDIA_ID; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mongodb.client.FindIterable; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.model.ReplaceOneModel; +import eu.dissco.core.handlemanager.domain.fdo.FdoType; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoAttribute; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoRecord; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.jooq.DSLContext; -import org.jooq.Query; -import org.jooq.Record4; -import org.jooq.SelectConditionStep; +import org.bson.Document; import org.springframework.stereotype.Repository; -@Slf4j @Repository @RequiredArgsConstructor +@Slf4j public class PidRepository { - private static final int TTL = 86400; - private final DSLContext context; - private final BatchInserter batchInserter; - - // For Handle Name Generation - public List getHandlesExist(List handles) { - // todo this won't work - return context.selectDistinct(HANDLES.HANDLE).from(HANDLES).where(HANDLES.HANDLE.in(handles)) - .fetch().getValues(HANDLES.HANDLE, String.class); - } - - public List checkHandlesWritable(List handles) { - return context.selectDistinct(HANDLES.HANDLE).from(HANDLES).where(HANDLES.HANDLE.in(handles)) - .and(HANDLES.TYPE.eq(PID_STATUS.get().getBytes(StandardCharsets.UTF_8))) - .and(HANDLES.DATA.notEqual("ARCHIVED".getBytes())).fetch() - .getValues(HANDLES.HANDLE, byte[].class); - } - - public List resolveHandleAttributes(byte[] handle) { - return context.select(HANDLES.IDX, HANDLES.HANDLE, HANDLES.TYPE, HANDLES.DATA).from(HANDLES) - .where(HANDLES.HANDLE.eq(handle)) - .and(HANDLES.TYPE.notEqual(HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) - .fetch(this::mapToAttribute); - } - - public List resolveHandleAttributes(List handles) { - return context.select(HANDLES.IDX, HANDLES.HANDLE, HANDLES.TYPE, HANDLES.DATA).from(HANDLES) - .where(HANDLES.HANDLE.in(handles)) - .and(HANDLES.TYPE.notEqual(HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) - .fetch(this::mapToAttribute); - } - - public List searchByNormalisedPhysicalIdentifier( - List normalisedPhysicalIdentifiers) { - return context.select(HANDLES.IDX, HANDLES.HANDLE, HANDLES.TYPE, HANDLES.DATA).from(HANDLES) - .where(HANDLES.HANDLE.in(select(HANDLES.HANDLE).from(HANDLES) - .where(HANDLES.TYPE.eq(PID_STATUS.get().getBytes(StandardCharsets.UTF_8))) - .and(HANDLES.DATA.notEqual("ARCHIVED".getBytes(StandardCharsets.UTF_8))).and( - HANDLES.HANDLE.in( - select(HANDLES.HANDLE).from(HANDLES).where(HANDLES.TYPE.eq( - NORMALISED_SPECIMEN_OBJECT_ID.get() - .getBytes(StandardCharsets.UTF_8))) - .and(HANDLES.DATA.in(normalisedPhysicalIdentifiers)))))) - .and(HANDLES.TYPE.eq( - NORMALISED_SPECIMEN_OBJECT_ID.get().getBytes(StandardCharsets.UTF_8))) - .fetch(this::mapToAttribute); - } - - public List getPrimarySpecimenObjectId(List handles) { - return context.select(HANDLES.IDX, HANDLES.HANDLE, HANDLES.TYPE, HANDLES.DATA).from(HANDLES) - .where(HANDLES.HANDLE.in(handles)) - .and(HANDLES.TYPE.eq(PRIMARY_SPECIMEN_OBJECT_ID.get().getBytes(StandardCharsets.UTF_8))) - .fetch(this::mapToAttribute); - } + private final MongoCollection collection; + private final ObjectMapper mapper; + private static final String ID = "_id"; - public List searchByNormalisedPhysicalIdentifierFullRecord( - List normalisedPhysicalIdentifiers) { - var normalisedPhysicalIdentifierTable = searchByNormalisedPhysicalIdentifierQuery( - normalisedPhysicalIdentifiers).asTable("normalisedPhysicalIdentifierTable"); - - return context.select(HANDLES.IDX, HANDLES.HANDLE, HANDLES.TYPE, HANDLES.DATA).from(HANDLES) - .join(normalisedPhysicalIdentifierTable) - .on(HANDLES.HANDLE.eq(normalisedPhysicalIdentifierTable.field(HANDLES.HANDLE))) - .where(HANDLES.TYPE.notEqual(HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) - .fetch(this::mapToAttribute); - } - - private SelectConditionStep> searchByNormalisedPhysicalIdentifierQuery( - List normalisedPhysicalIdentifiers) { - return context.select(HANDLES.IDX, HANDLES.HANDLE, HANDLES.TYPE, HANDLES.DATA).from(HANDLES) - .where( - HANDLES.TYPE.eq(NORMALISED_SPECIMEN_OBJECT_ID.get().getBytes(StandardCharsets.UTF_8))) - .and((HANDLES.DATA).in(normalisedPhysicalIdentifiers)); + public List getHandleRecords(List ids) throws JsonProcessingException { + var results = collection.find(in(ID, ids)); + return formatResults(results); } - // Get List of Pids - public List getAllHandles(byte[] pidStatus, int pageNum, int pageSize) { - int offset = getOffset(pageNum, pageSize); - - return context.selectDistinct(HANDLES.HANDLE).from(HANDLES) - .where(HANDLES.TYPE.eq(PID_STATUS.get().getBytes(StandardCharsets.UTF_8))) - .and(HANDLES.DATA.eq(pidStatus)).limit(pageSize).offset(offset).fetch() - .getValues(HANDLES.HANDLE, String.class); - } - - public List getAllHandles(int pageNum, int pageSize) { - int offset = getOffset(pageNum, pageSize); - return context.selectDistinct(HANDLES.HANDLE).from(HANDLES).limit(pageSize).offset(offset) - .fetch().getValues(HANDLES.HANDLE, String.class); - } - - private HandleAttribute mapToAttribute(Record4 row) { - return new HandleAttribute(row.get(HANDLES.IDX), row.get(HANDLES.HANDLE), - new String(row.get(HANDLES.TYPE), StandardCharsets.UTF_8), row.get(HANDLES.DATA)); - } - - // Post - public void postAttributesToDb(long recordTimestamp, List handleAttributes) { - batchInserter.batchCopy(recordTimestamp, handleAttributes); - } - - private List prepareBatchPostQuery(long recordTimestamp, - List handleAttributes) { - var queryList = new ArrayList(); - - for (var handleAttribute : handleAttributes) { - var query = context.insertInto(HANDLES).set(HANDLES.HANDLE, handleAttribute.getHandle()) - .set(HANDLES.IDX, handleAttribute.getIndex()) - .set(HANDLES.TYPE, handleAttribute.getType().getBytes(StandardCharsets.UTF_8)) - .set(HANDLES.DATA, handleAttribute.getData()).set(HANDLES.TTL, TTL) - .set(HANDLES.TIMESTAMP, recordTimestamp).set(HANDLES.ADMIN_READ, true) - .set(HANDLES.ADMIN_WRITE, true).set(HANDLES.PUB_READ, true) - .set(HANDLES.PUB_WRITE, false); - queryList.add(query); + public List getExistingHandles(List ids) { + var results = collection.find(in(ID, ids)); + if (results.first() == null) { + return Collections.emptyList(); } - return queryList; + var existingHandles = new ArrayList(); + results.forEach(r -> existingHandles.add(r.get(ID).toString())); + return existingHandles; } - // Archive - public void archiveRecords(long recordTimestamp, List handleAttributes, - List handles) { - updateRecord(recordTimestamp, handleAttributes, true); - deleteNonTombstoneAttributes(handles); + public void postBatchHandleRecord(List handleRecords) { + collection.insertMany(handleRecords); } - private void deleteNonTombstoneAttributes(List handles) { - context.delete(HANDLES).where(HANDLES.HANDLE.in(handles)).and(HANDLES.IDX.notBetween(1).and(39)) - .and(HANDLES.IDX.notBetween(100).and(101)).execute(); + public void updateHandleRecord(List handleRecords) { + var queryList = handleRecords.stream().map(doc -> { + var filter = in(doc.get(ID).toString()); + return new ReplaceOneModel<>(filter, doc); + }).toList(); + collection.bulkWrite(queryList); } - public void postAndUpdateHandles(long recordTimestamp, List createAttributes, - List> updateAttributes) { - var queryList = prepareBatchUpdateQuery(recordTimestamp, updateAttributes, true); - queryList.addAll(prepareBatchPostQuery(recordTimestamp, createAttributes)); - context.batch(queryList).execute(); + public List searchByPrimaryLocalId(String localIdField, List localIds) + throws JsonProcessingException { + var results = collection.find(in(localIdField, localIds)); + return formatResults(results); } - // Update - public void updateRecord(long recordTimestamp, List handleAttributes, - boolean incrementVersion) { - var queryList = prepareUpdateQuery(recordTimestamp, handleAttributes); - if (incrementVersion) { - queryList.addAll( - getVersionIncrementQuery(Set.of(handleAttributes.get(0).getHandle()), - recordTimestamp)); - } - context.batch(queryList).execute(); + public void rollbackHandles(List ids) { + var filter = in(ID, ids); + collection.deleteMany(filter); } - public void updateRecordBatch(long recordTimestamp, List> handleRecords, - boolean incrementVersion) { - var queryList = prepareBatchUpdateQuery(recordTimestamp, handleRecords, incrementVersion); - context.batch(queryList).execute(); + public void rollbackHandles(String localIdField, List localIds) { + var filter = in(localIdField, localIds); + collection.deleteMany(filter); } - private List prepareBatchUpdateQuery(long recordTimestamp, - List> handleRecords, boolean incrementVersion) { - List queryList = new ArrayList<>(); - for (List handleRecord : handleRecords) { - queryList.addAll(prepareUpdateQuery(recordTimestamp, handleRecord)); - } - if (incrementVersion) { - var handles = handleRecords.stream().map(h -> h.get(0).getHandle()) - .collect(Collectors.toSet()); - queryList.addAll(getVersionIncrementQuery(handles, recordTimestamp)); + private List formatResults(FindIterable results) + throws JsonProcessingException { + var handleRecords = new ArrayList(); + for (var result : results) { + var jsonRecord = mapper.readValue(result.toJson(), JsonNode.class); + if (jsonRecord.get("values") == null) { + log.warn("Unable to read handle record values \n {}", + mapper.writeValueAsString(jsonRecord)); + } else { + var attributes = mapper.convertValue(jsonRecord.get("values"), + new TypeReference>() { + }); + var fdoType = getFdoType(attributes, jsonRecord.get("_id").asText()); + handleRecords.add(new FdoRecord(jsonRecord.get("_id").asText(), + fdoType, attributes, getLocalId(jsonRecord, fdoType))); + } } - return queryList; + return handleRecords; } - private ArrayList prepareUpdateQuery(long recordTimestamp, - List handleAttributes) { - var queryList = new ArrayList(); - for (var handleAttribute : handleAttributes) { - var query = context.insertInto(HANDLES).set(HANDLES.HANDLE, handleAttribute.getHandle()) - .set(HANDLES.IDX, handleAttribute.getIndex()) - .set(HANDLES.TYPE, handleAttribute.getType().getBytes(StandardCharsets.UTF_8)) - .set(HANDLES.DATA, handleAttribute.getData()).set(HANDLES.TTL, TTL) - .set(HANDLES.TIMESTAMP, recordTimestamp).set(HANDLES.ADMIN_READ, true) - .set(HANDLES.ADMIN_WRITE, true).set(HANDLES.PUB_READ, true) - .set(HANDLES.PUB_WRITE, false) - .onDuplicateKeyUpdate().set(HANDLES.HANDLE, handleAttribute.getHandle()) - .set(HANDLES.IDX, handleAttribute.getIndex()) - .set(HANDLES.TYPE, handleAttribute.getType().getBytes(StandardCharsets.UTF_8)) - .set(HANDLES.DATA, handleAttribute.getData()).set(HANDLES.TTL, TTL) - .set(HANDLES.TIMESTAMP, recordTimestamp).set(HANDLES.ADMIN_READ, true) - .set(HANDLES.ADMIN_WRITE, true).set(HANDLES.PUB_READ, true) - .set(HANDLES.PUB_WRITE, false); - queryList.add(query); + private FdoType getFdoType(List fdoAttributes, String id) { + for (var fdoAttribute : fdoAttributes) { + if (DIGITAL_OBJECT_TYPE.index() == fdoAttribute.getIndex()) { + return FdoType.fromString(fdoAttribute.getValue()); + } } - return queryList; + log.error("Unable to determine Fdo Type for record of handle {}", id); + throw new IllegalStateException(); } - private List getVersionIncrementQuery(Set handles, long recordTimestamp) { - var versions = getIncrementedVersions(handles); - var handleStr = handles.stream().map(h -> new String(h, StandardCharsets.UTF_8)).toList(); - List queryList = new ArrayList<>(); - for (var handle : handleStr) { - queryList.add(context.update(HANDLES) - .set(HANDLES.DATA, versions.get(handle)) - .set(HANDLES.TIMESTAMP, recordTimestamp) - .where(HANDLES.HANDLE.eq(handle.getBytes(StandardCharsets.UTF_8))) - .and(HANDLES.TYPE.eq( - PID_RECORD_ISSUE_NUMBER.get().getBytes(StandardCharsets.UTF_8)))); + private String getLocalId(JsonNode jsonRecord, FdoType fdoType) { + if (FdoType.DIGITAL_SPECIMEN.equals(fdoType)) { + return jsonRecord.get(NORMALISED_SPECIMEN_OBJECT_ID.get()).asText(); } - return queryList; - } - - private Map getIncrementedVersions(Set handles) { - var versions = context.select(HANDLES.HANDLE, HANDLES.DATA) - .from(HANDLES) - .where(HANDLES.HANDLE.in(handles) - .and(HANDLES.TYPE.eq( - PID_RECORD_ISSUE_NUMBER.get().getBytes(StandardCharsets.UTF_8)))) - .fetchMap(HANDLES.HANDLE, HANDLES.DATA); - return versions.entrySet().stream() - .collect(Collectors.toMap( - e -> new String(e.getKey(), StandardCharsets.UTF_8), - e -> incrementByteArray(e.getValue()) - )); - } - - private static byte[] incrementByteArray(byte[] version) { - var originalVersion = Integer.parseInt(new String(version, StandardCharsets.UTF_8)); - return String.valueOf(originalVersion + 1).getBytes(StandardCharsets.UTF_8); - } - - public void rollbackHandles(List handles) { - context.delete(HANDLES).where(HANDLES.HANDLE.in(handles)).execute(); - } - - private int getOffset(int pageNum, int pageSize) { - int offset = 0; - if (pageNum > 1) { - offset = offset + (pageSize * (pageNum - 1)); + if (FdoType.DIGITAL_MEDIA.equals(fdoType)) { + return jsonRecord.get(PRIMARY_MEDIA_ID.get()).asText(); } - return offset; + return null; } - } diff --git a/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java b/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java index e81139ac..0890ea3d 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java @@ -1,11 +1,8 @@ package eu.dissco.core.handlemanager.service; -import static eu.dissco.core.handlemanager.domain.fdo.FdoType.DIGITAL_SPECIMEN; -import static eu.dissco.core.handlemanager.domain.fdo.FdoType.MEDIA_OBJECT; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ATTRIBUTES; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_DATA; -import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ID; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -13,21 +10,14 @@ import eu.dissco.core.handlemanager.Profiles; import eu.dissco.core.handlemanager.domain.datacite.DataCiteEvent; import eu.dissco.core.handlemanager.domain.datacite.EventType; -import eu.dissco.core.handlemanager.domain.fdo.FdoProfile; -import eu.dissco.core.handlemanager.domain.fdo.FdoType; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperWrite; import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoRecord; -import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.exceptions.UnprocessableEntityException; import eu.dissco.core.handlemanager.properties.ProfileProperties; -import eu.dissco.core.handlemanager.repository.PidMongoRepository; import eu.dissco.core.handlemanager.repository.PidRepository; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.bson.Document; import org.springframework.context.annotation.Profile; @@ -40,11 +30,11 @@ public class DoiService extends PidService { private final DataCiteService dataCiteService; - public DoiService(PidRepository pidRepository, - FdoRecordService fdoRecordService, PidNameGeneratorService pidNameGeneratorService, + public DoiService(FdoRecordService fdoRecordService, + PidNameGeneratorService pidNameGeneratorService, ObjectMapper mapper, ProfileProperties profileProperties, - DataCiteService dataCiteService, PidMongoRepository mongoRepository) { - super(pidRepository, fdoRecordService, pidNameGeneratorService, mapper, profileProperties, + DataCiteService dataCiteService, PidRepository mongoRepository) { + super(fdoRecordService, pidNameGeneratorService, mapper, profileProperties, mongoRepository); this.dataCiteService = dataCiteService; } @@ -63,7 +53,7 @@ public JsonApiWrapperWrite createRecords(List requests) try { switch (type) { case DIGITAL_SPECIMEN -> fdoRecords = createDigitalSpecimen(requestAttributes, handles); - case MEDIA_OBJECT -> fdoRecords = createMediaObject(requestAttributes, handles); + case DIGITAL_MEDIA -> fdoRecords = createDigitalMedia(requestAttributes, handles); default -> throw new UnsupportedOperationException( String.format(TYPE_ERROR_MESSAGE, type.getDigitalObjectName())); } @@ -76,82 +66,51 @@ public JsonApiWrapperWrite createRecords(List requests) log.info("Persisting new DOIs to Document Store"); mongoRepository.postBatchHandleRecord(fdoDocuments); log.info("Publishing to DataCite"); - //todo publishToDataCite(fdoRecords, EventType.CREATE, type); - return new JsonApiWrapperWrite(formatCreateRecord(fdoRecords, type)); + publishToDataCite(fdoRecords, EventType.CREATE); + return new JsonApiWrapperWrite(formatFdoRecord(fdoRecords, type)); } @Override - public JsonApiWrapperWrite updateRecords(List requests) + public JsonApiWrapperWrite updateRecords(List requests, boolean incrementVersion) throws InvalidRequestException, UnprocessableEntityException { var updateRequests = requests.stream() .map(request -> request.get(NODE_DATA)).toList(); - var handles = updateRequests.stream() - .map(request -> request.get(NODE_ID).asText()).toList(); - var previousVersions = getPreviousVersions(handles); - checkInternalDuplicates(handles); - checkHandlesWritableFullRecord(previousVersions); - var fdoRecordMap = previousVersions.stream() - .collect(Collectors.toMap(FdoRecord::handle, f -> f)); - var type = getObjectTypeFromJsonNode(requests); + var fdoRecordMap = processUpdateRequest(updateRequests); + var fdoType = getObjectTypeFromJsonNode(requests); List fdoRecords; List fdoDocuments; try { - switch (type) { - case DIGITAL_SPECIMEN -> fdoRecords = updateDigitalSpecimen(updateRequests, fdoRecordMap); - case MEDIA_OBJECT -> fdoRecords = updateDigitalMedia(updateRequests, fdoRecordMap); + switch (fdoType) { + case DIGITAL_SPECIMEN -> + fdoRecords = updateDigitalSpecimen(updateRequests, fdoRecordMap, incrementVersion); + case DIGITAL_MEDIA -> + fdoRecords = updateDigitalMedia(updateRequests, fdoRecordMap, incrementVersion); default -> throw new UnsupportedOperationException( - String.format(TYPE_ERROR_MESSAGE, type.getDigitalObjectName())); + String.format(TYPE_ERROR_MESSAGE, fdoType.getDigitalObjectName())); } fdoDocuments = fdoRecordService.toMongoDbDocument(fdoRecords); - var fdoType = getObjectTypeFromJsonNode(requests); - mongoRepository.updateHandleRecord(fdoDocuments, handles); - // todo figure out response - return new JsonApiWrapperWrite(formatCreateRecord(fdoRecords, fdoType)); + mongoRepository.updateHandleRecord(fdoDocuments); + publishToDataCite(fdoRecords, EventType.UPDATE); + return new JsonApiWrapperWrite(formatFdoRecord(fdoRecords, fdoType)); } catch (JsonProcessingException e) { log.error("An error has occurred processing JSON data", e); throw new UnprocessableEntityException("Json Processing Error"); } } - @Override - public JsonApiWrapperWrite updateRecords(List requests, boolean incrementVersion) - throws InvalidRequestException, UnprocessableEntityException { - var type = getObjectTypeFromJsonNode(requests); - if (!DIGITAL_SPECIMEN.equals(type) && !MEDIA_OBJECT.equals(type)) { - throw new InvalidRequestException( - String.format(TYPE_ERROR_MESSAGE, type.getDigitalObjectName())); - } - var attributesToUpdate = getAttributesToUpdate(requests); - var response = updateRecords(attributesToUpdate, incrementVersion, type); - log.info("Publishing to datacite"); - var flatList = attributesToUpdate.stream().flatMap(List::stream).toList(); - publishToDataCite(flatList, EventType.UPDATE, type); - return response; - } - - private void publishToDataCite(List handleAttributes, EventType eventType, - FdoType objectType) throws UnprocessableEntityException { - var handleMap = mapRecords(handleAttributes); - var eventList = new ArrayList(); - handleMap.forEach( - (key, value) -> { - if (eventType.equals(EventType.UPDATE)) { - value.add( - new HandleAttribute(FdoProfile.PID, key.getBytes(StandardCharsets.UTF_8), - key)); - } - eventList.add( - new DataCiteEvent(jsonFormatSingleRecordOld(value), eventType)); - }); - + private void publishToDataCite(List fdoRecords, EventType eventType) + throws UnprocessableEntityException { + var eventList = fdoRecords.stream() + .map(fdoRecord -> new DataCiteEvent(jsonFormatSingleRecord(fdoRecord.attributes()), + eventType)).toList(); for (var event : eventList) { try { - dataCiteService.publishToDataCite(event, objectType); + dataCiteService.publishToDataCite(event, fdoRecords.get(0).fdoType()); } catch (JsonProcessingException e) { log.error("Critical error: Unable to publish datacite event to queue", e); - log.info("Rolling back handles"); if (eventType.equals(EventType.CREATE)) { - rollbackHandles(new ArrayList<>(handleMap.keySet())); + log.info("Rolling back handles"); + rollbackHandles(fdoRecords.stream().map(FdoRecord::handle).toList()); } throw new UnprocessableEntityException("Unable to publish datacite event to queue"); } diff --git a/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java b/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java index b5d57557..ddad75ca 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java @@ -14,6 +14,7 @@ import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DIGITAL_OBJECT_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.FDO_PROFILE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.FDO_RECORD_LICENSE; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.HAS_RELATED_PID; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.HS_ADMIN; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.INFORMATION_ARTEFACT_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.ISSUED_FOR_AGENT; @@ -58,53 +59,50 @@ import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.RIGHTSHOLDER_PID; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.RIGHTSHOLDER_PID_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SOURCE_DATA_STANDARD; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SOURCE_SYSTEM_NAME; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SPECIMEN_HOST; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SPECIMEN_HOST_NAME; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SPECIMEN_OBJECT_ID_ABSENCE_REASON; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.STRUCTURAL_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TARGET_PID; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TARGET_TYPE; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOMBSTONED_DATE; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOMBSTONED_TEXT; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOPIC_CATEGORY; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOPIC_DISCIPLINE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOPIC_DOMAIN; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOPIC_ORIGIN; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.WAS_DERIVED_FROM_ENTITY; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.ANNOTATION; +import static eu.dissco.core.handlemanager.domain.fdo.FdoType.DATA_MAPPING; +import static eu.dissco.core.handlemanager.domain.fdo.FdoType.DIGITAL_MEDIA; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.DIGITAL_SPECIMEN; -import static eu.dissco.core.handlemanager.domain.fdo.FdoType.MAPPING; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.MAS; -import static eu.dissco.core.handlemanager.domain.fdo.FdoType.MEDIA_OBJECT; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.ORGANISATION; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.SOURCE_SYSTEM; +import static eu.dissco.core.handlemanager.service.ServiceUtils.getField; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.BaseJsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.node.TextNode; import eu.dissco.core.handlemanager.domain.fdo.AnnotationRequest; +import eu.dissco.core.handlemanager.domain.fdo.DataMappingRequest; +import eu.dissco.core.handlemanager.domain.fdo.DigitalMediaRequest; import eu.dissco.core.handlemanager.domain.fdo.DigitalSpecimenRequest; import eu.dissco.core.handlemanager.domain.fdo.DoiRecordRequest; -import eu.dissco.core.handlemanager.domain.fdo.FdoProfile; import eu.dissco.core.handlemanager.domain.fdo.FdoType; import eu.dissco.core.handlemanager.domain.fdo.HandleRecordRequest; -import eu.dissco.core.handlemanager.domain.fdo.MappingRequest; import eu.dissco.core.handlemanager.domain.fdo.MasRequest; -import eu.dissco.core.handlemanager.domain.fdo.MediaObjectRequest; import eu.dissco.core.handlemanager.domain.fdo.OrganisationRequest; import eu.dissco.core.handlemanager.domain.fdo.SourceSystemRequest; +import eu.dissco.core.handlemanager.domain.fdo.TombstoneRecordRequest; +import eu.dissco.core.handlemanager.domain.fdo.vocabulary.PidStatus; import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoAttribute; import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoRecord; -import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; -import eu.dissco.core.handlemanager.exceptions.InvalidRequestRuntimeException; import eu.dissco.core.handlemanager.properties.ApplicationProperties; import eu.dissco.core.handlemanager.web.PidResolver; import jakarta.annotation.Nullable; -import java.io.IOException; import java.io.StringWriter; -import java.nio.charset.StandardCharsets; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; @@ -113,7 +111,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -149,12 +146,19 @@ public class FdoRecordService { private static final String LOC_REQUEST = "locations"; public static final Map RESOLVABLE_KEYS; public static final List GENERATED_KEYS; + public static final List TOMBSTONE_KEYS; static { GENERATED_KEYS = List.of(FDO_RECORD_LICENSE.index(), PID.index(), PID_RECORD_ISSUE_DATE.index(), PID_STATUS.index(), HS_ADMIN.index()); } + static { + TOMBSTONE_KEYS = List.of(FDO_RECORD_LICENSE.index(), PID.index(), PID_RECORD_ISSUE_DATE.index(), + PID_ISSUER.index(), PID_ISSUER_NAME.index(), ISSUED_FOR_AGENT.index(), + ISSUED_FOR_AGENT_NAME.index(), STRUCTURAL_TYPE.index(), HS_ADMIN.index()); + } + static { HashMap hashMap = new HashMap<>(); hashMap.put(DIGITAL_OBJECT_TYPE.get(), DIGITAL_OBJECT_NAME.get()); @@ -171,43 +175,43 @@ public List toMongoDbDocument(List fdoRecords) throws JsonProcessingException { var documentList = new ArrayList(); for (var fdoRecord : fdoRecords) { - var arrayNode = mapper.createArrayNode(); - for (var attribute : fdoRecord.attributes()) { - arrayNode.add(mapper.valueToTree(attribute)); - } - var vals = mapper.createObjectNode() - .set("values", arrayNode); - var doc = Document.parse(mapper.writeValueAsString(vals)) - .append("_id", fdoRecord.handle()); - if (DIGITAL_SPECIMEN.equals(fdoRecord.fdoType())) { - doc.append(NORMALISED_SPECIMEN_OBJECT_ID.get(), fdoRecord.primaryLocalId()); - } else if (MEDIA_OBJECT.equals(fdoRecord.fdoType())) { - doc.append(PRIMARY_MEDIA_ID.get(), fdoRecord.primaryLocalId()); - } else if (ANNOTATION.equals(fdoRecord.fdoType())) { - doc.append(ANNOTATION_HASH.get(), fdoRecord.primaryLocalId()); - } + var doc = Document.parse(mapper.writeValueAsString(fdoRecord)); + addLocalId(fdoRecord, doc); documentList.add(doc); } return documentList; } + private void addLocalId(FdoRecord fdoRecord, Document doc) { + if (fdoRecord.primaryLocalId() == null) { + return; + } + if (DIGITAL_SPECIMEN.equals(fdoRecord.fdoType())) { + doc.append(NORMALISED_SPECIMEN_OBJECT_ID.get(), fdoRecord.primaryLocalId()); + } else if (DIGITAL_MEDIA.equals(fdoRecord.fdoType())) { + doc.append(PRIMARY_MEDIA_ID.get(), fdoRecord.primaryLocalId()); + } else if (ANNOTATION.equals(fdoRecord.fdoType())) { + doc.append(ANNOTATION_HASH.get(), fdoRecord.primaryLocalId()); + } + } + /* Handle Record Creation */ public FdoRecord prepareNewHandleRecord(HandleRecordRequest request, String handle, FdoType fdoType, Instant timestamp) throws InvalidRequestException { - var fdoAttributes = prepareNewHandleAttributeList(request, handle, fdoType, timestamp); + var fdoAttributes = prepareNewHandleAttributes(request, handle, fdoType, timestamp); return new FdoRecord(handle, fdoType, fdoAttributes, null); } public FdoRecord prepareUpdatedHandleRecord(HandleRecordRequest recordRequest, FdoType fdoType, Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) throws InvalidRequestException { - var fdoAttributes = prepareUpdatedHandleAttributesRecord(recordRequest, + var fdoAttributes = prepareUpdatedHandleAttributes(recordRequest, previousVersion.handle(), fdoType, timestamp, previousVersion, incrementVersion); return new FdoRecord(previousVersion.handle(), fdoType, fdoAttributes, null); } - private List prepareNewHandleAttributeList(HandleRecordRequest request, + private List prepareNewHandleAttributes(HandleRecordRequest request, String handle, FdoType fdoType, Instant timestamp) throws InvalidRequestException { var handleAttributes = prepareHandleAttributesFromRequest(request, handle, fdoType, timestamp); @@ -215,27 +219,29 @@ private List prepareNewHandleAttributeList(HandleRecordRequest req return handleAttributes; } - private List prepareUpdatedHandleAttributesRecord(HandleRecordRequest request, + private List prepareUpdatedHandleAttributes(HandleRecordRequest request, String handle, FdoType fdoType, Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) throws InvalidRequestException { var previousAttributes = new ArrayList<>(previousVersion.attributes()); - var newAttributes = prepareHandleAttributesFromRequest(request, handle, fdoType, timestamp); - var generatedAttributes = new ArrayList(); - for (var prevAttribute : previousAttributes) { - if (GENERATED_KEYS.contains(prevAttribute.getIndex())) { - generatedAttributes.add(prevAttribute); - } else if (PID_RECORD_ISSUE_NUMBER.index() == prevAttribute.getIndex()) { - var prevIssueNumber = Integer.parseInt(prevAttribute.getValue()); - var issueNumber = incrementVersion ? prevIssueNumber + 1 : prevIssueNumber; - generatedAttributes.add( - new FdoAttribute(PID_RECORD_ISSUE_NUMBER, timestamp, String.valueOf(issueNumber))); - } + var updatedAttributes = prepareHandleAttributesFromRequest(request, handle, fdoType, timestamp); + updatedAttributes.addAll(previousAttributes.stream() + .filter(previousAttribute -> GENERATED_KEYS.contains(previousAttribute.getIndex())) + .toList()); + var previousIssueNumber = getField(previousVersion.attributes(), PID_RECORD_ISSUE_NUMBER); + if (incrementVersion) { + updatedAttributes.add(incrementIssueNumber(previousIssueNumber, timestamp)); + } else { + updatedAttributes.add(previousIssueNumber); } - newAttributes.addAll(generatedAttributes); - return newAttributes; + return updatedAttributes; } + private FdoAttribute incrementIssueNumber(FdoAttribute previousVersion, Instant timestamp) { + var previousIssueNumber = previousVersion.getValue(); + var incrementedIssueNumber = String.valueOf(Integer.parseInt(previousIssueNumber) + 1); + return new FdoAttribute(PID_RECORD_ISSUE_NUMBER, timestamp, incrementedIssueNumber); + } // These attributes may change on an update private ArrayList prepareHandleAttributesFromRequest( @@ -257,7 +263,7 @@ private ArrayList prepareHandleAttributesFromRequest( new FdoAttribute(DIGITAL_OBJECT_TYPE, timestamp, fdoType.getDigitalObjectType())); // 4: Digital ObjectName handleAttributeList.add( - new FdoAttribute(DIGITAL_OBJECT_NAME, timestamp, fdoType.getDigitalObjectType())); + new FdoAttribute(DIGITAL_OBJECT_NAME, timestamp, fdoType.getDigitalObjectName())); // 6: PID Issuer handleAttributeList.add(new FdoAttribute(PID_ISSUER, timestamp, request.getPidIssuer())); // 7: PID Issuer Name @@ -293,41 +299,41 @@ private List prepareHandleAttributesGenerated(String handle, FdoTy new FdoAttribute(PID_RECORD_ISSUE_NUMBER, timestamp, "1")); // This gets replaced on an update // 13: Pid Status - handleAttributeList.add(new FdoAttribute(PID_STATUS, timestamp, "TEST")); + handleAttributeList.add(new FdoAttribute(PID_STATUS, timestamp, PidStatus.ACTIVE.name())); // 100 HS Admin handleAttributeList.add(new FdoAttribute(timestamp, applicationProperties.getPrefix())); return handleAttributeList; } /* DOI Record Creation */ - public FdoRecord prepareNewDoiDocument(DoiRecordRequest request, String handle, - FdoType fdoType, Instant timestamp) throws InvalidRequestException, JsonProcessingException { - var fdoAttributes = prepareNewDoiFdoAttributes(request, handle, fdoType, timestamp); + public FdoRecord prepareNewDoiRecord(DoiRecordRequest request, String handle, + FdoType fdoType, Instant timestamp) throws InvalidRequestException { + var fdoAttributes = prepareNewDoiAttributes(request, handle, fdoType, timestamp); return new FdoRecord(handle, fdoType, fdoAttributes, null); } - public FdoRecord prepareUpdatedDoiFdoRecord(DoiRecordRequest request, + public FdoRecord prepareUpdatedDoiRecord(DoiRecordRequest request, FdoType fdoType, Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) throws InvalidRequestException { - var fdoAttributes = prepareUpdatedDoiFdoAttributes(request, previousVersion.handle(), fdoType, + var fdoAttributes = prepareUpdatedDoiAttributes(request, previousVersion.handle(), fdoType, timestamp, previousVersion, incrementVersion); return new FdoRecord(previousVersion.handle(), fdoType, fdoAttributes, null); } - private List prepareNewDoiFdoAttributes(DoiRecordRequest request, + private List prepareNewDoiAttributes(DoiRecordRequest request, String handle, FdoType fdoType, Instant timestamp) throws InvalidRequestException { - var fdoAttributes = prepareNewHandleAttributeList(request, handle, fdoType, timestamp); + var fdoAttributes = prepareNewHandleAttributes(request, handle, fdoType, timestamp); fdoAttributes.addAll(prepareDoiAttributesFromRequest(request, handle, timestamp)); return fdoAttributes; } - private List prepareUpdatedDoiFdoAttributes(DoiRecordRequest request, + private List prepareUpdatedDoiAttributes(DoiRecordRequest request, String handle, FdoType fdoType, Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) throws InvalidRequestException { - var fdoAttributes = prepareUpdatedHandleAttributesRecord(request, handle, fdoType, timestamp, + var fdoAttributes = prepareUpdatedHandleAttributes(request, handle, fdoType, timestamp, previousVersion, incrementVersion); fdoAttributes.addAll(prepareDoiAttributesFromRequest(request, handle, timestamp)); @@ -355,21 +361,24 @@ private List prepareDoiAttributesFromRequest(DoiRecordRequest requ /* Annotation Record Creation */ public FdoRecord prepareNewAnnotationRecord(AnnotationRequest request, String handle, Instant timestamp) throws InvalidRequestException { - var fdoAttributes = prepareNewHandleAttributeList(request, handle, ANNOTATION, + var fdoAttributes = prepareNewHandleAttributes(request, handle, ANNOTATION, timestamp); fdoAttributes.addAll(prepareAnnotationAttributesFromRequest(request, timestamp)); - return new FdoRecord(handle, ANNOTATION, fdoAttributes, request.getAnnotationHash().toString()); + var localId = + request.getAnnotationHash() != null ? request.getAnnotationHash().toString() : null; + return new FdoRecord(handle, ANNOTATION, fdoAttributes, localId); } - public FdoRecord prepareUpdatedAnnotationFdoRecord(AnnotationRequest request, + public FdoRecord prepareUpdatedAnnotationRecord(AnnotationRequest request, Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) throws InvalidRequestException { - var fdoAttributes = prepareUpdatedHandleAttributesRecord(request, previousVersion.handle(), + var fdoAttributes = prepareUpdatedHandleAttributes(request, previousVersion.handle(), ANNOTATION, timestamp, previousVersion, incrementVersion); fdoAttributes.addAll(prepareAnnotationAttributesFromRequest(request, timestamp)); - return new FdoRecord(previousVersion.handle(), ANNOTATION, fdoAttributes, - request.getAnnotationHash().toString()); + var localId = + request.getAnnotationHash() == null ? null : request.getAnnotationHash().toString(); + return new FdoRecord(previousVersion.handle(), ANNOTATION, fdoAttributes, localId); } private List prepareAnnotationAttributesFromRequest( @@ -385,30 +394,36 @@ private List prepareAnnotationAttributesFromRequest( handleAttributeList.add(new FdoAttribute(MOTIVATION, timestamp, request.getMotivation().toString())); // 503 Annotation Hash - handleAttributeList.add(new FdoAttribute(PRIMARY_REFERENT_TYPE, timestamp, - request.getMotivation().toString())); + if (request.getAnnotationHash() != null) { + handleAttributeList.add(new FdoAttribute(ANNOTATION_HASH, timestamp, + request.getAnnotationHash().toString())); + } else { + handleAttributeList.add(new FdoAttribute(ANNOTATION_HASH, timestamp, + null)); + } + return handleAttributeList; } /* Data Mapping Record Creation */ - public FdoRecord prepareNewDataMappingRecord(MappingRequest request, String handle, + public FdoRecord prepareNewDataMappingRecord(DataMappingRequest request, String handle, Instant timestamp) throws InvalidRequestException { - var fdoAttributes = prepareNewHandleAttributeList(request, handle, MAPPING, timestamp); + var fdoAttributes = prepareNewHandleAttributes(request, handle, DATA_MAPPING, timestamp); fdoAttributes.addAll(prepareDataMappingAttributesFromRequest(request, timestamp)); - return new FdoRecord(handle, MAPPING, fdoAttributes, null); + return new FdoRecord(handle, DATA_MAPPING, fdoAttributes, null); } - public FdoRecord prepareUpdatedDataMappingRecord(MappingRequest request, + public FdoRecord prepareUpdatedDataMappingRecord(DataMappingRequest request, Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) throws InvalidRequestException { - var fdoAttributes = prepareUpdatedHandleAttributesRecord(request, previousVersion.handle(), - MAPPING, timestamp, + var fdoAttributes = prepareUpdatedHandleAttributes(request, previousVersion.handle(), + DATA_MAPPING, timestamp, previousVersion, incrementVersion); fdoAttributes.addAll(prepareDataMappingAttributesFromRequest(request, timestamp)); - return new FdoRecord(previousVersion.handle(), MAPPING, fdoAttributes, null); + return new FdoRecord(previousVersion.handle(), DATA_MAPPING, fdoAttributes, null); } - private List prepareDataMappingAttributesFromRequest(MappingRequest request, + private List prepareDataMappingAttributesFromRequest(DataMappingRequest request, Instant timestamp) { return List.of( new FdoAttribute(SOURCE_DATA_STANDARD, timestamp, request.getSourceDataStandard())); @@ -418,8 +433,8 @@ private List prepareDataMappingAttributesFromRequest(MappingReques public FdoRecord prepareNewDigitalSpecimenRecord(DigitalSpecimenRequest request, String handle, Instant timestamp) throws InvalidRequestException, JsonProcessingException { - var fdoAttributes = prepareNewDoiFdoAttributes(request, handle, DIGITAL_SPECIMEN, timestamp); - fdoAttributes.addAll(prepareDigitalSpecimenRecordAttributesFromRequest(request, timestamp)); + var fdoAttributes = prepareNewDoiAttributes(request, handle, DIGITAL_SPECIMEN, timestamp); + fdoAttributes.addAll(prepareDigitalSpecimenAttributesFromRequest(request, timestamp)); return new FdoRecord(handle, DIGITAL_SPECIMEN, fdoAttributes, request.getNormalisedPrimarySpecimenObjectId()); } @@ -428,14 +443,14 @@ public FdoRecord prepareUpdatedDigitalSpecimenRecord( DigitalSpecimenRequest request, Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) throws InvalidRequestException, JsonProcessingException { - var fdoAttributes = prepareUpdatedDoiFdoAttributes(request, previousVersion.handle(), + var fdoAttributes = prepareUpdatedDoiAttributes(request, previousVersion.handle(), DIGITAL_SPECIMEN, timestamp, previousVersion, incrementVersion); - fdoAttributes.addAll(prepareDigitalSpecimenRecordAttributesFromRequest(request, timestamp)); + fdoAttributes.addAll(prepareDigitalSpecimenAttributesFromRequest(request, timestamp)); return new FdoRecord(previousVersion.handle(), DIGITAL_SPECIMEN, fdoAttributes, request.getNormalisedPrimarySpecimenObjectId()); } - private List prepareDigitalSpecimenRecordAttributesFromRequest( + private List prepareDigitalSpecimenAttributesFromRequest( DigitalSpecimenRequest request, Instant timestamp) throws InvalidRequestException, JsonProcessingException { var handleAttributeList = new ArrayList(); @@ -454,7 +469,7 @@ private List prepareDigitalSpecimenRecordAttributesFromRequest( request.getPrimarySpecimenObjectIdType().toString()))); // 204 Primary Specimen Object ID Name handleAttributeList.add(new FdoAttribute(PRIMARY_SPECIMEN_OBJECT_ID_NAME, timestamp, - getObjectName(request.getSpecimenHost(), request.getPrimarySpecimenObjectIdName()))); + request.getPrimarySpecimenObjectIdName())); // 205 Normalised Specimen Object Id handleAttributeList.add(new FdoAttribute(NORMALISED_SPECIMEN_OBJECT_ID, timestamp, request.getNormalisedPrimarySpecimenObjectId())); @@ -465,8 +480,13 @@ private List prepareDigitalSpecimenRecordAttributesFromRequest( handleAttributeList.add(new FdoAttribute(SPECIMEN_OBJECT_ID_ABSENCE_REASON, timestamp, request.getSpecimenObjectIdAbsenceReason())); // 207 Other Specimen Ids - handleAttributeList.add(new FdoAttribute(OTHER_SPECIMEN_IDS, timestamp, - mapper.writeValueAsString(request.getOtherSpecimenIds()))); + if (request.getOtherSpecimenIds() != null && !request.getOtherSpecimenIds().isEmpty()) { + handleAttributeList.add(new FdoAttribute(OTHER_SPECIMEN_IDS, timestamp, + mapper.writeValueAsString(request.getOtherSpecimenIds()))); + } else { + handleAttributeList.add(new FdoAttribute(OTHER_SPECIMEN_IDS, timestamp, + null)); + } // 208 Topic Origin if (request.getTopicOrigin() != null) { handleAttributeList.add( @@ -489,9 +509,9 @@ private List prepareDigitalSpecimenRecordAttributesFromRequest( handleAttributeList.add(new FdoAttribute(TOPIC_DISCIPLINE, timestamp, null)); } // 211 Topic Category - if (request.getTopicDiscipline() != null) { + if (request.getTopicCategory() != null) { handleAttributeList.add(new FdoAttribute(TOPIC_CATEGORY, timestamp, - request.getTopicDiscipline().toString())); + request.getTopicCategory().toString())); } else { handleAttributeList.add(new FdoAttribute(TOPIC_CATEGORY, timestamp, null)); } @@ -537,9 +557,10 @@ private List prepareDigitalSpecimenRecordAttributesFromRequest( } else { handleAttributeList.add(new FdoAttribute(MARKED_AS_TYPE, timestamp, null)); } - // 218 Derived From Entity + // 218 Was Derived From Entity handleAttributeList.add( - new FdoAttribute(DERIVED_FROM_ENTITY, timestamp, request.getDerivedFromEntity())); + new FdoAttribute(WAS_DERIVED_FROM_ENTITY, timestamp, + String.valueOf(request.getDerivedFromEntity() != null))); // 219 Catalog ID handleAttributeList.add( new FdoAttribute(CATALOG_IDENTIFIER, timestamp, request.getCatalogIdentifier())); @@ -550,7 +571,7 @@ private List prepareDigitalSpecimenRecordAttributesFromRequest( public FdoRecord prepareNewMasRecord(MasRequest request, String handle, Instant timestamp) throws InvalidRequestException { - var fdoAttributes = prepareNewHandleAttributeList(request, handle, MAS, timestamp); + var fdoAttributes = prepareNewHandleAttributes(request, handle, MAS, timestamp); fdoAttributes.addAll(prepareMasAttributesFromRequest(request, timestamp)); return new FdoRecord(handle, MAS, fdoAttributes, null); } @@ -558,7 +579,7 @@ public FdoRecord prepareNewMasRecord(MasRequest request, String handle, public FdoRecord prepareUpdatedMasRecord(MasRequest request, Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) throws InvalidRequestException { - var fdoAttributes = prepareUpdatedHandleAttributesRecord(request, previousVersion.handle(), MAS, + var fdoAttributes = prepareUpdatedHandleAttributes(request, previousVersion.handle(), MAS, timestamp, previousVersion, incrementVersion); fdoAttributes.addAll(prepareMasAttributesFromRequest(request, timestamp)); @@ -572,28 +593,26 @@ private List prepareMasAttributesFromRequest(MasRequest request, } /* Media Object Record Creation */ - public FdoRecord prepareNewDigitalMediaRecord(MediaObjectRequest request, - String handle, Instant timestamp) - throws InvalidRequestException, JsonProcessingException { - var fdoAttributes = prepareNewDoiFdoAttributes(request, handle, MEDIA_OBJECT, timestamp); + public FdoRecord prepareNewDigitalMediaRecord(DigitalMediaRequest request, + String handle, Instant timestamp) throws InvalidRequestException { + var fdoAttributes = prepareNewDoiAttributes(request, handle, DIGITAL_MEDIA, timestamp); fdoAttributes.addAll(prepareDigitalMediaAttributesFromRequest(request, timestamp)); - return new FdoRecord(handle, MEDIA_OBJECT, fdoAttributes, request.getPrimaryMediaId()); - + return new FdoRecord(handle, DIGITAL_MEDIA, fdoAttributes, request.getPrimaryMediaId()); } - public FdoRecord prepareUpdatedDigitalMediaRecord(MediaObjectRequest request, + public FdoRecord prepareUpdatedDigitalMediaRecord(DigitalMediaRequest request, Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) throws InvalidRequestException { - var fdoAttributes = prepareUpdatedDoiFdoAttributes(request, previousVersion.handle(), - MEDIA_OBJECT, timestamp, + var fdoAttributes = prepareUpdatedDoiAttributes(request, previousVersion.handle(), + DIGITAL_MEDIA, timestamp, previousVersion, incrementVersion); fdoAttributes.addAll(prepareDigitalMediaAttributesFromRequest(request, timestamp)); - return new FdoRecord(previousVersion.handle(), MEDIA_OBJECT, fdoAttributes, + return new FdoRecord(previousVersion.handle(), DIGITAL_MEDIA, fdoAttributes, request.getPrimaryMediaId()); } private List prepareDigitalMediaAttributesFromRequest( - MediaObjectRequest request, Instant timestamp) + DigitalMediaRequest request, Instant timestamp) throws InvalidRequestException { var handleAttributeList = new ArrayList(); // 400 Media Host @@ -660,27 +679,30 @@ private List prepareDigitalMediaAttributesFromRequest( handleAttributeList.add( new FdoAttribute(LICENSE_NAME, timestamp, request.getLicenseName())); // 415 License URL - new FdoAttribute(LICENSE_URL, timestamp, request.getLicenseName()); + handleAttributeList.add(new FdoAttribute(LICENSE_URL, timestamp, request.getLicenseName())); // 416 RightsholderName - new FdoAttribute(RIGHTSHOLDER_NAME, timestamp, request.getRightsholderName()); + handleAttributeList.add( + new FdoAttribute(RIGHTSHOLDER_NAME, timestamp, request.getRightsholderName())); // 417 Rightsholder PID - new FdoAttribute(RIGHTSHOLDER_PID, timestamp, request.getRightsholderPid()); + handleAttributeList.add( + new FdoAttribute(RIGHTSHOLDER_PID, timestamp, request.getRightsholderPid())); // 418 RightsholderPidType if (request.getRightsholderPidType() != null) { - new FdoAttribute(RIGHTSHOLDER_PID_TYPE, timestamp, - request.getRightsholderPidType().toString()); + handleAttributeList.add(new FdoAttribute(RIGHTSHOLDER_PID_TYPE, timestamp, + request.getRightsholderPidType().toString())); } else { - new FdoAttribute(RIGHTSHOLDER_PID_TYPE, timestamp, null); + handleAttributeList.add(new FdoAttribute(RIGHTSHOLDER_PID_TYPE, timestamp, null)); } // 419 dcterms:conformsTo - new FdoAttribute(DC_TERMS_CONFORMS, timestamp, request.getDctermsConformsTo()); + handleAttributeList.add( + new FdoAttribute(DC_TERMS_CONFORMS, timestamp, request.getDctermsConformsTo())); return handleAttributeList; } /* Organisation Record Creation */ public FdoRecord prepareNewOrganisationRecord(OrganisationRequest request, String handle, Instant timestamp) throws InvalidRequestException { - var fdoAttributes = prepareNewDoiFdoAttributes(request, handle, ORGANISATION, timestamp); + var fdoAttributes = prepareNewDoiAttributes(request, handle, ORGANISATION, timestamp); fdoAttributes.addAll(prepareOrganisationAttributesFromRequest(request, handle, timestamp)); return new FdoRecord(handle, ORGANISATION, fdoAttributes, null); } @@ -688,7 +710,7 @@ public FdoRecord prepareNewOrganisationRecord(OrganisationRequest request, Strin public FdoRecord prepareUpdatedOrganisationRecord(OrganisationRequest request, Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) throws InvalidRequestException { - var fdoAttributes = prepareUpdatedDoiFdoAttributes(request, previousVersion.handle(), + var fdoAttributes = prepareUpdatedDoiAttributes(request, previousVersion.handle(), ORGANISATION, timestamp, previousVersion, incrementVersion); fdoAttributes.addAll( prepareOrganisationAttributesFromRequest(request, previousVersion.handle(), timestamp)); @@ -720,25 +742,54 @@ private List prepareOrganisationAttributesFromRequest( /* Source System Record Creation */ public FdoRecord prepareNewSourceSystemRecord(SourceSystemRequest request, String handle, Instant timestamp) throws InvalidRequestException { - var fdoAttributes = prepareNewHandleAttributeList(request, handle, SOURCE_SYSTEM, timestamp); + var fdoAttributes = prepareNewHandleAttributes(request, handle, SOURCE_SYSTEM, timestamp); fdoAttributes.addAll(prepareSourceSystemAttributesFromRequest(request, timestamp)); return new FdoRecord(handle, SOURCE_SYSTEM, fdoAttributes, null); } public FdoRecord prepareUpdatedSourceSystemRecord(SourceSystemRequest request, Instant timestamp, FdoRecord previousVersion, boolean incrementVersion) throws InvalidRequestException { - var fdoAttributes = prepareUpdatedHandleAttributesRecord(request, previousVersion.handle(), + var fdoAttributes = prepareUpdatedHandleAttributes(request, previousVersion.handle(), SOURCE_SYSTEM, timestamp, previousVersion, incrementVersion); fdoAttributes.addAll(prepareSourceSystemAttributesFromRequest(request, timestamp)); - return new FdoRecord(previousVersion.handle(), ORGANISATION, fdoAttributes, null); + return new FdoRecord(previousVersion.handle(), SOURCE_SYSTEM, fdoAttributes, null); + } + + /* Tombstone Record Creation */ + public FdoRecord prepareTombstoneRecord(TombstoneRecordRequest recordRequest, Instant timestamp, + FdoRecord previousVersion) throws JsonProcessingException { + var fdoAttributes = prepareTombstoneAttributes(recordRequest, timestamp, previousVersion); + return new FdoRecord(previousVersion.handle(), previousVersion.fdoType(), fdoAttributes, null); + } + + private List prepareTombstoneAttributes(TombstoneRecordRequest request, + Instant timestamp, FdoRecord previousVersion) throws JsonProcessingException { + var handleAttributeList = new ArrayList<>(previousVersion.attributes()); + var previousIssueNum = getField(previousVersion.attributes(), PID_RECORD_ISSUE_NUMBER); + var newIssueNum = incrementIssueNumber(previousIssueNum, timestamp); + handleAttributeList.set(handleAttributeList.indexOf(previousIssueNum), newIssueNum); + // 30: Tombstoned Text + handleAttributeList.add( + new FdoAttribute(TOMBSTONED_TEXT, timestamp, request.getTombstonedText())); + // 31: hasRelatedPID + if (request.getHasRelatedPID() != null && !request.getHasRelatedPID().isEmpty()) { + handleAttributeList.add(new FdoAttribute(HAS_RELATED_PID, timestamp, + mapper.writeValueAsString(request.getHasRelatedPID()))); + } else { + handleAttributeList.add(new FdoAttribute(HAS_RELATED_PID, timestamp, + mapper.writeValueAsString(Collections.emptyList()))); + } + // 32: tombstonedDate + handleAttributeList.add(new FdoAttribute(TOMBSTONED_DATE, timestamp, getDate(timestamp))); + return handleAttributeList; } private List prepareSourceSystemAttributesFromRequest( SourceSystemRequest request, Instant timestamp) { return List.of( - new FdoAttribute(MAS_NAME, timestamp, request.getSourceSystemName())); + new FdoAttribute(SOURCE_SYSTEM_NAME, timestamp, request.getSourceSystemName())); } private String getObjectName(String url, String name) throws InvalidRequestException { @@ -763,102 +814,6 @@ private static String getRor(String url) throws InvalidRequestException { return url.replace(ROR_DOMAIN, ROR_API_DOMAIN); } - public List prepareUpdateAttributes(byte[] handle, JsonNode requestAttributes, - FdoType type) throws InvalidRequestException { - requestAttributes = setLocationXmlFromJson(requestAttributes, - new String(handle, StandardCharsets.UTF_8), type); - Map updateRequestMap = mapper.convertValue(requestAttributes, - new TypeReference>() { - }); - try { - var updatedAttributeList = new ArrayList<>( - updateRequestMap.entrySet().stream() - .filter(entry -> !entry.getValue().isNull()) - .map(entry -> new HandleAttribute(FdoProfile.retrieveIndex(entry.getKey()), handle, - entry.getKey(), - getUpdateAttributeAsByte(entry.getValue()))) - .toList()); - updatedAttributeList.addAll(addResolvedNames(updateRequestMap, handle)); - return updatedAttributeList; - } catch (InvalidRequestRuntimeException e) { - throw new InvalidRequestException("Unable to parse update request"); - } - } - - private byte[] getUpdateAttributeAsByte(BaseJsonNode attribute) { - try { - if (attribute instanceof TextNode) { - return attribute.asText().getBytes(StandardCharsets.UTF_8); - } else { - return mapper.writeValueAsString(attribute).getBytes(StandardCharsets.UTF_8); - } - } catch (JsonProcessingException e) { - log.error("Unable to parse update request", e); - throw new InvalidRequestRuntimeException(); - } - } - - private List addResolvedNames(Map updateRequestMap, - byte[] handle) throws InvalidRequestException { - var resolvableKeys = updateRequestMap.entrySet().stream().filter( - entry -> RESOLVABLE_KEYS.containsKey(entry.getKey()) && !hasResolvedPairInRequest( - updateRequestMap, entry.getKey())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - if (resolvableKeys.isEmpty()) { - return new ArrayList<>(); - } - ArrayList resolvedPidNameAttributes = new ArrayList<>(); - for (var resolvableKey : resolvableKeys.entrySet()) { - var targetAttribute = RESOLVABLE_KEYS.get(resolvableKey.getKey()); - var resolvedPid = getObjectName(resolvableKey.getValue().asText(), null); - resolvedPidNameAttributes.add(new HandleAttribute(FdoProfile.retrieveIndex(targetAttribute), - handle, targetAttribute, resolvedPid.getBytes(StandardCharsets.UTF_8))); - } - return resolvedPidNameAttributes; - } - - private boolean hasResolvedPairInRequest(Map updateRequestMap, - String pidToResolve) { - var targetName = RESOLVABLE_KEYS.get(pidToResolve); - return updateRequestMap.containsKey(targetName); - } - - public List prepareTombstoneAttributes(byte[] handle, - JsonNode requestAttributes) throws InvalidRequestException { - var tombstoneAttributes = new ArrayList<>( - prepareUpdateAttributes(handle, requestAttributes, FdoType.TOMBSTONE)); - tombstoneAttributes.add(new HandleAttribute(PID_STATUS, handle, "ARCHIVED")); - tombstoneAttributes.add(genLandingPage(handle)); - return tombstoneAttributes; - } - - private HandleAttribute genLandingPage(byte[] handle) throws InvalidRequestException { - var landingPage = new String[]{"Placeholder landing page"}; - var data = setLocationsByte(landingPage, new String(handle, StandardCharsets.UTF_8), - FdoType.TOMBSTONE); - return new HandleAttribute(LOC.index(), handle, LOC.get(), data); - } - - private JsonNode setLocationXmlFromJson(JsonNode request, String handle, FdoType type) - throws InvalidRequestException { - // Format request so that the given locations array is formatted according to 10320/loc specifications - if (request.findValue(LOC_REQUEST) == null) { - return request; - } - JsonNode locNode = request.get(LOC_REQUEST); - ObjectNode requestObjectNode = request.deepCopy(); - try { - String[] locArr = mapper.treeToValue(locNode, String[].class); - requestObjectNode.put(LOC.get(), - new String(setLocationsByte(locArr, handle, type), StandardCharsets.UTF_8)); - requestObjectNode.remove(LOC_REQUEST); - } catch (IOException e) { - throw new InvalidRequestException( - "An error has occurred parsing \"locations\" array. " + e.getMessage()); - } - return requestObjectNode; - } - private String getDate(Instant timestamp) { return dt.format(timestamp); } @@ -870,13 +825,13 @@ private List defaultLocations(String handle, FdoType fdoType) { String ui = applicationProperties.getUiUrl() + "/ds/" + handle; return List.of(api, ui); } - case MAPPING -> { + case DATA_MAPPING -> { return List.of(applicationProperties.getOrchestrationUrl() + "/mapping/" + handle); } case SOURCE_SYSTEM -> { return List.of(applicationProperties.getOrchestrationUrl() + "/source-system/" + handle); } - case MEDIA_OBJECT -> { + case DIGITAL_MEDIA -> { String api = applicationProperties.getApiUrl() + "/digitalMedia/" + handle; String ui = applicationProperties.getUiUrl() + "/dm/" + handle; return List.of(api, ui); @@ -933,37 +888,6 @@ private String setLocations(@Nullable String[] userLocations, String handle, Fdo } } - // Todo delete - public byte[] setLocationsByte(@Nullable String[] userLocations, String handle, FdoType type) - throws InvalidRequestException { - - DocumentBuilder documentBuilder; - try { - documentBuilder = dbf.newDocumentBuilder(); - } catch (ParserConfigurationException e) { - throw new InvalidRequestException(e.getMessage()); - } - - var doc = documentBuilder.newDocument(); - var locations = doc.createElement(LOC_REQUEST); - doc.appendChild(locations); - String[] objectLocations = concatLocations(userLocations, defaultLocations(handle, type)); - - for (int i = 0; i < objectLocations.length; i++) { - var locs = doc.createElement("location"); - locs.setAttribute("id", String.valueOf(i)); - locs.setAttribute("href", objectLocations[i]); - String weight = i < 1 ? "1" : "0"; - locs.setAttribute("weight", weight); - locations.appendChild(locs); - } - try { - return documentToString(doc).getBytes(StandardCharsets.UTF_8); - } catch (TransformerException e) { - throw new InvalidRequestException("An error has occurred parsing location data"); - } - } - private String[] concatLocations(String[] userLocations, List defaultLocations) { var objectLocations = new ArrayList<>(defaultLocations); if (userLocations != null) { diff --git a/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java b/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java index d0e251c6..cab27c4a 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java @@ -11,9 +11,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import eu.dissco.core.handlemanager.Profiles; import eu.dissco.core.handlemanager.domain.fdo.AnnotationRequest; +import eu.dissco.core.handlemanager.domain.fdo.DataMappingRequest; import eu.dissco.core.handlemanager.domain.fdo.DoiRecordRequest; import eu.dissco.core.handlemanager.domain.fdo.HandleRecordRequest; -import eu.dissco.core.handlemanager.domain.fdo.MappingRequest; import eu.dissco.core.handlemanager.domain.fdo.MasRequest; import eu.dissco.core.handlemanager.domain.fdo.OrganisationRequest; import eu.dissco.core.handlemanager.domain.fdo.SourceSystemRequest; @@ -23,14 +23,12 @@ import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.exceptions.UnprocessableEntityException; import eu.dissco.core.handlemanager.properties.ProfileProperties; -import eu.dissco.core.handlemanager.repository.PidMongoRepository; import eu.dissco.core.handlemanager.repository.PidRepository; import java.time.Instant; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.bson.Document; import org.springframework.context.annotation.Profile; @@ -41,10 +39,10 @@ @Profile(Profiles.HANDLE) public class HandleService extends PidService { - public HandleService(PidRepository pidRepository, FdoRecordService fdoRecordService, + public HandleService(FdoRecordService fdoRecordService, PidNameGeneratorService hf, ObjectMapper mapper, ProfileProperties profileProperties, - PidMongoRepository mongoRepository) { - super(pidRepository, fdoRecordService, hf, mapper, profileProperties, mongoRepository); + PidRepository mongoRepository) { + super(fdoRecordService, hf, mapper, profileProperties, mongoRepository); } // Pid Record Creation @@ -53,18 +51,18 @@ public JsonApiWrapperWrite createRecords(List requests) throws Invalid var handles = hf.genHandleList(requests.size()).iterator(); var requestAttributes = requests.stream() .map(request -> request.get(NODE_DATA).get(NODE_ATTRIBUTES)).toList(); - var type = getObjectTypeFromJsonNode(requests); + var fdoType = getObjectTypeFromJsonNode(requests); List fdoRecords; List fdoDocuments; try { - switch (type) { + switch (fdoType) { case ANNOTATION -> fdoRecords = createAnnotation(requestAttributes, handles); case DIGITAL_SPECIMEN -> fdoRecords = createDigitalSpecimen(requestAttributes, handles); case DOI -> fdoRecords = createDoi(requestAttributes, handles); case HANDLE -> fdoRecords = createHandle(requestAttributes, handles); - case MAPPING -> fdoRecords = createMapping(requestAttributes, handles); + case DATA_MAPPING -> fdoRecords = createDataMapping(requestAttributes, handles); case MAS -> fdoRecords = createMas(requestAttributes, handles); - case MEDIA_OBJECT -> fdoRecords = createMediaObject(requestAttributes, handles); + case DIGITAL_MEDIA -> fdoRecords = createDigitalMedia(requestAttributes, handles); case ORGANISATION -> fdoRecords = createOrganisation(requestAttributes, handles); case SOURCE_SYSTEM -> fdoRecords = createSourceSystem(requestAttributes, handles); default -> throw new UnsupportedOperationException("Unrecognized type"); @@ -77,42 +75,40 @@ public JsonApiWrapperWrite createRecords(List requests) throws Invalid } log.info("Persisting new handles to Document Store"); mongoRepository.postBatchHandleRecord(fdoDocuments); - return new JsonApiWrapperWrite(formatCreateRecord(fdoRecords, type)); + return new JsonApiWrapperWrite(formatFdoRecord(fdoRecords, fdoType)); } @Override - public JsonApiWrapperWrite updateRecords(List requests) + public JsonApiWrapperWrite updateRecords(List requests, boolean incrementVersion) throws UnprocessableEntityException, InvalidRequestException { var updateRequests = requests.stream() .map(request -> request.get(NODE_DATA)).toList(); - var handles = updateRequests.stream() - .map(request -> request.get(NODE_ID).asText()).toList(); - var previousVersions = getPreviousVersions(handles); - checkInternalDuplicates(handles); - checkHandlesWritableFullRecord(previousVersions); - var fdoRecordMap = previousVersions.stream() - .collect(Collectors.toMap(FdoRecord::handle, f -> f)); - var type = getObjectTypeFromJsonNode(requests); + var fdoRecordMap = processUpdateRequest(updateRequests); + var fdoType = getObjectTypeFromJsonNode(requests); List fdoRecords; List fdoDocuments; try { - switch (type) { - case ANNOTATION -> fdoRecords = updateAnnotation(updateRequests, fdoRecordMap); - case DIGITAL_SPECIMEN -> fdoRecords = updateDigitalSpecimen(updateRequests, fdoRecordMap); - case DOI -> fdoRecords = updateDoi(updateRequests, fdoRecordMap); - case HANDLE -> fdoRecords = updateHandle(updateRequests, fdoRecordMap); - case MAPPING -> fdoRecords = updateDataMapping(updateRequests, fdoRecordMap); - case MAS -> fdoRecords = updateMas(updateRequests, fdoRecordMap); - case MEDIA_OBJECT -> fdoRecords = updateDigitalMedia(updateRequests, fdoRecordMap); - case ORGANISATION -> fdoRecords = updateOrganisation(updateRequests, fdoRecordMap); - case SOURCE_SYSTEM -> fdoRecords = updateSourceSystem(updateRequests, fdoRecordMap); + switch (fdoType) { + case ANNOTATION -> + fdoRecords = updateAnnotation(updateRequests, fdoRecordMap, incrementVersion); + case DIGITAL_SPECIMEN -> + fdoRecords = updateDigitalSpecimen(updateRequests, fdoRecordMap, incrementVersion); + case DOI -> fdoRecords = updateDoi(updateRequests, fdoRecordMap, incrementVersion); + case HANDLE -> fdoRecords = updateHandle(updateRequests, fdoRecordMap, incrementVersion); + case DATA_MAPPING -> + fdoRecords = updateDataMapping(updateRequests, fdoRecordMap, incrementVersion); + case MAS -> fdoRecords = updateMas(updateRequests, fdoRecordMap, incrementVersion); + case DIGITAL_MEDIA -> + fdoRecords = updateDigitalMedia(updateRequests, fdoRecordMap, incrementVersion); + case ORGANISATION -> + fdoRecords = updateOrganisation(updateRequests, fdoRecordMap, incrementVersion); + case SOURCE_SYSTEM -> + fdoRecords = updateSourceSystem(updateRequests, fdoRecordMap, incrementVersion); default -> throw new UnsupportedOperationException("Unrecognized type"); } fdoDocuments = fdoRecordService.toMongoDbDocument(fdoRecords); - var fdoType = getObjectTypeFromJsonNode(requests); - mongoRepository.updateHandleRecord(fdoDocuments, handles); - // todo figure out response - return new JsonApiWrapperWrite(formatCreateRecord(fdoRecords, fdoType)); + mongoRepository.updateHandleRecord(fdoDocuments); + return new JsonApiWrapperWrite(formatFdoRecord(fdoRecords, fdoType)); } catch (JsonProcessingException e) { log.error("An error has occurred processing JSON data", e); throw new InvalidRequestException(); @@ -134,15 +130,15 @@ private List createAnnotation(List requestAttributes, } private List updateAnnotation(List updateRequests, - Map previousVersionMap) + Map previousVersionMap, boolean incrementVersion) throws JsonProcessingException, InvalidRequestException { List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); for (var request : updateRequests) { var requestObject = mapper.treeToValue(request.get(NODE_ATTRIBUTES), AnnotationRequest.class); fdoRecords.add( - fdoRecordService.prepareUpdatedAnnotationFdoRecord(requestObject, timestamp, - previousVersionMap.get(request.get(NODE_ID).asText()), true)); + fdoRecordService.prepareUpdatedAnnotationRecord(requestObject, timestamp, + previousVersionMap.get(request.get(NODE_ID).asText()), incrementVersion)); } return fdoRecords; } @@ -154,22 +150,22 @@ private List createDoi(List requestAttributes, var timestamp = Instant.now(); for (var request : requestAttributes) { var requestObject = mapper.treeToValue(request, DoiRecordRequest.class); - fdoRecords.add(fdoRecordService.prepareNewDoiDocument(requestObject, handleIterator.next(), + fdoRecords.add(fdoRecordService.prepareNewDoiRecord(requestObject, handleIterator.next(), DOI, timestamp)); } return fdoRecords; } private List updateDoi(List updateRequests, - Map previousVersionMap) + Map previousVersionMap, boolean incrementVersion) throws JsonProcessingException, InvalidRequestException { List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); for (var request : updateRequests) { var requestObject = mapper.treeToValue(request.get(NODE_ATTRIBUTES), DoiRecordRequest.class); fdoRecords.add( - fdoRecordService.prepareUpdatedDoiFdoRecord(requestObject, DOI, timestamp, - previousVersionMap.get(request.get(NODE_ID).asText()), true)); + fdoRecordService.prepareUpdatedDoiRecord(requestObject, DOI, timestamp, + previousVersionMap.get(request.get(NODE_ID).asText()), incrementVersion)); } return fdoRecords; } @@ -188,7 +184,7 @@ private List createHandle(List requestAttributes, } private List updateHandle(List updateRequests, - Map previousVersionMap) + Map previousVersionMap, boolean incrementVersion) throws JsonProcessingException, InvalidRequestException { List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); @@ -197,17 +193,17 @@ private List updateHandle(List updateRequests, HandleRecordRequest.class); fdoRecords.add( fdoRecordService.prepareUpdatedHandleRecord(requestObject, HANDLE, timestamp, - previousVersionMap.get(request.get(NODE_ID).asText()), true)); + previousVersionMap.get(request.get(NODE_ID).asText()), incrementVersion)); } return fdoRecords; } - private List createMapping(List requestAttributes, + private List createDataMapping(List requestAttributes, Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); for (var request : requestAttributes) { - var requestObject = mapper.treeToValue(request, MappingRequest.class); + var requestObject = mapper.treeToValue(request, DataMappingRequest.class); fdoRecords.add( fdoRecordService.prepareNewDataMappingRecord(requestObject, handleIterator.next(), timestamp)); @@ -216,15 +212,16 @@ private List createMapping(List requestAttributes, } private List updateDataMapping(List updateRequests, - Map previousVersionMap) + Map previousVersionMap, boolean incrementVersion) throws JsonProcessingException, InvalidRequestException { List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); for (var request : updateRequests) { - var requestObject = mapper.treeToValue(request.get(NODE_ATTRIBUTES), MappingRequest.class); + var requestObject = mapper.treeToValue(request.get(NODE_ATTRIBUTES), + DataMappingRequest.class); fdoRecords.add( fdoRecordService.prepareUpdatedDataMappingRecord(requestObject, timestamp, - previousVersionMap.get(request.get(NODE_ID).asText()), true)); + previousVersionMap.get(request.get(NODE_ID).asText()), incrementVersion)); } return fdoRecords; } @@ -242,7 +239,7 @@ private List createMas(List requestAttributes, } private List updateMas(List updateRequests, - Map previousVersionMap) + Map previousVersionMap, boolean incrementVersion) throws JsonProcessingException, InvalidRequestException { List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); @@ -250,7 +247,7 @@ private List updateMas(List updateRequests, var requestObject = mapper.treeToValue(request.get(NODE_ATTRIBUTES), MasRequest.class); fdoRecords.add( fdoRecordService.prepareUpdatedMasRecord(requestObject, timestamp, - previousVersionMap.get(request.get(NODE_ID).asText()), true)); + previousVersionMap.get(request.get(NODE_ID).asText()), incrementVersion)); } return fdoRecords; } @@ -269,7 +266,7 @@ private List createOrganisation(List requestAttributes, } private List updateOrganisation(List updateRequests, - Map previousVersionMap) + Map previousVersionMap, boolean incrementVersion) throws JsonProcessingException, InvalidRequestException { List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); @@ -278,7 +275,7 @@ private List updateOrganisation(List updateRequests, OrganisationRequest.class); fdoRecords.add( fdoRecordService.prepareUpdatedOrganisationRecord(requestObject, timestamp, - previousVersionMap.get(request.get(NODE_ID).asText()), true)); + previousVersionMap.get(request.get(NODE_ID).asText()), incrementVersion)); } return fdoRecords; } @@ -297,7 +294,7 @@ private List createSourceSystem(List requestAttributes, } private List updateSourceSystem(List updateRequests, - Map previousVersionMap) + Map previousVersionMap, boolean incrementVersion) throws JsonProcessingException, InvalidRequestException { List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); @@ -306,7 +303,7 @@ private List updateSourceSystem(List updateRequests, SourceSystemRequest.class); fdoRecords.add( fdoRecordService.prepareUpdatedSourceSystemRecord(requestObject, timestamp, - previousVersionMap.get(request.get(NODE_ID).asText()), true)); + previousVersionMap.get(request.get(NODE_ID).asText()), incrementVersion)); } return fdoRecords; } diff --git a/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java b/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java index b827a9da..73e48974 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java @@ -22,14 +22,18 @@ public class PidNameGeneratorService { private final ApplicationProperties applicationProperties; private static final int LENGTH = 11; - private static final String ALPHA_NUM = "ABCDEFGHJKLMNPQRSTVWXYZ1234567890"; - private final char[] symbols = ALPHA_NUM.toCharArray(); + private static final char[] SYMBOLS = "ABCDEFGHJKLMNPQRSTVWXYZ1234567890".toCharArray(); private final char[] buf = new char[LENGTH]; private final PidRepository pidRepository; private final Random random; public List genHandleList(int h) { + if (h > applicationProperties.getMaxHandles()) { + log.warn("Max number of handles exceeded. Generating maximum {} handles instead", + applicationProperties.getMaxHandles()); + h = applicationProperties.getMaxHandles(); + } return new ArrayList<>(genHandleHashSet(h)); } @@ -47,7 +51,7 @@ private Set genHandleHashSet(int h) { var handleSet = new HashSet<>(handleList); // Check for duplicates from repository and wrap the duplicates - var duplicates = new HashSet<>(pidRepository.getHandlesExist(handleList)); + var duplicates = new HashSet<>(pidRepository.getExistingHandles(handleList)); // If a duplicate was found, recursively call this function // Generate new handles for every duplicate found and add it to our hash list @@ -56,7 +60,6 @@ private Set genHandleHashSet(int h) { handleSet.removeAll(duplicates); handleSet.addAll(genHandleHashSet(duplicates.size())); } - /* * It's possible we have a collision within our list now i.e. on two different * recursive cal)ls to this function, we generate the same If this occurs, we @@ -72,21 +75,11 @@ private List newHandles(int numberOfHandles) { // Generates h number of if (numberOfHandles < 1) { return Collections.emptyList(); } - if (numberOfHandles > applicationProperties.getMaxHandles()) { - log.warn("Max number of handles exceeded. Generating maximum {} handles instead", - applicationProperties.getMaxHandles()); - numberOfHandles = applicationProperties.getMaxHandles(); - } - // We'll use this to make sure we're not duplicating results - // It's of type ByteBuffer and not byte[] because ByteBuffer has equality testing - // byte[] is too primitive for our needs HashSet handleHash = new HashSet<>(); - // This is the object we'll actually return var handleList = new ArrayList(); String hdl; - for (int i = 0; i < numberOfHandles; i++) { hdl = newHandle(); while (!handleHash.add(hdl)) { @@ -101,13 +94,12 @@ private String newHandle() { return applicationProperties.getPrefix() + "/" + newSuffix(); } - private String newSuffix() { for (int idx = 0; idx < buf.length; ++idx) { if (idx == 3 || idx == 7) { // buf[idx] = '-'; // Sneak a lil dash in the middle } else { - buf[idx] = symbols[random.nextInt(symbols.length)]; + buf[idx] = SYMBOLS[random.nextInt(SYMBOLS.length)]; } } return new String(buf); diff --git a/src/main/java/eu/dissco/core/handlemanager/service/PidService.java b/src/main/java/eu/dissco/core/handlemanager/service/PidService.java index 2806f0a0..762ddac4 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/PidService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/PidService.java @@ -1,43 +1,42 @@ package eu.dissco.core.handlemanager.service; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.ANNOTATION_HASH; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DIGITAL_OBJECT_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.HS_ADMIN; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.LINKED_DO_PID; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.NORMALISED_SPECIMEN_OBJECT_ID; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PID; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PID_STATUS; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PRIMARY_SPECIMEN_OBJECT_ID; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PRIMARY_MEDIA_ID; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.ANNOTATION; +import static eu.dissco.core.handlemanager.domain.fdo.FdoType.DIGITAL_MEDIA; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.DIGITAL_SPECIMEN; +import static eu.dissco.core.handlemanager.domain.fdo.FdoType.TOMBSTONE; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ATTRIBUTES; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_DATA; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ID; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_TYPE; +import static eu.dissco.core.handlemanager.service.ServiceUtils.getField; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import eu.dissco.core.handlemanager.domain.fdo.DigitalMediaRequest; import eu.dissco.core.handlemanager.domain.fdo.DigitalSpecimenRequest; import eu.dissco.core.handlemanager.domain.fdo.FdoProfile; import eu.dissco.core.handlemanager.domain.fdo.FdoType; -import eu.dissco.core.handlemanager.domain.fdo.MediaObjectRequest; +import eu.dissco.core.handlemanager.domain.fdo.TombstoneRecordRequest; +import eu.dissco.core.handlemanager.domain.fdo.vocabulary.PidStatus; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiDataLinks; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiLinks; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperRead; -import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperReadSingle; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperWrite; import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoAttribute; import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoRecord; -import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.exceptions.UnprocessableEntityException; import eu.dissco.core.handlemanager.properties.ProfileProperties; -import eu.dissco.core.handlemanager.repository.PidMongoRepository; import eu.dissco.core.handlemanager.repository.PidRepository; -import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.ArrayList; import java.util.Collections; @@ -49,112 +48,77 @@ import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.bson.Document; @Slf4j @RequiredArgsConstructor public abstract class PidService { - protected final PidRepository pidRepository; protected final FdoRecordService fdoRecordService; protected final PidNameGeneratorService hf; protected final ObjectMapper mapper; protected final ProfileProperties profileProperties; - protected final PidMongoRepository mongoRepository; + protected final PidRepository mongoRepository; - private List formatRecords(List dbRecord) { - var handleMap = mapRecords(dbRecord); - return handleMap.values().stream().map(this::jsonFormatSingleRecordOld).toList(); - } - - // todo Delete - protected JsonNode jsonFormatSingleRecordOld(List dbRecord) { + protected JsonNode jsonFormatSingleRecord(List fdoAttributes) { ObjectNode rootNode = mapper.createObjectNode(); - for (var row : dbRecord) { - if (row.getIndex() != HS_ADMIN.index()) { - var rowData = new String(row.getData(), StandardCharsets.UTF_8); - try { - var nodeData = mapper.readTree(rowData); - rootNode.set(row.getType(), nodeData); - } catch (JsonProcessingException ignored) { - rootNode.put(row.getType(), rowData); - } - } - } + fdoAttributes.stream() + .filter(attribute -> attribute.getIndex() != HS_ADMIN.index()) + .forEach(attribute -> setNodeData(attribute, rootNode)); return rootNode; } - protected JsonNode jsonFormatSingleRecord(List dbRecord) { - ObjectNode rootNode = mapper.createObjectNode(); - for (var row : dbRecord) { - if (row.getIndex() != HS_ADMIN.index()) { - try { - var nodeData = mapper.readTree(row.getValue()); - rootNode.set(row.getType(), nodeData); - } catch (JsonProcessingException ignored) { - rootNode.put(row.getType(), row.getValue()); - } - } - } - return rootNode; - } - - protected JsonNode jsonFormatSingleRecord(List dbRecord, + protected JsonNode jsonFormatSingleRecord(List fdoAttributes, List keyAttributes) { ObjectNode rootNode = mapper.createObjectNode(); var indexList = keyAttributes.stream().map(FdoProfile::index).toList(); - for (var row : dbRecord) { - if (indexList.contains(row.getIndex())) { - try { - var nodeData = mapper.readTree(row.getValue()); - rootNode.set(row.getType(), nodeData); - } catch (JsonProcessingException ignored) { - rootNode.put(row.getType(), row.getValue()); - } - } - } + fdoAttributes.stream() + .filter(attribute -> indexList.contains(attribute.getIndex())) + .forEach(attribute -> setNodeData(attribute, rootNode)); return rootNode; } - - protected Map> mapRecords(List flatList) { - return flatList.stream() - .collect(Collectors.groupingBy(row -> new String(row.getHandle(), StandardCharsets.UTF_8))); - } - - private JsonApiDataLinks wrapResolvedData(JsonNode recordAttributes, String recordType) { - String pidLink = recordAttributes.get(PID.get()).asText(); - String pidName = getPidName(pidLink); - var handleLink = new JsonApiLinks(pidLink); - return new JsonApiDataLinks(pidName, recordType, recordAttributes, handleLink); - } - - private String getPidName(String pidLink) { - return pidLink.substring(profileProperties.getDomain().length()); + private void setNodeData(FdoAttribute attribute, ObjectNode rootNode) { + if (attribute.getValue() == null) { + rootNode.set(attribute.getType(), mapper.nullNode()); + } else { + try { + var nodeData = mapper.readTree(attribute.getValue()); + rootNode.set(attribute.getType(), nodeData); + } catch (JsonProcessingException ignored) { + rootNode.put(attribute.getType(), attribute.getValue()); + } + } } - protected List formatCreateRecord(List fdoRecords, + protected List formatFdoRecord(List fdoRecords, FdoType fdoType) { switch (fdoType) { case ANNOTATION -> { - return formatCreateRecordsAnnotation(fdoRecords); + return formatAnnotationResponse(fdoRecords); } case DIGITAL_SPECIMEN -> { - return formatCreateRecordsSpecimen(fdoRecords); + return formatSpecimenResponse(fdoRecords); } - case MEDIA_OBJECT -> { - return formatCreateRecordsMedia(fdoRecords); + case DIGITAL_MEDIA -> { + return formatMediaResponse(fdoRecords); } default -> { - return formatCreateRecordsDefault(fdoRecords, fdoType); + return formatFullRecordResponse(fdoRecords); } } } - private List formatCreateRecordsAnnotation(List fdoRecords) { + private List formatAnnotationResponse(List fdoRecords) { List dataLinksList = new ArrayList<>(); for (var handleRecord : fdoRecords) { - var attributeNode = jsonFormatSingleRecord(handleRecord.attributes(), - List.of(ANNOTATION_HASH)); + JsonNode attributeNode; + if (handleRecord.primaryLocalId() == null) { + attributeNode = jsonFormatSingleRecord(handleRecord.attributes()); + } else { + attributeNode = jsonFormatSingleRecord(handleRecord.attributes(), + List.of(ANNOTATION_HASH)); + } String pidLink = profileProperties.getDomain() + handleRecord.handle(); dataLinksList.add( new JsonApiDataLinks(handleRecord.handle(), ANNOTATION.getDigitalObjectType(), @@ -164,11 +128,11 @@ private List formatCreateRecordsAnnotation(List fdo return dataLinksList; } - private List formatCreateRecordsSpecimen(List fdoRecords) { + private List formatSpecimenResponse(List fdoRecords) { List dataLinksList = new ArrayList<>(); for (var handleRecord : fdoRecords) { var attributeNode = jsonFormatSingleRecord(handleRecord.attributes(), - List.of(PRIMARY_SPECIMEN_OBJECT_ID)); + List.of(NORMALISED_SPECIMEN_OBJECT_ID)); String pidLink = profileProperties.getDomain() + handleRecord.handle(); dataLinksList.add( new JsonApiDataLinks(handleRecord.handle(), DIGITAL_SPECIMEN.getDigitalObjectType(), @@ -177,110 +141,77 @@ private List formatCreateRecordsSpecimen(List fdoRe return dataLinksList; } - private List formatCreateRecordsMedia(List fdoRecords) { + private List formatMediaResponse(List fdoRecords) { List dataLinksList = new ArrayList<>(); for (var handleRecord : fdoRecords) { var attributeNode = jsonFormatSingleRecord(handleRecord.attributes(), - List.of(PRIMARY_SPECIMEN_OBJECT_ID, LINKED_DO_PID)); + List.of(PRIMARY_MEDIA_ID, LINKED_DO_PID)); String pidLink = profileProperties.getDomain() + handleRecord.handle(); dataLinksList.add( - new JsonApiDataLinks(handleRecord.handle(), DIGITAL_SPECIMEN.getDigitalObjectType(), + new JsonApiDataLinks(handleRecord.handle(), DIGITAL_MEDIA.getDigitalObjectType(), attributeNode, new JsonApiLinks(pidLink))); } return dataLinksList; } - private List formatCreateRecordsDefault(List fdoRecords, - FdoType fdoType) { + private List formatFullRecordResponse(List fdoRecords) { List dataLinksList = new ArrayList<>(); - for (var handleRecord : fdoRecords) { - var rootNode = jsonFormatSingleRecord(handleRecord.attributes()); - String pidLink = profileProperties.getDomain() + handleRecord.handle(); + for (var fdoRecord : fdoRecords) { + var rootNode = jsonFormatSingleRecord(fdoRecord.attributes()); + String pidLink = profileProperties.getDomain() + fdoRecord.handle(); dataLinksList.add( - new JsonApiDataLinks(handleRecord.handle(), fdoType.getDigitalObjectType(), rootNode, + new JsonApiDataLinks(fdoRecord.handle(), fdoRecord.fdoType().getDigitalObjectType(), + rootNode, new JsonApiLinks(pidLink))); } return dataLinksList; } - private JsonApiWrapperWrite formatArchives(List> archiveRecords) { - List dataList = new ArrayList<>(); - for (var archiveRecord : archiveRecords) { - String handle = new String(archiveRecord.get(0).getHandle(), StandardCharsets.UTF_8); - var attributeNode = jsonFormatSingleRecordOld(archiveRecord); - dataList.add( - new JsonApiDataLinks(handle, FdoType.TOMBSTONE.getDigitalObjectType(), attributeNode, - new JsonApiLinks(profileProperties.getDomain() + handle))); - } - return new JsonApiWrapperWrite(dataList); - } - - public JsonApiWrapperReadSingle resolveSingleRecord(byte[] handle, String path) - throws PidResolutionException { - var dbRecord = pidRepository.resolveHandleAttributes(handle); - verifyHandleResolution(List.of(handle), dbRecord); - var recordAttributeList = formatRecords(dbRecord).get(0); - var dataNode = wrapResolvedData(recordAttributeList, getRecordTypeFromResolvedRecord(dbRecord)); - var linksNode = new JsonApiLinks(path); - return new JsonApiWrapperReadSingle(linksNode, dataNode); - } - - private String getRecordTypeFromResolvedRecord(List dbRecord) { - var type = dbRecord.stream().filter(row -> row.getType().equals(DIGITAL_OBJECT_TYPE.get())) - .map(val -> new String(val.getData(), StandardCharsets.UTF_8)).findFirst(); - return type.orElse(FdoType.HANDLE.getDigitalObjectType()); - } - - public JsonApiWrapperRead resolveBatchRecord(List handles, String path) - throws PidResolutionException { - var dbRecords = pidRepository.resolveHandleAttributes(handles); - verifyHandleResolution(handles, dbRecords); - var recordAttributeList = formatRecords(dbRecords); - var dataList = recordAttributeList.stream().map( - recordAttributes -> wrapResolvedData(recordAttributes, - getRecordTypeFromResolvedRecord(dbRecords))).toList(); - return new JsonApiWrapperRead(new JsonApiLinks(path), dataList); - } - // Getters - public List getHandlesPaged(int pageNum, int pageSize, byte[] pidStatus) { - return pidRepository.getAllHandles(pidStatus, pageNum, pageSize); - } - - public List getHandlesPaged(int pageNum, int pageSize) { - return pidRepository.getAllHandles(pageNum, pageSize); + public JsonApiWrapperRead resolveSingleRecord(String handle, String path) + throws PidResolutionException { + return resolveBatchRecord(List.of(handle), path); } - private void verifyHandleResolution(List handles, List dbRecords) + public JsonApiWrapperRead resolveBatchRecord(List handles, String path) throws PidResolutionException { - var resolvedHandles = dbRecords.stream().map(HandleAttribute::getHandle) - .map(handle -> new String(handle, StandardCharsets.UTF_8)).collect(Collectors.toSet()); - if (handles.size() == resolvedHandles.size()) { - return; + List fdoRecords; + try { + fdoRecords = mongoRepository.getHandleRecords(handles); + } catch (JsonProcessingException e) { + log.error("JsonProcessingException", e); + throw new PidResolutionException(""); } - var handlesString = handles.stream().map(handle -> new String(handle, StandardCharsets.UTF_8)) - .collect(Collectors.toSet()); - handlesString.removeAll(resolvedHandles); - log.error("Unable to resolve the following identifiers: {}", handlesString); - throw new PidResolutionException("PIDs not found: " + handlesString); + if (fdoRecords.size() < handles.size()) { + var hdl = new ArrayList<>(handles); + var missingHandles = hdl.removeAll(fdoRecords.stream().map(FdoRecord::handle).toList()); + log.error("Some handles do not exist: {}", missingHandles); + throw new PidResolutionException( + "Attempting to resolve handles that do not exist: \n" + missingHandles); + } + return new JsonApiWrapperRead(new JsonApiLinks(path), formatFullRecordResponse(fdoRecords)); } public JsonApiWrapperWrite searchByPhysicalSpecimenId(String normalisedPhysicalId) throws PidResolutionException { - var returnedRows = pidRepository.searchByNormalisedPhysicalIdentifierFullRecord( - List.of(normalisedPhysicalId.getBytes(StandardCharsets.UTF_8))); - var handleNames = returnedRows.stream() - .map(row -> new String(row.getHandle(), StandardCharsets.UTF_8)) - .collect(Collectors.toSet()); - if (handleNames.size() > 1) { + List specimen; + try { + specimen = mongoRepository.searchByPrimaryLocalId(NORMALISED_SPECIMEN_OBJECT_ID.get(), + List.of(normalisedPhysicalId)); + if (specimen.size() != 1) { + if (specimen.size() > 1) { + log.error("Multiple fdo records found for normalised specimen id {}", + normalisedPhysicalId); + } + throw new PidResolutionException( + "Unable to resolve specimen with id" + normalisedPhysicalId); + } + } catch (JsonProcessingException e) { + log.error("JsonProcessingException", e); throw new PidResolutionException( - "More than one handle record corresponds to the provided collection facility and physical identifier."); + "Unable to resolve specimen with with id " + normalisedPhysicalId); } - List dataNode = new ArrayList<>(); - - var jsonFormattedRecord = jsonFormatSingleRecordOld(returnedRows); - dataNode.add(wrapResolvedData(jsonFormattedRecord, DIGITAL_SPECIMEN.getDigitalObjectType())); - return new JsonApiWrapperWrite(dataNode); + return new JsonApiWrapperWrite(formatFullRecordResponse(specimen)); } // Create @@ -302,28 +233,30 @@ protected List getPreviousVersions(List handles) } // Update - public abstract JsonApiWrapperWrite updateRecords(List requests) + public abstract JsonApiWrapperWrite updateRecords(List requests, + boolean incrementVersion) throws InvalidRequestException, UnprocessableEntityException; - protected void checkHandlesWritableFullRecord(List previousVersions) + protected void checkHandlesWritable(List previousVersions) throws InvalidRequestException { for (var fdoRecord : previousVersions) { - var pidStatus = getField(fdoRecord.attributes(), PID_STATUS); - if ("ARCHIVED".equals(pidStatus)) { + var pidStatus = getField(fdoRecord.attributes(), PID_STATUS).getValue(); + if (PidStatus.TOMBSTONED.name().equals(pidStatus)) { log.error("Attempting to update a FDO record that has been archived"); throw new InvalidRequestException(); } } } - protected static FdoAttribute getField(List fdoAttributes, FdoProfile targetField) { - for (var attribute : fdoAttributes) { - if (attribute.getIndex() == targetField.index()) { - return attribute; - } - } - log.error("Unable to find field {} in record {}", targetField, fdoAttributes); - throw new IllegalStateException(); + protected Map processUpdateRequest(List updateRequests) + throws InvalidRequestException { + var handles = updateRequests.stream() + .map(request -> request.get(NODE_ID).asText()).toList(); + var previousVersions = getPreviousVersions(handles); + checkInternalDuplicates(handles); + checkHandlesWritable(previousVersions); + return previousVersions.stream() + .collect(Collectors.toMap(FdoRecord::handle, f -> f)); } protected FdoType getObjectTypeFromJsonNode(List requests) { @@ -360,7 +293,7 @@ protected List createDigitalSpecimen(List requestAttributes } protected List updateDigitalSpecimen(List updateRequests, - Map previousVersionMap) + Map previousVersionMap, boolean incrementVersion) throws JsonProcessingException, InvalidRequestException { List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); @@ -369,26 +302,26 @@ protected List updateDigitalSpecimen(List updateRequests, DigitalSpecimenRequest.class); fdoRecords.add( fdoRecordService.prepareUpdatedDigitalSpecimenRecord(requestObject, timestamp, - previousVersionMap.get(request.get(NODE_ID).asText()), true)); + previousVersionMap.get(request.get(NODE_ID).asText()), incrementVersion)); } return fdoRecords; } - protected List createMediaObject(List requestAttributes, + protected List createDigitalMedia(List requestAttributes, Iterator handleIterator) throws JsonProcessingException, InvalidRequestException { List fdoRecords = new ArrayList<>(); - List mediaRequests = new ArrayList<>(); + List mediaRequests = new ArrayList<>(); var timestamp = Instant.now(); for (var request : requestAttributes) { - mediaRequests.add(mapper.treeToValue(request, MediaObjectRequest.class)); + mediaRequests.add(mapper.treeToValue(request, DigitalMediaRequest.class)); } if (mediaRequests.isEmpty()) { return Collections.emptyList(); } verifyObjectsAreNew(mediaRequests .stream() - .map(MediaObjectRequest::getPrimaryMediaId) + .map(DigitalMediaRequest::getPrimaryMediaId) .toList()); for (var mediaRequest : mediaRequests) { fdoRecords.add( @@ -398,18 +331,17 @@ protected List createMediaObject(List requestAttributes, return fdoRecords; } - protected List updateDigitalMedia(List updateRequests, - Map previousVersionMap) + Map previousVersionMap, boolean incrementVersion) throws JsonProcessingException, InvalidRequestException { List fdoRecords = new ArrayList<>(); var timestamp = Instant.now(); for (var request : updateRequests) { var requestObject = mapper.treeToValue(request.get(NODE_ATTRIBUTES), - MediaObjectRequest.class); + DigitalMediaRequest.class); fdoRecords.add( fdoRecordService.prepareUpdatedDigitalMediaRecord(requestObject, timestamp, - previousVersionMap.get(request.get(NODE_ID).asText()), true)); + previousVersionMap.get(request.get(NODE_ID).asText()), incrementVersion)); } return fdoRecords; } @@ -432,41 +364,6 @@ private void verifyObjectsAreNew(List normalisedIds) } } - // Update - public JsonApiWrapperWrite updateRecords(List> attributesToUpdate, - boolean incrementVersion, FdoType recordType) throws InvalidRequestException { - var recordTimestamp = Instant.now().getEpochSecond(); - var handles = attributesToUpdate.stream().map(pidRecord -> pidRecord.get(0).getHandle()) - .toList(); - //checkInternalDuplicates(handles); - //checkHandlesWritable(handles); - log.info("Writing updates to db"); - pidRepository.updateRecordBatch(recordTimestamp, attributesToUpdate, incrementVersion); - return formatUpdates(handles.stream().map(h -> new String(h, StandardCharsets.UTF_8)).toList(), - recordType); - } - - public JsonApiWrapperWrite updateRecords(List requests, boolean incrementVersion) - throws InvalidRequestException, UnprocessableEntityException { - List> attributesToUpdate = getAttributesToUpdate(requests); - var recordType = getObjectTypeFromJsonNode(requests); - return updateRecords(attributesToUpdate, incrementVersion, recordType); - } - - protected List> getAttributesToUpdate(List requests) - throws InvalidRequestException { - List> attributesToUpdate = new ArrayList<>(); - for (JsonNode root : requests) { - JsonNode data = root.get(NODE_DATA); - byte[] handle = data.get(NODE_ID).asText().getBytes(StandardCharsets.UTF_8); - JsonNode requestAttributes = data.get(NODE_ATTRIBUTES); - FdoType type = FdoType.fromString(data.get(NODE_TYPE).asText()); - var attributes = fdoRecordService.prepareUpdateAttributes(handle, requestAttributes, type); - attributesToUpdate.add(attributes); - } - return attributesToUpdate; - } - protected void checkInternalDuplicates(List handles) throws InvalidRequestException { Set handlesToUpdate = new HashSet<>(handles); if (handlesToUpdate.size() < handles.size()) { @@ -487,65 +384,37 @@ private Set findDuplicates(List handles, Set handlesToUp return duplicateHandles; } - protected JsonApiWrapperWrite formatUpdates(List handles, FdoType type) { - List dataList = new ArrayList<>(); - for (var handle : handles) { - dataList.add(new JsonApiDataLinks(handle, type.getDigitalObjectType(), null, - new JsonApiLinks(profileProperties.getDomain() + handle))); - } - return new JsonApiWrapperWrite(dataList); - } - - protected void checkHandlesWritable(List handles) throws PidResolutionException { - Set handlesToUpdate = new HashSet<>(handles); - Set handlesExist = new HashSet<>(pidRepository.checkHandlesWritable(handles)); - if (handlesExist.size() < handles.size()) { - handlesToUpdate.removeAll(handlesExist); - Set handlesDontExist = handlesToUpdate.stream() - .map(h -> new String(h, StandardCharsets.UTF_8)).collect(Collectors.toSet()); - throw new PidResolutionException( - "INVALID INPUT. One or more identifiers in request do not exist or are archived. Verify the following handle(s): " - + handlesDontExist); - } - } - - // Archive - public JsonApiWrapperWrite archiveRecordBatch(List requests) + // Tombstone + public JsonApiWrapperWrite tombstoneRecordBatch(List requests) throws InvalidRequestException { - var recordTimestamp = Instant.now().getEpochSecond(); - var handles = new ArrayList(); - var archiveAttributesFlat = new ArrayList(); - var archiveAttributes = new ArrayList>(); - - for (JsonNode root : requests) { - JsonNode data = root.get(NODE_DATA); - JsonNode requestAttributes = data.get(NODE_ATTRIBUTES); - var handle = data.get(NODE_ID).asText(); - handles.add(handle); - var recordAttributes = fdoRecordService.prepareTombstoneAttributes(handle.getBytes( - StandardCharsets.UTF_8), requestAttributes); - archiveAttributesFlat.addAll(recordAttributes); - archiveAttributes.add(recordAttributes); + var tombstoneRequestData = requests.stream() + .map(request -> request.get(NODE_DATA)).toList(); + var fdoRecordMap = processUpdateRequest(tombstoneRequestData); + var fdoRecords = new ArrayList(); + var timestamp = Instant.now(); + List fdoDocuments; + try { + for (var requestData : tombstoneRequestData) { + var tombstoneRequest = mapper.treeToValue(requestData.get(NODE_ATTRIBUTES), + TombstoneRecordRequest.class); + fdoRecords.add(fdoRecordService.prepareTombstoneRecord(tombstoneRequest, timestamp, + fdoRecordMap.get(requestData.get(NODE_ID).asText()))); + } + fdoDocuments = fdoRecordService.toMongoDbDocument(fdoRecords); + } catch (JsonProcessingException e) { + log.error("JsonProcessingException", e); + throw new InvalidRequestException("Unable to read request"); } - - checkInternalDuplicates(handles); -// checkHandlesWritable(handles); - - pidRepository.archiveRecords(recordTimestamp, archiveAttributesFlat, handles); - - return formatArchives(archiveAttributes); + mongoRepository.updateHandleRecord(fdoDocuments); + return new JsonApiWrapperWrite(formatFdoRecord(fdoRecords, TOMBSTONE)); } public void rollbackHandles(List handles) { - pidRepository.rollbackHandles(handles); + mongoRepository.rollbackHandles(handles); } public void rollbackHandlesFromPhysId(List physicalIds) { - var physicalIdsBytes = physicalIds.stream().map(id -> id.getBytes(StandardCharsets.UTF_8)) - .toList(); - var handles = pidRepository.searchByNormalisedPhysicalIdentifier(physicalIdsBytes).stream() - .map(ha -> new String(ha.getHandle(), StandardCharsets.UTF_8)).toList(); - pidRepository.rollbackHandles(handles); + mongoRepository.rollbackHandles(NORMALISED_SPECIMEN_OBJECT_ID.get(), physicalIds); } } diff --git a/src/main/java/eu/dissco/core/handlemanager/service/ServiceUtils.java b/src/main/java/eu/dissco/core/handlemanager/service/ServiceUtils.java new file mode 100644 index 00000000..edfc4ef4 --- /dev/null +++ b/src/main/java/eu/dissco/core/handlemanager/service/ServiceUtils.java @@ -0,0 +1,25 @@ +package eu.dissco.core.handlemanager.service; + +import eu.dissco.core.handlemanager.domain.fdo.FdoProfile; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoAttribute; +import java.util.List; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class ServiceUtils { + + private ServiceUtils() { + } + + public static FdoAttribute getField(List fdoAttributes, FdoProfile targetField) { + for (var attribute : fdoAttributes) { + if (attribute.getIndex() == targetField.index()) { + return attribute; + } + } + log.error("Unable to find field {} in record {}", targetField, fdoAttributes); + throw new IllegalStateException(); + } + + +} diff --git a/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java b/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java index 917b2ba8..1eb57be2 100644 --- a/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java @@ -6,27 +6,26 @@ import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_TYPE; import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE; import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE_ALT; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE_DOMAIN; import static eu.dissco.core.handlemanager.testUtils.TestUtils.MAPPER; import static eu.dissco.core.handlemanager.testUtils.TestUtils.PREFIX; import static eu.dissco.core.handlemanager.testUtils.TestUtils.PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.SUFFIX; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genCreateRecordRequest; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genTombstoneRecordRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genTombstoneRequest; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genUpdateRequestAltLoc; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDataMappingRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenRequestObjectNullOptionals; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDoiRecordRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenHandleRecordRequestObject; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMappingRequestObject; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMediaRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseRead; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseReadSingle; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseWrite; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseWriteAltLoc; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseWriteArchive; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseWriteGeneric; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenSourceSystemRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenTombstoneRecordRequestObject; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; import static org.mockito.ArgumentMatchers.anyList; @@ -49,7 +48,7 @@ import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.properties.ApplicationProperties; import eu.dissco.core.handlemanager.service.PidService; -import java.nio.charset.StandardCharsets; +import eu.dissco.core.handlemanager.testUtils.TestUtils; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -89,16 +88,16 @@ void setup() { } @Test - void testResolveSingleHandle() throws PidResolutionException { + void testResolveSingleHandle() throws Exception { // Given String path = SANDBOX_URI + PREFIX + "/" + SUFFIX; - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); MockHttpServletRequest r = new MockHttpServletRequest(); r.setRequestURI(PREFIX + "/" + SUFFIX); - var responseExpected = givenRecordResponseReadSingle(HANDLE, path, FdoType.HANDLE, null); + var responseExpected = givenRecordResponseRead(List.of(HANDLE), path, FdoType.HANDLE, + HANDLE_DOMAIN); given(applicationProperties.getUiUrl()).willReturn(SANDBOX_URI); - given(service.resolveSingleRecord(handle, path)).willReturn(responseExpected); + given(service.resolveSingleRecord(HANDLE, path)).willReturn(responseExpected); given(applicationProperties.getPrefix()).willReturn(PREFIX); // When @@ -162,7 +161,8 @@ void testResolveBatchHandle() throws Exception { List handleString = List.of(HANDLE, HANDLE_ALT); - var responseExpected = givenRecordResponseRead(handleString, path, FdoType.HANDLE); + var responseExpected = givenRecordResponseRead(handleString, path, FdoType.HANDLE, + HANDLE_DOMAIN); given(applicationProperties.getUiUrl()).willReturn(SANDBOX_URI); given(applicationProperties.getMaxHandles()).willReturn(1000); given(service.resolveBatchRecord(anyList(), eq(path))).willReturn(responseExpected); @@ -201,7 +201,8 @@ void testCreateHandleRecord() throws Exception { // Given HandleRecordRequest requestObject = givenHandleRecordRequestObject(); ObjectNode requestNode = genCreateRecordRequest(requestObject, FdoType.HANDLE); - JsonApiWrapperWrite responseExpected = givenRecordResponseWrite(List.of(HANDLE), + JsonApiWrapperWrite responseExpected = TestUtils.givenRecordResponseWriteFullResponse( + List.of(HANDLE), FdoType.HANDLE); given(service.createRecords(List.of(requestNode))).willReturn(responseExpected); @@ -219,7 +220,8 @@ void testCreateDoiRecord() throws Exception { // Given HandleRecordRequest requestObject = givenDoiRecordRequestObject(); ObjectNode requestNode = genCreateRecordRequest(requestObject, FdoType.DOI); - JsonApiWrapperWrite responseExpected = givenRecordResponseWrite(List.of(HANDLE), + JsonApiWrapperWrite responseExpected = TestUtils.givenRecordResponseWriteFullResponse( + List.of(HANDLE), FdoType.DOI); given(service.createRecords(List.of(requestNode))).willReturn(responseExpected); @@ -237,7 +239,8 @@ void testCreateDigitalSpecimenRecord() throws Exception { // Given DigitalSpecimenRequest requestObject = givenDigitalSpecimenRequestObjectNullOptionals(); ObjectNode requestNode = genCreateRecordRequest(requestObject, FdoType.DIGITAL_SPECIMEN); - JsonApiWrapperWrite responseExpected = givenRecordResponseWrite(List.of(HANDLE), + JsonApiWrapperWrite responseExpected = TestUtils.givenRecordResponseWriteFullResponse( + List.of(HANDLE), FdoType.DIGITAL_SPECIMEN); given(service.createRecords(List.of(requestNode))).willReturn(responseExpected); @@ -251,12 +254,13 @@ void testCreateDigitalSpecimenRecord() throws Exception { } @Test - void testCreateMediaObjectRecord() throws Exception { + void testCreateDigitalMediaRecord() throws Exception { // Given - HandleRecordRequest requestObject = givenMediaRequestObject(); - ObjectNode requestNode = genCreateRecordRequest(requestObject, FdoType.MEDIA_OBJECT); - JsonApiWrapperWrite responseExpected = givenRecordResponseWrite(List.of(HANDLE), - FdoType.MEDIA_OBJECT); + HandleRecordRequest requestObject = givenDigitalMediaRequestObject(); + ObjectNode requestNode = genCreateRecordRequest(requestObject, FdoType.DIGITAL_MEDIA); + JsonApiWrapperWrite responseExpected = TestUtils.givenRecordResponseWriteFullResponse( + List.of(HANDLE), + FdoType.DIGITAL_MEDIA); given(service.createRecords(List.of(requestNode))).willReturn(responseExpected); @@ -278,7 +282,7 @@ void testCreateHandleRecordBatch() throws Exception { handles.forEach(handle -> requests.add( genCreateRecordRequest(givenHandleRecordRequestObject(), FdoType.HANDLE))); - var responseExpected = givenRecordResponseWrite(handles, FdoType.HANDLE); + var responseExpected = TestUtils.givenRecordResponseWriteFullResponse(handles, FdoType.HANDLE); given(service.createRecords(requests)).willReturn(responseExpected); // When @@ -298,7 +302,7 @@ void testCreateDoiRecordBatch() throws Exception { handles.forEach( handle -> requests.add(genCreateRecordRequest(givenDoiRecordRequestObject(), FdoType.DOI))); - var responseExpected = givenRecordResponseWrite(handles, FdoType.DOI); + var responseExpected = TestUtils.givenRecordResponseWriteFullResponse(handles, FdoType.DOI); given(service.createRecords(requests)).willReturn(responseExpected); // When @@ -319,7 +323,8 @@ void testCreateDigitalSpecimenBatch() throws Exception { genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), FdoType.DIGITAL_SPECIMEN)) ); - var responseExpected = givenRecordResponseWrite(handles, FdoType.DIGITAL_SPECIMEN); + var responseExpected = TestUtils.givenRecordResponseWriteFullResponse(handles, + FdoType.DIGITAL_SPECIMEN); given(service.createRecords(requests)).willReturn(responseExpected); // When @@ -336,9 +341,9 @@ void testCreateMediaRecordBatch() throws Exception { var handles = List.of(HANDLE, HANDLE_ALT); List requests = new ArrayList<>(); for (int i = 0; i < handles.size(); i++) { - requests.add(genCreateRecordRequest(givenMediaRequestObject(), FdoType.MEDIA_OBJECT)); + requests.add(genCreateRecordRequest(givenDigitalMediaRequestObject(), FdoType.DIGITAL_MEDIA)); } - var responseExpected = givenRecordResponseWrite(handles, FdoType.DOI); + var responseExpected = TestUtils.givenRecordResponseWriteFullResponse(handles, FdoType.DOI); given(service.createRecords(requests)).willReturn(responseExpected); // When @@ -357,7 +362,8 @@ void testCreateSourceSystemsBatch() throws Exception { handles.forEach(handle -> requests.add( genCreateRecordRequest(givenSourceSystemRequestObject(), FdoType.SOURCE_SYSTEM))); - var responseExpected = givenRecordResponseWrite(handles, FdoType.SOURCE_SYSTEM); + var responseExpected = TestUtils.givenRecordResponseWriteFullResponse(handles, + FdoType.SOURCE_SYSTEM); given(service.createRecords(requests)).willReturn(responseExpected); // When @@ -375,7 +381,8 @@ void testCreateAnnotationsBatch() throws Exception { List requests = new ArrayList<>(); handles.forEach(handle -> requests.add( genCreateRecordRequest(givenAnnotationRequestObject(), FdoType.ANNOTATION))); - var responseExpected = givenRecordResponseWrite(handles, FdoType.ANNOTATION); + var responseExpected = TestUtils.givenRecordResponseWriteFullResponse(handles, + FdoType.ANNOTATION); given(service.createRecords(requests)).willReturn(responseExpected); // When @@ -392,9 +399,10 @@ void testCreateMappingBatch() throws Exception { var handles = List.of(HANDLE, HANDLE_ALT); List requests = new ArrayList<>(); handles.forEach(handle -> requests.add( - genCreateRecordRequest(givenMappingRequestObject(), FdoType.MAPPING))); + genCreateRecordRequest(givenDataMappingRequestObject(), FdoType.DATA_MAPPING))); - var responseExpected = givenRecordResponseWrite(handles, FdoType.MAPPING); + var responseExpected = TestUtils.givenRecordResponseWriteFullResponse(handles, + FdoType.DATA_MAPPING); given(service.createRecords(requests)).willReturn(responseExpected); // When @@ -542,7 +550,7 @@ void testArchiveRecord() throws Exception { // Given var responseExpected = givenRecordResponseWriteArchive(List.of(HANDLE)); var archiveRequest = givenArchiveRequest(); - given(service.archiveRecordBatch(List.of(archiveRequest))).willReturn( + given(service.tombstoneRecordBatch(List.of(archiveRequest))).willReturn( responseExpected); // When @@ -571,7 +579,7 @@ void testArchiveRecordBatch() throws Exception { List archiveRequestList = new ArrayList<>(); handles.forEach(h -> archiveRequestList.add(givenArchiveRequest())); var responseExpected = givenRecordResponseWriteArchive(handles); - given(service.archiveRecordBatch(archiveRequestList)).willReturn(responseExpected); + given(service.tombstoneRecordBatch(archiveRequestList)).willReturn(responseExpected); // When var responseReceived = controller.archiveRecords(archiveRequestList, authentication); @@ -585,7 +593,8 @@ private JsonNode givenArchiveRequest() { ObjectNode archiveRequest = MAPPER.createObjectNode(); ObjectNode archiveRequestData = MAPPER.createObjectNode(); archiveRequestData.put(NODE_ID, HANDLE); - archiveRequestData.set(NODE_ATTRIBUTES, MAPPER.valueToTree(genTombstoneRecordRequestObject())); + archiveRequestData.set(NODE_ATTRIBUTES, + MAPPER.valueToTree(givenTombstoneRecordRequestObject())); archiveRequest.set(NODE_DATA, archiveRequestData); return archiveRequest; } diff --git a/src/test/java/eu/dissco/core/handlemanager/domain/FdoProfileTest.java b/src/test/java/eu/dissco/core/handlemanager/domain/FdoProfileTest.java index 8267d48f..0e560a07 100644 --- a/src/test/java/eu/dissco/core/handlemanager/domain/FdoProfileTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/domain/FdoProfileTest.java @@ -1,31 +1,6 @@ package eu.dissco.core.handlemanager.domain; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PID_ISSUER; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertThrows; - -import eu.dissco.core.handlemanager.domain.fdo.FdoProfile; -import org.junit.jupiter.api.Test; - class FdoProfileTest { - @Test - void testSearchFdoProfileSuccess() { - // Given - var targetStr = PID_ISSUER.get(); - var expected = PID_ISSUER.index(); - - // When - var result = FdoProfile.retrieveIndex(targetStr); - - // Then - assertThat(result).isEqualTo(expected); - } - - @Test - void testUnrecognizedProperty() { - assertThrows(IllegalStateException.class, - () -> FdoProfile.retrieveIndex("aaa")); - } } diff --git a/src/test/java/eu/dissco/core/handlemanager/domain/JsonSchemaValidatorTest.java b/src/test/java/eu/dissco/core/handlemanager/domain/JsonSchemaValidatorTest.java index cf6b9bad..4ab38a95 100644 --- a/src/test/java/eu/dissco/core/handlemanager/domain/JsonSchemaValidatorTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/domain/JsonSchemaValidatorTest.java @@ -9,7 +9,7 @@ import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SOURCE_SYSTEM_NAME; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SPECIMEN_HOST; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TARGET_TYPE; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOMBSTONE_TEXT; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOMBSTONED_TEXT; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ATTRIBUTES; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_DATA; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ID; @@ -26,12 +26,12 @@ import static eu.dissco.core.handlemanager.testUtils.TestUtils.genTombstoneRequestBatch; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genUpdateRequestAltLoc; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDataMappingRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenRequestObjectNullOptionals; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDoiRecordRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenHandleRecordRequestObject; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMappingRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMasRecordRequestObject; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMediaRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenOrganisationRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenSourceSystemRequestObject; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; @@ -116,9 +116,9 @@ void testPostDigitalSpecimenRequest() { } @Test - void testPostMediaObjectRequest() throws Exception { + void testPostDigitalMediaRequest() throws Exception { // Given - var request = genCreateRecordRequest(givenMediaRequestObject(), FdoType.MEDIA_OBJECT); + var request = genCreateRecordRequest(givenDigitalMediaRequestObject(), FdoType.DIGITAL_MEDIA); // Then assertDoesNotThrow(() -> schemaValidator.validatePostRequest(request)); @@ -164,7 +164,7 @@ void testPostOrganisationNullTypeRequest() { @Test void testPostMappingRequest() { // Given - var request = genCreateRecordRequest(givenMappingRequestObject(), FdoType.MAPPING); + var request = genCreateRecordRequest(givenDataMappingRequestObject(), FdoType.DATA_MAPPING); // Then assertDoesNotThrow(() -> schemaValidator.validatePostRequest(request)); @@ -220,9 +220,9 @@ void testDigitalSpecimenPatchRequest() { } @Test - void testMediaObjectPatchRequest() { + void testDigitalMediaPatchRequest() { // Given - var request = givenUpdateRequest(FdoType.MEDIA_OBJECT, MEDIA_HOST.get(), MEDIA_HOST_TESTVAL); + var request = givenUpdateRequest(FdoType.DIGITAL_MEDIA, MEDIA_HOST.get(), MEDIA_HOST_TESTVAL); // Then assertDoesNotThrow(() -> schemaValidator.validatePatchRequest(request)); @@ -250,7 +250,7 @@ void testOrganisationPatchRequest() { @Test void testMappingPatchRequest() { // Given - var request = givenUpdateRequest(FdoType.MAPPING, SOURCE_DATA_STANDARD.get(), "new"); + var request = givenUpdateRequest(FdoType.DATA_MAPPING, SOURCE_DATA_STANDARD.get(), "new"); // Then assertDoesNotThrow(() -> schemaValidator.validatePatchRequest(request)); @@ -384,10 +384,10 @@ void testBadPostDigitalSpecimenRequestUnknownProperty() { } @Test - void testBadPostMediaObjectRequestUnknownProperty() { + void testBadPostDigitalMediaRequestUnknownProperty() { // Given var request = genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), - FdoType.MEDIA_OBJECT); + FdoType.DIGITAL_MEDIA); ((ObjectNode) request.get(NODE_DATA)).put(UNKNOWN_ATTRIBUTE, UNKNOWN_VAL); // Then @@ -398,11 +398,11 @@ void testBadPostMediaObjectRequestUnknownProperty() { } @Test - void testBadPostMediaObjectRequestMissingProperty() { + void testBadPostDigitalMediaRequestMissingProperty() { // Given String missingAttribute = MEDIA_HOST.get(); var request = genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), - FdoType.MEDIA_OBJECT); + FdoType.DIGITAL_MEDIA); ((ObjectNode) request.get(NODE_DATA).get(NODE_ATTRIBUTES)).remove(missingAttribute); // Then @@ -445,7 +445,7 @@ private static Stream provideFdoTypes() { Arguments.of(FdoType.HANDLE), Arguments.of(FdoType.DOI), Arguments.of(FdoType.DIGITAL_SPECIMEN), - Arguments.of(FdoType.MEDIA_OBJECT) + Arguments.of(FdoType.DIGITAL_MEDIA) ); } @@ -470,14 +470,14 @@ void testBadArchiveRequestMissingProperty() { // Given var request = genTombstoneRequestBatch(List.of(HANDLE)).get(0); ((ObjectNode) request.get(NODE_DATA)).remove(NODE_TYPE); - ((ObjectNode) request.get(NODE_DATA).get(NODE_ATTRIBUTES)).remove(TOMBSTONE_TEXT.get()); + ((ObjectNode) request.get(NODE_DATA).get(NODE_ATTRIBUTES)).remove(TOMBSTONED_TEXT.get()); // When Exception e = assertThrowsExactly(InvalidRequestException.class, () -> schemaValidator.validatePutRequest(request)); // Then - assertThat(e.getMessage()).contains(MISSING_MSG).contains(TOMBSTONE_TEXT.get()); + assertThat(e.getMessage()).contains(MISSING_MSG).contains(TOMBSTONED_TEXT.get()); } } diff --git a/src/test/java/eu/dissco/core/handlemanager/repository/BaseRepositoryIT.java b/src/test/java/eu/dissco/core/handlemanager/repository/BaseRepositoryIT.java deleted file mode 100644 index 4af4323c..00000000 --- a/src/test/java/eu/dissco/core/handlemanager/repository/BaseRepositoryIT.java +++ /dev/null @@ -1,52 +0,0 @@ -package eu.dissco.core.handlemanager.repository; - -import static org.testcontainers.containers.PostgreSQLContainer.IMAGE; - -import com.zaxxer.hikari.HikariDataSource; -import eu.dissco.core.handlemanager.database.jooq.tables.Handles; -import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; -import org.flywaydb.core.Flyway; -import org.jooq.DSLContext; -import org.jooq.Record4; -import org.jooq.SQLDialect; -import org.jooq.impl.DefaultDSLContext; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.testcontainers.containers.PostgreSQLContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.utility.DockerImageName; - -@Testcontainers -public class BaseRepositoryIT { - - private static final DockerImageName POSTGIS = - DockerImageName.parse("postgres:15.2").asCompatibleSubstituteFor(IMAGE); - - @Container - private static final PostgreSQLContainer CONTAINER = new PostgreSQLContainer<>(POSTGIS); - protected DSLContext context; - protected HikariDataSource dataSource; - - @BeforeEach - void prepareDatabase() { - dataSource = new HikariDataSource(); - dataSource.setJdbcUrl(CONTAINER.getJdbcUrl()); - dataSource.setUsername(CONTAINER.getUsername()); - dataSource.setPassword(CONTAINER.getPassword()); - dataSource.setMaximumPoolSize(2); - dataSource.setConnectionInitSql(CONTAINER.getTestQueryString()); - Flyway.configure().mixed(true).dataSource(dataSource).load().migrate(); - context = new DefaultDSLContext(dataSource, SQLDialect.POSTGRES); - } - - @AfterEach - void disposeDataSource() { - dataSource.close(); - } - - protected HandleAttribute mapToAttribute(Record4 row) { - return new HandleAttribute(row.get(Handles.HANDLES.IDX), row.get(Handles.HANDLES.HANDLE), - new String(row.get(Handles.HANDLES.TYPE)), row.get(Handles.HANDLES.DATA)); - } -} \ No newline at end of file diff --git a/src/test/java/eu/dissco/core/handlemanager/repository/BatchInserterIT.java b/src/test/java/eu/dissco/core/handlemanager/repository/BatchInserterIT.java deleted file mode 100644 index f6f9ea92..00000000 --- a/src/test/java/eu/dissco/core/handlemanager/repository/BatchInserterIT.java +++ /dev/null @@ -1,86 +0,0 @@ -package eu.dissco.core.handlemanager.repository; - -import static eu.dissco.core.handlemanager.database.jooq.Tables.HANDLES; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.CREATED; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genDigitalSpecimenAttributes; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertThrows; - -import eu.dissco.core.handlemanager.database.jooq.tables.Handles; -import eu.dissco.core.handlemanager.domain.fdo.FdoProfile; -import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; -import eu.dissco.core.handlemanager.exceptions.DatabaseCopyException; -import java.nio.charset.StandardCharsets; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.util.List; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.postgresql.copy.CopyManager; -import org.postgresql.core.BaseConnection; - -class BatchInserterIT extends BaseRepositoryIT { - - private BatchInserter batchInserter; - - @BeforeEach - void setup() throws SQLException { - var connection = DriverManager.getConnection(dataSource.getJdbcUrl(), dataSource.getUsername(), - dataSource.getPassword()); - var copyManager = new CopyManager((BaseConnection) connection); - batchInserter = new BatchInserter(copyManager); - } - - @AfterEach - void destroy() { - context.truncate(HANDLES).execute(); - } - - @Test - void testBatchInsert() throws Exception { - // Given - var attributes = genDigitalSpecimenAttributes(HANDLE); - - // When - batchInserter.batchCopy(CREATED.getEpochSecond(), attributes); - var response = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(HANDLES).fetch(this::mapToAttribute); - - // Then - assertThat(response).hasSameElementsAs(attributes); - } - - @Test - void testBatchInsertIllegalChar() { - // Given - var attributes = List.of( - new HandleAttribute(FdoProfile.SPECIMEN_HOST, - HANDLE.getBytes(StandardCharsets.UTF_8), - "this is \n bad data") - ); - var created = CREATED.getEpochSecond(); - - // Then - assertThrows(DatabaseCopyException.class, - () -> batchInserter.batchCopy(created, attributes)); - } - - @Test - void testDelimiterInData() throws Exception { - var attributes = List.of(new HandleAttribute( - FdoProfile.SPECIMEN_HOST, HANDLE.getBytes(StandardCharsets.UTF_8), - "this, has a comma" - )); - - // When - batchInserter.batchCopy(CREATED.getEpochSecond(), attributes); - var response = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(HANDLES).fetch(this::mapToAttribute); - - // Then - assertThat(response).isEqualTo(attributes); - } - -} diff --git a/src/test/java/eu/dissco/core/handlemanager/repository/MongoRepositoryIT.java b/src/test/java/eu/dissco/core/handlemanager/repository/MongoRepositoryIT.java new file mode 100644 index 00000000..638e7e2d --- /dev/null +++ b/src/test/java/eu/dissco/core/handlemanager/repository/MongoRepositoryIT.java @@ -0,0 +1,43 @@ +package eu.dissco.core.handlemanager.repository; + + +import static eu.dissco.core.handlemanager.testUtils.TestUtils.MAPPER; + +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import com.mongodb.client.MongoDatabase; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.testcontainers.containers.MongoDBContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +@Testcontainers +public class MongoRepositoryIT { + + private static final DockerImageName MONGODB = + DockerImageName.parse("mongo:6.0.4"); + + @Container + private static final MongoDBContainer CONTAINER = new MongoDBContainer(MONGODB); + private MongoDatabase database; + private MongoClient client; + private PidRepository repository; + + @BeforeEach + void prepareDocumentStore() { + client = MongoClients.create(CONTAINER.getConnectionString()); + database = client.getDatabase("dissco"); + var collection = database.getCollection("handles"); + repository = new PidRepository(collection, MAPPER); + } + + @AfterEach + void disposeDocumentStore() { + database.drop(); + client.close(); + } + + +} \ No newline at end of file diff --git a/src/test/java/eu/dissco/core/handlemanager/repository/PidRepositoryIT.java b/src/test/java/eu/dissco/core/handlemanager/repository/PidRepositoryIT.java deleted file mode 100644 index 8b822dd5..00000000 --- a/src/test/java/eu/dissco/core/handlemanager/repository/PidRepositoryIT.java +++ /dev/null @@ -1,602 +0,0 @@ -package eu.dissco.core.handlemanager.repository; - -import static eu.dissco.core.handlemanager.database.jooq.Tables.HANDLES; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.HS_ADMIN; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MATERIAL_SAMPLE_TYPE; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.NORMALISED_SPECIMEN_OBJECT_ID; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PID_RECORD_ISSUE_NUMBER; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PID_STATUS; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PRIMARY_SPECIMEN_OBJECT_ID; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SPECIMEN_HOST; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.CREATED; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE_ALT; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.PID_STATUS_TESTVAL; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.SPECIMEN_HOST_TESTVAL; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genDigitalSpecimenAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genDoiRecordAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genHandleRecordAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genHandleRecordAttributesAltLoc; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genTombstoneRecordFullAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genUpdateRecordAttributesAltLoc; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.mockito.BDDMockito.then; - -import eu.dissco.core.handlemanager.database.jooq.tables.Handles; -import eu.dissco.core.handlemanager.domain.fdo.FdoType; -import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Random; -import java.util.Set; -import java.util.stream.Stream; -import org.jooq.Query; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -class PidRepositoryIT extends BaseRepositoryIT { - - private PidRepository pidRepository; - @Mock - BatchInserter batchInserter; - - @BeforeEach - void setup() { - pidRepository = new PidRepository(context, batchInserter); - } - - @AfterEach - void destroy() { - context.truncate(HANDLES).execute(); - } - - @Test - void testCreateRecord() throws Exception { - // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - List attributesToPost = genHandleRecordAttributes(handle, FdoType.HANDLE); - - // When - pidRepository.postAttributesToDb(CREATED.getEpochSecond(), attributesToPost); - - // Then - then(batchInserter).should().batchCopy(CREATED.getEpochSecond(), attributesToPost); - } - - @Test - void testHandlesExistTrue() { - // Given - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - List rows = List.of(new HandleAttribute(1, handles.get(0), PID_STATUS.get(), - PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8)), - new HandleAttribute(1, handles.get(1), PID_STATUS.get(), - PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8))); - - postAttributes(rows); - - // When - List collisions = pidRepository.getHandlesExist(handles); - - // Then - assertThat(collisions).hasSize(handles.size()); - assert (byteArrListsAreEqual(handles, collisions)); - } - - @Test - void testHandlesExistFalse() { - // Given - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - - // When - List collisions = pidRepository.getHandlesExist(handles); - - // Then - assertThat(collisions).isEmpty(); - assertFalse(byteArrListsAreEqual(handles, collisions)); - } - - @Test - void testHandlesWritableTrue() { - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - List rows = List.of(new HandleAttribute(1, handles.get(0), PID_STATUS.get(), - PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8)), - new HandleAttribute(1, handles.get(1), PID_STATUS.get(), - PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8))); - - postAttributes(rows); - - // When - List collisions = pidRepository.checkHandlesWritable(handles); - - // Then - assertThat(collisions).hasSize(handles.size()); - assert (byteArrListsAreEqual(handles, collisions)); - } - - @Test - void testHandlesWritableFalse() { - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - List rows = List.of(new HandleAttribute(1, handles.get(0), PID_STATUS.get(), - "ARCHIVED".getBytes(StandardCharsets.UTF_8)), - new HandleAttribute(1, handles.get(1), PID_STATUS.get(), - "ARCHIVED".getBytes(StandardCharsets.UTF_8))); - - postAttributes(rows); - - // When - List collisions = pidRepository.checkHandlesWritable(handles); - - // Then - assertThat(collisions).isEmpty(); - assertFalse(byteArrListsAreEqual(handles, collisions)); - } - - @Test - void testResolveSingleRecord() throws Exception { - // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - List responseExpected = genHandleRecordAttributes(handle, FdoType.HANDLE); - postAttributes(responseExpected); - - // When - var responseReceived = pidRepository.resolveHandleAttributes(handle); - - // Then - assertThat(responseReceived).isEqualTo(responseExpected); - } - - @Test - void testGetPrimarySpecimenObjectIds() throws Exception { - // Given - var handle = HANDLE.getBytes(StandardCharsets.UTF_8); - var handleAlt = HANDLE_ALT.getBytes(StandardCharsets.UTF_8); - var postedAttributes = Stream.concat( - genHandleRecordAttributes(handle, FdoType.DIGITAL_SPECIMEN).stream(), - genHandleRecordAttributes(handleAlt, FdoType.DIGITAL_SPECIMEN).stream()).toList(); - postAttributes(postedAttributes); - List expected = new ArrayList<>(); - for (var row : postedAttributes) { - if (row.getType().equals(PRIMARY_SPECIMEN_OBJECT_ID.get())) { - expected.add(row); - } - } - // When - var result = pidRepository.getPrimarySpecimenObjectId(List.of(handle, handleAlt)); - - // Then - assertThat(result).hasSameElementsAs(expected); - } - - @Test - void testResolveBatchRecord() throws Exception { - // Given - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - - List responseExpected = new ArrayList<>(); - for (byte[] handle : handles) { - responseExpected.addAll(genHandleRecordAttributes(handle, FdoType.HANDLE)); - } - - postAttributes(responseExpected); - - // When - var responseReceived = pidRepository.resolveHandleAttributes(handles); - - // Then - assertThat(responseReceived).isEqualTo(responseExpected); - } - - @Test - void testGetAllHandlesPaging() { - // Given - int pageNum = 1; - int pageSize = 5; - - List handles = genListofHandlesString(pageSize); - List rows = new ArrayList<>(); - - for (String handle : handles) { - rows.add(new HandleAttribute(1, handle.getBytes(StandardCharsets.UTF_8), PID_STATUS.get(), - PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8))); - } - postAttributes(rows); - - // When - List responseReceived = pidRepository.getAllHandles(pageNum, pageSize); - - // Then - assertThat(responseReceived).hasSameElementsAs(handles); - } - - @Test - void testGetAllHandlesLastPage() { - // Given - int pageNum = 2; - int pageSize = 5; - - List handles = genListofHandlesString(pageSize + 1); - List rows = new ArrayList<>(); - - for (String handle : handles) { - rows.add(new HandleAttribute(1, handle.getBytes(StandardCharsets.UTF_8), PID_STATUS.get(), - PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8))); - } - postAttributes(rows); - - // When - List responseReceived = pidRepository.getAllHandles(pageNum, pageSize); - - // Then - assertThat(responseReceived).hasSize(1); - } - - @Test - void testGetAllHandlesPagingByPidStatus() { - // Given - int pageNum = 1; - int pageSize = 5; - byte[] pidStatusTarget = "ARCHIVED".getBytes(StandardCharsets.UTF_8); - - List handles = genListofHandlesString(pageSize + 2); - List responseExpected = handles.subList(0, pageSize); - List extraHandles = handles.subList(pageSize, handles.size()); - List rows = new ArrayList<>(); - - for (String handle : responseExpected) { - rows.add(new HandleAttribute(1, handle.getBytes(StandardCharsets.UTF_8), PID_STATUS.get(), - pidStatusTarget)); - } - for (String handle : extraHandles) { - rows.add(new HandleAttribute(1, handle.getBytes(StandardCharsets.UTF_8), PID_STATUS.get(), - PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8))); - } - postAttributes(rows); - - // When - List responseReceived = pidRepository.getAllHandles(pidStatusTarget, pageNum, pageSize); - - // Then - assertThat(responseReceived).hasSameElementsAs(responseExpected); - } - - @Test - void testSearchByPhysicalIdentifierFullRecord() { - // Given - var targetPhysicalIdentifier = NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL.getBytes( - StandardCharsets.UTF_8); - List responseExpected = new ArrayList<>(); - responseExpected.add(new HandleAttribute(1, HANDLE.getBytes(StandardCharsets.UTF_8), - NORMALISED_SPECIMEN_OBJECT_ID.get(), targetPhysicalIdentifier)); - responseExpected.add( - new HandleAttribute(2, HANDLE.getBytes(StandardCharsets.UTF_8), SPECIMEN_HOST.get(), - SPECIMEN_HOST_TESTVAL.getBytes( - StandardCharsets.UTF_8))); - - List nonTargetAttributes = new ArrayList<>(); - nonTargetAttributes.add(new HandleAttribute(1, HANDLE_ALT.getBytes(StandardCharsets.UTF_8), - NORMALISED_SPECIMEN_OBJECT_ID.get(), "A".getBytes( - StandardCharsets.UTF_8))); - - postAttributes(responseExpected); - postAttributes(nonTargetAttributes); - - // When - var responseReceived = pidRepository.searchByNormalisedPhysicalIdentifierFullRecord( - List.of(targetPhysicalIdentifier)); - - // Then - assertThat(responseReceived).hasSameElementsAs(responseExpected); - } - - @Test - void testSearchByPhysicalSpecimenId() throws Exception { - //Given - var handle = HANDLE.getBytes(StandardCharsets.UTF_8); - var dbRecord = List.of(new HandleAttribute(NORMALISED_SPECIMEN_OBJECT_ID.index(), handle, - NORMALISED_SPECIMEN_OBJECT_ID.get(), - NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL.getBytes(StandardCharsets.UTF_8))); - - postAttributes(genDoiRecordAttributes(handle, FdoType.DOI)); - postAttributes(dbRecord); - - // When - var response = pidRepository.searchByNormalisedPhysicalIdentifier( - List.of(NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL.getBytes(StandardCharsets.UTF_8))); - - // Then - assertThat(response).isEqualTo(dbRecord); - } - - @Test - void testSearchByPhysicalSpecimenIdIsArchived() { - //Given - var handle = HANDLE.getBytes(StandardCharsets.UTF_8); - var dbRecord = List.of(new HandleAttribute(NORMALISED_SPECIMEN_OBJECT_ID, handle, - NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL), - new HandleAttribute(PID_STATUS, handle, "ARCHIVED")); - - postAttributes(dbRecord); - - // When - var response = pidRepository.searchByNormalisedPhysicalIdentifier( - List.of(NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL.getBytes(StandardCharsets.UTF_8))); - - // Then - assertThat(response).isEmpty(); - } - - @Test - void testUpdateRecord() throws Exception { - // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - List originalRecord = genHandleRecordAttributes(handle); - List recordUpdate = genUpdateRecordAttributesAltLoc(handle); - var responseExpected = incrementVersion(genHandleRecordAttributesAltLoc(handle), true); - postAttributes(originalRecord); - - // When - pidRepository.updateRecord(CREATED.getEpochSecond(), recordUpdate, true); - var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) - .where(Handles.HANDLES.HANDLE.eq(handle)).and(Handles.HANDLES.TYPE.notEqual( - HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN - .fetch(this::mapToAttribute); - - // Then - assertThat(responseReceived).hasSameElementsAs(responseExpected); - } - - @Test - void testUpdateRecordBatch() throws Exception { - - // Given - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - - List> updateAttributes = new ArrayList<>(); - List responseExpected = new ArrayList<>(); - for (byte[] handle : handles) { - postAttributes(genHandleRecordAttributes(handle)); - updateAttributes.add(genUpdateRecordAttributesAltLoc(handle)); - responseExpected.addAll(incrementVersion(genHandleRecordAttributesAltLoc(handle), true)); - } - - // When - pidRepository.updateRecordBatch(CREATED.getEpochSecond(), updateAttributes, true); - var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) - .where(Handles.HANDLES.HANDLE.in(handles)).and(Handles.HANDLES.TYPE.notEqual( - HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN - .fetch(this::mapToAttribute); - - // Then - assertThat(responseReceived).hasSameElementsAs(responseExpected); - } - - @Test - void testUpdateRecordBatchNoIncrement() throws Exception { - - // Given - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - - List> updateAttributes = new ArrayList<>(); - List responseExpected = new ArrayList<>(); - for (byte[] handle : handles) { - postAttributes(genHandleRecordAttributes(handle)); - updateAttributes.add(genUpdateRecordAttributesAltLoc(handle)); - responseExpected.addAll(incrementVersion(genHandleRecordAttributesAltLoc(handle), false)); - } - - // When - pidRepository.updateRecordBatch(CREATED.getEpochSecond(), updateAttributes, false); - var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) - .where(Handles.HANDLES.HANDLE.in(handles)).and(Handles.HANDLES.TYPE.notEqual( - HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN - .fetch(this::mapToAttribute); - - // Then - assertThat(responseReceived).hasSameElementsAs(responseExpected); - } - - @Test - void testArchiveRecordBatch() throws Exception { - - // Given - List handles = List.of(HANDLE.getBytes(StandardCharsets.UTF_8), - HANDLE_ALT.getBytes(StandardCharsets.UTF_8)); - List handlesStr = List.of(HANDLE, HANDLE_ALT); - - List tombstoneAttributes = new ArrayList<>(); - for (var handle : handles) { - postAttributes(genDigitalSpecimenAttributes(handle)); - tombstoneAttributes.addAll(incrementVersion(genTombstoneRecordFullAttributes(handle), true)); - } - - // When - pidRepository.archiveRecords(CREATED.getEpochSecond(), tombstoneAttributes, handlesStr); - var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) - .where(Handles.HANDLES.HANDLE.in(handles)).and(Handles.HANDLES.TYPE.notEqual( - HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN - .fetch(this::mapToAttribute); - - // Then - assertThat(responseReceived).hasSameElementsAs(tombstoneAttributes); - } - - @Test - void testArchiveRecord() throws Exception { - // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - List originalRecord = genDigitalSpecimenAttributes(handle); - var tombstoneAttributes = incrementVersion(genTombstoneRecordFullAttributes(handle), true); - - postAttributes(originalRecord); - - // When - pidRepository.archiveRecords(CREATED.getEpochSecond(), tombstoneAttributes, List.of(HANDLE)); - var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) - .where(Handles.HANDLES.HANDLE.eq(handle)).and(Handles.HANDLES.TYPE.notEqual( - HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN - .fetch(this::mapToAttribute); - - // Then - assertThat(responseReceived).hasSameElementsAs(tombstoneAttributes); - } - - @Test - void testRollbackHandleCreation() throws Exception { - // Given - var expected = genHandleRecordAttributes(HANDLE.getBytes(StandardCharsets.UTF_8)); - postAttributes(expected); - postAttributes(genHandleRecordAttributes(HANDLE_ALT.getBytes(StandardCharsets.UTF_8))); - - // When - pidRepository.rollbackHandles(List.of(HANDLE_ALT)); - var response = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(HANDLES).fetch(this::mapToAttribute); - - // Then - assertThat(response).hasSameElementsAs(expected); - } - - @Test - void testRollbackHandleUpdate() throws Exception { - // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - List originalRecord = genHandleRecordAttributes(handle); - List recordUpdate = genUpdateRecordAttributesAltLoc(handle); - var responseExpected = incrementVersion(genHandleRecordAttributesAltLoc(handle), false); - postAttributes(originalRecord); - - // When - pidRepository.updateRecord(CREATED.getEpochSecond(), recordUpdate, false); - var responseReceived = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(Handles.HANDLES) - .where(Handles.HANDLES.HANDLE.eq(handle)).and(Handles.HANDLES.TYPE.notEqual( - HS_ADMIN.get().getBytes(StandardCharsets.UTF_8))) // Omit HS_ADMIN - .fetch(this::mapToAttribute); - - // Then - assertThat(responseReceived).hasSameElementsAs(responseExpected); - } - - @Test - void testPostAndUpdateHandles() throws Exception { - // Given - var handle = HANDLE.getBytes(StandardCharsets.UTF_8); - var handleAlt = HANDLE_ALT.getBytes(StandardCharsets.UTF_8); - var existingRecord = genHandleRecordAttributes(handle, FdoType.HANDLE); - postAttributes(existingRecord); - var updatedRecord = new ArrayList<>(existingRecord); - updatedRecord.add( - new HandleAttribute(MATERIAL_SAMPLE_TYPE.index(), handle, MATERIAL_SAMPLE_TYPE.get(), - "digital".getBytes(StandardCharsets.UTF_8))); - updatedRecord.add( - new HandleAttribute(PID_RECORD_ISSUE_NUMBER.index(), handle, PID_RECORD_ISSUE_NUMBER.get(), - String.valueOf(2).getBytes( - StandardCharsets.UTF_8))); - updatedRecord.remove( - new HandleAttribute(PID_RECORD_ISSUE_NUMBER.index(), handle, PID_RECORD_ISSUE_NUMBER.get(), - String.valueOf(1).getBytes( - StandardCharsets.UTF_8))); - - var newRecord = genHandleRecordAttributes(handleAlt, FdoType.HANDLE); - var expected = Stream.concat(updatedRecord.stream(), newRecord.stream()).toList(); - - // When - pidRepository.postAndUpdateHandles(CREATED.getEpochSecond(), newRecord, List.of(updatedRecord)); - var response = context.select(Handles.HANDLES.IDX, Handles.HANDLES.HANDLE, - Handles.HANDLES.TYPE, Handles.HANDLES.DATA).from(HANDLES).fetch(this::mapToAttribute); - - // Then - assertThat(response).hasSameElementsAs(expected); - } - - private void postAttributes(List rows) { - List queryList = new ArrayList<>(); - for (var handleAttribute : rows) { - var query = context.insertInto(Handles.HANDLES) - .set(Handles.HANDLES.HANDLE, handleAttribute.getHandle()) - .set(Handles.HANDLES.IDX, handleAttribute.getIndex()) - .set(Handles.HANDLES.TYPE, handleAttribute.getType().getBytes(StandardCharsets.UTF_8)) - .set(Handles.HANDLES.DATA, handleAttribute.getData()).set(Handles.HANDLES.TTL, 86400) - .set(Handles.HANDLES.TIMESTAMP, CREATED.getEpochSecond()) - .set(Handles.HANDLES.ADMIN_READ, true).set(Handles.HANDLES.ADMIN_WRITE, true) - .set(Handles.HANDLES.PUB_READ, true).set(Handles.HANDLES.PUB_WRITE, false); - queryList.add(query); - } - context.batch(queryList).execute(); - } - - private boolean byteArrListsAreEqual(List a, List b) { - if (a.size() != b.size()) { - return false; - } - - List aStr = new ArrayList<>(); - List bStr = new ArrayList<>(); - - for (int i = 0; i < a.size(); i++) { - aStr.add(new String(a.get(i))); - bStr.add(new String(b.get(i))); - } - - Collections.sort(aStr); - Collections.sort(bStr); - - return aStr.equals(bStr); - } - - private List genListofHandlesString(int numberOfHandles) { - Random random = new Random(); - int length = 3; - char[] buffer = new char[length]; - char[] symbols = "ABCDEFGHJKLMNPQRSTUVWXYZ1234567890".toCharArray(); - Set handles = new HashSet<>(); - - while (handles.size() < numberOfHandles) { - for (int j = 0; j < length; j++) { - buffer[j] = symbols[random.nextInt(symbols.length)]; - } - handles.add(new String(buffer)); - } - return new ArrayList<>(handles); - } - - private List incrementVersion(List handleAttributes, - boolean increaseVersionNum) { - for (int i = 0; i < handleAttributes.size(); i++) { - if (handleAttributes.get(i).getType().equals(PID_RECORD_ISSUE_NUMBER.get())) { - var removedRecord = handleAttributes.remove(i); - var currentVersion = Integer.parseInt(new String(removedRecord.getData())); - var newVersionNum = increaseVersionNum ? currentVersion + 1 : currentVersion; - byte[] issueNum = String.valueOf(newVersionNum).getBytes(StandardCharsets.UTF_8); - handleAttributes.add(i, - new HandleAttribute(removedRecord.getIndex(), removedRecord.getHandle(), - removedRecord.getType(), - issueNum)); - } - } - return handleAttributes; - } - -} diff --git a/src/test/java/eu/dissco/core/handlemanager/service/DataCiteServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/DataCiteServiceTest.java index 68aec12f..4752f38d 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/DataCiteServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/DataCiteServiceTest.java @@ -34,13 +34,13 @@ void setup() { } @Test - void testPublishMediaObject() throws Exception { + void testPublishDigitalMedia() throws Exception { // Given given(kafkaProperties.getDcMediaTopic()).willReturn(MEDIA_TOPIC); var event = new DataCiteEvent(MAPPER.createObjectNode(), EventType.CREATE); // When - dataCiteService.publishToDataCite(event, FdoType.MEDIA_OBJECT); + dataCiteService.publishToDataCite(event, FdoType.DIGITAL_MEDIA); // Then then(kafkaService).should().sendObjectToQueue(eq(MEDIA_TOPIC), any()); diff --git a/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java index dd7cb6fe..d46dc627 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java @@ -1,24 +1,29 @@ package eu.dissco.core.handlemanager.service; import static eu.dissco.core.handlemanager.testUtils.TestUtils.CREATED; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.DOI_DOMAIN; import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE_DOMAIN; import static eu.dissco.core.handlemanager.testUtils.TestUtils.MAPPER; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.PRIMARY_MEDIA_ID_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genCreateRecordRequest; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genObjectNodeAttributeRecord; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genUpdateRecordAttributesAltLoc; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genUpdateRequestBatch; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaFdoRecord; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaRequestObjectUpdate; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenRequestObjectNullOptionals; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMediaRequestObject; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseNullAttributes; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMongoDocument; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseWriteSmallResponse; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenUpdatedFdoRecord; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; import static org.mockito.Mockito.doThrow; @@ -26,7 +31,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import eu.dissco.core.handlemanager.Profiles; import eu.dissco.core.handlemanager.domain.datacite.DataCiteEvent; import eu.dissco.core.handlemanager.domain.datacite.EventType; @@ -34,9 +38,7 @@ import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.UnprocessableEntityException; import eu.dissco.core.handlemanager.properties.ProfileProperties; -import eu.dissco.core.handlemanager.repository.PidMongoRepository; import eu.dissco.core.handlemanager.repository.PidRepository; -import java.nio.charset.StandardCharsets; import java.time.Clock; import java.time.Instant; import java.time.ZoneOffset; @@ -54,8 +56,6 @@ @ActiveProfiles(profiles = Profiles.DOI) class DoiServiceTest { - @Mock - private PidRepository pidRepository; @Mock private FdoRecordService fdoRecordService; @Mock @@ -65,7 +65,7 @@ class DoiServiceTest { @Mock private DataCiteService dataCiteService; @Mock - private PidMongoRepository mongoRepository; + private PidRepository mongoRepository; private PidService service; private MockedStatic mockedStatic; private MockedStatic mockedClock; @@ -73,8 +73,8 @@ class DoiServiceTest { @BeforeEach void setup() { initTime(); - service = new DoiService(pidRepository, fdoRecordService, pidNameGeneratorService, MAPPER, - profileProperties, dataCiteService, mongoRepository); + service = new DoiService(fdoRecordService, pidNameGeneratorService, MAPPER, profileProperties, + dataCiteService, mongoRepository); } private void initTime() { @@ -98,52 +98,48 @@ void testCreateDigitalSpecimen() throws Exception { // Given var request = genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), FdoType.DIGITAL_SPECIMEN); - var digitalSpecimen = givenDigitalSpecimenFdoRecord(HANDLE); - var responseExpected = givenRecordResponseWriteSmallResponse(List.of(digitalSpecimen), - FdoType.DIGITAL_SPECIMEN); - var dataCiteEvent = new DataCiteEvent( - genObjectNodeAttributeRecord(digitalSpecimen.attributes()), + var fdoRecord = givenDigitalSpecimenFdoRecord(HANDLE); + var responseExpected = givenRecordResponseWriteSmallResponse(List.of(fdoRecord), + FdoType.DIGITAL_SPECIMEN, DOI_DOMAIN); + var dataCiteEvent = new DataCiteEvent(genObjectNodeAttributeRecord(fdoRecord.attributes()), EventType.CREATE); given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); given(fdoRecordService.prepareNewDigitalSpecimenRecord(any(), any(), any())).willReturn( - digitalSpecimen); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); + fdoRecord); + given(profileProperties.getDomain()).willReturn(DOI_DOMAIN); // When var responseReceived = service.createRecords(List.of(request)); // Then assertThat(responseReceived).isEqualTo(responseExpected); - // Todo - //then(dataCiteService).should().publishToDataCite(dataCiteEvent, FdoType.DIGITAL_SPECIMEN); + then(dataCiteService).should().publishToDataCite(dataCiteEvent, FdoType.DIGITAL_SPECIMEN); } @Test - void testCreateMediaObject() throws Exception { + void testCreateDigitalMedia() throws Exception { // Given - var request = genCreateRecordRequest(givenMediaRequestObject(), FdoType.MEDIA_OBJECT); - var mediaObject = givenDigitalMediaFdoRecord(HANDLE); - var responseExpected = givenRecordResponseWriteSmallResponse(List.of(mediaObject), - FdoType.MEDIA_OBJECT); - var dataCiteEvent = new DataCiteEvent(genObjectNodeAttributeRecord(mediaObject.attributes()), + var request = genCreateRecordRequest(givenDigitalMediaRequestObject(), FdoType.DIGITAL_MEDIA); + var digitalMedia = givenDigitalMediaFdoRecord(HANDLE); + var responseExpected = givenRecordResponseWriteSmallResponse(List.of(digitalMedia), + FdoType.DIGITAL_MEDIA, DOI_DOMAIN); + var dataCiteEvent = new DataCiteEvent(genObjectNodeAttributeRecord(digitalMedia.attributes()), EventType.CREATE); given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); given(fdoRecordService.prepareNewDigitalMediaRecord(any(), any(), any())).willReturn( - mediaObject); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); + digitalMedia); + given(profileProperties.getDomain()).willReturn(DOI_DOMAIN); // When var responseReceived = service.createRecords(List.of(request)); // Then assertThat(responseReceived).isEqualTo(responseExpected); - // todo - //then(dataCiteService).should().publishToDataCite(dataCiteEvent, FdoType.MEDIA_OBJECT); + then(dataCiteService).should().publishToDataCite(dataCiteEvent, FdoType.DIGITAL_MEDIA); } @Test void testCreateDigitalSpecimenDataCiteFails() throws Exception { - // todo // Given var request = List.of( (JsonNode) genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), @@ -155,62 +151,95 @@ void testCreateDigitalSpecimenDataCiteFails() throws Exception { doThrow(JsonProcessingException.class).when(dataCiteService).publishToDataCite(any(), any()); // When - //assertThrows(UnprocessableEntityException.class, () -> service.createRecords(request)); + assertThrows(UnprocessableEntityException.class, () -> service.createRecords(request)); // Then - //then(pidRepository).should().rollbackHandles(List.of(HANDLE)); + then(mongoRepository).should().rollbackHandles(List.of(HANDLE)); } @Test - void testUpdateRecordLocation() throws Exception { + void testUpdateDigitalSpecimen() throws Exception { // Given - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN); - var updatedAttributeRecord = genUpdateRecordAttributesAltLoc(HANDLE); - var responseExpected = givenRecordResponseNullAttributes(List.of(HANDLE), - FdoType.DIGITAL_SPECIMEN); + var previousVersion = givenDigitalSpecimenFdoRecord(HANDLE); + var request = MAPPER.valueToTree(givenDigitalSpecimenRequestObjectUpdate()); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN, request); + var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.DIGITAL_SPECIMEN, + NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL); + var expectedDocument = givenMongoDocument(updatedAttributeRecord); + var responseExpected = givenRecordResponseWriteSmallResponse(List.of(updatedAttributeRecord), + FdoType.DIGITAL_SPECIMEN, DOI_DOMAIN); var expectedEvent = new DataCiteEvent( - ((ObjectNode) genObjectNodeAttributeRecord(updatedAttributeRecord)) - .put("pid", HANDLE), + (genObjectNodeAttributeRecord(updatedAttributeRecord.attributes())), EventType.UPDATE); - given(pidRepository.checkHandlesWritable(anyList())).willReturn(List.of(HANDLE.getBytes( - StandardCharsets.UTF_8))); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); given(fdoRecordService.prepareUpdatedDigitalSpecimenRecord(any(), any(), any(), - any())).willReturn( - updatedAttributeRecord); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); + anyBoolean())).willReturn(updatedAttributeRecord); + given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); + given(profileProperties.getDomain()).willReturn(DOI_DOMAIN); // When var responseReceived = service.updateRecords(updateRequest, true); // Then assertThat(responseReceived).isEqualTo(responseExpected); - //then(pidRepository).should() - // .updateRecordBatch(CREATED.getEpochSecond(), List.of(updatedAttributeRecord), true); + then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); then(dataCiteService).should().publishToDataCite(expectedEvent, FdoType.DIGITAL_SPECIMEN); } @Test - void testUpdateInvalidType() { + void testUpdateDigitalMedia() throws Exception { + // Given + var previousVersion = givenDigitalMediaFdoRecord(HANDLE); + var request = MAPPER.valueToTree(givenDigitalMediaRequestObjectUpdate()); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DIGITAL_MEDIA, request); + var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.DIGITAL_MEDIA, + PRIMARY_MEDIA_ID_TESTVAL); + var expectedDocument = givenMongoDocument(updatedAttributeRecord); + var responseExpected = givenRecordResponseWriteSmallResponse(List.of(updatedAttributeRecord), + FdoType.DIGITAL_MEDIA, DOI_DOMAIN); + var expectedEvent = new DataCiteEvent( + (genObjectNodeAttributeRecord(updatedAttributeRecord.attributes())), + EventType.UPDATE); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); + given(fdoRecordService.prepareUpdatedDigitalMediaRecord(any(), any(), any(), + anyBoolean())).willReturn(updatedAttributeRecord); + given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); + given(profileProperties.getDomain()).willReturn(DOI_DOMAIN); + + // When + var responseReceived = service.updateRecords(updateRequest, true); + + // Then + assertThat(responseReceived).isEqualTo(responseExpected); + then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); + then(dataCiteService).should().publishToDataCite(expectedEvent, FdoType.DIGITAL_MEDIA); + } + + @Test + void testUpdateInvalidType() throws Exception { // Given - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), - FdoType.HANDLE); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.HANDLE, + MAPPER.valueToTree(givenDigitalSpecimenRequestObjectUpdate())); // When Then assertThrowsExactly(InvalidRequestException.class, () -> service.updateRecords(updateRequest, true)); } - @Test void testUpdateRecordLocationDataCiteFails() throws Exception { // Given - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN); - var updatedAttributeRecord = genUpdateRecordAttributesAltLoc(HANDLE); - - given(pidRepository.checkHandlesWritable(anyList())).willReturn(List.of(HANDLE)); - given(fdoRecordService.prepareUpdateAttributes(any(), any(), any())).willReturn( - updatedAttributeRecord); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); + var requestAttributes = MAPPER.valueToTree(givenDigitalSpecimenRequestObjectUpdate()); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN, + requestAttributes); + var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.DIGITAL_SPECIMEN, + NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL); + var previousVersion = givenDigitalSpecimenFdoRecord(HANDLE); + var expectedDocument = givenMongoDocument(updatedAttributeRecord); + given(fdoRecordService.prepareUpdatedDigitalSpecimenRecord(any(), any(), eq(previousVersion), + eq(true))).willReturn(updatedAttributeRecord); + given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); doThrow(JsonProcessingException.class).when(dataCiteService).publishToDataCite(any(), any()); // When @@ -218,9 +247,24 @@ void testUpdateRecordLocationDataCiteFails() throws Exception { () -> service.updateRecords(updateRequest, true)); // Then - then(pidRepository).should() - .updateRecordBatch(CREATED.getEpochSecond(), List.of(updatedAttributeRecord), true); - then(pidRepository).shouldHaveNoMoreInteractions(); + then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); + then(mongoRepository).shouldHaveNoMoreInteractions(); + } + + @Test + void testCreateInvalidType() { + // Given + var requestJson = + MAPPER.createObjectNode() + .set("data", MAPPER.createObjectNode() + .put("type", FdoType.HANDLE.getFdoProfile()) + .set("attributes", MAPPER.createObjectNode())); + var request = List.of(requestJson); + + // When Then + assertThrows(UnsupportedOperationException.class, () -> + service.createRecords(request)); + } } diff --git a/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java index 679a1ff1..0014a23c 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java @@ -1,111 +1,91 @@ package eu.dissco.core.handlemanager.service; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.HS_ADMIN; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.LOC; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SPECIMEN_HOST; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SPECIMEN_HOST_NAME; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.STRUCTURAL_TYPE; import static eu.dissco.core.handlemanager.testUtils.TestUtils.API_URL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.CREATED; import static eu.dissco.core.handlemanager.testUtils.TestUtils.DOC_BUILDER_FACTORY; import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE_ALT; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE_DOMAIN; import static eu.dissco.core.handlemanager.testUtils.TestUtils.ISSUED_FOR_AGENT_TESTVAL; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.LICENSE_NAME_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.LINKED_DIGITAL_OBJECT_TYPE_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.LINKED_DO_PID_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.LOC_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.MAPPER; import static eu.dissco.core.handlemanager.testUtils.TestUtils.MEDIA_HOST_NAME_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.MEDIA_HOST_TESTVAL; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.MOTIVATION_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.ORCHESTRATION_URL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.PID_ISSUER_TESTVAL_OTHER; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.PREFIX; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.PRIMARY_MEDIA_ID_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.PRIMARY_REFERENT_TYPE_TESTVAL; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.REFERENT_NAME_TESTVAL; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.ROR_DOMAIN; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.SPECIMEN_HOST_NAME_TESTVAL; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.SPECIMEN_HOST_TESTVAL; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.STRUCTURAL_TYPE_TESTVAL; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.TARGET_DOI_TESTVAL; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.TARGET_TYPE_TESTVAL; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.TOMBSTONE_TEXT_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.TRANSFORMER_FACTORY; import static eu.dissco.core.handlemanager.testUtils.TestUtils.UI_URL; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genAnnotationAttributes; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.UPDATED; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.genDigitalMediaAttributes; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genDigitalSpecimenAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genDoiRecordAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genHandleRecordAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genMappingAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genMasAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genMediaObjectAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genOrganisationAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genSourceSystemAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genTombstoneRecordRequestAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genTombstoneRequest; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genUpdateRecordAttributesAltLoc; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genUpdateRequestAltLoc; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.genTombstoneAttributes; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationRequestObjectNoHash; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDataMappingFdoRecord; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDataMappingRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDataMappingRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaFdoRecord; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenFdoRecord; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenRequestObjectNullOptionals; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDoiFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDoiRecordRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDoiRecordRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenHandleFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenHandleRecordRequestObject; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMappingRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenHandleRecordRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMasFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMasRecordRequestObject; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMediaRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMasRecordRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMongoDocument; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenOrganisationFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenOrganisationRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenOrganisationRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenSourceSystemFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenSourceSystemRequestObject; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.setLocations; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenSourceSystemRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenTombstoneRecordRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenUpdatedFdoRecord; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrowsExactly; -import static org.mockito.AdditionalMatchers.not; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; -import static org.mockito.BDDMockito.then; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.verifyNoInteractions; -import com.fasterxml.jackson.databind.JsonNode; import eu.dissco.core.handlemanager.Profiles; -import eu.dissco.core.handlemanager.domain.fdo.AnnotationRequest; -import eu.dissco.core.handlemanager.domain.fdo.DigitalSpecimenRequest; +import eu.dissco.core.handlemanager.domain.fdo.DigitalMediaRequest; import eu.dissco.core.handlemanager.domain.fdo.FdoProfile; import eu.dissco.core.handlemanager.domain.fdo.FdoType; -import eu.dissco.core.handlemanager.domain.fdo.HandleRecordRequest; -import eu.dissco.core.handlemanager.domain.fdo.MediaObjectRequest; +import eu.dissco.core.handlemanager.domain.fdo.TombstoneRecordRequest; import eu.dissco.core.handlemanager.domain.fdo.vocabulary.media.DcTermsType; import eu.dissco.core.handlemanager.domain.fdo.vocabulary.media.MediaFormat; -import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.BaseTypeOfSpecimen; -import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.InformationArtefactType; -import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.LivingOrPreserved; -import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.MaterialOrDigitalEntity; -import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.MaterialSampleType; -import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.OtherSpecimenId; import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.PrimarySpecimenObjectIdType; -import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.TopicCategory; -import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.TopicDiscipline; -import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.TopicDomain; -import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.TopicOrigin; -import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; -import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoAttribute; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoRecord; import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.properties.ApplicationProperties; -import eu.dissco.core.handlemanager.properties.ProfileProperties; -import eu.dissco.core.handlemanager.repository.PidRepository; import eu.dissco.core.handlemanager.web.PidResolver; -import java.nio.charset.StandardCharsets; import java.time.Clock; import java.time.Instant; -import java.time.ZoneOffset; import java.util.ArrayList; -import java.util.HashSet; +import java.util.Collections; import java.util.List; -import org.junit.jupiter.api.AfterEach; +import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; @@ -117,615 +97,477 @@ @MockitoSettings(strictness = Strictness.LENIENT) class FdoRecordServiceTest { - private static final HandleAttribute ADMIN_HANDLE; - - static { - ADMIN_HANDLE = new HandleAttribute(100, HANDLE.getBytes(StandardCharsets.UTF_8), - HS_ADMIN.get(), - "\\\\x0FFF000000153330303A302E4E412F32302E353030302E31303235000000C8".getBytes( - StandardCharsets.UTF_8)); - } - - private final byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); private FdoRecordService fdoRecordService; @Mock private PidResolver pidResolver; @Mock - private PidRepository pidRepository; - @Mock - private ApplicationProperties appProperties; + private ApplicationProperties applicationProperties; @Mock Environment environment; - @Mock - ProfileProperties profileProperties; private MockedStatic mockedStatic; private MockedStatic mockedClock; - private static final String ROR_API = "https://api.ror.org/organizations/"; @BeforeEach - void init() { + void init() throws PidResolutionException { fdoRecordService = new FdoRecordService(TRANSFORMER_FACTORY, DOC_BUILDER_FACTORY, pidResolver, - MAPPER, appProperties, profileProperties); - initTime(); - given(appProperties.getApiUrl()).willReturn(API_URL); - given(appProperties.getOrchestrationUrl()).willReturn(ORCHESTRATION_URL); - given(appProperties.getUiUrl()).willReturn(UI_URL); + MAPPER, applicationProperties); + given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) + .willReturn(ISSUED_FOR_AGENT_TESTVAL); + given(applicationProperties.getPrefix()).willReturn(PREFIX); + given(applicationProperties.getApiUrl()).willReturn(API_URL); + given(applicationProperties.getOrchestrationUrl()).willReturn(ORCHESTRATION_URL); + given(applicationProperties.getUiUrl()).willReturn(UI_URL); given(environment.matchesProfiles(Profiles.DOI)).willReturn(false); } - @AfterEach - void destroy() { - mockedStatic.close(); - mockedClock.close(); - } - @Test - void testPrepareHandleRecordAttributes() throws Exception { + void testPrepareNewHandleRecord() throws Exception { // Given var request = givenHandleRecordRequestObject(); - given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) - .willReturn(ISSUED_FOR_AGENT_TESTVAL); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - var expected = genHandleRecordAttributes(handle, FdoType.HANDLE); - expected.add(ADMIN_HANDLE); + var expected = givenHandleFdoRecord(HANDLE); // When - var result = fdoRecordService.prepareHandleRecordAttributes(request, handle, FdoType.HANDLE); + var result = fdoRecordService.prepareNewHandleRecord(request, HANDLE, FdoType.HANDLE, CREATED); // Then - assertThat(result).hasSameSizeAs(expected).hasSameSizeAs(expected); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isNull(); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testPrepareDoiRecordAttributes() throws Exception { + void testPrepareUpdatedHandleRecord() throws Exception { // Given - var request = givenDoiRecordRequestObject(); - var expected = genDoiRecordAttributes(handle, FdoType.DOI); - expected.add(ADMIN_HANDLE); - given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) - .willReturn(ISSUED_FOR_AGENT_TESTVAL); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); + var previousVersion = givenHandleFdoRecord(HANDLE); + var expected = givenUpdatedFdoRecord(FdoType.HANDLE, null); + var request = givenHandleRecordRequestObjectUpdate(); // When - var result = fdoRecordService.prepareDoiRecordAttributes(request, handle, FdoType.DOI); + var result = fdoRecordService.prepareUpdatedHandleRecord(request, FdoType.HANDLE, UPDATED, + previousVersion, true); // Then - assertThat(result).hasSameElementsAs(expected).hasSameSizeAs(expected); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isNull(); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testPrepareDoiRecordAttributesDoiProfile() throws Exception { + void testPrepareUpdatedHandleRecordNoIncrement() throws Exception { // Given - var request = givenDoiRecordRequestObject(); - given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) - .willReturn(ISSUED_FOR_AGENT_TESTVAL); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - var expected = genDoiRecordAttributes(handle, FdoType.DOI); - expected.add(ADMIN_HANDLE); - var replaceThis = new HandleAttribute(LOC, handle, new String( - setLocations(request.getLocations(), new String(handle, StandardCharsets.UTF_8), - FdoType.DOI, false), StandardCharsets.UTF_8)); - var withThis = new HandleAttribute(LOC, handle, new String( - setLocations(request.getLocations(), new String(handle, StandardCharsets.UTF_8), - FdoType.DOI, true), StandardCharsets.UTF_8)); - expected.remove(replaceThis); - expected.add(withThis); + var previousVersion = givenHandleFdoRecord(HANDLE); + var expectedAttributes = new ArrayList<>( + givenUpdatedFdoRecord(FdoType.HANDLE, null).attributes()); + expectedAttributes.set(expectedAttributes.indexOf( + new FdoAttribute(FdoProfile.PID_RECORD_ISSUE_NUMBER, UPDATED, "2")), + new FdoAttribute(FdoProfile.PID_RECORD_ISSUE_NUMBER, CREATED, "1")); + var expected = new FdoRecord(HANDLE, FdoType.HANDLE, expectedAttributes, null); + + var request = givenHandleRecordRequestObjectUpdate(); // When - var result = fdoRecordService.prepareDoiRecordAttributes(request, handle, FdoType.DOI); + var result = fdoRecordService.prepareUpdatedHandleRecord(request, FdoType.HANDLE, UPDATED, + previousVersion, false); // Then - assertThat(result).hasSameElementsAs(expected).hasSameSizeAs(expected); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isNull(); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testPrepareMediaObjectAttributesMandatory() throws Exception { + void testPrepareNewDoiRecord() throws Exception { // Given - given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) - .willReturn(ISSUED_FOR_AGENT_TESTVAL); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - var request = givenMediaRequestObject(); - var expected = genMediaObjectAttributes(handle); - expected.add(ADMIN_HANDLE); + var request = givenDoiRecordRequestObject(); + var expected = givenDoiFdoRecord(HANDLE); // When - var result = fdoRecordService.prepareMediaObjectAttributes(request, handle); + var result = fdoRecordService.prepareNewDoiRecord(request, HANDLE, FdoType.DOI, CREATED); // Then - assertThat(result).hasSameElementsAs(expected).hasSameSizeAs(expected); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isNull(); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testPrepareMediaObjectAttributesOptional() throws Exception { + void testPrepareUpdatedDoiRecord() throws Exception { // Given - given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) - .willReturn(ISSUED_FOR_AGENT_TESTVAL).willReturn(MEDIA_HOST_NAME_TESTVAL); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - var request = new MediaObjectRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, - LOC_TESTVAL, REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, - MEDIA_HOST_TESTVAL, null, - MediaFormat.TEXT, Boolean.TRUE, LINKED_DO_PID_TESTVAL, - LINKED_DIGITAL_OBJECT_TYPE_TESTVAL, - "a", HANDLE, PrimarySpecimenObjectIdType.RESOLVABLE, "b", DcTermsType.IMAGE, "jpeg", - "c", - "license", "license", "c", "d", null, "e"); - var expected = genMediaObjectAttributes(handle, request); - expected.add(ADMIN_HANDLE); + var previousVersion = givenDoiFdoRecord(HANDLE); + var expected = givenUpdatedFdoRecord(FdoType.DOI, null); + var request = givenDoiRecordRequestObjectUpdate(); // When - var result = fdoRecordService.prepareMediaObjectAttributes(request, handle); + var result = fdoRecordService.prepareUpdatedDoiRecord(request, FdoType.DOI, UPDATED, + previousVersion, true); // Then - assertThat(result).hasSameElementsAs(expected).hasSameSizeAs(expected); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isNull(); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testPrepareMediaObjectFullAttributes() throws Exception { - // Given - given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) - .willReturn(ISSUED_FOR_AGENT_TESTVAL).willReturn(MEDIA_HOST_NAME_TESTVAL); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - var request = new MediaObjectRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, - LOC_TESTVAL, REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, - MEDIA_HOST_TESTVAL, - MEDIA_HOST_NAME_TESTVAL, MediaFormat.TEXT, Boolean.TRUE, LINKED_DO_PID_TESTVAL, - LINKED_DIGITAL_OBJECT_TYPE_TESTVAL, "a", "b", PrimarySpecimenObjectIdType.GLOBAL, - "d", - DcTermsType.IMAGE, "e", "f", LICENSE_NAME_TESTVAL, "g", "h", "i", - PrimarySpecimenObjectIdType.LOCAL, "j"); - var expected = genMediaObjectAttributes(handle, request); - expected.add(ADMIN_HANDLE); + void testPrepareNewMediaRecordMin() throws Exception { + var request = givenDigitalMediaRequestObject(); + var expected = givenDigitalMediaFdoRecord(HANDLE); // When - var result = fdoRecordService.prepareMediaObjectAttributes(request, handle); + var result = fdoRecordService.prepareNewDigitalMediaRecord(request, HANDLE, CREATED); // Then - assertThat(result).hasSameElementsAs(expected).hasSameSizeAs(expected); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isEqualTo(expected.primaryLocalId()); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testPrepareDigitalSpecimenRecordMandatoryAttributes() throws Exception { - // Given - given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) - .willReturn(ISSUED_FOR_AGENT_TESTVAL).willReturn(SPECIMEN_HOST_NAME_TESTVAL); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - var request = givenDigitalSpecimenRequestObjectNullOptionals(); - var expected = genDigitalSpecimenAttributes(handle, request); - expected.add(ADMIN_HANDLE); + void testPrepareNewMediaRecordFull() throws Exception { + var request = new DigitalMediaRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, + LOC_TESTVAL, REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, MEDIA_HOST_TESTVAL, + MEDIA_HOST_NAME_TESTVAL, MediaFormat.TEXT, Boolean.TRUE, LINKED_DO_PID_TESTVAL, + LINKED_DIGITAL_OBJECT_TYPE_TESTVAL, "a", PRIMARY_MEDIA_ID_TESTVAL, + PrimarySpecimenObjectIdType.RESOLVABLE, "b", DcTermsType.IMAGE, "jpeg", "c", "license", + "license", "c", "d", null, "e"); + var expected = new FdoRecord(HANDLE, FdoType.DIGITAL_MEDIA, + genDigitalMediaAttributes(HANDLE, request, CREATED), PRIMARY_MEDIA_ID_TESTVAL); // When - var result = fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle); + var result = fdoRecordService.prepareNewDigitalMediaRecord(request, HANDLE, CREATED); // Then - assertThat(result).hasSameElementsAs(expected).hasSameSizeAs(expected); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isEqualTo(expected.primaryLocalId()); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testPrepareDigitalSpecimenRecordMandatoryAttributesQNumber() throws Exception { + void testPrepareUpdatedMediaRecord() throws Exception { // Given - String qid = "https://www.wikidata.org/wiki/Q12345"; - String qidUrl = "https://wikidata.org/w/rest.php/wikibase/v0/entities/items/Q12345"; - given(pidResolver.getObjectName(any())).willReturn("placeholder"); - given(pidResolver.resolveQid(any())).willReturn("placeholder"); - var request = new DigitalSpecimenRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, - LOC_TESTVAL, REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, qid, null, - "PhysicalId", - null, null, NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL, null, null, null, null, - null, - null, null, null, null, null, null, null, null, null); - + var previousVersion = givenDigitalMediaFdoRecord(HANDLE); + var expected = givenUpdatedFdoRecord(FdoType.DIGITAL_MEDIA, PRIMARY_MEDIA_ID_TESTVAL); + var request = givenDigitalMediaRequestObjectUpdate(); // When - fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle); + var result = fdoRecordService.prepareUpdatedDigitalMediaRecord(request, UPDATED, + previousVersion, true); // Then - then(pidResolver).should().resolveQid(qidUrl); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isEqualTo(expected.primaryLocalId()); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testPrepareDigitalSpecimenRecordMandatoryAttributesBadSpecimenHost() throws Exception { - // Given - String specimenId = "12345"; - given(pidResolver.getObjectName(any())).willReturn("placeholder"); - var request = new DigitalSpecimenRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, - LOC_TESTVAL, REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, specimenId, null, - "PhysicalId", null, null, NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL, null, null, - null, - null, null, null, null, null, null, null, null, null, null, null); + void testPrepareNewDigitalSpecimenRecordMin() throws Exception { + var request = givenDigitalSpecimenRequestObjectNullOptionals(); + var expected = givenDigitalSpecimenFdoRecord(HANDLE); // When - assertThrowsExactly(PidResolutionException.class, - () -> fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle)); + var result = fdoRecordService.prepareNewDigitalSpecimenRecord(request, HANDLE, CREATED); + + // Then + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isEqualTo(expected.primaryLocalId()); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testPrepareDigitalSpecimenRecordOptionalAttributes() throws Exception { - // Given - given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) - .willReturn(ISSUED_FOR_AGENT_TESTVAL).willReturn(SPECIMEN_HOST_NAME_TESTVAL); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - var request = givenDigitalSpecimenRequestObjectOptionalsInit(); - var expected = genDigitalSpecimenAttributes(handle, request); - expected.add(ADMIN_HANDLE); + void testPrepareNewSpecimenRecordFull() throws Exception { + var request = givenDigitalSpecimenRequestObject(); + var expected = new FdoRecord(HANDLE, FdoType.DIGITAL_SPECIMEN, + genDigitalSpecimenAttributes(HANDLE, request), + NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL); // When - var result = fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle); + var result = fdoRecordService.prepareNewDigitalSpecimenRecord(request, HANDLE, CREATED); // Then - assertThat(result).hasSameElementsAs(expected).hasSameSizeAs(expected); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isEqualTo(expected.primaryLocalId()); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testPrepareAnnotationAttributesOptional() throws Exception { + void testPrepareUpdatedSpecimenRecord() throws Exception { // Given - given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) - .willReturn(ISSUED_FOR_AGENT_TESTVAL); - given(pidRepository.resolveHandleAttributes(any(byte[].class))).willReturn( - genHandleRecordAttributes(handle, FdoType.ANNOTATION)); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - var request = givenAnnotationRequestObject(); - var expected = genAnnotationAttributes(handle, true); - expected.add(ADMIN_HANDLE); + var previousVersion = givenDigitalSpecimenFdoRecord(HANDLE); + var expected = givenUpdatedFdoRecord(FdoType.DIGITAL_SPECIMEN, + NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL); + var request = givenDigitalSpecimenRequestObjectUpdate(); // When - var result = fdoRecordService.prepareAnnotationAttributes(request, handle); + var result = fdoRecordService.prepareUpdatedDigitalSpecimenRecord(request, UPDATED, + previousVersion, true); // Then - assertThat(result).hasSameElementsAs(expected).hasSameSizeAs(expected); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isEqualTo(expected.primaryLocalId()); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testPrepareAnnotationAttributes() throws Exception { + void testPrepareNewAnnotationRecordMin() throws Exception { // Given - given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) - .willReturn(ISSUED_FOR_AGENT_TESTVAL); - given(pidRepository.resolveHandleAttributes(any(byte[].class))).willReturn( - genHandleRecordAttributes(handle, FdoType.ANNOTATION)); - given(pidRepository.resolveHandleAttributes(any(byte[].class))).willReturn( - genHandleRecordAttributes(handle, FdoType.ANNOTATION)); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - var request = new AnnotationRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, - LOC_TESTVAL, TARGET_DOI_TESTVAL, TARGET_TYPE_TESTVAL, MOTIVATION_TESTVAL, null); - var expected = genAnnotationAttributes(handle, false); - expected.add(ADMIN_HANDLE); + var request = givenAnnotationRequestObjectNoHash(); + var expected = givenAnnotationFdoRecord(HANDLE, false); // When - var result = fdoRecordService.prepareAnnotationAttributes(request, handle); + var result = fdoRecordService.prepareNewAnnotationRecord(request, HANDLE, CREATED); // Then - assertThat(result).hasSameElementsAs(expected).hasSameSizeAs(expected); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isEqualTo(expected.primaryLocalId()); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testPrepareMasRecordAttributes() throws Exception { + void testPrepareNewAnnotationRecordFull() throws Exception { // Given - given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) - .willReturn(ISSUED_FOR_AGENT_TESTVAL); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - var request = givenMasRecordRequestObject(); - var expected = genMasAttributes(handle); - expected.add(ADMIN_HANDLE); + var request = givenAnnotationRequestObject(); + var expected = givenAnnotationFdoRecord(HANDLE, true); // When - var result = fdoRecordService.prepareMasRecordAttributes(request, handle); + var result = fdoRecordService.prepareNewAnnotationRecord(request, HANDLE, CREATED); // Then - assertThat(result).hasSameElementsAs(expected).hasSameSizeAs(expected); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isEqualTo(expected.primaryLocalId()); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testPrepareMappingAttributes() throws Exception { + void testPrepareUpdatedAnnotationRecord() throws Exception { // Given - given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) - .willReturn(ISSUED_FOR_AGENT_TESTVAL); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - var request = givenMappingRequestObject(); - var expected = genMappingAttributes(handle); - expected.add(ADMIN_HANDLE); + var previousVersion = givenAnnotationFdoRecord(HANDLE, false); + var expected = givenUpdatedFdoRecord(FdoType.ANNOTATION, null); + var request = givenAnnotationRequestObjectUpdate(); // When - var result = fdoRecordService.prepareMappingAttributes(request, handle); + var result = fdoRecordService.prepareUpdatedAnnotationRecord(request, UPDATED, + previousVersion, true); // Then - assertThat(result).hasSameElementsAs(expected).hasSameSizeAs(expected); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isNull(); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } - @Test - void testPrepareSourceSystemAttributes() throws Exception { - given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) - .willReturn(ISSUED_FOR_AGENT_TESTVAL); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - var request = givenSourceSystemRequestObject(); - var expected = genSourceSystemAttributes(handle); - expected.add(ADMIN_HANDLE); + void testPrepareNewMasRecord() throws Exception { + // Given + var request = givenMasRecordRequestObject(); + var expected = givenMasFdoRecord(HANDLE); // When - var result = fdoRecordService.prepareSourceSystemAttributes(request, handle); + var result = fdoRecordService.prepareNewMasRecord(request, HANDLE, CREATED); // Then - assertThat(result).hasSameElementsAs(expected).hasSameSizeAs(expected); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isEqualTo(expected.primaryLocalId()); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testPrepareOrganisationAttributes() throws Exception { - given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) - .willReturn(ISSUED_FOR_AGENT_TESTVAL).willReturn(SPECIMEN_HOST_NAME_TESTVAL); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - var request = givenOrganisationRequestObject(); - var expected = genOrganisationAttributes(handle, request); - expected.add(ADMIN_HANDLE); + void testPrepareUpdatedMasRecord() throws Exception { + // Given + var previousVersion = givenMasFdoRecord(HANDLE); + var expected = givenUpdatedFdoRecord(FdoType.MAS, null); + var request = givenMasRecordRequestObjectUpdate(); // When - var result = fdoRecordService.prepareOrganisationAttributes(request, handle); + var result = fdoRecordService.prepareUpdatedMasRecord(request, UPDATED, previousVersion, true); // Then - assertThat(result).hasSameElementsAs(expected).hasSameSizeAs(expected); - + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isNull(); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testPidIssuerIsRor() throws Exception { + void testPrepareNewDataMappingRecord() throws Exception { // Given - given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) - .willReturn(ISSUED_FOR_AGENT_TESTVAL); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - var request = new HandleRecordRequest(ISSUED_FOR_AGENT_TESTVAL, ISSUED_FOR_AGENT_TESTVAL, - STRUCTURAL_TYPE_TESTVAL, null); - var expected = genHandleRecordAttributes(handle, FdoType.HANDLE); - expected.add(ADMIN_HANDLE); + var request = givenDataMappingRequestObject(); + var expected = givenDataMappingFdoRecord(HANDLE); // When - var result = fdoRecordService.prepareHandleRecordAttributes(request, handle, FdoType.HANDLE); + var result = fdoRecordService.prepareNewDataMappingRecord(request, HANDLE, CREATED); // Then - assertThat(result).hasSameSizeAs(expected).hasSameSizeAs(expected); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isEqualTo(expected.primaryLocalId()); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testPidIssuerBad() throws Exception { + void testPrepareUpdatedDataMappingRecord() throws Exception { // Given - given(pidResolver.getObjectName(any())).willReturn("placeholder"); - var request = new HandleRecordRequest(ISSUED_FOR_AGENT_TESTVAL, "abc", - STRUCTURAL_TYPE_TESTVAL, - null); + var previousVersion = givenDataMappingFdoRecord(HANDLE); + var expected = givenUpdatedFdoRecord(FdoType.DATA_MAPPING, null); + var request = givenDataMappingRequestObjectUpdate(); + + // When + var result = fdoRecordService.prepareUpdatedDataMappingRecord(request, UPDATED, previousVersion, + true); // Then - var e = assertThrowsExactly(InvalidRequestException.class, - () -> fdoRecordService.prepareHandleRecordAttributes(request, handle, FdoType.HANDLE)); - assertThat(e.getMessage()).contains(ROR_DOMAIN).contains(HANDLE_DOMAIN); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isNull(); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testBadRor() throws Exception { + void testPrepareNewSourceSystemRecord() throws Exception { // Given - given(pidResolver.getObjectName(any())).willReturn("placeholder"); - var request = new HandleRecordRequest("abc", ISSUED_FOR_AGENT_TESTVAL, - STRUCTURAL_TYPE_TESTVAL, - null); - - var e = assertThrowsExactly(InvalidRequestException.class, - () -> fdoRecordService.prepareHandleRecordAttributes(request, handle, FdoType.HANDLE)); - assertThat(e.getMessage()).contains(ROR_DOMAIN); - } - - @Test - void testSpecimenHostResolvable() throws Exception { - given(pidResolver.getObjectName(any())).willReturn(PID_ISSUER_TESTVAL_OTHER) - .willReturn(ISSUED_FOR_AGENT_TESTVAL); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - var expected = genDigitalSpecimenAttributes(handle); - expected.add(ADMIN_HANDLE); - - var request = new DigitalSpecimenRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, - LOC_TESTVAL, REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, - SPECIMEN_HOST_TESTVAL, - null, PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL, null, null, - NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL, null, null, null, null, null, null, null, - null, null, null, null, null, null, null); + var request = givenSourceSystemRequestObject(); + var expected = givenSourceSystemFdoRecord(HANDLE); // When - var result = fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle); + var result = fdoRecordService.prepareNewSourceSystemRecord(request, HANDLE, CREATED); // Then - assertThat(result).hasSameSizeAs(expected).hasSameSizeAs(expected); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isEqualTo(expected.primaryLocalId()); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testSpecimenHostNotResolvable() throws Exception { - var request = new DigitalSpecimenRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, - LOC_TESTVAL, REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, - SPECIMEN_HOST_TESTVAL, - null, PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL, null, null, - NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL, null, null, null, null, null, null, null, - null, null, null, null, null, null, null); - - var specimenHostRorApi = request.getSpecimenHost().replace(ROR_DOMAIN, ROR_API); - given(pidResolver.getObjectName(specimenHostRorApi)).willThrow(PidResolutionException.class); - given(pidResolver.getObjectName(not(eq(specimenHostRorApi)))).willReturn("placeholder"); - - // Then - assertThrowsExactly(PidResolutionException.class, - () -> fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle)); - } - - @Test - void testUpdateSpecimenHostResolveName() throws Exception { + void testPrepareUpdatedSourceSystemRecord() throws Exception { // Given - var request = generalUpdateRequest(List.of(SPECIMEN_HOST.get()), SPECIMEN_HOST_TESTVAL); - var apiLocation = "https://api.ror.org/organizations/0x123"; - given(pidResolver.getObjectName(apiLocation)).willReturn(SPECIMEN_HOST_NAME_TESTVAL); - ArrayList expected = new ArrayList<>(); - expected.add(new HandleAttribute(SPECIMEN_HOST.index(), handle, SPECIMEN_HOST.get(), - SPECIMEN_HOST_TESTVAL.getBytes(StandardCharsets.UTF_8))); - expected.add(new HandleAttribute(SPECIMEN_HOST_NAME.index(), handle, SPECIMEN_HOST_NAME.get(), - SPECIMEN_HOST_NAME_TESTVAL.getBytes(StandardCharsets.UTF_8))); + var previousVersion = givenSourceSystemFdoRecord(HANDLE); + var expected = givenUpdatedFdoRecord(FdoType.SOURCE_SYSTEM, null); + var request = givenSourceSystemRequestObjectUpdate(); // When - var response = fdoRecordService.prepareUpdateAttributes(HANDLE.getBytes(), request, - FdoType.DIGITAL_SPECIMEN); + var result = fdoRecordService.prepareUpdatedSourceSystemRecord(request, UPDATED, + previousVersion, true); // Then - assertThat(response).isEqualTo(expected); - assertThat(hasNoDuplicateElements(response)).isTrue(); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isNull(); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testUpdateSpecimenHostNameInRequest() throws Exception { + void testPrepareNewOrganisationRecord() throws Exception { // Given - var pid = HANDLE.getBytes(StandardCharsets.UTF_8); - var request = generalUpdateRequest(List.of(SPECIMEN_HOST.get(), SPECIMEN_HOST_NAME.get()), - SPECIMEN_HOST_TESTVAL); - ArrayList expected = new ArrayList<>(); - expected.add(new HandleAttribute(SPECIMEN_HOST.index(), pid, SPECIMEN_HOST.get(), - SPECIMEN_HOST_TESTVAL.getBytes(StandardCharsets.UTF_8))); - expected.add(new HandleAttribute(SPECIMEN_HOST_NAME.index(), pid, SPECIMEN_HOST_NAME.get(), - SPECIMEN_HOST_TESTVAL.getBytes(StandardCharsets.UTF_8))); + var request = givenOrganisationRequestObject(); + var expected = givenOrganisationFdoRecord(HANDLE); // When - var response = fdoRecordService.prepareUpdateAttributes(HANDLE.getBytes(), request, - FdoType.DIGITAL_SPECIMEN); + var result = fdoRecordService.prepareNewOrganisationRecord(request, HANDLE, CREATED); // Then - assertThat(response).isEqualTo(expected); - verifyNoInteractions(pidResolver); - assertThat(hasNoDuplicateElements(response)).isTrue(); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isEqualTo(expected.primaryLocalId()); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testUpdateAttributesAltLoc() throws Exception { + void testPrepareUpdatedOrganisationRecord() throws Exception { // Given - var updateRequest = genUpdateRequestAltLoc(); - var expected = genUpdateRecordAttributesAltLoc(HANDLE.getBytes(StandardCharsets.UTF_8)); + var previousVersion = givenOrganisationFdoRecord(HANDLE); + var expected = givenUpdatedFdoRecord(FdoType.ORGANISATION, null); + var request = givenOrganisationRequestObjectUpdate(); // When - var response = fdoRecordService.prepareUpdateAttributes(HANDLE.getBytes(StandardCharsets.UTF_8), - updateRequest, FdoType.HANDLE); + var result = fdoRecordService.prepareUpdatedOrganisationRecord(request, UPDATED, + previousVersion, true); // Then - assertThat(response).isEqualTo(expected); - assertThat(hasNoDuplicateElements(response)).isTrue(); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isNull(); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testUpdateAttributesStructuralType() throws Exception { - // Given - var updateRequest = MAPPER.createObjectNode(); - updateRequest.put(STRUCTURAL_TYPE.get(), STRUCTURAL_TYPE_TESTVAL.toString()); - var expected = List.of( - new HandleAttribute(STRUCTURAL_TYPE.index(), HANDLE.getBytes(StandardCharsets.UTF_8), - STRUCTURAL_TYPE.get(), - STRUCTURAL_TYPE_TESTVAL.toString().getBytes(StandardCharsets.UTF_8))); + void testPrepareTombstoneRecordNoRelatedIds() throws Exception { + var previousVersion = givenHandleFdoRecord(HANDLE); + var request = new TombstoneRecordRequest(TOMBSTONE_TEXT_TESTVAL, null); + var expected = new FdoRecord(HANDLE, FdoType.HANDLE, genTombstoneAttributes(request), null); // When - var response = fdoRecordService.prepareUpdateAttributes(HANDLE.getBytes(StandardCharsets.UTF_8), - updateRequest, FdoType.DIGITAL_SPECIMEN); + var result = fdoRecordService.prepareTombstoneRecord(request, UPDATED, previousVersion); // Then - assertThat(response).isEqualTo(expected); - assertThat(hasNoDuplicateElements(response)).isTrue(); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isNull(); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testUpdateAttributesOtherSpecimenIds() throws Exception { - // Given - var updateRequest = MAPPER.readTree(""" - { - "otherSpecimenIds": [ - { - "identifierValue":"a", - "identifierType":"localId" - } - ] - } - """); - var expectedStr = MAPPER.writeValueAsString(MAPPER.readTree(""" - [ - { - "identifierValue":"a", - "identifierType":"localId" - } - ] - """)); - var expected = List.of( - new HandleAttribute(FdoProfile.OTHER_SPECIMEN_IDS, - HANDLE.getBytes(StandardCharsets.UTF_8), - expectedStr)); + void testPrepareTombstoneRecordEmptyRelatedIds() throws Exception { + var previousVersion = givenHandleFdoRecord(HANDLE); + var request = new TombstoneRecordRequest(TOMBSTONE_TEXT_TESTVAL, Collections.emptyList()); + var expected = new FdoRecord(HANDLE, FdoType.HANDLE, genTombstoneAttributes(request), null); // When - var response = fdoRecordService.prepareUpdateAttributes(HANDLE.getBytes(StandardCharsets.UTF_8), - updateRequest, FdoType.DIGITAL_SPECIMEN); + var result = fdoRecordService.prepareTombstoneRecord(request, UPDATED, previousVersion); // Then - assertThat(response).isEqualTo(expected); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isNull(); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } @Test - void testUpdateAttributesNullValue() throws Exception { - // Given - var updateRequest = MAPPER.readTree(""" - { - "otherSpecimenIds": null - } - """); + void testPrepareTombstoneRecordFull() throws Exception { + var previousVersion = givenHandleFdoRecord(HANDLE); + var request = givenTombstoneRecordRequestObject(); + var expected = new FdoRecord(HANDLE, FdoType.HANDLE, genTombstoneAttributes(request), null); // When - var response = fdoRecordService.prepareUpdateAttributes(HANDLE.getBytes(StandardCharsets.UTF_8), - updateRequest, FdoType.DIGITAL_SPECIMEN); + var result = fdoRecordService.prepareTombstoneRecord(request, UPDATED, previousVersion); // Then - assertThat(response).isEmpty(); + assertThat(result.attributes()).hasSameElementsAs(expected.attributes()); + assertThat(result.primaryLocalId()).isNull(); + assertThat(result.fdoType()).isEqualTo(expected.fdoType()); + assertThat(result.handle()).isEqualTo(expected.handle()); } - @Test - void testTombstoneAttributes() throws Exception { + @ParameterizedTest + @MethodSource("fdoRecords") + void testToMongoDbDocumentHandle(FdoRecord fdoRecord) throws Exception { // Given - var expected = genTombstoneRecordRequestAttributes(HANDLE.getBytes(StandardCharsets.UTF_8)); + var expected = List.of(givenMongoDocument(fdoRecord)); // When - var response = fdoRecordService.prepareTombstoneAttributes(HANDLE.getBytes(), - genTombstoneRequest()); + var result = fdoRecordService.toMongoDbDocument(List.of(fdoRecord)); // Then - assertThat(response).isEqualTo(expected); - assertThat(hasNoDuplicateElements(response)).isTrue(); - } - - private DigitalSpecimenRequest givenDigitalSpecimenRequestObjectOptionalsInit() - throws InvalidRequestException { - return new DigitalSpecimenRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, - LOC_TESTVAL, REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, - SPECIMEN_HOST_TESTVAL, - SPECIMEN_HOST_NAME_TESTVAL, PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL, - PrimarySpecimenObjectIdType.LOCAL, "b", - NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL, null, - List.of(new OtherSpecimenId("Id", "local identifier")), TopicOrigin.NATURAL, - TopicDomain.LIFE, TopicDiscipline.ZOO, TopicCategory.AMPHIBIANS, - LivingOrPreserved.LIVING, - BaseTypeOfSpecimen.INFO, InformationArtefactType.MOVING_IMG, - MaterialSampleType.ORG_PART, - MaterialOrDigitalEntity.DIGITAL, false, HANDLE_ALT, HANDLE_ALT); - } - - private JsonNode generalUpdateRequest(List attributesToUpdate, String placeholder) { - var requestAttributes = MAPPER.createObjectNode(); - for (var attribute : attributesToUpdate) { - requestAttributes.put(attribute, placeholder); - } - return requestAttributes; - } - - private boolean hasNoDuplicateElements(List fdoRecord) { - return fdoRecord.size() == (new HashSet<>(fdoRecord).size()); + assertThat(result).isEqualTo(expected); } - private void initTime() { - Clock clock = Clock.fixed(CREATED, ZoneOffset.UTC); - Instant instant = Instant.now(clock); - mockedStatic = mockStatic(Instant.class); - mockedStatic.when(Instant::now).thenReturn(instant); - mockedStatic.when(() -> Instant.from(any())).thenReturn(instant); - mockedClock = mockStatic(Clock.class); - mockedClock.when(Clock::systemUTC).thenReturn(clock); + private static Stream fdoRecords() throws Exception { + return Stream.of(Arguments.of(givenHandleFdoRecord(HANDLE)), + Arguments.of(givenDigitalSpecimenFdoRecord(HANDLE)), + Arguments.of(givenDigitalMediaFdoRecord(HANDLE)), + Arguments.of(givenAnnotationFdoRecord(HANDLE, true)), + Arguments.of(givenAnnotationFdoRecord(HANDLE, false))); } } diff --git a/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java index ff8efef4..ae1e6467 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java @@ -1,76 +1,73 @@ package eu.dissco.core.handlemanager.service; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.HS_ADMIN; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.LINKED_DO_PID; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.NORMALISED_SPECIMEN_OBJECT_ID; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PRIMARY_MEDIA_ID; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PRIMARY_SPECIMEN_OBJECT_ID; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PID_STATUS; import static eu.dissco.core.handlemanager.testUtils.TestUtils.CREATED; import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE; import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE_ALT; import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE_DOMAIN; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE_LIST_STR; import static eu.dissco.core.handlemanager.testUtils.TestUtils.MAPPER; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.PATH; import static eu.dissco.core.handlemanager.testUtils.TestUtils.PID_STATUS_TESTVAL; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.UI_URL; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genAnnotationAttributes; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.PRIMARY_MEDIA_ID_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genCreateRecordRequest; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genDigitalSpecimenAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genDoiRecordAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genHandleRecordAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genMappingAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genMasAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genMediaObjectAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genObjectNodeAttributeRecord; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genOrganisationAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genSourceSystemAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genTombstoneRecordRequestAttributes; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genTombstoneRequestBatch; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genUpdateRecordAttributesAltLoc; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genUpdateRequestBatch; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationRequestObject; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationRequestObjectNoHash; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationResponseWrite; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenRequestObjectNullOptionals; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDataMappingFdoRecord; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDataMappingRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDataMappingRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaFdoRecord; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenFdoRecord; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDoiFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDoiRecordRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDoiRecordRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenHandleFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenHandleRecordRequestObject; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMappingRequestObject; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMediaRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenHandleRecordRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMasFdoRecord; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMasRecordRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMasRecordRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMongoDocument; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenOrganisationFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenOrganisationRequestObject; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseNullAttributes; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenOrganisationRequestObjectUpdate; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseRead; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseReadSingle; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseWrite; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseWriteArchive; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseWriteFullResponse; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseWriteSmallResponse; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenSourceSystemFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenSourceSystemRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenSourceSystemRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenUpdatedFdoRecord; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; import static org.mockito.Mockito.mockStatic; -import com.fasterxml.jackson.databind.JsonNode; import eu.dissco.core.handlemanager.Profiles; -import eu.dissco.core.handlemanager.domain.fdo.FdoProfile; import eu.dissco.core.handlemanager.domain.fdo.FdoType; -import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; +import eu.dissco.core.handlemanager.domain.fdo.vocabulary.PidStatus; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoAttribute; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoRecord; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.properties.ProfileProperties; -import eu.dissco.core.handlemanager.repository.PidMongoRepository; import eu.dissco.core.handlemanager.repository.PidRepository; import java.nio.charset.StandardCharsets; import java.time.Clock; import java.time.Instant; import java.time.ZoneOffset; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.stream.Stream; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -84,8 +81,6 @@ @ActiveProfiles(profiles = Profiles.HANDLE) class HandleServiceTest { - @Mock - private PidRepository pidRepository; @Mock private FdoRecordService fdoRecordService; @Mock @@ -93,7 +88,7 @@ class HandleServiceTest { @Mock private ProfileProperties profileProperties; @Mock - PidMongoRepository mongoRepository; + PidRepository mongoRepository; private PidService service; private List handles; private MockedStatic mockedStatic; @@ -103,7 +98,7 @@ class HandleServiceTest { void setup() { initTime(); initHandleList(); - service = new HandleService(pidRepository, fdoRecordService, pidNameGeneratorService, MAPPER, + service = new HandleService(fdoRecordService, pidNameGeneratorService, MAPPER, profileProperties, mongoRepository); } @@ -129,663 +124,559 @@ void destroy() { mockedClock.close(); } - @Test - void testResolveSingleRecord() throws Exception { - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - String path = UI_URL + HANDLE; - List recordAttributeList = genHandleRecordAttributes(handle, - FdoType.HANDLE); - - var responseExpected = givenRecordResponseReadSingle(HANDLE, path, FdoType.HANDLE, - genObjectNodeAttributeRecord(recordAttributeList)); - - given(pidRepository.resolveHandleAttributes(any(byte[].class))).willReturn(recordAttributeList); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); + void testCreateAnnotationNoHash() throws Exception { + var request = genCreateRecordRequest(givenAnnotationRequestObject(), FdoType.ANNOTATION); + var fdoRecord = givenAnnotationFdoRecord(HANDLE, false); + var expected = givenRecordResponseWriteFullResponse(List.of(HANDLE), FdoType.ANNOTATION); + var expectedDocument = givenMongoDocument(fdoRecord); + given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(fdoRecordService.prepareNewAnnotationRecord(any(), any(), any())).willReturn(fdoRecord); + given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( + List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.resolveSingleRecord(handle, path); + var result = service.createRecords(List.of(request)); // Then - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); } @Test - void testRemoveHsAdmin() throws Exception { - - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - String path = UI_URL + HANDLE; - var adminHandle = new HandleAttribute(HS_ADMIN.index(), handle, HS_ADMIN.get(), - "\\\\x0FFF000000153330303A302E4E412F32302E353030302E31303235000000C8".getBytes( - StandardCharsets.UTF_8)); - var recordAttributeList = genHandleRecordAttributes(handle, FdoType.HANDLE); - recordAttributeList.add(adminHandle); - - var responseExpected = givenRecordResponseReadSingle(HANDLE, path, FdoType.HANDLE, - genObjectNodeAttributeRecord(recordAttributeList)); - - given(pidRepository.resolveHandleAttributes(any(byte[].class))).willReturn(recordAttributeList); + void testCreateAnnotationIncludeHash() throws Exception { + var request = genCreateRecordRequest(givenAnnotationRequestObject(), FdoType.ANNOTATION); + var fdoRecord = givenAnnotationFdoRecord(HANDLE, true); + var expected = givenRecordResponseWriteSmallResponse(List.of(fdoRecord), FdoType.ANNOTATION, + HANDLE_DOMAIN); + var expectedDocument = givenMongoDocument(fdoRecord); + given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(fdoRecordService.prepareNewAnnotationRecord(any(), any(), any())).willReturn(fdoRecord); + given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( + List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.resolveSingleRecord(handle, path); + var result = service.createRecords(List.of(request)); // Then - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); } - @Test - void testResolveSingleRecordNotFound() { - - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - String path = UI_URL + HANDLE; - given(pidRepository.resolveHandleAttributes(any(byte[].class))).willReturn(new ArrayList<>()); - - // When - var exception = assertThrowsExactly(PidResolutionException.class, - () -> service.resolveSingleRecord(handle, path)); - // Then - assertThat(exception.getMessage()).contains(HANDLE); - } @Test - void testResolveBatchRecord() throws Exception { + void testUpdateAnnotation() throws Exception { // Given - String path = UI_URL; - List repositoryResponse = new ArrayList<>(); - for (byte[] handle : handles) { - repositoryResponse.addAll(genHandleRecordAttributes(handle, FdoType.HANDLE)); - } - var responseExpected = givenRecordResponseRead(handles, path, FdoType.HANDLE); - - given(pidRepository.resolveHandleAttributes(anyList())).willReturn(repositoryResponse); + var previousVersion = givenAnnotationFdoRecord(HANDLE, false); + var request = MAPPER.valueToTree(givenAnnotationRequestObjectUpdate()); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.ANNOTATION, request); + var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.ANNOTATION, null); + var expectedDocument = givenMongoDocument(updatedAttributeRecord); + var expected = givenRecordResponseWriteSmallResponse(List.of(updatedAttributeRecord), + FdoType.ANNOTATION, HANDLE_DOMAIN); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); + given(fdoRecordService.prepareUpdatedAnnotationRecord(any(), any(), any(), + anyBoolean())).willReturn(updatedAttributeRecord); + given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.resolveBatchRecord(handles, path); + var result = service.updateRecords(updateRequest, true); // Then - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); + then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); } @Test - void testResolveBatchDigitalSpecimenRecord() throws Exception { - // Given - String path = UI_URL; - List repositoryResponse = new ArrayList<>(); - for (byte[] handle : handles) { - repositoryResponse.addAll(genDigitalSpecimenAttributes(handle)); - } - var responseExpected = givenRecordResponseRead(handles, path, + void testCreateDigitalSpecimen() throws Exception { + var request = genCreateRecordRequest(givenDigitalSpecimenRequestObject(), FdoType.DIGITAL_SPECIMEN); - - given(pidRepository.resolveHandleAttributes(anyList())).willReturn(repositoryResponse); + var fdoRecord = givenDigitalSpecimenFdoRecord(HANDLE); + var expected = givenRecordResponseWriteSmallResponse(List.of(fdoRecord), + FdoType.DIGITAL_SPECIMEN, HANDLE_DOMAIN); + var expectedDocument = givenMongoDocument(fdoRecord); + given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(fdoRecordService.prepareNewDigitalSpecimenRecord(any(), any(), any())).willReturn( + fdoRecord); + given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( + List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.resolveBatchRecord(handles, path); + var result = service.createRecords(List.of(request)); // Then - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); } @Test - void testSearchByPhysicalSpecimenId() throws Exception { - // Given - var expectedAttributes = genDigitalSpecimenAttributes(HANDLE.getBytes(StandardCharsets.UTF_8)); - var responseExpected = givenRecordResponseWrite( - List.of(HANDLE.getBytes(StandardCharsets.UTF_8)), FdoType.DIGITAL_SPECIMEN); + void testCreateDigitalSpecimenObjectExists() throws Exception { + var request = genCreateRecordRequest(givenDigitalSpecimenRequestObject(), + FdoType.DIGITAL_SPECIMEN); + var fdoRecord = givenDigitalSpecimenFdoRecord(HANDLE); + given(mongoRepository.searchByPrimaryLocalId(any(), any())).willReturn(List.of(fdoRecord)); + + // When / Then + assertThrows(InvalidRequestException.class, () -> service.createRecords(List.of(request))); + + } - given(pidRepository.searchByNormalisedPhysicalIdentifierFullRecord(anyList())).willReturn( - expectedAttributes); + @Test + void testUpdateDigitalSpecimen() throws Exception { + // Given + var previousVersion = givenDigitalSpecimenFdoRecord(HANDLE); + var request = MAPPER.valueToTree(givenDigitalSpecimenRequestObjectUpdate()); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN, request); + var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.DIGITAL_SPECIMEN, + NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL); + var expectedDocument = givenMongoDocument(updatedAttributeRecord); + var expected = givenRecordResponseWriteSmallResponse(List.of(updatedAttributeRecord), + FdoType.DIGITAL_SPECIMEN, HANDLE_DOMAIN); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); + given(fdoRecordService.prepareUpdatedDigitalSpecimenRecord(any(), any(), any(), + anyBoolean())).willReturn(updatedAttributeRecord); + given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.searchByPhysicalSpecimenId(PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL); + var result = service.updateRecords(updateRequest, true); // Then - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); + then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); } @Test - void testSearchByPhysicalSpecimenIdTwoResolution() throws Exception { - // Given - List attributeList = new ArrayList<>(); - attributeList.addAll(genDigitalSpecimenAttributes(HANDLE.getBytes(StandardCharsets.UTF_8))); - attributeList.addAll(genDigitalSpecimenAttributes(HANDLE_ALT.getBytes(StandardCharsets.UTF_8))); - - given(pidRepository.searchByNormalisedPhysicalIdentifierFullRecord(anyList())).willReturn( - attributeList); + void testCreateDoi() throws Exception { + var request = genCreateRecordRequest(givenDoiRecordRequestObject(), FdoType.DOI); + var fdoRecord = givenDoiFdoRecord(HANDLE); + var expected = givenRecordResponseWriteFullResponse(List.of(HANDLE), FdoType.DOI); + var expectedDocument = givenMongoDocument(fdoRecord); + given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(fdoRecordService.prepareNewDoiRecord(any(), any(), any(), any())).willReturn(fdoRecord); + given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( + List.of(expectedDocument)); + given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - Exception e = assertThrowsExactly(PidResolutionException.class, - () -> service.searchByPhysicalSpecimenId(PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL)); + var result = service.createRecords(List.of(request)); // Then - assertThat(e).hasMessage( - "More than one handle record corresponds to the provided collection facility and physical identifier."); + assertThat(result).isEqualTo(expected); } @Test - void testCreateHandleRecord() throws Exception { + void testUpdateDoiRecord() throws Exception { // Given - byte[] handle = handles.get(0); - var request = genCreateRecordRequest(givenHandleRecordRequestObject(), FdoType.HANDLE); - var responseExpected = givenRecordResponseWrite(List.of(handle), FdoType.HANDLE); - List handleRecord = genHandleRecordAttributes(handle, FdoType.HANDLE); - - given(pidNameGeneratorService.genHandleList(1)).willReturn(new ArrayList<>(List.of(handle))); - given(fdoRecordService.prepareHandleRecordAttributes(any(), any(), - eq(FdoType.HANDLE))).willReturn(handleRecord); + var previousVersion = givenDoiFdoRecord(HANDLE); + var request = MAPPER.valueToTree(givenDoiRecordRequestObjectUpdate()); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DOI, request); + var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.DOI, null); + var expectedDocument = givenMongoDocument(updatedAttributeRecord); + var expected = givenRecordResponseWriteFullResponse(updatedAttributeRecord); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); + given(fdoRecordService.prepareUpdatedDoiRecord(any(), any(), any(), any(), + anyBoolean())).willReturn(updatedAttributeRecord); + given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.createRecords(List.of(request)); + var result = service.updateRecords(updateRequest, true); // Then - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); + then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); } @Test - void testCreateDoiRecord() throws Exception { - // Given - byte[] handle = handles.get(0); - var request = genCreateRecordRequest(givenDoiRecordRequestObject(), FdoType.DOI); - var responseExpected = givenRecordResponseWrite(List.of(handle), FdoType.DOI); - List doiRecord = genDoiRecordAttributes(handle, FdoType.DOI); - - given(pidNameGeneratorService.genHandleList(1)).willReturn(new ArrayList<>(List.of(handle))); - given(fdoRecordService.prepareDoiRecordAttributes(any(), any(), eq(FdoType.DOI))).willReturn( - doiRecord); + void testCreateHandle() throws Exception { + var request = genCreateRecordRequest(givenHandleRecordRequestObject(), FdoType.HANDLE); + var fdoRecord = givenHandleFdoRecord(HANDLE); + var expected = givenRecordResponseWriteFullResponse(List.of(HANDLE), FdoType.HANDLE); + var expectedDocument = givenMongoDocument(fdoRecord); + given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(fdoRecordService.prepareNewHandleRecord(any(), any(), any(), any())).willReturn( + fdoRecord); + given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( + List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.createRecords(List.of(request)); + var result = service.createRecords(List.of(request)); // Then - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); } @Test - void testCreateDigitalSpecimen() throws Exception { + void testUpdateHandleRecord() throws Exception { // Given - byte[] handle = handles.get(0); - var request = genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), - FdoType.DIGITAL_SPECIMEN); - List digitalSpecimen = genDigitalSpecimenAttributes(handle); - var digitalSpecimenSublist = digitalSpecimen.stream() - .filter(row -> row.getType().equals(PRIMARY_SPECIMEN_OBJECT_ID.get())).toList(); - - var responseExpected = givenRecordResponseWriteSmallResponse(digitalSpecimenSublist, - List.of(handle), FdoType.DIGITAL_SPECIMEN); - - given(pidNameGeneratorService.genHandleList(1)).willReturn(new ArrayList<>(List.of(handle))); - given(fdoRecordService.prepareDigitalSpecimenRecordAttributes(any(), any())).willReturn( - digitalSpecimen); + var previousVersion = givenHandleFdoRecord(HANDLE); + var request = MAPPER.valueToTree(givenHandleRecordRequestObjectUpdate()); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.HANDLE, request); + var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.HANDLE, null); + var expectedDocument = givenMongoDocument(updatedAttributeRecord); + var expected = givenRecordResponseWriteFullResponse(updatedAttributeRecord); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); + given(fdoRecordService.prepareUpdatedHandleRecord(any(), any(), any(), any(), + anyBoolean())).willReturn(updatedAttributeRecord); + given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.createRecords(List.of(request)); + var result = service.updateRecords(updateRequest, true); // Then - then(pidRepository).should().postAttributesToDb(CREATED.getEpochSecond(), digitalSpecimen); - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); + then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); } @Test - void testCreateDigitalSpecimenSpecimenAlreadyExists() { + void testUpdateHandleRecordInternalDuplicates() throws Exception { // Given - byte[] handle = handles.get(0); - var digitalSpecimen = givenDigitalSpecimenRequestObjectNullOptionals(); - var request = List.of((JsonNode) genCreateRecordRequest(digitalSpecimen, - FdoType.DIGITAL_SPECIMEN)); - given(pidNameGeneratorService.genHandleList(1)).willReturn(new ArrayList<>(List.of(handle))); - given(pidRepository.searchByNormalisedPhysicalIdentifier(anyList())).willReturn(List.of( - new HandleAttribute(FdoProfile.NORMALISED_SPECIMEN_OBJECT_ID, handle, - digitalSpecimen.getNormalisedPrimarySpecimenObjectId()))); + var attributes = new ArrayList<>(givenHandleFdoRecord(HANDLE).attributes()); + attributes.set(attributes.indexOf(new FdoAttribute(PID_STATUS, CREATED, PID_STATUS_TESTVAL)), + new FdoAttribute(PID_STATUS, CREATED, PidStatus.TOMBSTONED.name())); + var previousVersion = new FdoRecord(HANDLE_ALT, FdoType.HANDLE, attributes, null); + var request = MAPPER.valueToTree(givenHandleRecordRequestObjectUpdate()); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.HANDLE, request); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); - // When Then - assertThrowsExactly(InvalidRequestException.class, () -> service.createRecords(request)); + // When / Then + assertThrows(InvalidRequestException.class, () -> service.updateRecords(updateRequest, true)); } @Test - void testCreateMediaObjectRecord() throws Exception { + void testUpdateHandleRecordNotWritableInternalDuplicates() throws Exception { // Given - byte[] handle = handles.get(0); - var request = genCreateRecordRequest(givenMediaRequestObject(), FdoType.MEDIA_OBJECT); - var handleRecord = genMediaObjectAttributes(handle); - var handleRecordSublist = handleRecord.stream().filter( - row -> row.getType().equals(PRIMARY_MEDIA_ID.get()) || row.getType() - .equals(LINKED_DO_PID.get())).toList(); - - var responseExpected = givenRecordResponseWriteSmallResponse(handleRecordSublist, - List.of(handle), FdoType.MEDIA_OBJECT); - - given(pidNameGeneratorService.genHandleList(1)).willReturn(new ArrayList<>(List.of(handle))); - given(fdoRecordService.prepareMediaObjectAttributes(any(), any())).willReturn( - handleRecordSublist); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - - // When - var responseReceived = service.createRecords(List.of(request)); + var request = MAPPER.valueToTree(givenHandleRecordRequestObjectUpdate()); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.HANDLE, request); - // Then - assertThat(responseReceived).isEqualTo(responseExpected); + // When / Then + assertThrows(InvalidRequestException.class, () -> service.updateRecords(updateRequest, true)); } @Test - void testCreateMasRecord() throws Exception { + void testUpdateHandleRecordNotFound() throws Exception { // Given - byte[] handle = handles.get(0); - var request = genCreateRecordRequest(givenHandleRecordRequestObject(), FdoType.MAS); - var responseExpected = givenRecordResponseWrite(List.of(handle), FdoType.MAS); - List handleRecord = genMasAttributes(handle); - - given(pidNameGeneratorService.genHandleList(1)).willReturn(new ArrayList<>(List.of(handle))); - given(fdoRecordService.prepareMasRecordAttributes(any(), any())).willReturn(handleRecord); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); + var request = MAPPER.valueToTree(givenHandleRecordRequestObjectUpdate()); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.HANDLE, request); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(Collections.emptyList()); // When - - var responseReceived = service.createRecords(List.of(request)); - - // Then - assertThat(responseReceived).isEqualTo(responseExpected); + assertThrows(InvalidRequestException.class, + () -> service.updateRecords(updateRequest, true)); } @Test - void testCreateHandleRecordBatch() throws Exception { - // Given - - List requests = new ArrayList<>(); - for (int i = 0; i < handles.size(); i++) { - requests.add(genCreateRecordRequest(givenHandleRecordRequestObject(), FdoType.HANDLE)); - } - - var responseExpected = givenRecordResponseWrite(handles, FdoType.HANDLE); - - given(pidNameGeneratorService.genHandleList(handles.size())).willReturn(handles); - given(fdoRecordService.prepareHandleRecordAttributes(any(), any(), any())).willReturn( - genHandleRecordAttributes(handles.get(0), FdoType.HANDLE)) - .willReturn(genHandleRecordAttributes(handles.get(1), FdoType.HANDLE)); + void testCreateDataMapping() throws Exception { + var request = genCreateRecordRequest(givenDataMappingRequestObject(), FdoType.DATA_MAPPING); + var fdoRecord = givenDataMappingFdoRecord(HANDLE); + var expected = givenRecordResponseWriteFullResponse(List.of(HANDLE), FdoType.DATA_MAPPING); + var expectedDocument = givenMongoDocument(fdoRecord); + given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(fdoRecordService.prepareNewDataMappingRecord(any(), any(), any())).willReturn(fdoRecord); + given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( + List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.createRecords(requests); + var result = service.createRecords(List.of(request)); // Then - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); } @Test - void testCreateDoiRecordBatch() throws Exception { + void testUpdateDataMapping() throws Exception { // Given - List flatList = new ArrayList<>(); - - List requests = new ArrayList<>(); - for (byte[] handle : handles) { - requests.add(genCreateRecordRequest(givenDoiRecordRequestObject(), FdoType.DOI)); - flatList.addAll(genDoiRecordAttributes(handle, FdoType.DOI)); - } - - var responseExpected = givenRecordResponseWrite(handles, FdoType.DOI); - - given(pidNameGeneratorService.genHandleList(handles.size())).willReturn(handles); - given(fdoRecordService.prepareDoiRecordAttributes(any(), any(), any())).willReturn(flatList); + var previousVersion = givenDataMappingFdoRecord(HANDLE); + var request = MAPPER.valueToTree(givenDataMappingRequestObjectUpdate()); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DATA_MAPPING, request); + var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.DATA_MAPPING, null); + var expectedDocument = givenMongoDocument(updatedAttributeRecord); + var expected = givenRecordResponseWriteFullResponse(updatedAttributeRecord); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); + given(fdoRecordService.prepareUpdatedDataMappingRecord(any(), any(), any(), + anyBoolean())).willReturn(updatedAttributeRecord); + given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.createRecords(requests); + var result = service.updateRecords(updateRequest, true); // Then - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); + then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); } @Test - void testCreateDigitalSpecimenBatch() throws Exception { - // Given - List requests = new ArrayList<>(); - for (byte[] handle : handles) { - var physId = new String(handle) + "a"; - requests.add(genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(physId), - FdoType.DIGITAL_SPECIMEN)); - } - var sublist = Stream.concat(genDigitalSpecimenAttributes(handles.get(0)).stream() - .filter(row -> row.getType().equals(PRIMARY_SPECIMEN_OBJECT_ID.get())), - genDigitalSpecimenAttributes(handles.get(1)).stream() - .filter(row -> row.getType().equals(PRIMARY_SPECIMEN_OBJECT_ID.get()))).toList(); - - var responseExpected = givenRecordResponseWriteSmallResponse(sublist, handles, - FdoType.DIGITAL_SPECIMEN); - - given(pidNameGeneratorService.genHandleList(handles.size())).willReturn(handles); - given(fdoRecordService.prepareDigitalSpecimenRecordAttributes(any(), any())).willReturn( - genDigitalSpecimenAttributes(handles.get(0))) - .willReturn(genDigitalSpecimenAttributes(handles.get(1))); + void testCreateMas() throws Exception { + var request = genCreateRecordRequest(givenMasRecordRequestObject(), FdoType.MAS); + var fdoRecord = givenMasFdoRecord(HANDLE); + var expected = givenRecordResponseWriteFullResponse(List.of(HANDLE), FdoType.MAS); + var expectedDocument = givenMongoDocument(fdoRecord); + given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(fdoRecordService.prepareNewMasRecord(any(), any(), any())).willReturn(fdoRecord); + given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( + List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.createRecords(requests); + var result = service.createRecords(List.of(request)); // Then - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); } @Test - void testCreateAnnotationsBatch() throws Exception { + void testUpdateMas() throws Exception { // Given - - List requests = new ArrayList<>(); - for (int i = 0; i < handles.size(); i++) { - requests.add(genCreateRecordRequest(givenAnnotationRequestObject(), FdoType.ANNOTATION)); - } - - var responseExpected = givenAnnotationResponseWrite(handles); - given(pidNameGeneratorService.genHandleList(handles.size())).willReturn(handles); - given(fdoRecordService.prepareAnnotationAttributes(any(), any())).willReturn( - genAnnotationAttributes(handles.get(0), true)) - .willReturn(genAnnotationAttributes(handles.get(1), true)); + var previousVersion = givenMasFdoRecord(HANDLE); + var request = MAPPER.valueToTree(givenMasRecordRequestObjectUpdate()); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.MAS, request); + var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.MAS, null); + var expectedDocument = givenMongoDocument(updatedAttributeRecord); + var expected = givenRecordResponseWriteFullResponse(updatedAttributeRecord); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); + given(fdoRecordService.prepareUpdatedMasRecord(any(), any(), any(), anyBoolean())).willReturn( + updatedAttributeRecord); + given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.createRecords(requests); + var result = service.updateRecords(updateRequest, true); // Then - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); + then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); } @Test - void testCreateAnnotationsBatchNoHash() throws Exception { - // Given - - List requests = new ArrayList<>(); - for (int i = 0; i < handles.size(); i++) { - requests.add( - genCreateRecordRequest(givenAnnotationRequestObjectNoHash(), FdoType.ANNOTATION)); - } - - var responseExpected = givenRecordResponseWrite(handles, FdoType.ANNOTATION); - given(pidNameGeneratorService.genHandleList(handles.size())).willReturn(handles); - given(fdoRecordService.prepareAnnotationAttributes(any(), any())).willReturn( - genAnnotationAttributes(handles.get(0), false)) - .willReturn(genAnnotationAttributes(handles.get(1), false)); + void testCreateDigitalMedia() throws Exception { + var request = genCreateRecordRequest(givenDigitalMediaRequestObject(), FdoType.DIGITAL_MEDIA); + var fdoRecord = givenDigitalMediaFdoRecord(HANDLE); + var expected = givenRecordResponseWriteSmallResponse(List.of(fdoRecord), FdoType.DIGITAL_MEDIA, + HANDLE_DOMAIN); + var expectedDocument = givenMongoDocument(fdoRecord); + given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(fdoRecordService.prepareNewDigitalMediaRecord(any(), any(), any())).willReturn(fdoRecord); + given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( + List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.createRecords(requests); + var result = service.createRecords(List.of(request)); // Then - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); } @Test - void testCreateMappingBatch() throws Exception { - // Given - List requests = new ArrayList<>(); - for (int i = 0; i < handles.size(); i++) { - requests.add(genCreateRecordRequest(givenMappingRequestObject(), FdoType.MAPPING)); - } - - var responseExpected = givenRecordResponseWrite(handles, FdoType.MAPPING); - given(pidNameGeneratorService.genHandleList(handles.size())).willReturn(handles); - given(fdoRecordService.prepareMappingAttributes(any(), any())).willReturn( - genMappingAttributes(handles.get(0))).willReturn(genMappingAttributes(handles.get(1))); + void testUpdateDigitalMedia() throws Exception { + var previousVersion = givenDigitalMediaFdoRecord(HANDLE); + var request = MAPPER.valueToTree(givenDigitalMediaRequestObjectUpdate()); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DIGITAL_MEDIA, request); + var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.DIGITAL_MEDIA, + PRIMARY_MEDIA_ID_TESTVAL); + var expectedDocument = givenMongoDocument(updatedAttributeRecord); + var responseExpected = givenRecordResponseWriteSmallResponse(List.of(updatedAttributeRecord), + FdoType.DIGITAL_MEDIA, HANDLE_DOMAIN); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); + given(fdoRecordService.prepareUpdatedDigitalMediaRecord(any(), any(), any(), + anyBoolean())).willReturn(updatedAttributeRecord); + given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.createRecords(requests); + var responseReceived = service.updateRecords(updateRequest, true); // Then assertThat(responseReceived).isEqualTo(responseExpected); + then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); } @Test - void testCreateSourceSystemBatch() throws Exception { - // Given - List requests = new ArrayList<>(); - for (int i = 0; i < handles.size(); i++) { - requests.add( - genCreateRecordRequest(givenSourceSystemRequestObject(), FdoType.SOURCE_SYSTEM)); - } - - var responseExpected = givenRecordResponseWrite(handles, FdoType.SOURCE_SYSTEM); - given(pidNameGeneratorService.genHandleList(handles.size())).willReturn(handles); - given(fdoRecordService.prepareSourceSystemAttributes(any(), any())).willReturn( - genSourceSystemAttributes(handles.get(0))) - .willReturn(genSourceSystemAttributes(handles.get(1))); + void testCreateSourceSystem() throws Exception { + var request = genCreateRecordRequest(givenSourceSystemRequestObject(), FdoType.SOURCE_SYSTEM); + var fdoRecord = givenSourceSystemFdoRecord(HANDLE); + var expected = givenRecordResponseWriteFullResponse(List.of(HANDLE), FdoType.SOURCE_SYSTEM); + var expectedDocument = givenMongoDocument(fdoRecord); + given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(fdoRecordService.prepareNewSourceSystemRecord(any(), any(), any())).willReturn(fdoRecord); + given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( + List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.createRecords(requests); + var result = service.createRecords(List.of(request)); // Then - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); } @Test - void testCreateOrganisationBatch() throws Exception { + void testUpdateSourceSystem() throws Exception { // Given - List flatList = new ArrayList<>(); - - List requests = new ArrayList<>(); - for (byte[] handle : handles) { - requests.add( - genCreateRecordRequest(givenOrganisationRequestObject(), FdoType.ORGANISATION)); - flatList.addAll(genOrganisationAttributes(handle)); - } - - var responseExpected = givenRecordResponseWrite(handles, FdoType.ORGANISATION); - given(pidNameGeneratorService.genHandleList(handles.size())).willReturn(handles); - given(fdoRecordService.prepareOrganisationAttributes(any(), any())).willReturn(flatList); + var previousVersion = givenMasFdoRecord(HANDLE); + var request = MAPPER.valueToTree(givenSourceSystemRequestObjectUpdate()); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.SOURCE_SYSTEM, request); + var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.SOURCE_SYSTEM, null); + var expectedDocument = givenMongoDocument(updatedAttributeRecord); + var expected = givenRecordResponseWriteFullResponse(updatedAttributeRecord); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); + given(fdoRecordService.prepareUpdatedSourceSystemRecord(any(), any(), any(), + anyBoolean())).willReturn(updatedAttributeRecord); + given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.createRecords(requests); + var result = service.updateRecords(updateRequest, true); // Then - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); + then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); } @Test - void testCreateMediaObjectBatch() throws Exception { - // Given - List requests = new ArrayList<>(); - for (int i = 0; i < handles.size(); i++) { - requests.add(genCreateRecordRequest(givenMediaRequestObject(), FdoType.MEDIA_OBJECT)); - } - var sublist = Stream.concat(genMediaObjectAttributes(handles.get(0)).stream().filter( - row -> row.getType().equals(PRIMARY_MEDIA_ID.get()) || row.getType() - .equals(LINKED_DO_PID.get())), genMediaObjectAttributes(handles.get(1)).stream().filter( - row -> row.getType().equals(PRIMARY_MEDIA_ID.get()) || row.getType() - .equals(LINKED_DO_PID.get()))).toList(); - - var responseExpected = givenRecordResponseWriteSmallResponse(sublist, handles, - FdoType.MEDIA_OBJECT); - given(pidNameGeneratorService.genHandleList(handles.size())).willReturn(handles); - given(fdoRecordService.prepareMediaObjectAttributes(any(), any())).willReturn( - genMediaObjectAttributes(handles.get(0))) - .willReturn(genMediaObjectAttributes(handles.get(1))); + void testCreateOrganisation() throws Exception { + var request = genCreateRecordRequest(givenOrganisationRequestObject(), FdoType.ORGANISATION); + var fdoRecord = givenOrganisationFdoRecord(HANDLE); + var expected = givenRecordResponseWriteFullResponse(List.of(HANDLE), FdoType.ORGANISATION); + var expectedDocument = givenMongoDocument(fdoRecord); + given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(fdoRecordService.prepareNewOrganisationRecord(any(), any(), any())).willReturn(fdoRecord); + given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( + List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.createRecords(requests); + var result = service.createRecords(List.of(request)); // Then - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); } @Test - void testUpdateRecordLocation() throws Exception { + void testUpdateOrganisation() throws Exception { // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - var updateRequest = genUpdateRequestBatch(List.of(handle)); - var updatedAttributeRecord = genUpdateRecordAttributesAltLoc(handle); - var responseExpected = givenRecordResponseNullAttributes(List.of(handle)); - - given(pidRepository.checkHandlesWritable(anyList())).willReturn(List.of(handle)); - given(fdoRecordService.prepareUpdateAttributes(any(), any(), any())).willReturn( - updatedAttributeRecord); + var previousVersion = givenOrganisationFdoRecord(HANDLE); + var request = MAPPER.valueToTree(givenOrganisationRequestObjectUpdate()); + var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.ORGANISATION, request); + var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.ORGANISATION, null); + var expectedDocument = givenMongoDocument(updatedAttributeRecord); + var expected = givenRecordResponseWriteFullResponse(updatedAttributeRecord); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); + given(fdoRecordService.prepareUpdatedOrganisationRecord(any(), any(), any(), + anyBoolean())).willReturn(updatedAttributeRecord); + given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.updateRecords(updateRequest, true); + var result = service.updateRecords(updateRequest, true); // Then - assertThat(responseReceived).isEqualTo(responseExpected); - then(pidRepository).should() - .updateRecordBatch(CREATED.getEpochSecond(), List.of(updatedAttributeRecord), true); + assertThat(result).isEqualTo(expected); + then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); } @Test - void testUpdateRecordLocationBatch() throws Exception { + void testResolveSingleRecord() throws Exception { // Given - - List updateRequest = genUpdateRequestBatch(handles); - - var responseExpected = givenRecordResponseNullAttributes(handles); - var updatedAttributes = List.of(genUpdateRecordAttributesAltLoc(handles.get(0)), - genUpdateRecordAttributesAltLoc(handles.get(1))); - - given(pidRepository.checkHandlesWritable(anyList())).willReturn(handles); - given(fdoRecordService.prepareUpdateAttributes(any(), any(), any())).willReturn( - genUpdateRecordAttributesAltLoc(handles.get(0))) - .willReturn(genUpdateRecordAttributesAltLoc(handles.get(1))); + var expected = givenRecordResponseRead(List.of(HANDLE), PATH, FdoType.HANDLE, HANDLE_DOMAIN); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn( + List.of(givenHandleFdoRecord(HANDLE))); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.updateRecords(updateRequest, true); + var result = service.resolveSingleRecord(HANDLE, PATH); // Then - then(pidRepository).should() - .updateRecordBatch(CREATED.getEpochSecond(), updatedAttributes, true); - assertThat(responseReceived).isEqualTo(responseExpected); + assertThat(result).isEqualTo(expected); } @Test - void testUpdateRecordInternalDuplicates() throws Exception { + void testResolveBatchRecord() throws Exception { // Given - List updateRequest = genUpdateRequestBatch(handles); - given(fdoRecordService.prepareUpdateAttributes(any(), any(), any())).willReturn( - genDigitalSpecimenAttributes(HANDLE.getBytes(StandardCharsets.UTF_8))); - - // Then - assertThrowsExactly(InvalidRequestException.class, () -> { - service.updateRecords(updateRequest, true); - }); - } + var expected = givenRecordResponseRead(List.of(HANDLE), PATH, FdoType.HANDLE, HANDLE_DOMAIN); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn( + List.of(givenHandleFdoRecord(HANDLE))); + given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - @Test - void testUpdateRecordNonWritable() throws Exception { - // Given - List updateRequest = genUpdateRequestBatch(handles); - given(pidRepository.checkHandlesWritable(anyList())).willReturn(new ArrayList<>()); - given(fdoRecordService.prepareUpdateAttributes(any(), any(), any())) - .willReturn(genDigitalSpecimenAttributes(HANDLE.getBytes(StandardCharsets.UTF_8))) - .willReturn(genDigitalSpecimenAttributes(HANDLE_ALT.getBytes(StandardCharsets.UTF_8))); + // When + var result = service.resolveBatchRecord(List.of(HANDLE), PATH); // Then - assertThrowsExactly(PidResolutionException.class, () -> { - service.updateRecords(updateRequest, true); - }); + assertThat(result).isEqualTo(expected); } @Test - void testArchiveRecord() throws Exception { + void testResolveBatchRecordNotFound() throws Exception { // Given - byte[] handle = HANDLE.getBytes(StandardCharsets.UTF_8); - var archiveRequest = genTombstoneRequestBatch(List.of(HANDLE)); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(Collections.emptyList()); - var responseExpected = givenRecordResponseWriteArchive(List.of(handle)); - var tombstoneAttributes = genTombstoneRecordRequestAttributes(handle); - - given(pidRepository.checkHandlesWritable(anyList())).willReturn(handles); - given(fdoRecordService.prepareTombstoneAttributes(any(), any())).willReturn( - tombstoneAttributes); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - - // When - var responseReceived = service.archiveRecordBatch(archiveRequest); - - // Then - assertThat(responseReceived).isEqualTo(responseExpected); - then(pidRepository).should() - .archiveRecords(CREATED.getEpochSecond(), tombstoneAttributes, List.of(HANDLE)); + // When / Then + assertThrows(PidResolutionException.class, + () -> service.resolveBatchRecord(List.of(HANDLE), "")); } @Test - void testArchiveRecordBatch() throws Exception { + void testSearchByPhysicalSpecimenId() throws Exception { // Given - var handle = HANDLE.getBytes(StandardCharsets.UTF_8); - var handleAlt = HANDLE_ALT.getBytes(StandardCharsets.UTF_8); - var archiveRequest = genTombstoneRequestBatch(List.of(HANDLE, HANDLE_ALT)); - - var responseExpected = givenRecordResponseWriteArchive(List.of(handle, handleAlt)); - - var tombstoneAttributes = List.of(genTombstoneRecordRequestAttributes(handle), - genTombstoneRecordRequestAttributes(handleAlt)); - var tombstoneFlatlist = Stream.concat(tombstoneAttributes.get(0).stream(), - tombstoneAttributes.get(1).stream()).toList(); - - given(pidRepository.checkHandlesWritable(anyList())).willReturn(handles); - given(fdoRecordService.prepareTombstoneAttributes(any(), any())).willReturn( - tombstoneAttributes.get(0), tombstoneAttributes.get(1)); + var expected = givenRecordResponseWriteFullResponse(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN); + given(mongoRepository.searchByPrimaryLocalId(any(), any())).willReturn( + List.of(givenDigitalSpecimenFdoRecord(HANDLE))); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When - var responseReceived = service.archiveRecordBatch(archiveRequest); + var result = service.searchByPhysicalSpecimenId(NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL); // Then - assertThat(responseReceived).isEqualTo(responseExpected); - then(pidRepository).should() - .archiveRecords(CREATED.getEpochSecond(), tombstoneFlatlist, List.of(HANDLE, HANDLE_ALT)); + assertThat(result).isEqualTo(expected); } @Test - void testGetHandlesPaged() { + void testSearchByPhysicalSpecimenIdDuplicates() throws Exception { // Given - int pageNum = 0; - int pageSize = 2; - byte[] pidStatus = PID_STATUS_TESTVAL.getBytes(StandardCharsets.UTF_8); + given(mongoRepository.searchByPrimaryLocalId(any(), any())).willReturn( + List.of(givenDigitalSpecimenFdoRecord(HANDLE), + (givenDigitalSpecimenFdoRecord(HANDLE_ALT)))); - given(pidRepository.getAllHandles(pageNum, pageSize)).willReturn(HANDLE_LIST_STR); - given(pidRepository.getAllHandles(pidStatus, pageNum, pageSize)).willReturn(HANDLE_LIST_STR); - - // When - var responseExpectedFirst = service.getHandlesPaged(pageNum, pageSize); - var responseExpectedSecond = service.getHandlesPaged(pageNum, pageSize, pidStatus); - - // Then - assertThat(responseExpectedFirst).isEqualTo(HANDLE_LIST_STR); - assertThat(responseExpectedSecond).isEqualTo(HANDLE_LIST_STR); + // When / Then + assertThrows(PidResolutionException.class, + () -> service.searchByPhysicalSpecimenId(NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL)); } @Test - void testRollbackHandleCreation() { + void testSearchByPhysicalSpecimenIdNotFound() throws Exception { // Given - var handleList = List.of(HANDLE, HANDLE_ALT); + given(mongoRepository.searchByPrimaryLocalId(any(), any())).willReturn(Collections.emptyList()); - // When - service.rollbackHandles(handleList); - - // Then - then(pidRepository).should().rollbackHandles(handleList); + // When / Then + assertThrows(PidResolutionException.class, + () -> service.searchByPhysicalSpecimenId(NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL)); } @Test - void testRollbackHandlesFromPhysId() { + void testDifferentTypes() { // Given - given(pidRepository.searchByNormalisedPhysicalIdentifier(anyList())).willReturn( - List.of(new HandleAttribute(NORMALISED_SPECIMEN_OBJECT_ID, - HANDLE.getBytes(StandardCharsets.UTF_8), PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL))); - - // When - service.rollbackHandlesFromPhysId( - List.of(PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL, PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL)); - - // Then - then(pidRepository).should().rollbackHandles(List.of(HANDLE)); + var request1 = MAPPER.createObjectNode() + .set("data", MAPPER.createObjectNode() + .put("type", FdoType.HANDLE.getFdoProfile()) + .set("attributes", MAPPER.createObjectNode() + .put("field", "val"))); + var request2 = MAPPER.createObjectNode() + .set("data", MAPPER.createObjectNode() + .put("type", FdoType.DOI.getFdoProfile()) + .set("attributes", MAPPER.createObjectNode() + .put("field", "val"))); + var requests = List.of(request1, request2); + + // When / Then + assertThrows(UnsupportedOperationException.class, () -> service.createRecords(requests)); } + } diff --git a/src/test/java/eu/dissco/core/handlemanager/service/PidNameGeneratorServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/PidNameGeneratorServiceTest.java index e0ccac6c..1e31c995 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/PidNameGeneratorServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/PidNameGeneratorServiceTest.java @@ -9,7 +9,6 @@ import eu.dissco.core.handlemanager.properties.ApplicationProperties; import eu.dissco.core.handlemanager.repository.PidRepository; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Random; @@ -49,8 +48,7 @@ void testSingleBatchGen() { given(applicationProperties.getPrefix()).willReturn(PREFIX); // When - String generatedHandle = new String(pidNameGeneratorService.genHandleList(1).get(0), - StandardCharsets.UTF_8); + String generatedHandle = pidNameGeneratorService.genHandleList(1).get(0); // Then assertThat(generatedHandle).isEqualTo(expectedHandle); @@ -59,26 +57,21 @@ void testSingleBatchGen() { @Test void testBatchGen() { // Given - String expectedHandle1 = PREFIX + "/BBB-BBB-BBB"; - String expectedHandle2 = PREFIX + "/ABB-BBB-BBB"; + var expected = List.of(PREFIX + "/ABB-BBB-BBB", PREFIX + "/BBB-BBB-BBB"); given(random.nextInt(anyInt())).willReturn(0, 1); given(applicationProperties.getPrefix()).willReturn(PREFIX); // When - List handleList = pidNameGeneratorService.genHandleList(2); - String generatedHandle1 = new String(handleList.get(0)); - String generatedHandle2 = new String(handleList.get(1)); + var handleList = pidNameGeneratorService.genHandleList(2); // Then - assertThat(generatedHandle1).isEqualTo(expectedHandle1); - assertThat(generatedHandle2).isEqualTo(expectedHandle2); + assertThat(handleList).isEqualTo(expected); } @Test void testInternalCollision() { - String expectedHandle1 = PREFIX + "/BBB-BBB-BBB"; - String expectedHandle2 = PREFIX + "/AAA-AAA-AAA"; + var expected = List.of(PREFIX + "/AAA-AAA-AAA", PREFIX + "/BBB-BBB-BBB"); given(random.nextInt(anyInt())).willReturn(0, 0, 0, 0, 0, 0, 0, 0, 0)// First .willReturn(0, 0, 0, 0, 0, 0, 0, 0, 0) // Collision @@ -86,38 +79,28 @@ void testInternalCollision() { given(applicationProperties.getPrefix()).willReturn(PREFIX); // When - List handleList = pidNameGeneratorService.genHandleList(2); - String generatedHandle1 = new String(handleList.get(0)); - String generatedHandle2 = new String(handleList.get(1)); + var result = pidNameGeneratorService.genHandleList(2); // Then - assertThat(generatedHandle1).isEqualTo(expectedHandle1); - assertThat(generatedHandle2).isEqualTo(expectedHandle2); + assertThat(expected).isEqualTo(result); } @Test void testDbCollision() { // Given - byte[] expectedHandle1 = (PREFIX + "/BBB-BBB-BBB").getBytes(StandardCharsets.UTF_8); - byte[] expectedHandle2 = (PREFIX + "/ABB-BBB-BBB").getBytes(StandardCharsets.UTF_8); - - List handleListInternalDuplicate = new ArrayList<>(); - handleListInternalDuplicate.add(expectedHandle1); - + var expectedHandle1 = PREFIX + "/BBB-BBB-BBB"; + var expectedHandle2 = PREFIX + "/ABB-BBB-BBB"; given(random.nextInt(anyInt())).willReturn(0, 1); - given(pidRepository.getHandlesExist(anyList())) - .willReturn(handleListInternalDuplicate) + given(pidRepository.getExistingHandles(anyList())) + .willReturn(List.of(expectedHandle1)) .willReturn(new ArrayList<>()); given(applicationProperties.getPrefix()).willReturn(PREFIX); // When - List generatedHandleList = pidNameGeneratorService.genHandleList(2); - byte[] generatedHandle1 = generatedHandleList.get(0); - byte[] generatedHandle2 = generatedHandleList.get(1); + var result = pidNameGeneratorService.genHandleList(2); // Then - assertThat(generatedHandle1).isEqualTo(expectedHandle1); - assertThat(generatedHandle2).isEqualTo(expectedHandle2); + assertThat(result).hasSameElementsAs(List.of(expectedHandle1, expectedHandle2)); } @Test @@ -129,7 +112,6 @@ void testInvalidNumberOfHandles() { assertThat(tooFew).isEmpty(); } - } diff --git a/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java b/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java index ebbfde1a..166d1190 100644 --- a/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java +++ b/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java @@ -12,6 +12,7 @@ import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DIGITAL_OBJECT_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.FDO_PROFILE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.FDO_RECORD_LICENSE; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.HAS_RELATED_PID; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.HS_ADMIN; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.INFORMATION_ARTEFACT_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.ISSUED_FOR_AGENT; @@ -63,12 +64,16 @@ import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.STRUCTURAL_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TARGET_PID; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TARGET_TYPE; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOMBSTONE_TEXT; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOMBSTONED_DATE; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOMBSTONED_TEXT; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOPIC_CATEGORY; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOPIC_DISCIPLINE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOPIC_DOMAIN; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOPIC_ORIGIN; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.WAS_DERIVED_FROM_ENTITY; +import static eu.dissco.core.handlemanager.domain.fdo.FdoType.ANNOTATION; +import static eu.dissco.core.handlemanager.domain.fdo.FdoType.DIGITAL_MEDIA; +import static eu.dissco.core.handlemanager.domain.fdo.FdoType.DIGITAL_SPECIMEN; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ATTRIBUTES; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_DATA; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ID; @@ -79,34 +84,45 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import eu.dissco.core.handlemanager.domain.fdo.AnnotationRequest; +import eu.dissco.core.handlemanager.domain.fdo.DataMappingRequest; +import eu.dissco.core.handlemanager.domain.fdo.DigitalMediaRequest; import eu.dissco.core.handlemanager.domain.fdo.DigitalSpecimenRequest; import eu.dissco.core.handlemanager.domain.fdo.DoiRecordRequest; import eu.dissco.core.handlemanager.domain.fdo.FdoProfile; import eu.dissco.core.handlemanager.domain.fdo.FdoType; import eu.dissco.core.handlemanager.domain.fdo.HandleRecordRequest; -import eu.dissco.core.handlemanager.domain.fdo.MappingRequest; import eu.dissco.core.handlemanager.domain.fdo.MasRequest; -import eu.dissco.core.handlemanager.domain.fdo.MediaObjectRequest; import eu.dissco.core.handlemanager.domain.fdo.OrganisationRequest; import eu.dissco.core.handlemanager.domain.fdo.SourceSystemRequest; import eu.dissco.core.handlemanager.domain.fdo.TombstoneRecordRequest; import eu.dissco.core.handlemanager.domain.fdo.vocabulary.annotation.Motivation; import eu.dissco.core.handlemanager.domain.fdo.vocabulary.media.LinkedDigitalObjectType; +import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.BaseTypeOfSpecimen; +import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.LivingOrPreserved; +import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.MaterialOrDigitalEntity; +import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.MaterialSampleType; +import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.OtherSpecimenId; import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.PrimarySpecimenObjectIdType; import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.StructuralType; +import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.TopicCategory; +import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.TopicDiscipline; +import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.TopicDomain; +import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.TopicOrigin; +import eu.dissco.core.handlemanager.domain.fdo.vocabulary.tombstone.HasRelatedPid; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiDataLinks; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiLinks; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperRead; -import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperReadSingle; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperWrite; import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoAttribute; import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoRecord; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; +import eu.dissco.core.handlemanager.schema.Mas.PidStatus; import java.io.IOException; import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.UUID; import javax.xml.XMLConstants; @@ -119,22 +135,24 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import lombok.extern.slf4j.Slf4j; +import org.bson.Document; import org.springframework.core.io.ClassPathResource; -import org.w3c.dom.Document; @Slf4j public class TestUtils { public static final String ISSUE_DATE_TESTVAL = "2022-11-01T09:59:24.000Z"; + public static final String UPDATE_DATE_TESTVAL = "2023-11-01T09:59:24.000Z"; public static final Instant CREATED = Instant.parse(ISSUE_DATE_TESTVAL); + public static final Instant UPDATED = Instant.parse(UPDATE_DATE_TESTVAL); public static final String PREFIX = "20.5000.1025"; public static final String HANDLE = PREFIX + "/QRS-321-ABC"; public static final String SUFFIX = "QRS-321-ABC"; public static final String HANDLE_ALT = PREFIX + "/ABC-123-QRS"; - public static final List HANDLE_LIST_STR; // Handles public static final String HANDLE_DOMAIN = "https://hdl.handle.net/"; + public static final String DOI_DOMAIN = "https://doi.org/"; public static final String ROR_DOMAIN = "https://ror.org/"; public static final String ISSUED_FOR_AGENT_TESTVAL = ROR_DOMAIN + "0566bfb96"; public static final String PID_ISSUER_TESTVAL_OTHER = HANDLE_DOMAIN + "20.5000.1025/PID-ISSUER"; @@ -148,7 +166,7 @@ public class TestUtils { // Generated Attributes - public static final String PID_STATUS_TESTVAL = "TEST"; + public static final String PID_STATUS_TESTVAL = PidStatus.ACTIVE.name(); public static final String REFERENT_DOI_NAME_TESTVAL = PREFIX + "/" + SUFFIX; //DOIs @@ -176,7 +194,8 @@ public class TestUtils { public static final String MAS_NAME_TESTVAL = "Plant Organ detection"; public static final String API_URL = "https://sandbox.dissco.tech/api/v1"; - public static final String UI_URL = "https://sandbox.dissco.tech/"; + public static final String UI_URL = "https://sandbox.dissco.tech"; + public static final String PATH = UI_URL + HANDLE; public static final String ORCHESTRATION_URL = "https://orchestration.dissco.tech/api/v1"; public static final String PTR_TYPE_DOI = "doi"; public final static String PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL = "BOTANICAL.QRS.123"; @@ -189,10 +208,6 @@ public class TestUtils { // Pid Type Record vals public static ObjectMapper MAPPER = new ObjectMapper().findAndRegisterModules(); - static { - HANDLE_LIST_STR = List.of(HANDLE, HANDLE_ALT); - } - public static TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance(); public static DocumentBuilderFactory DOC_BUILDER_FACTORY = DocumentBuilderFactory.newInstance(); @@ -200,7 +215,7 @@ private TestUtils() { throw new IllegalStateException("Utility class"); } - // Single Handle Attribute Lists + // Handle Attribute Lists public static FdoRecord givenHandleFdoRecord(String handle) throws Exception { return new FdoRecord(handle, FdoType.HANDLE, genHandleRecordAttributes(handle), null); } @@ -209,97 +224,105 @@ public static List genHandleRecordAttributes(String handle) throws return genHandleRecordAttributes(handle, FdoType.HANDLE); } - public static List genHandleRecordAttributes(String handle, FdoType fdoType) throws Exception { + return genHandleRecordAttributes(handle, CREATED, fdoType); + } + + + public static List genHandleRecordAttributes(String handle, Instant timestamp, + FdoType fdoType) throws Exception { List fdoAttributes = new ArrayList<>(); var request = givenHandleRecordRequestObject(); - var loc = setLocations(request.getLocations(), handle, fdoType, false); + var loc = setLocations(request.getLocations(), handle, fdoType); fdoAttributes.add(new FdoAttribute(LOC, CREATED, loc)); // 1: FDO Profile - fdoAttributes.add(new FdoAttribute(FDO_PROFILE, CREATED, fdoType.getFdoProfile())); + fdoAttributes.add(new FdoAttribute(FDO_PROFILE, timestamp, fdoType.getFdoProfile())); // 2: FDO Record License - fdoAttributes.add(new FdoAttribute(FDO_RECORD_LICENSE, CREATED, + fdoAttributes.add(new FdoAttribute(FDO_RECORD_LICENSE, timestamp, "https://creativecommons.org/publicdomain/zero/1.0/")); // 3: DigitalObjectType fdoAttributes.add( - new FdoAttribute(DIGITAL_OBJECT_TYPE, CREATED, fdoType.getDigitalObjectType())); + new FdoAttribute(DIGITAL_OBJECT_TYPE, timestamp, fdoType.getDigitalObjectType())); // 4: DigitalObjectName fdoAttributes.add( - new FdoAttribute(DIGITAL_OBJECT_NAME, CREATED, fdoType.getDigitalObjectName())); + new FdoAttribute(DIGITAL_OBJECT_NAME, timestamp, fdoType.getDigitalObjectName())); // 5: Pid - fdoAttributes.add(new FdoAttribute(PID, CREATED, HANDLE_DOMAIN + handle)); + fdoAttributes.add(new FdoAttribute(PID, timestamp, fdoType.getDomain() + handle)); // 6: PidIssuer - fdoAttributes.add(new FdoAttribute(PID_ISSUER, CREATED, request.getPidIssuer())); + fdoAttributes.add(new FdoAttribute(PID_ISSUER, timestamp, request.getPidIssuer())); // 7: pidIssuerName - fdoAttributes.add(new FdoAttribute(PID_ISSUER_NAME, CREATED, PID_ISSUER_TESTVAL_OTHER)); + fdoAttributes.add(new FdoAttribute(PID_ISSUER_NAME, timestamp, PID_ISSUER_TESTVAL_OTHER)); // 8: issuedForAgent - fdoAttributes.add(new FdoAttribute(ISSUED_FOR_AGENT, CREATED, request.getIssuedForAgent())); + fdoAttributes.add(new FdoAttribute(ISSUED_FOR_AGENT, timestamp, request.getIssuedForAgent())); // 9: issuedForAgentName - fdoAttributes.add(new FdoAttribute(ISSUED_FOR_AGENT_NAME, CREATED, ISSUED_FOR_AGENT_TESTVAL)); + fdoAttributes.add(new FdoAttribute(ISSUED_FOR_AGENT_NAME, timestamp, ISSUED_FOR_AGENT_TESTVAL)); // 10: pidRecordIssueDate - fdoAttributes.add(new FdoAttribute(PID_RECORD_ISSUE_DATE, CREATED, ISSUE_DATE_TESTVAL)); + fdoAttributes.add(new FdoAttribute(PID_RECORD_ISSUE_DATE, timestamp, ISSUE_DATE_TESTVAL)); // 11: pidRecordIssueNumber - fdoAttributes.add(new FdoAttribute(PID_RECORD_ISSUE_NUMBER, CREATED, "1")); + fdoAttributes.add(new FdoAttribute(PID_RECORD_ISSUE_NUMBER, timestamp, "1")); // 12: structuralType fdoAttributes.add( - new FdoAttribute(STRUCTURAL_TYPE, CREATED, STRUCTURAL_TYPE_TESTVAL.toString())); + new FdoAttribute(STRUCTURAL_TYPE, timestamp, STRUCTURAL_TYPE_TESTVAL.toString())); // 13: PidStatus - fdoAttributes.add(new FdoAttribute(PID_STATUS, CREATED, PID_STATUS_TESTVAL)); + fdoAttributes.add(new FdoAttribute(PID_STATUS, timestamp, PID_STATUS_TESTVAL)); - return fdoAttributes; - } + // 100 ADMIN + fdoAttributes.add(new FdoAttribute(timestamp, PREFIX)); - public static List genHandleRecordAttributesAltLoc(String handle) throws Exception { - var attributes = genHandleRecordAttributes(handle, FdoType.HANDLE); - var locOriginal = setLocations(LOC_TESTVAL, handle, FdoType.HANDLE, false); - var locOriginalAttr = new FdoAttribute(LOC, CREATED, locOriginal); - var locAlt = setLocations(LOC_ALT_TESTVAL, handle, FdoType.HANDLE, false); - var locAltAttr = new FdoAttribute(LOC, CREATED, locAlt); - attributes.set(attributes.indexOf(locOriginalAttr), locAltAttr); - return attributes; + return fdoAttributes; } - public static List genTombstoneRecordFullAttributes(String handle) + public static FdoRecord givenUpdatedFdoRecord(FdoType fdoType, String primaryLocalId) throws Exception { - var fdoAttributes = genHandleRecordAttributes(handle, FdoType.TOMBSTONE); - var oldPidStatus = new FdoAttribute(PID_STATUS, CREATED, PID_STATUS_TESTVAL); - fdoAttributes.addAll(genHandleRecordAttributes(handle, FdoType.TOMBSTONE)); - fdoAttributes.remove(oldPidStatus); - fdoAttributes = new ArrayList<>( - (fdoAttributes.stream().filter(row -> row.getIndex() != LOC.index())).toList()); - fdoAttributes.add(givenLandingPageAttribute(handle)); - return fdoAttributes; + var attributes = new ArrayList<>(genAttributes(fdoType, HANDLE, UPDATED)); + var locUpdated = setLocations(LOC_ALT_TESTVAL, HANDLE, fdoType); + var attributesWithUpdatedTimeStamp = attributes.stream().map(attribute -> { + if (attribute.getIndex() == LOC.index()) { + return new FdoAttribute(LOC, UPDATED, locUpdated); + } + if (attribute.getIndex() == FDO_RECORD_LICENSE.index()) { + return new FdoAttribute(FDO_RECORD_LICENSE, CREATED, attribute.getValue()); + } + if (attribute.getIndex() == PID.index()) { + return new FdoAttribute(PID, CREATED, attribute.getValue()); + } + if (attribute.getIndex() == PID_RECORD_ISSUE_DATE.index()) { + return new FdoAttribute(PID_RECORD_ISSUE_DATE, CREATED, attribute.getValue()); + } + if (attribute.getIndex() == PID_RECORD_ISSUE_NUMBER.index()) { + return new FdoAttribute(PID_RECORD_ISSUE_NUMBER, UPDATED, "2"); + } + if (attribute.getIndex() == PID_STATUS.index()) { + return new FdoAttribute(PID_STATUS, CREATED, attribute.getValue()); + } + if (attribute.getIndex() == HS_ADMIN.index()) { + return new FdoAttribute(CREATED, PREFIX); + } + return attribute; + }).toList(); + return new FdoRecord(HANDLE, fdoType, attributesWithUpdatedTimeStamp, primaryLocalId); } public static List genUpdateRecordAttributesAltLoc(String handle) throws ParserConfigurationException, TransformerException { - var locAlt = setLocations(LOC_ALT_TESTVAL, handle, FdoType.HANDLE, false); + var locAlt = setLocations(LOC_ALT_TESTVAL, handle, FdoType.HANDLE); return List.of(new FdoAttribute(LOC, CREATED, locAlt)); } - public static List genTombstoneRecordRequestAttributes(String handle) - throws Exception { - var tombstoneAttributes = new ArrayList(); - tombstoneAttributes.add(new FdoAttribute(TOMBSTONE_TEXT, CREATED, TOMBSTONE_TEXT_TESTVAL)); - tombstoneAttributes.add(new FdoAttribute(PID_STATUS, CREATED, "ARCHIVED")); - tombstoneAttributes.add(givenLandingPageAttribute(handle)); - return tombstoneAttributes; - } - public static FdoRecord givenDoiFdoRecord(String handle) throws Exception { return new FdoRecord(handle, FdoType.DOI, genDoiRecordAttributes(handle, FdoType.DOI), null); } @@ -311,302 +334,308 @@ public static List genDoiRecordAttributes(String handle, FdoType t public static List genDoiRecordAttributes(String handle, FdoType type, DoiRecordRequest request) throws Exception { - var fdoRecord = genHandleRecordAttributes(handle, type); + return genDoiRecordAttributes(handle, CREATED, type, request); + } + + public static List genDoiRecordAttributes(String handle, Instant timestamp, + FdoType type, DoiRecordRequest request) throws Exception { + var fdoRecord = genHandleRecordAttributes(handle, timestamp, type); // 40: referentType - fdoRecord.add(new FdoAttribute(REFERENT_TYPE, CREATED, request.getReferentType())); + fdoRecord.add(new FdoAttribute(REFERENT_TYPE, timestamp, request.getReferentType())); // 41: referentDoiName - fdoRecord.add(new FdoAttribute(REFERENT_DOI_NAME, CREATED, REFERENT_DOI_NAME_TESTVAL)); + fdoRecord.add(new FdoAttribute(REFERENT_DOI_NAME, timestamp, REFERENT_DOI_NAME_TESTVAL)); // 42: referentName - fdoRecord.add(new FdoAttribute(REFERENT_NAME, CREATED, request.getReferentName())); + fdoRecord.add(new FdoAttribute(REFERENT_NAME, timestamp, request.getReferentName())); // 43: primaryReferentType fdoRecord.add( - new FdoAttribute(PRIMARY_REFERENT_TYPE, CREATED, request.getPrimaryReferentType())); + new FdoAttribute(PRIMARY_REFERENT_TYPE, timestamp, request.getPrimaryReferentType())); return fdoRecord; } public static FdoRecord givenDigitalSpecimenFdoRecord(String handle) throws Exception { - return new FdoRecord(handle, FdoType.DIGITAL_SPECIMEN, genDigitalSpecimenAttributes(handle), + return new FdoRecord(handle, FdoType.DIGITAL_SPECIMEN, + genDigitalSpecimenAttributes(handle, givenDigitalSpecimenRequestObjectNullOptionals()), NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL); } public static List genDigitalSpecimenAttributes(String handle, DigitalSpecimenRequest request) throws Exception { - List fdoRecord = genDoiRecordAttributes(handle, FdoType.DIGITAL_SPECIMEN, - request); - // 200: Specimen Host - fdoRecord.add(new FdoAttribute(SPECIMEN_HOST, CREATED, request.getSpecimenHost())); + return genDigitalSpecimenAttributes(handle, request, CREATED); - // 201: Specimen Host name - fdoRecord.add(new FdoAttribute(SPECIMEN_HOST_NAME, CREATED, SPECIMEN_HOST_NAME_TESTVAL)); + } + public static List genDigitalSpecimenAttributes(String handle, + DigitalSpecimenRequest request, Instant timestamp) throws Exception { + List fdoRecord = genDoiRecordAttributes(handle, timestamp, + FdoType.DIGITAL_SPECIMEN, request); + // 200: Specimen Host + fdoRecord.add(new FdoAttribute(SPECIMEN_HOST, timestamp, request.getSpecimenHost())); + // 201: Specimen Host name + fdoRecord.add(new FdoAttribute(SPECIMEN_HOST_NAME, timestamp, SPECIMEN_HOST_NAME_TESTVAL)); // 202: primarySpecimenObjectId - fdoRecord.add(new FdoAttribute(PRIMARY_SPECIMEN_OBJECT_ID, CREATED, + fdoRecord.add(new FdoAttribute(PRIMARY_SPECIMEN_OBJECT_ID, timestamp, request.getPrimarySpecimenObjectId())); - // 203: primarySpecimenObjectIdType - fdoRecord.add(new FdoAttribute(PRIMARY_SPECIMEN_OBJECT_ID_TYPE, CREATED, + fdoRecord.add(new FdoAttribute(PRIMARY_SPECIMEN_OBJECT_ID_TYPE, timestamp, request.getPrimarySpecimenObjectIdType().toString())); - - // 204-217 are optional - // 204: primarySpecimenObjectIdName - if (request.getPrimarySpecimenObjectIdName() != null) { - fdoRecord.add(new FdoAttribute(PRIMARY_SPECIMEN_OBJECT_ID_NAME, CREATED, - request.getPrimarySpecimenObjectIdName())); - } - + fdoRecord.add(new FdoAttribute(PRIMARY_SPECIMEN_OBJECT_ID_NAME, timestamp, + request.getPrimarySpecimenObjectIdName())); // 205: normalisedSpecimenObjectId - fdoRecord.add(new FdoAttribute(NORMALISED_SPECIMEN_OBJECT_ID, CREATED, + fdoRecord.add(new FdoAttribute(NORMALISED_SPECIMEN_OBJECT_ID, timestamp, request.getNormalisedPrimarySpecimenObjectId())); - // 206: specimenObjectIdAbsenceReason - if (request.getSpecimenObjectIdAbsenceReason() != null) { - fdoRecord.add(new FdoAttribute(SPECIMEN_OBJECT_ID_ABSENCE_REASON, CREATED, - request.getSpecimenObjectIdAbsenceReason())); - } - + fdoRecord.add(new FdoAttribute(SPECIMEN_OBJECT_ID_ABSENCE_REASON, timestamp, + request.getSpecimenObjectIdAbsenceReason())); // 207: otherSpecimenIds if (request.getOtherSpecimenIds() != null && !request.getOtherSpecimenIds().isEmpty()) { var otherSpecimenIds = MAPPER.writeValueAsString(request.getOtherSpecimenIds()); - fdoRecord.add(new FdoAttribute(OTHER_SPECIMEN_IDS, CREATED, otherSpecimenIds)); + fdoRecord.add(new FdoAttribute(OTHER_SPECIMEN_IDS, timestamp, otherSpecimenIds)); + } else { + fdoRecord.add(new FdoAttribute(OTHER_SPECIMEN_IDS, timestamp, null)); } - // 208: topicOrigin if (request.getTopicOrigin() != null) { - fdoRecord.add(new FdoAttribute(TOPIC_ORIGIN, CREATED, request.getTopicOrigin().toString())); + fdoRecord.add(new FdoAttribute(TOPIC_ORIGIN, timestamp, request.getTopicOrigin().toString())); + } else { + fdoRecord.add(new FdoAttribute(TOPIC_ORIGIN, timestamp, null)); } - // 209: topicDomain if (request.getTopicDomain() != null) { - fdoRecord.add(new FdoAttribute(TOPIC_DOMAIN, CREATED, request.getTopicDomain().toString())); + fdoRecord.add(new FdoAttribute(TOPIC_DOMAIN, timestamp, request.getTopicDomain().toString())); + } else { + fdoRecord.add(new FdoAttribute(TOPIC_DOMAIN, timestamp, null)); } - // 210: topicDiscipline if (request.getTopicDiscipline() != null) { fdoRecord.add( - new FdoAttribute(TOPIC_DISCIPLINE, CREATED, request.getTopicDiscipline().toString())); + new FdoAttribute(TOPIC_DISCIPLINE, timestamp, request.getTopicDiscipline().toString())); + } else { + fdoRecord.add(new FdoAttribute(TOPIC_DISCIPLINE, timestamp, null)); } - // 211: topicCategory if (request.getTopicCategory() != null) { fdoRecord.add( - new FdoAttribute(TOPIC_CATEGORY, CREATED, request.getTopicCategory().toString())); + new FdoAttribute(TOPIC_CATEGORY, timestamp, request.getTopicCategory().toString())); + } else { + fdoRecord.add(new FdoAttribute(TOPIC_CATEGORY, timestamp, null)); } - // 212: livingOrPreserved if (request.getLivingOrPreserved() != null) { - fdoRecord.add(new FdoAttribute(LIVING_OR_PRESERVED, CREATED, + fdoRecord.add(new FdoAttribute(LIVING_OR_PRESERVED, timestamp, request.getLivingOrPreserved().toString())); + } else { + fdoRecord.add(new FdoAttribute(LIVING_OR_PRESERVED, timestamp, null)); } - // 213: baseTypeOfSpecimen if (request.getBaseTypeOfSpecimen() != null) { - fdoRecord.add(new FdoAttribute(BASE_TYPE_OF_SPECIMEN, CREATED, + fdoRecord.add(new FdoAttribute(BASE_TYPE_OF_SPECIMEN, timestamp, request.getBaseTypeOfSpecimen().toString())); + } else { + fdoRecord.add(new FdoAttribute(BASE_TYPE_OF_SPECIMEN, timestamp, null)); } - // 214: informationArtefactType if (request.getInformationArtefactType() != null) { - fdoRecord.add(new FdoAttribute(INFORMATION_ARTEFACT_TYPE, CREATED, + fdoRecord.add(new FdoAttribute(INFORMATION_ARTEFACT_TYPE, timestamp, request.getInformationArtefactType().toString())); + } else { + fdoRecord.add(new FdoAttribute(INFORMATION_ARTEFACT_TYPE, timestamp, null)); } - // 215: materialSampleType if (request.getMaterialSampleType() != null) { - fdoRecord.add(new FdoAttribute(MATERIAL_SAMPLE_TYPE, CREATED, + fdoRecord.add(new FdoAttribute(MATERIAL_SAMPLE_TYPE, timestamp, request.getMaterialSampleType().toString())); + } else { + fdoRecord.add(new FdoAttribute(MATERIAL_SAMPLE_TYPE, timestamp, null)); } - // 216: materialOrDigitalEntity - if (request.getMaterialSampleType() != null) { - fdoRecord.add(new FdoAttribute(MATERIAL_OR_DIGITAL_ENTITY, CREATED, + if (request.getMaterialOrDigitalEntity() != null) { + fdoRecord.add(new FdoAttribute(MATERIAL_OR_DIGITAL_ENTITY, timestamp, request.getMaterialOrDigitalEntity().toString())); + } else { + fdoRecord.add(new FdoAttribute(MATERIAL_OR_DIGITAL_ENTITY, timestamp, null)); } - // 217: markedAsType if (request.getMarkedAsType() != null) { fdoRecord.add( - new FdoAttribute(MARKED_AS_TYPE, CREATED, request.getMarkedAsType().toString())); + new FdoAttribute(MARKED_AS_TYPE, timestamp, request.getMarkedAsType().toString())); + } else { + fdoRecord.add(new FdoAttribute(MARKED_AS_TYPE, timestamp, null)); } - // 218: wasDerivedFromEntity - if (request.getDerivedFromEntity() != null) { - fdoRecord.add( - new FdoAttribute(WAS_DERIVED_FROM_ENTITY, CREATED, request.getDerivedFromEntity())); - } - var catId = request.getCatalogIdentifier(); - if (catId != null) { - fdoRecord.add(new FdoAttribute(CATALOG_IDENTIFIER, CREATED, catId)); - } - + fdoRecord.add(new FdoAttribute(WAS_DERIVED_FROM_ENTITY, timestamp, + String.valueOf(request.getDerivedFromEntity() != null))); + fdoRecord.add(new FdoAttribute(CATALOG_IDENTIFIER, timestamp, request.getCatalogIdentifier())); return fdoRecord; - - } - - public static List genDigitalSpecimenAttributes(String handle) throws Exception { - var request = givenDigitalSpecimenRequestObjectNullOptionals(); - return genDigitalSpecimenAttributes(handle, request); } public static FdoRecord givenDigitalMediaFdoRecord(String handle) throws Exception { - return new FdoRecord(handle, FdoType.DIGITAL_SPECIMEN, genMediaObjectAttributes(handle), + return new FdoRecord(handle, DIGITAL_MEDIA, + genDigitalMediaAttributes(handle, givenDigitalMediaRequestObject(), CREATED), PRIMARY_MEDIA_ID_TESTVAL); } - public static List genMediaObjectAttributes(String handle) throws Exception { - var request = givenMediaRequestObject(); - return genMediaObjectAttributes(handle, request); - } - - public static List genMediaObjectAttributes(String handle, - MediaObjectRequest request) throws Exception { - var fdoRecord = genDoiRecordAttributes(handle, FdoType.MEDIA_OBJECT); - fdoRecord.add(new FdoAttribute(MEDIA_HOST, CREATED, request.getMediaHost())); + public static List genDigitalMediaAttributes(String handle, + DigitalMediaRequest request, Instant timestamp) throws Exception { + var fdoRecord = genDoiRecordAttributes(handle, timestamp, FdoType.DIGITAL_MEDIA, request); + fdoRecord.add(new FdoAttribute(MEDIA_HOST, timestamp, request.getMediaHost())); if (request.getMediaHostName() == null) { - fdoRecord.add(new FdoAttribute(MEDIA_HOST_NAME, CREATED, MEDIA_HOST_NAME_TESTVAL)); + fdoRecord.add(new FdoAttribute(MEDIA_HOST_NAME, timestamp, MEDIA_HOST_NAME_TESTVAL)); } else { - fdoRecord.add(new FdoAttribute(MEDIA_HOST_NAME, CREATED, request.getMediaHostName())); + fdoRecord.add(new FdoAttribute(MEDIA_HOST_NAME, timestamp, request.getMediaHostName())); } if (request.getDctermsFormat() != null) { fdoRecord.add( - new FdoAttribute(DCTERMS_FORMAT, CREATED, request.getDctermsFormat().toString())); + new FdoAttribute(DCTERMS_FORMAT, timestamp, request.getDctermsFormat().toString())); + } else { + fdoRecord.add(new FdoAttribute(DCTERMS_FORMAT, timestamp, null)); } - fdoRecord.add(new FdoAttribute(IS_DERIVED_FROM_SPECIMEN, CREATED, + fdoRecord.add(new FdoAttribute(IS_DERIVED_FROM_SPECIMEN, timestamp, request.getIsDerivedFromSpecimen().toString())); - if (request.getLinkedDigitalObjectPid() != null) { - fdoRecord.add( - new FdoAttribute(LINKED_DO_PID, CREATED, request.getLinkedDigitalObjectPid())); - } + fdoRecord.add(new FdoAttribute(LINKED_DO_PID, timestamp, request.getLinkedDigitalObjectPid())); if (request.getLinkedDigitalObjectType() != null) { - fdoRecord.add(new FdoAttribute(LINKED_DO_TYPE, CREATED, + fdoRecord.add(new FdoAttribute(LINKED_DO_TYPE, timestamp, request.getLinkedDigitalObjectType().toString())); + } else { + fdoRecord.add(new FdoAttribute(LINKED_DO_TYPE, timestamp, null)); } - if (request.getLinkedAttribute() != null) { - fdoRecord.add(new FdoAttribute(LINKED_ATTRIBUTE, CREATED, request.getLinkedAttribute())); - } - if (request.getPrimaryMediaId() != null) { - fdoRecord.add(new FdoAttribute(PRIMARY_MEDIA_ID, CREATED, request.getPrimaryMediaId())); - } + fdoRecord.add(new FdoAttribute(LINKED_ATTRIBUTE, timestamp, request.getLinkedAttribute())); + fdoRecord.add(new FdoAttribute(PRIMARY_MEDIA_ID, timestamp, request.getPrimaryMediaId())); if (request.getPrimaryMediaObjectIdType() != null) { - fdoRecord.add(new FdoAttribute(PRIMARY_MO_ID_TYPE, CREATED, + fdoRecord.add(new FdoAttribute(PRIMARY_MO_ID_TYPE, timestamp, request.getPrimaryMediaObjectIdType().toString())); + } else { + fdoRecord.add(new FdoAttribute(PRIMARY_MO_ID_TYPE, timestamp, null)); } - if (request.getPrimaryMediaObjectIdName() != null) { - fdoRecord.add( - new FdoAttribute(PRIMARY_MO_ID_NAME, CREATED, request.getPrimaryMediaObjectIdName())); - } + fdoRecord.add( + new FdoAttribute(PRIMARY_MO_ID_NAME, timestamp, request.getPrimaryMediaObjectIdName())); if (request.getDcTermsType() != null) { - fdoRecord.add(new FdoAttribute(DCTERMS_TYPE, CREATED, request.getDcTermsType().toString())); - } - if (request.getDctermsSubject() != null) { - fdoRecord.add(new FdoAttribute(DCTERMS_SUBJECT, CREATED, request.getDctermsSubject())); - } - if (request.getDerivedFromEntity() != null) { - fdoRecord.add( - new FdoAttribute(DERIVED_FROM_ENTITY, CREATED, request.getDerivedFromEntity())); - } - if (request.getLicenseName() != null) { - fdoRecord.add(new FdoAttribute(LICENSE_NAME, CREATED, request.getLicenseName())); - } - if (request.getLicenseUrl() != null) { - fdoRecord.add(new FdoAttribute(LICENSE_URL, CREATED, request.getLicenseUrl())); - } - if (request.getRightsholderName() != null) { - fdoRecord.add(new FdoAttribute(RIGHTSHOLDER_NAME, CREATED, request.getRightsholderName())); - } - if (request.getRightsholderPid() != null) { - fdoRecord.add(new FdoAttribute(RIGHTSHOLDER_PID, CREATED, request.getRightsholderPid())); + fdoRecord.add(new FdoAttribute(DCTERMS_TYPE, timestamp, request.getDcTermsType().toString())); + } else { + fdoRecord.add(new FdoAttribute(DCTERMS_TYPE, timestamp, null)); } + fdoRecord.add(new FdoAttribute(DCTERMS_SUBJECT, timestamp, request.getDctermsSubject())); + fdoRecord.add(new FdoAttribute(DERIVED_FROM_ENTITY, timestamp, request.getDerivedFromEntity())); + fdoRecord.add(new FdoAttribute(LICENSE_NAME, timestamp, request.getLicenseName())); + fdoRecord.add(new FdoAttribute(LICENSE_URL, timestamp, request.getLicenseUrl())); + fdoRecord.add(new FdoAttribute(RIGHTSHOLDER_NAME, timestamp, request.getRightsholderName())); + fdoRecord.add(new FdoAttribute(RIGHTSHOLDER_PID, timestamp, request.getRightsholderPid())); if (request.getRightsholderPidType() != null) { - fdoRecord.add(new FdoAttribute(RIGHTSHOLDER_PID_TYPE, CREATED, + fdoRecord.add(new FdoAttribute(RIGHTSHOLDER_PID_TYPE, timestamp, request.getRightsholderPidType().toString())); + } else { + fdoRecord.add(new FdoAttribute(RIGHTSHOLDER_PID_TYPE, timestamp, null)); } - if (request.getDctermsConformsTo() != null) { - fdoRecord.add(new FdoAttribute(DC_TERMS_CONFORMS, CREATED, request.getDctermsConformsTo())); - } + fdoRecord.add(new FdoAttribute(DC_TERMS_CONFORMS, timestamp, request.getDctermsConformsTo())); return fdoRecord; } public static FdoRecord givenAnnotationFdoRecord(String handle, boolean includeHash) throws Exception { + var localId = includeHash ? ANNOTATION_HASH_TESTVAL.toString() : null; return new FdoRecord(handle, FdoType.ANNOTATION, genAnnotationAttributes(handle, includeHash), + localId); + } + + public static FdoRecord givenMasFdoRecord(String handle) throws Exception { + return new FdoRecord(handle, FdoType.MAS, genMasAttributes(handle, CREATED), null); + } + + public static FdoRecord givenSourceSystemFdoRecord(String handle) throws Exception { + return new FdoRecord(handle, FdoType.SOURCE_SYSTEM, genSourceSystemAttributes(handle, CREATED), null); } + public static FdoRecord givenOrganisationFdoRecord(String handle) throws Exception { + return new FdoRecord(handle, FdoType.ORGANISATION, + genOrganisationAttributes(handle, CREATED, givenOrganisationRequestObject()), null); + } + + public static FdoRecord givenDataMappingFdoRecord(String handle) throws Exception { + return new FdoRecord(handle, FdoType.DATA_MAPPING, genMappingAttributes(handle, CREATED), null); + } + public static List genAnnotationAttributes(String handle, boolean includeHash) throws Exception { - var fdoRecord = genHandleRecordAttributes(handle, FdoType.ANNOTATION); + return genAnnotationAttributes(handle, CREATED, includeHash); + } + public static List genAnnotationAttributes(String handle, Instant timestamp, + boolean includeHash) throws Exception { + var fdoRecord = genHandleRecordAttributes(handle, timestamp, FdoType.ANNOTATION); // 500 TargetPid - fdoRecord.add(new FdoAttribute(TARGET_PID, CREATED, TARGET_DOI_TESTVAL)); - + fdoRecord.add(new FdoAttribute(TARGET_PID, timestamp, TARGET_DOI_TESTVAL)); // 501 TargetType - fdoRecord.add(new FdoAttribute(TARGET_TYPE, CREATED, TARGET_TYPE_TESTVAL)); - + fdoRecord.add(new FdoAttribute(TARGET_TYPE, timestamp, TARGET_TYPE_TESTVAL)); // 502 motivation - fdoRecord.add(new FdoAttribute(MOTIVATION, CREATED, MOTIVATION_TESTVAL.toString())); - + fdoRecord.add(new FdoAttribute(MOTIVATION, timestamp, MOTIVATION_TESTVAL.toString())); // 503 AnnotationHash if (includeHash) { fdoRecord.add( - new FdoAttribute(ANNOTATION_HASH, CREATED, ANNOTATION_HASH_TESTVAL.toString())); + new FdoAttribute(ANNOTATION_HASH, timestamp, ANNOTATION_HASH_TESTVAL.toString())); + } else { + fdoRecord.add(new FdoAttribute(ANNOTATION_HASH, timestamp, null)); } return fdoRecord; } - public static List genMasAttributes(String handle) throws Exception { - var fdoRecord = genHandleRecordAttributes(handle, FdoType.MAS); - fdoRecord.add(new FdoAttribute(MAS_NAME, CREATED, (MAS_NAME_TESTVAL))); + public static List genMasAttributes(String handle, Instant timestamp) + throws Exception { + var fdoRecord = genHandleRecordAttributes(handle, timestamp, FdoType.MAS); + fdoRecord.add(new FdoAttribute(MAS_NAME, timestamp, MAS_NAME_TESTVAL)); return fdoRecord; } - public static List genMappingAttributes(String handle) throws Exception { - var fdoRecord = genHandleRecordAttributes(handle, FdoType.MAPPING); - + public static List genMappingAttributes(String handle, Instant timestamp) + throws Exception { + var fdoRecord = genHandleRecordAttributes(handle, timestamp, FdoType.DATA_MAPPING); // 500 subjectDigitalObjectId - fdoRecord.add( - new FdoAttribute(SOURCE_DATA_STANDARD, CREATED, - SOURCE_DATA_STANDARD_TESTVAL)); - + fdoRecord.add(new FdoAttribute(SOURCE_DATA_STANDARD, timestamp, SOURCE_DATA_STANDARD_TESTVAL)); return fdoRecord; } - public static List genSourceSystemAttributes(String handle) throws Exception { - var fdoRecord = genHandleRecordAttributes(handle, FdoType.SOURCE_SYSTEM); - + public static List genSourceSystemAttributes(String handle, Instant timestamp) + throws Exception { + var fdoRecord = genHandleRecordAttributes(handle, timestamp, FdoType.SOURCE_SYSTEM); // 600 hostInstitution - fdoRecord.add(new FdoAttribute(SOURCE_SYSTEM_NAME, CREATED, - SPECIMEN_HOST_TESTVAL)); - + fdoRecord.add(new FdoAttribute(SOURCE_SYSTEM_NAME, timestamp, SPECIMEN_HOST_TESTVAL)); return fdoRecord; } - public static List genOrganisationAttributes(String handle, + public static List genOrganisationAttributes(String handle, Instant timestamp, OrganisationRequest request) throws Exception { - var fdoRecord = genDoiRecordAttributes(handle, FdoType.ORGANISATION, request); - + var fdoRecord = genDoiRecordAttributes(handle, timestamp, FdoType.ORGANISATION, request); // 800 OrganisationIdentifier - fdoRecord.add(new FdoAttribute(ORGANISATION_ID, CREATED, - SPECIMEN_HOST_TESTVAL)); - + fdoRecord.add(new FdoAttribute(ORGANISATION_ID, timestamp, SPECIMEN_HOST_TESTVAL)); // 801 OrganisationIdentifier - fdoRecord.add( - new FdoAttribute(ORGANISATION_ID_TYPE, CREATED, - PTR_TYPE_DOI)); - + fdoRecord.add(new FdoAttribute(ORGANISATION_ID_TYPE, timestamp, PTR_TYPE_DOI)); // 802 OrganisationName - fdoRecord.add(new FdoAttribute(ORGANISATION_NAME, CREATED, - SPECIMEN_HOST_NAME_TESTVAL)); - + fdoRecord.add(new FdoAttribute(ORGANISATION_NAME, timestamp, ISSUED_FOR_AGENT_TESTVAL)); return fdoRecord; } - public static List genOrganisationAttributes(String handle) throws Exception { - return genOrganisationAttributes(handle, givenOrganisationRequestObject()); + public static List genTombstoneAttributes(TombstoneRecordRequest request) + throws Exception { + var fdoRecord = genHandleRecordAttributes(HANDLE, CREATED, FdoType.HANDLE); + fdoRecord.add(new FdoAttribute(TOMBSTONED_TEXT, UPDATED, request.getTombstonedText())); + fdoRecord.set(fdoRecord.indexOf(new FdoAttribute(PID_RECORD_ISSUE_NUMBER, CREATED, "1")), + new FdoAttribute(PID_RECORD_ISSUE_NUMBER, UPDATED, "2")); + + // 31: hasRelatedPID + if (request.getHasRelatedPID() != null && !request.getHasRelatedPID().isEmpty()) { + fdoRecord.add(new FdoAttribute(HAS_RELATED_PID, UPDATED, + MAPPER.writeValueAsString(request.getHasRelatedPID()))); + } else { + fdoRecord.add(new FdoAttribute(HAS_RELATED_PID, UPDATED, + MAPPER.writeValueAsString(Collections.emptyList()))); + } + // 32: tombstonedDate + fdoRecord.add(new FdoAttribute(TOMBSTONED_DATE, UPDATED, UPDATE_DATE_TESTVAL)); + return fdoRecord; } public static ObjectNode genCreateRecordRequest(T request, @@ -634,10 +663,21 @@ public static HandleRecordRequest givenHandleRecordRequestObject() { STRUCTURAL_TYPE_TESTVAL, LOC_TESTVAL); } + public static HandleRecordRequest givenHandleRecordRequestObjectUpdate() { + return new HandleRecordRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, + STRUCTURAL_TYPE_TESTVAL, LOC_ALT_TESTVAL); + } + public static DoiRecordRequest givenDoiRecordRequestObject() { return new DoiRecordRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, STRUCTURAL_TYPE_TESTVAL, LOC_TESTVAL, REFERENT_NAME_TESTVAL, - FdoType.MEDIA_OBJECT.getDigitalObjectName(), PRIMARY_REFERENT_TYPE_TESTVAL); + FdoType.DIGITAL_MEDIA.getDigitalObjectName(), PRIMARY_REFERENT_TYPE_TESTVAL); + } + + public static DoiRecordRequest givenDoiRecordRequestObjectUpdate() { + return new DoiRecordRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, + STRUCTURAL_TYPE_TESTVAL, LOC_ALT_TESTVAL, REFERENT_NAME_TESTVAL, + FdoType.DIGITAL_MEDIA.getDigitalObjectName(), PRIMARY_REFERENT_TYPE_TESTVAL); } public static DigitalSpecimenRequest givenDigitalSpecimenRequestObjectNullOptionals() { @@ -650,20 +690,49 @@ public static DigitalSpecimenRequest givenDigitalSpecimenRequestObjectNullOption return new DigitalSpecimenRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, LOC_TESTVAL, REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, SPECIMEN_HOST_TESTVAL, SPECIMEN_HOST_NAME_TESTVAL, primarySpecimenObjectId, PrimarySpecimenObjectIdType.GLOBAL, - null, primarySpecimenObjectId, null, null, null, null, null, null, null, null, null, null, - null, null, null, null); + null, NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL, null, null, null, null, null, null, + null, null, null, null, null, null, null, null); } catch (InvalidRequestException e) { throw new RuntimeException(e.getMessage()); } } - public static MediaObjectRequest givenMediaRequestObject() throws InvalidRequestException { - return new MediaObjectRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, LOC_TESTVAL, + public static DigitalSpecimenRequest givenDigitalSpecimenRequestObject() throws Exception { + var otherSpecimenIds = new OtherSpecimenId("Catalog Id", "Catalog Id"); + return new DigitalSpecimenRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, + LOC_TESTVAL, REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, SPECIMEN_HOST_TESTVAL, + SPECIMEN_HOST_NAME_TESTVAL, PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL, + PrimarySpecimenObjectIdType.GLOBAL, null, NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL, + null, List.of(otherSpecimenIds), TopicOrigin.HUMAN_MADE, TopicDomain.ARCHIVE, + TopicDiscipline.ANTHRO, TopicCategory.HUMAN, LivingOrPreserved.PRESERVED, + BaseTypeOfSpecimen.MATERIAL, null, MaterialSampleType.ORG_PART, + MaterialOrDigitalEntity.DIGITAL, true, "Entity this was derived from", "Catalog id"); + } + + public static DigitalSpecimenRequest givenDigitalSpecimenRequestObjectUpdate() throws Exception { + return new DigitalSpecimenRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, + LOC_ALT_TESTVAL, REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, + SPECIMEN_HOST_TESTVAL, SPECIMEN_HOST_NAME_TESTVAL, PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL, + PrimarySpecimenObjectIdType.GLOBAL, null, NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL, + null, null, null, null, null, null, null, null, null, null, null, null, null, null); + } + + public static DigitalMediaRequest givenDigitalMediaRequestObject() + throws InvalidRequestException { + return new DigitalMediaRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, LOC_TESTVAL, REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, MEDIA_HOST_TESTVAL, MEDIA_HOST_NAME_TESTVAL, null, Boolean.TRUE, LINKED_DO_PID_TESTVAL, LINKED_DIGITAL_OBJECT_TYPE_TESTVAL, null, PRIMARY_MEDIA_ID_TESTVAL, null, null, null, null, - null, null, null, - SPECIMEN_HOST_TESTVAL, SPECIMEN_HOST_NAME_TESTVAL, null, null); + null, null, null, SPECIMEN_HOST_TESTVAL, SPECIMEN_HOST_NAME_TESTVAL, null, null); + } + + public static DigitalMediaRequest givenDigitalMediaRequestObjectUpdate() + throws InvalidRequestException { + return new DigitalMediaRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, + LOC_ALT_TESTVAL, REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, MEDIA_HOST_TESTVAL, + MEDIA_HOST_NAME_TESTVAL, null, Boolean.TRUE, LINKED_DO_PID_TESTVAL, + LINKED_DIGITAL_OBJECT_TYPE_TESTVAL, null, PRIMARY_MEDIA_ID_TESTVAL, null, null, null, null, + null, null, null, SPECIMEN_HOST_TESTVAL, SPECIMEN_HOST_NAME_TESTVAL, null, null); } public static AnnotationRequest givenAnnotationRequestObject() { @@ -671,63 +740,81 @@ public static AnnotationRequest givenAnnotationRequestObject() { TARGET_DOI_TESTVAL, TARGET_TYPE_TESTVAL, MOTIVATION_TESTVAL, ANNOTATION_HASH_TESTVAL); } + public static AnnotationRequest givenAnnotationRequestObjectUpdate() { + return new AnnotationRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, + LOC_ALT_TESTVAL, TARGET_DOI_TESTVAL, TARGET_TYPE_TESTVAL, MOTIVATION_TESTVAL, null); + } + + public static AnnotationRequest givenAnnotationRequestObjectNoHash() { return new AnnotationRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, LOC_TESTVAL, TARGET_DOI_TESTVAL, TARGET_TYPE_TESTVAL, MOTIVATION_TESTVAL, null); } - public static MappingRequest givenMappingRequestObject() { - return new MappingRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, LOC_TESTVAL, + public static DataMappingRequest givenDataMappingRequestObject() { + return new DataMappingRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, LOC_TESTVAL, SOURCE_DATA_STANDARD_TESTVAL); } + public static DataMappingRequest givenDataMappingRequestObjectUpdate() { + return new DataMappingRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, + LOC_ALT_TESTVAL, SOURCE_DATA_STANDARD_TESTVAL); + } + public static SourceSystemRequest givenSourceSystemRequestObject() { return new SourceSystemRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, LOC_TESTVAL, SPECIMEN_HOST_TESTVAL); } + public static SourceSystemRequest givenSourceSystemRequestObjectUpdate() { + return new SourceSystemRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, + LOC_ALT_TESTVAL, SPECIMEN_HOST_TESTVAL); + } + public static OrganisationRequest givenOrganisationRequestObject() { return new OrganisationRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, LOC_TESTVAL, REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, SPECIMEN_HOST_TESTVAL, PTR_TYPE_DOI); } + public static OrganisationRequest givenOrganisationRequestObjectUpdate() { + return new OrganisationRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, + LOC_ALT_TESTVAL, REFERENT_NAME_TESTVAL, PRIMARY_REFERENT_TYPE_TESTVAL, + SPECIMEN_HOST_TESTVAL, PTR_TYPE_DOI); + } + public static MasRequest givenMasRecordRequestObject() { return new MasRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, LOC_TESTVAL, MAS_NAME_TESTVAL); } + public static MasRequest givenMasRecordRequestObjectUpdate() { + return new MasRequest(ISSUED_FOR_AGENT_TESTVAL, PID_ISSUER_TESTVAL_OTHER, + LOC_ALT_TESTVAL, MAS_NAME_TESTVAL); + } - public static TombstoneRecordRequest genTombstoneRecordRequestObject() { - return new TombstoneRecordRequest(TOMBSTONE_TEXT_TESTVAL); + public static TombstoneRecordRequest givenTombstoneRecordRequestObject() { + return new TombstoneRecordRequest(TOMBSTONE_TEXT_TESTVAL, + List.of(new HasRelatedPid(HANDLE_ALT, "Media ID"))); } public static JsonApiWrapperRead givenRecordResponseRead(List handles, String path, - FdoType recordType) throws Exception { + FdoType recordType, String domain) throws Exception { List dataNodes = new ArrayList<>(); - for (String handle : handles) { var testDbRecord = genAttributes(recordType, handle); JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); - var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); - dataNodes.add(new JsonApiDataLinks(handle, - recordType.getDigitalObjectType(), recordAttributes, pidLink)); + var pidLink = new JsonApiLinks(domain + handle); + dataNodes.add( + new JsonApiDataLinks(handle, recordType.getDigitalObjectType(), recordAttributes, + pidLink)); } - var responseLink = new JsonApiLinks(path); return new JsonApiWrapperRead(responseLink, dataNodes); } - public static JsonApiWrapperReadSingle givenRecordResponseReadSingle(String handle, String path, - FdoType type, JsonNode attributes) { - return new JsonApiWrapperReadSingle(new JsonApiLinks(path), - new JsonApiDataLinks(handle, type.getDigitalObjectType(), attributes, - new JsonApiLinks("https://hdl.handle.net/" + handle))); - } - - public static JsonApiWrapperWrite givenRecordResponseWrite(List handles, + public static JsonApiWrapperWrite givenRecordResponseWriteFullResponse(List handles, FdoType recordType) throws Exception { List dataNodes = new ArrayList<>(); - for (var handle : handles) { var testDbRecord = genAttributes(recordType, handle); JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); @@ -740,35 +827,33 @@ public static JsonApiWrapperWrite givenRecordResponseWrite(List handles, return new JsonApiWrapperWrite(dataNodes); } - public static JsonApiWrapperWrite givenAnnotationResponseWrite(List handles) { - List dataNodes = new ArrayList<>(); - - for (var handle : handles) { - var testDbRecord = List.of( - new FdoAttribute(ANNOTATION_HASH, CREATED, ANNOTATION_HASH_TESTVAL.toString())); - JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); - - var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); - dataNodes.add( - new JsonApiDataLinks(handle, FdoType.ANNOTATION.getDigitalObjectType(), recordAttributes, - pidLink)); - } + public static JsonApiWrapperWrite givenRecordResponseWriteFullResponse( + FdoRecord fdoRecord) { + JsonNode recordAttributes = genObjectNodeAttributeRecord(fdoRecord.attributes()); + var pidLink = new JsonApiLinks(HANDLE_DOMAIN + HANDLE); + var dataNodes = List.of( + new JsonApiDataLinks(HANDLE, fdoRecord.fdoType().getDigitalObjectType(), recordAttributes, + pidLink)); return new JsonApiWrapperWrite(dataNodes); } public static JsonApiWrapperWrite givenRecordResponseWriteSmallResponse( - List fdoRecords, FdoType fdoType) { + List fdoRecords, FdoType fdoType, String domain) { List dataNodes = new ArrayList<>(); List fdoSublist; for (var fdoRecord : fdoRecords) { switch (fdoType) { case ANNOTATION -> { - fdoSublist = List.of(getField(fdoRecord.attributes(), ANNOTATION_HASH)); + if (fdoRecord.primaryLocalId() == null) { + fdoSublist = fdoRecord.attributes(); + } else { + fdoSublist = List.of(getField(fdoRecord.attributes(), ANNOTATION_HASH)); + } } case DIGITAL_SPECIMEN -> { fdoSublist = List.of(getField(fdoRecord.attributes(), NORMALISED_SPECIMEN_OBJECT_ID)); } - case MEDIA_OBJECT -> { + case DIGITAL_MEDIA -> { fdoSublist = List.of(getField(fdoRecord.attributes(), PRIMARY_MEDIA_ID), getField(fdoRecord.attributes(), LINKED_DO_PID)); } @@ -777,7 +862,7 @@ public static JsonApiWrapperWrite givenRecordResponseWriteSmallResponse( } } var recordAttributes = genObjectNodeAttributeRecord(fdoSublist); - var pidLink = new JsonApiLinks(HANDLE_DOMAIN + fdoRecord.handle()); + var pidLink = new JsonApiLinks(domain + fdoRecord.handle()); dataNodes.add( new JsonApiDataLinks(fdoRecord.handle(), fdoType.getDigitalObjectType(), recordAttributes, pidLink)); @@ -805,9 +890,7 @@ public static JsonApiWrapperWrite givenRecordResponseWriteGeneric(List h JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); - dataNodes.add( - new JsonApiDataLinks(handle, "PID", recordAttributes, - pidLink)); + dataNodes.add(new JsonApiDataLinks(handle, "PID", recordAttributes, pidLink)); } return new JsonApiWrapperWrite(dataNodes); } @@ -815,14 +898,11 @@ public static JsonApiWrapperWrite givenRecordResponseWriteGeneric(List h public static JsonApiWrapperWrite givenRecordResponseWrite(List handles, FdoType attributeType, String recordType) throws Exception { List dataNodes = new ArrayList<>(); - for (var handle : handles) { var testDbRecord = genAttributes(attributeType, handle); JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); - var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); - dataNodes.add(new JsonApiDataLinks(handle, recordType, - recordAttributes, pidLink)); + dataNodes.add(new JsonApiDataLinks(handle, recordType, recordAttributes, pidLink)); } return new JsonApiWrapperWrite(dataNodes); } @@ -841,23 +921,19 @@ public static JsonApiWrapperWrite givenRecordResponseWriteAltLoc(List ha JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); - dataNodes.add(new JsonApiDataLinks(handle, - recordType.getDigitalObjectType(), recordAttributes, pidLink)); + dataNodes.add( + new JsonApiDataLinks(handle, recordType.getDigitalObjectType(), recordAttributes, + pidLink)); } return new JsonApiWrapperWrite(dataNodes); } - public static JsonApiWrapperWrite givenRecordResponseNullAttributes(List handles) { - return givenRecordResponseNullAttributes(handles, FdoType.HANDLE); - } - public static JsonApiWrapperWrite givenRecordResponseNullAttributes(List handles, FdoType type) { List dataNodes = new ArrayList<>(); for (var handle : handles) { var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); - dataNodes.add(new JsonApiDataLinks(handle, - type.getDigitalObjectType(), null, pidLink)); + dataNodes.add(new JsonApiDataLinks(handle, type.getDigitalObjectType(), null, pidLink)); } return new JsonApiWrapperWrite(dataNodes); } @@ -868,51 +944,59 @@ public static JsonApiWrapperWrite givenRecordResponseWriteArchive(List h List dataNodes = new ArrayList<>(); for (var handle : handles) { - var testDbRecord = genTombstoneRecordRequestAttributes(handle); + List testDbRecord = Collections.emptyList(); // todo JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); - dataNodes.add(new JsonApiDataLinks(handle, - FdoType.TOMBSTONE.getDigitalObjectType(), recordAttributes, pidLink)); + dataNodes.add( + new JsonApiDataLinks(handle, FdoType.TOMBSTONE.getDigitalObjectType(), recordAttributes, + pidLink)); } return new JsonApiWrapperWrite(dataNodes); } - public static List genAttributes(FdoType recordType, String handle) + public static List genAttributes(FdoType fdoType, String handle) throws Exception { + return genAttributes(fdoType, handle, CREATED); + } + + + public static List genAttributes(FdoType fdoType, String handle, Instant timestamp) throws Exception { - switch (recordType) { + switch (fdoType) { case DOI -> { - return genDoiRecordAttributes(handle, recordType); + return genDoiRecordAttributes(handle, timestamp, fdoType, givenDoiRecordRequestObject()); } case DIGITAL_SPECIMEN -> { - return genDigitalSpecimenAttributes(handle); + return genDigitalSpecimenAttributes(handle, + givenDigitalSpecimenRequestObjectNullOptionals(), timestamp); } - case MEDIA_OBJECT -> { - return genMediaObjectAttributes(handle); + case DIGITAL_MEDIA -> { + return genDigitalMediaAttributes(handle, givenDigitalMediaRequestObject(), timestamp); } case ANNOTATION -> { - return genAnnotationAttributes(handle, false); + return genAnnotationAttributes(handle, timestamp, false); } - case MAPPING -> { - return genMappingAttributes(handle); + case DATA_MAPPING -> { + return genMappingAttributes(handle, timestamp); } case SOURCE_SYSTEM -> { - return genSourceSystemAttributes(handle); + return genSourceSystemAttributes(handle, timestamp); } case ORGANISATION -> { - return genOrganisationAttributes(handle); + return genOrganisationAttributes(handle, timestamp, givenOrganisationRequestObject()); } case MAS -> { - return genMasAttributes(handle); + return genMasAttributes(handle, timestamp); } default -> { log.warn("Default type"); - return genHandleRecordAttributes(handle, FdoType.HANDLE); + return genHandleRecordAttributes(handle, timestamp, FdoType.HANDLE); } } } - public static List genUpdateRequestBatch(List handles, FdoType type) { + public static List genUpdateRequestBatch(List handles, FdoType type, + JsonNode requestAttributes) { ObjectMapper mapper = new ObjectMapper(); ObjectNode requestNodeRoot = mapper.createObjectNode(); ObjectNode requestNodeData = mapper.createObjectNode(); @@ -921,7 +1005,7 @@ public static List genUpdateRequestBatch(List handles, FdoType for (var handle : handles) { requestNodeData.put("type", type.getDigitalObjectType()); requestNodeData.put("id", handle); - requestNodeData.set("attributes", genUpdateRequestAltLoc()); + requestNodeData.set("attributes", requestAttributes); requestNodeRoot.set("data", requestNodeData); requestNodeList.add(requestNodeRoot.deepCopy()); @@ -932,10 +1016,6 @@ public static List genUpdateRequestBatch(List handles, FdoType return requestNodeList; } - public static List genUpdateRequestBatch(List handles) { - return genUpdateRequestBatch(handles, FdoType.HANDLE); - } - public static List genTombstoneRequestBatch(List handles) { ObjectMapper mapper = new ObjectMapper(); ObjectNode requestNodeRoot = mapper.createObjectNode(); @@ -967,7 +1047,7 @@ public static JsonNode genUpdateRequestAltLoc() { public static JsonNode genTombstoneRequest() { ObjectMapper mapper = new ObjectMapper(); ObjectNode rootNode = mapper.createObjectNode(); - rootNode.put(TOMBSTONE_TEXT.get(), TOMBSTONE_TEXT_TESTVAL); + rootNode.put(TOMBSTONED_TEXT.get(), TOMBSTONE_TEXT_TESTVAL); return rootNode; } @@ -978,11 +1058,15 @@ public static JsonNode genObjectNodeAttributeRecord(List dbRecord) for (var row : dbRecord) { if (row.getIndex() != HS_ADMIN.index()) { var rowData = row.getValue(); - try { - var nodeData = mapper.readTree(rowData); - rootNode.set(row.getType(), nodeData); - } catch (JsonProcessingException ignored) { - rootNode.put(row.getType(), rowData); + if (row.getValue() == null) { + rootNode.set(row.getType(), mapper.nullNode()); + } else { + try { + var nodeData = mapper.readTree(rowData); + rootNode.set(row.getType(), nodeData); + } catch (JsonProcessingException ignored) { + rootNode.put(row.getType(), rowData); + } } } } @@ -990,14 +1074,21 @@ public static JsonNode genObjectNodeAttributeRecord(List dbRecord) } // Other Functions - public static FdoAttribute givenLandingPageAttribute(String handle) throws Exception { - var landingPage = new String[]{"Placeholder landing page"}; - var locations = setLocations(landingPage, handle, FdoType.TOMBSTONE, false); - return new FdoAttribute(LOC, CREATED, locations); + public static Document givenMongoDocument(FdoRecord fdoRecord) throws Exception { + var doc = org.bson.Document.parse(MAPPER.writeValueAsString(fdoRecord)) + .append("_id", fdoRecord.handle()); + if (DIGITAL_SPECIMEN.equals(fdoRecord.fdoType())) { + doc.append(NORMALISED_SPECIMEN_OBJECT_ID.get(), fdoRecord.primaryLocalId()); + } else if (DIGITAL_MEDIA.equals(fdoRecord.fdoType())) { + doc.append(PRIMARY_MEDIA_ID.get(), fdoRecord.primaryLocalId()); + } else if (ANNOTATION.equals(fdoRecord.fdoType()) && fdoRecord.primaryLocalId() != null) { + doc.append(ANNOTATION_HASH.get(), fdoRecord.primaryLocalId()); + } + return doc; } - public static String setLocations(String[] userLocations, String handle, FdoType type, - boolean isDoiProfileTest) throws TransformerException, ParserConfigurationException { + public static String setLocations(String[] userLocations, String handle, FdoType type) + throws TransformerException, ParserConfigurationException { DOC_BUILDER_FACTORY.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); DocumentBuilder documentBuilder = DOC_BUILDER_FACTORY.newDocumentBuilder(); @@ -1005,8 +1096,7 @@ public static String setLocations(String[] userLocations, String handle, FdoType var doc = documentBuilder.newDocument(); var locations = doc.createElement("locations"); doc.appendChild(locations); - String[] objectLocations = - isDoiProfileTest ? userLocations : concatLocations(userLocations, handle, type); + String[] objectLocations = concatLocations(userLocations, handle, type); for (int i = 0; i < objectLocations.length; i++) { var locs = doc.createElement("location"); @@ -1031,18 +1121,18 @@ private static String[] defaultLocations(String handle, FdoType type) { case DIGITAL_SPECIMEN -> { String api = API_URL + "/specimens/" + handle; String ui = UI_URL + "/ds/" + handle; - return new String[]{ui, api}; + return new String[]{api, ui}; } - case MAPPING -> { + case DATA_MAPPING -> { return new String[]{ORCHESTRATION_URL + "/mapping/" + handle}; } case SOURCE_SYSTEM -> { return new String[]{ORCHESTRATION_URL + "/source-system/" + handle}; } - case MEDIA_OBJECT -> { + case DIGITAL_MEDIA -> { String api = API_URL + "/digitalMedia/" + handle; String ui = UI_URL + "/dm/" + handle; - return new String[]{ui, api}; + return new String[]{api, ui}; } case ANNOTATION -> { return new String[]{API_URL + "/annotations/" + handle}; @@ -1060,7 +1150,8 @@ private static String[] defaultLocations(String handle, FdoType type) { } } - private static String documentToString(Document document) throws TransformerException { + private static String documentToString(org.w3c.dom.Document document) + throws TransformerException { TRANSFORMER_FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); TRANSFORMER_FACTORY.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); TRANSFORMER_FACTORY.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); From 87a6bce727bbf34888b28656d9e32da193c4c83d Mon Sep 17 00:00:00 2001 From: southeo Date: Wed, 17 Jul 2024 13:28:20 +0200 Subject: [PATCH 06/15] test --- .../FdoAttributeDeserializer.java | 19 ++ .../controller/PidController.java | 24 +- .../domain/repsitoryobjects/FdoAttribute.java | 29 +- .../domain/requests/PatchRequest.java | 10 - .../domain/requests/PatchRequestData.java | 19 -- .../validation/JsonSchemaValidator.java | 97 ------ ...idRepository.java => MongoRepository.java} | 11 +- .../handlemanager/service/DoiService.java | 12 +- .../service/FdoRecordService.java | 25 -- .../handlemanager/service/HandleService.java | 12 +- .../service/PidNameGeneratorService.java | 6 +- .../handlemanager/service/PidService.java | 62 ++-- .../controller/PidControllerTest.java | 134 +++------ .../domain/JsonSchemaValidatorTest.java | 168 +---------- .../repository/MongoRepositoryIT.java | 216 +++++++++++++- .../handlemanager/service/DoiServiceTest.java | 43 ++- .../service/FdoRecordServiceTest.java | 38 +-- .../service/HandleServiceTest.java | 198 +++++++------ .../service/PidNameGeneratorServiceTest.java | 9 +- .../handlemanager/testUtils/TestUtils.java | 279 +++++------------- 20 files changed, 585 insertions(+), 826 deletions(-) create mode 100644 src/main/java/eu/dissco/core/handlemanager/configuration/FdoAttributeDeserializer.java delete mode 100644 src/main/java/eu/dissco/core/handlemanager/domain/requests/PatchRequest.java delete mode 100644 src/main/java/eu/dissco/core/handlemanager/domain/requests/PatchRequestData.java rename src/main/java/eu/dissco/core/handlemanager/repository/{PidRepository.java => MongoRepository.java} (93%) diff --git a/src/main/java/eu/dissco/core/handlemanager/configuration/FdoAttributeDeserializer.java b/src/main/java/eu/dissco/core/handlemanager/configuration/FdoAttributeDeserializer.java new file mode 100644 index 00000000..b5562247 --- /dev/null +++ b/src/main/java/eu/dissco/core/handlemanager/configuration/FdoAttributeDeserializer.java @@ -0,0 +1,19 @@ +package eu.dissco.core.handlemanager.configuration; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoAttribute; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class FdoAttributeDeserializer extends JsonDeserializer { + + + @Override + public FdoAttribute deserialize(JsonParser jsonParser, + DeserializationContext deserializationContext) { + return null; + } + +} diff --git a/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java b/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java index d808562e..64f8eb04 100644 --- a/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java +++ b/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java @@ -119,19 +119,15 @@ public ResponseEntity updateRecord(@PathVariable("prefix") Authentication authentication) throws InvalidRequestException, UnprocessableEntityException { log.info("Received single update request for PID {}/{} from user {}", prefix, suffix, authentication.getName()); - schemaValidator.validatePatchRequest(request); - - JsonNode data = request.get(NODE_DATA); - byte[] handle = (prefix + "/" + suffix).getBytes(StandardCharsets.UTF_8); - byte[] handleData = data.get(NODE_ID).asText().getBytes(StandardCharsets.UTF_8); - - if (!Arrays.equals(handle, handleData)) { + schemaValidator.validatePutRequest(request); + var handle = (prefix + "/" + suffix); + var handleData = request.get(NODE_DATA).get(NODE_ID).asText(); + if (!handle.equals(handleData)) { throw new InvalidRequestException(String.format( "Handle in request path does not match id in request body. Path: %s, Body: %s", - new String(handle, StandardCharsets.UTF_8), - new String(handleData, StandardCharsets.UTF_8))); + handle, + handleData)); } - return ResponseEntity.status(HttpStatus.OK).body(service.updateRecords(List.of(request), true)); } @@ -141,7 +137,7 @@ public ResponseEntity updateRecords(@RequestBody List archiveRecord(@PathVariable("prefix") + ". Body: " + new String(handleRequest, StandardCharsets.UTF_8)); } return ResponseEntity.status(HttpStatus.OK) - .body(service.tombstoneRecordBatch(List.of(request))); + .body(service.tombstoneRecords(List.of(request))); } @Operation(summary = "rollback handle creation") @@ -196,7 +192,7 @@ public ResponseEntity rollbackHandleUpdate( log.info("Validating rollback update request from user {}", authentication.getName()); for (JsonNode request : requests) { - schemaValidator.validatePatchRequest(request); + schemaValidator.validatePutRequest(request); } var handles = requests.stream().map(r -> r.get(NODE_DATA).get(NODE_ID).asText()) .limit(LOG_LIMIT).toList(); @@ -224,7 +220,7 @@ public ResponseEntity archiveRecords(@RequestBody List scope.getType().getErasedType().isEnum() - ? Stream.of(scope.getType().getErasedType().getEnumConstants()) - .map(v -> ((Enum) v).name()).toList() - : null); - - // Min - configBuilder.forFields() - .withArrayMinItemsResolver(field -> field - .getAnnotationConsideringFieldAndGetterIfSupported(NotEmpty.class) == null ? null : 1); - - return configBuilder.build(); - } - private void setRequestJsonNodes() { var schemaGenerator = new SchemaGenerator(requestSchemaConfig()); postReqJsonNode = schemaGenerator.generateSchema(PostRequest.class); - patchReqJsonNode = schemaGenerator.generateSchema(PatchRequest.class); putReqJsonNode = schemaGenerator.generateSchema(PutRequest.class); } @@ -226,7 +165,6 @@ private void setJsonSchemas() { JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); postReqSchema = factory.getSchema(postReqJsonNode); - patchReqSchema = factory.getSchema(patchReqJsonNode); putReqSchema = factory.getSchema(putReqJsonNode); handlePostReqSchema = factory.getSchema(handlePostReqJsonNode); @@ -238,17 +176,6 @@ private void setJsonSchemas() { sourceSystemPostReqSchema = factory.getSchema(sourceSystemPostReqJsonNode); organisationPostReqSchema = factory.getSchema(organisationPostReqJsonNode); masPostReqSchema = factory.getSchema(masPostReqJsonNode); - - handlePatchReqSchema = factory.getSchema(handlePatchReqJsonNode); - doiPatchReqSchema = factory.getSchema(doiPatchReqJsonNode); - digitalSpecimenPatchReqSchema = factory.getSchema(digitalSpecimenPatchReqJsonNode); - digitalMediaPatchReqSchema = factory.getSchema(digitalMediaPatchReqJsonNode); - annotationPatchReqSchema = factory.getSchema(annotationPatchReqJsonNode); - mappingPatchReqSchema = factory.getSchema(mappingPatchReqJsonNode); - sourceSystemPatchReqSchema = factory.getSchema(sourceSystemPatchReqJsonNode); - organisationPatchReqSchema = factory.getSchema(organisationPatchReqJsonNode); - masPatchReqSchema = factory.getSchema(masPatchReqJsonNode); - tombstoneReqSchema = factory.getSchema(tombstoneReqJsonNode); } @@ -277,30 +204,6 @@ public void validatePostRequest(JsonNode requestRoot) throws InvalidRequestExcep } } - public void validatePatchRequest(JsonNode requestRoot) throws InvalidRequestException { - var validationErrors = patchReqSchema.validate(requestRoot); - if (!validationErrors.isEmpty()) { - throw new InvalidRequestException( - setErrorMessage(validationErrors, "PATCH (update)")); - } - FdoType type = FdoType.fromString(requestRoot.get(NODE_DATA).get(NODE_TYPE).asText()); - var attributes = requestRoot.get(NODE_DATA).get(NODE_ATTRIBUTES); - switch (type) { - case HANDLE -> validateRequestAttributes(attributes, handlePatchReqSchema, type); - case DOI -> validateRequestAttributes(attributes, doiPatchReqSchema, type); - case DIGITAL_SPECIMEN -> - validateRequestAttributes(attributes, digitalSpecimenPatchReqSchema, type); - case DIGITAL_MEDIA -> validateRequestAttributes(attributes, digitalMediaPatchReqSchema, type); - case ANNOTATION -> validateRequestAttributes(attributes, annotationPatchReqSchema, type); - case DATA_MAPPING -> validateRequestAttributes(attributes, mappingPatchReqSchema, type); - case SOURCE_SYSTEM -> validateRequestAttributes(attributes, sourceSystemPatchReqSchema, type); - case ORGANISATION -> validateRequestAttributes(attributes, organisationPatchReqSchema, type); - case MAS -> validateRequestAttributes(attributes, masPatchReqSchema, type); - default -> - throw new InvalidRequestException("Invalid Request. Reason: Invalid type: " + type); - } - } - public void validatePutRequest(JsonNode requestRoot) throws InvalidRequestException { var validationErrors = putReqSchema.validate(requestRoot); if (!validationErrors.isEmpty()) { diff --git a/src/main/java/eu/dissco/core/handlemanager/repository/PidRepository.java b/src/main/java/eu/dissco/core/handlemanager/repository/MongoRepository.java similarity index 93% rename from src/main/java/eu/dissco/core/handlemanager/repository/PidRepository.java rename to src/main/java/eu/dissco/core/handlemanager/repository/MongoRepository.java index 987ebafe..701ea37b 100644 --- a/src/main/java/eu/dissco/core/handlemanager/repository/PidRepository.java +++ b/src/main/java/eu/dissco/core/handlemanager/repository/MongoRepository.java @@ -1,5 +1,6 @@ package eu.dissco.core.handlemanager.repository; +import static com.mongodb.client.model.Filters.eq; import static com.mongodb.client.model.Filters.in; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.DIGITAL_OBJECT_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.NORMALISED_SPECIMEN_OBJECT_ID; @@ -26,7 +27,7 @@ @Repository @RequiredArgsConstructor @Slf4j -public class PidRepository { +public class MongoRepository { private final MongoCollection collection; private final ObjectMapper mapper; @@ -47,13 +48,13 @@ public List getExistingHandles(List ids) { return existingHandles; } - public void postBatchHandleRecord(List handleRecords) { + public void postHandleRecords(List handleRecords) { collection.insertMany(handleRecords); } - public void updateHandleRecord(List handleRecords) { + public void updateHandleRecords(List handleRecords) { var queryList = handleRecords.stream().map(doc -> { - var filter = in(doc.get(ID).toString()); + var filter = eq(doc.get(ID)); return new ReplaceOneModel<>(filter, doc); }).toList(); collection.bulkWrite(queryList); @@ -85,7 +86,7 @@ private List formatResults(FindIterable results) mapper.writeValueAsString(jsonRecord)); } else { var attributes = mapper.convertValue(jsonRecord.get("values"), - new TypeReference>() { + new TypeReference>() { }); var fdoType = getFdoType(attributes, jsonRecord.get("_id").asText()); handleRecords.add(new FdoRecord(jsonRecord.get("_id").asText(), diff --git a/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java b/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java index 0890ea3d..6a4d6066 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java @@ -16,7 +16,7 @@ import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.exceptions.UnprocessableEntityException; import eu.dissco.core.handlemanager.properties.ProfileProperties; -import eu.dissco.core.handlemanager.repository.PidRepository; +import eu.dissco.core.handlemanager.repository.MongoRepository; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.bson.Document; @@ -33,7 +33,7 @@ public class DoiService extends PidService { public DoiService(FdoRecordService fdoRecordService, PidNameGeneratorService pidNameGeneratorService, ObjectMapper mapper, ProfileProperties profileProperties, - DataCiteService dataCiteService, PidRepository mongoRepository) { + DataCiteService dataCiteService, MongoRepository mongoRepository) { super(fdoRecordService, pidNameGeneratorService, mapper, profileProperties, mongoRepository); this.dataCiteService = dataCiteService; @@ -57,14 +57,14 @@ public JsonApiWrapperWrite createRecords(List requests) default -> throw new UnsupportedOperationException( String.format(TYPE_ERROR_MESSAGE, type.getDigitalObjectName())); } - fdoDocuments = fdoRecordService.toMongoDbDocument(fdoRecords); + fdoDocuments = toMongoDbDocument(fdoRecords); } catch (JsonProcessingException | PidResolutionException e) { throw new InvalidRequestException( "An error has occurred parsing a record in request. More information: " + e.getMessage()); } log.info("Persisting new DOIs to Document Store"); - mongoRepository.postBatchHandleRecord(fdoDocuments); + mongoRepository.postHandleRecords(fdoDocuments); log.info("Publishing to DataCite"); publishToDataCite(fdoRecords, EventType.CREATE); return new JsonApiWrapperWrite(formatFdoRecord(fdoRecords, type)); @@ -88,8 +88,8 @@ public JsonApiWrapperWrite updateRecords(List requests, boolean increm default -> throw new UnsupportedOperationException( String.format(TYPE_ERROR_MESSAGE, fdoType.getDigitalObjectName())); } - fdoDocuments = fdoRecordService.toMongoDbDocument(fdoRecords); - mongoRepository.updateHandleRecord(fdoDocuments); + fdoDocuments = toMongoDbDocument(fdoRecords); + mongoRepository.updateHandleRecords(fdoDocuments); publishToDataCite(fdoRecords, EventType.UPDATE); return new JsonApiWrapperWrite(formatFdoRecord(fdoRecords, fdoType)); } catch (JsonProcessingException e) { diff --git a/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java b/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java index ddad75ca..36ca9afa 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java @@ -121,7 +121,6 @@ import javax.xml.transform.stream.StreamResult; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.bson.Document; import org.springframework.stereotype.Service; @@ -171,30 +170,6 @@ public class FdoRecordService { private final DateTimeFormatter dt = DateTimeFormatter.ofPattern(DATE_STRING) .withZone(ZoneId.of("UTC")); - public List toMongoDbDocument(List fdoRecords) - throws JsonProcessingException { - var documentList = new ArrayList(); - for (var fdoRecord : fdoRecords) { - var doc = Document.parse(mapper.writeValueAsString(fdoRecord)); - addLocalId(fdoRecord, doc); - documentList.add(doc); - } - return documentList; - } - - private void addLocalId(FdoRecord fdoRecord, Document doc) { - if (fdoRecord.primaryLocalId() == null) { - return; - } - if (DIGITAL_SPECIMEN.equals(fdoRecord.fdoType())) { - doc.append(NORMALISED_SPECIMEN_OBJECT_ID.get(), fdoRecord.primaryLocalId()); - } else if (DIGITAL_MEDIA.equals(fdoRecord.fdoType())) { - doc.append(PRIMARY_MEDIA_ID.get(), fdoRecord.primaryLocalId()); - } else if (ANNOTATION.equals(fdoRecord.fdoType())) { - doc.append(ANNOTATION_HASH.get(), fdoRecord.primaryLocalId()); - } - } - /* Handle Record Creation */ public FdoRecord prepareNewHandleRecord(HandleRecordRequest request, String handle, FdoType fdoType, Instant timestamp) throws InvalidRequestException { diff --git a/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java b/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java index cab27c4a..c17e9554 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java @@ -23,7 +23,7 @@ import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.exceptions.UnprocessableEntityException; import eu.dissco.core.handlemanager.properties.ProfileProperties; -import eu.dissco.core.handlemanager.repository.PidRepository; +import eu.dissco.core.handlemanager.repository.MongoRepository; import java.time.Instant; import java.util.ArrayList; import java.util.Iterator; @@ -41,7 +41,7 @@ public class HandleService extends PidService { public HandleService(FdoRecordService fdoRecordService, PidNameGeneratorService hf, ObjectMapper mapper, ProfileProperties profileProperties, - PidRepository mongoRepository) { + MongoRepository mongoRepository) { super(fdoRecordService, hf, mapper, profileProperties, mongoRepository); } @@ -67,14 +67,14 @@ public JsonApiWrapperWrite createRecords(List requests) throws Invalid case SOURCE_SYSTEM -> fdoRecords = createSourceSystem(requestAttributes, handles); default -> throw new UnsupportedOperationException("Unrecognized type"); } - fdoDocuments = fdoRecordService.toMongoDbDocument(fdoRecords); + fdoDocuments = toMongoDbDocument(fdoRecords); } catch (JsonProcessingException | PidResolutionException e) { log.error("An error has occurred in processing request", e); throw new InvalidRequestException( "An error has occurred parsing a record in request. More information: " + e.getMessage()); } log.info("Persisting new handles to Document Store"); - mongoRepository.postBatchHandleRecord(fdoDocuments); + mongoRepository.postHandleRecords(fdoDocuments); return new JsonApiWrapperWrite(formatFdoRecord(fdoRecords, fdoType)); } @@ -106,8 +106,8 @@ public JsonApiWrapperWrite updateRecords(List requests, boolean increm fdoRecords = updateSourceSystem(updateRequests, fdoRecordMap, incrementVersion); default -> throw new UnsupportedOperationException("Unrecognized type"); } - fdoDocuments = fdoRecordService.toMongoDbDocument(fdoRecords); - mongoRepository.updateHandleRecord(fdoDocuments); + fdoDocuments = toMongoDbDocument(fdoRecords); + mongoRepository.updateHandleRecords(fdoDocuments); return new JsonApiWrapperWrite(formatFdoRecord(fdoRecords, fdoType)); } catch (JsonProcessingException e) { log.error("An error has occurred processing JSON data", e); diff --git a/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java b/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java index 73e48974..c8cc3689 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java @@ -1,7 +1,7 @@ package eu.dissco.core.handlemanager.service; import eu.dissco.core.handlemanager.properties.ApplicationProperties; -import eu.dissco.core.handlemanager.repository.PidRepository; +import eu.dissco.core.handlemanager.repository.MongoRepository; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -24,7 +24,7 @@ public class PidNameGeneratorService { private static final int LENGTH = 11; private static final char[] SYMBOLS = "ABCDEFGHJKLMNPQRSTVWXYZ1234567890".toCharArray(); private final char[] buf = new char[LENGTH]; - private final PidRepository pidRepository; + private final MongoRepository mongoRepository; private final Random random; @@ -51,7 +51,7 @@ private Set genHandleHashSet(int h) { var handleSet = new HashSet<>(handleList); // Check for duplicates from repository and wrap the duplicates - var duplicates = new HashSet<>(pidRepository.getExistingHandles(handleList)); + var duplicates = new HashSet<>(mongoRepository.getExistingHandles(handleList)); // If a duplicate was found, recursively call this function // Generate new handles for every duplicate found and add it to our hash list diff --git a/src/main/java/eu/dissco/core/handlemanager/service/PidService.java b/src/main/java/eu/dissco/core/handlemanager/service/PidService.java index 762ddac4..a61c5439 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/PidService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/PidService.java @@ -36,7 +36,7 @@ import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.exceptions.UnprocessableEntityException; import eu.dissco.core.handlemanager.properties.ProfileProperties; -import eu.dissco.core.handlemanager.repository.PidRepository; +import eu.dissco.core.handlemanager.repository.MongoRepository; import java.time.Instant; import java.util.ArrayList; import java.util.Collections; @@ -58,7 +58,7 @@ public abstract class PidService { protected final PidNameGeneratorService hf; protected final ObjectMapper mapper; protected final ProfileProperties profileProperties; - protected final PidRepository mongoRepository; + protected final MongoRepository mongoRepository; protected JsonNode jsonFormatSingleRecord(List fdoAttributes) { ObjectNode rootNode = mapper.createObjectNode(); @@ -179,8 +179,8 @@ public JsonApiWrapperRead resolveBatchRecord(List handles, String path) try { fdoRecords = mongoRepository.getHandleRecords(handles); } catch (JsonProcessingException e) { - log.error("JsonProcessingException", e); - throw new PidResolutionException(""); + log.error("JsonProcessingException when resolving handles {}", handles, e); + throw new PidResolutionException("Unable to resolve handle records for handles: " + handles); } if (fdoRecords.size() < handles.size()) { var hdl = new ArrayList<>(handles); @@ -207,7 +207,9 @@ public JsonApiWrapperWrite searchByPhysicalSpecimenId(String normalisedPhysicalI "Unable to resolve specimen with id" + normalisedPhysicalId); } } catch (JsonProcessingException e) { - log.error("JsonProcessingException", e); + log.error( + "JsonProcessingException while reading record for specimen with normalised object id {}", + normalisedPhysicalId, e); throw new PidResolutionException( "Unable to resolve specimen with with id " + normalisedPhysicalId); } @@ -252,8 +254,8 @@ protected Map processUpdateRequest(List updateReque throws InvalidRequestException { var handles = updateRequests.stream() .map(request -> request.get(NODE_ID).asText()).toList(); - var previousVersions = getPreviousVersions(handles); checkInternalDuplicates(handles); + var previousVersions = getPreviousVersions(handles); checkHandlesWritable(previousVersions); return previousVersions.stream() .collect(Collectors.toMap(FdoRecord::handle, f -> f)); @@ -367,25 +369,17 @@ private void verifyObjectsAreNew(List normalisedIds) protected void checkInternalDuplicates(List handles) throws InvalidRequestException { Set handlesToUpdate = new HashSet<>(handles); if (handlesToUpdate.size() < handles.size()) { - Set duplicateHandles = findDuplicates(handles, handlesToUpdate); + Set duplicateHandles = handles.stream() + .filter(i -> Collections.frequency(handles, i) > 1) + .collect(Collectors.toSet()); throw new InvalidRequestException( "INVALID INPUT. Attempting to update the same record multiple times in one request. " + "The following handles are duplicated in the request: " + duplicateHandles); } } - private Set findDuplicates(List handles, Set handlesToUpdate) { - Set duplicateHandles = new HashSet<>(); - for (var handle : handles) { - if (!handlesToUpdate.add(handle)) { - duplicateHandles.add(handle); - } - } - return duplicateHandles; - } - // Tombstone - public JsonApiWrapperWrite tombstoneRecordBatch(List requests) + public JsonApiWrapperWrite tombstoneRecords(List requests) throws InvalidRequestException { var tombstoneRequestData = requests.stream() .map(request -> request.get(NODE_DATA)).toList(); @@ -400,12 +394,12 @@ public JsonApiWrapperWrite tombstoneRecordBatch(List requests) fdoRecords.add(fdoRecordService.prepareTombstoneRecord(tombstoneRequest, timestamp, fdoRecordMap.get(requestData.get(NODE_ID).asText()))); } - fdoDocuments = fdoRecordService.toMongoDbDocument(fdoRecords); + fdoDocuments = toMongoDbDocument(fdoRecords); } catch (JsonProcessingException e) { - log.error("JsonProcessingException", e); + log.error("JsonProcessingException while tombstoning records", e); throw new InvalidRequestException("Unable to read request"); } - mongoRepository.updateHandleRecord(fdoDocuments); + mongoRepository.updateHandleRecords(fdoDocuments); return new JsonApiWrapperWrite(formatFdoRecord(fdoRecords, TOMBSTONE)); } @@ -417,4 +411,30 @@ public void rollbackHandlesFromPhysId(List physicalIds) { mongoRepository.rollbackHandles(NORMALISED_SPECIMEN_OBJECT_ID.get(), physicalIds); } + + protected List toMongoDbDocument(List fdoRecords) + throws JsonProcessingException { + var documentList = new ArrayList(); + for (var fdoRecord : fdoRecords) { + var doc = Document.parse(mapper.writeValueAsString(fdoRecord)); + addLocalId(fdoRecord, doc); + documentList.add(doc); + } + return documentList; + } + + private void addLocalId(FdoRecord fdoRecord, Document doc) { + if (fdoRecord.primaryLocalId() == null) { + return; + } + if (DIGITAL_SPECIMEN.equals(fdoRecord.fdoType())) { + doc.append(NORMALISED_SPECIMEN_OBJECT_ID.get(), fdoRecord.primaryLocalId()); + } else if (DIGITAL_MEDIA.equals(fdoRecord.fdoType())) { + doc.append(PRIMARY_MEDIA_ID.get(), fdoRecord.primaryLocalId()); + } else if (ANNOTATION.equals(fdoRecord.fdoType())) { + doc.append(ANNOTATION_HASH.get(), fdoRecord.primaryLocalId()); + } + } + + } diff --git a/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java b/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java index 1eb57be2..3e0d9cfe 100644 --- a/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java @@ -12,20 +12,17 @@ import static eu.dissco.core.handlemanager.testUtils.TestUtils.PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.SUFFIX; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genCreateRecordRequest; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genTombstoneRequest; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genUpdateRequestAltLoc; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDataMappingRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenRequestObjectNullOptionals; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDoiRecordRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenHandleRecordRequestObject; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseRead; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseWriteAltLoc; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseWriteArchive; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseWriteGeneric; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenHandleRecordRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenReadResponse; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenSourceSystemRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenTombstoneRecordRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenUpdateRequest; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; import static org.mockito.ArgumentMatchers.anyList; @@ -34,7 +31,6 @@ import static org.mockito.BDDMockito.then; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import eu.dissco.core.handlemanager.Profiles; import eu.dissco.core.handlemanager.domain.fdo.DigitalSpecimenRequest; @@ -77,8 +73,6 @@ class PidControllerTest { private PidController controller; private final String SANDBOX_URI = "https://sandbox.dissco.tech"; - - public ObjectMapper mapper = new ObjectMapper().findAndRegisterModules(); @Mock private ApplicationProperties applicationProperties; @@ -93,7 +87,7 @@ void testResolveSingleHandle() throws Exception { String path = SANDBOX_URI + PREFIX + "/" + SUFFIX; MockHttpServletRequest r = new MockHttpServletRequest(); r.setRequestURI(PREFIX + "/" + SUFFIX); - var responseExpected = givenRecordResponseRead(List.of(HANDLE), path, FdoType.HANDLE, + var responseExpected = givenReadResponse(List.of(HANDLE), path, FdoType.HANDLE, HANDLE_DOMAIN); given(applicationProperties.getUiUrl()).willReturn(SANDBOX_URI); @@ -119,7 +113,7 @@ void testResolvePidBadPrefix() { void testSearchByPhysicalId() throws Exception { // Given - var responseExpected = givenRecordResponseWriteGeneric( + var responseExpected = TestUtils.givenWriteResponseFull( List.of(HANDLE), FdoType.DIGITAL_SPECIMEN); given( service.searchByPhysicalSpecimenId(PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL @@ -138,7 +132,7 @@ void testSearchByPhysicalId() throws Exception { void testSearchByPhysicalIdCombined() throws Exception { // Given String physicalId = PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL; - var responseExpected = givenRecordResponseWriteGeneric( + var responseExpected = TestUtils.givenWriteResponseFull( List.of(HANDLE), FdoType.DIGITAL_SPECIMEN); given( service.searchByPhysicalSpecimenId(physicalId)).willReturn( @@ -161,7 +155,7 @@ void testResolveBatchHandle() throws Exception { List handleString = List.of(HANDLE, HANDLE_ALT); - var responseExpected = givenRecordResponseRead(handleString, path, FdoType.HANDLE, + var responseExpected = givenReadResponse(handleString, path, FdoType.HANDLE, HANDLE_DOMAIN); given(applicationProperties.getUiUrl()).willReturn(SANDBOX_URI); given(applicationProperties.getMaxHandles()).willReturn(1000); @@ -201,7 +195,7 @@ void testCreateHandleRecord() throws Exception { // Given HandleRecordRequest requestObject = givenHandleRecordRequestObject(); ObjectNode requestNode = genCreateRecordRequest(requestObject, FdoType.HANDLE); - JsonApiWrapperWrite responseExpected = TestUtils.givenRecordResponseWriteFullResponse( + JsonApiWrapperWrite responseExpected = TestUtils.givenWriteResponseFull( List.of(HANDLE), FdoType.HANDLE); @@ -220,7 +214,7 @@ void testCreateDoiRecord() throws Exception { // Given HandleRecordRequest requestObject = givenDoiRecordRequestObject(); ObjectNode requestNode = genCreateRecordRequest(requestObject, FdoType.DOI); - JsonApiWrapperWrite responseExpected = TestUtils.givenRecordResponseWriteFullResponse( + JsonApiWrapperWrite responseExpected = TestUtils.givenWriteResponseFull( List.of(HANDLE), FdoType.DOI); @@ -239,7 +233,7 @@ void testCreateDigitalSpecimenRecord() throws Exception { // Given DigitalSpecimenRequest requestObject = givenDigitalSpecimenRequestObjectNullOptionals(); ObjectNode requestNode = genCreateRecordRequest(requestObject, FdoType.DIGITAL_SPECIMEN); - JsonApiWrapperWrite responseExpected = TestUtils.givenRecordResponseWriteFullResponse( + JsonApiWrapperWrite responseExpected = TestUtils.givenWriteResponseFull( List.of(HANDLE), FdoType.DIGITAL_SPECIMEN); @@ -258,7 +252,7 @@ void testCreateDigitalMediaRecord() throws Exception { // Given HandleRecordRequest requestObject = givenDigitalMediaRequestObject(); ObjectNode requestNode = genCreateRecordRequest(requestObject, FdoType.DIGITAL_MEDIA); - JsonApiWrapperWrite responseExpected = TestUtils.givenRecordResponseWriteFullResponse( + JsonApiWrapperWrite responseExpected = TestUtils.givenWriteResponseFull( List.of(HANDLE), FdoType.DIGITAL_MEDIA); @@ -282,7 +276,7 @@ void testCreateHandleRecordBatch() throws Exception { handles.forEach(handle -> requests.add( genCreateRecordRequest(givenHandleRecordRequestObject(), FdoType.HANDLE))); - var responseExpected = TestUtils.givenRecordResponseWriteFullResponse(handles, FdoType.HANDLE); + var responseExpected = TestUtils.givenWriteResponseFull(handles, FdoType.HANDLE); given(service.createRecords(requests)).willReturn(responseExpected); // When @@ -302,7 +296,7 @@ void testCreateDoiRecordBatch() throws Exception { handles.forEach( handle -> requests.add(genCreateRecordRequest(givenDoiRecordRequestObject(), FdoType.DOI))); - var responseExpected = TestUtils.givenRecordResponseWriteFullResponse(handles, FdoType.DOI); + var responseExpected = TestUtils.givenWriteResponseFull(handles, FdoType.DOI); given(service.createRecords(requests)).willReturn(responseExpected); // When @@ -323,7 +317,7 @@ void testCreateDigitalSpecimenBatch() throws Exception { genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), FdoType.DIGITAL_SPECIMEN)) ); - var responseExpected = TestUtils.givenRecordResponseWriteFullResponse(handles, + var responseExpected = TestUtils.givenWriteResponseFull(handles, FdoType.DIGITAL_SPECIMEN); given(service.createRecords(requests)).willReturn(responseExpected); @@ -343,7 +337,7 @@ void testCreateMediaRecordBatch() throws Exception { for (int i = 0; i < handles.size(); i++) { requests.add(genCreateRecordRequest(givenDigitalMediaRequestObject(), FdoType.DIGITAL_MEDIA)); } - var responseExpected = TestUtils.givenRecordResponseWriteFullResponse(handles, FdoType.DOI); + var responseExpected = TestUtils.givenWriteResponseFull(handles, FdoType.DOI); given(service.createRecords(requests)).willReturn(responseExpected); // When @@ -362,7 +356,7 @@ void testCreateSourceSystemsBatch() throws Exception { handles.forEach(handle -> requests.add( genCreateRecordRequest(givenSourceSystemRequestObject(), FdoType.SOURCE_SYSTEM))); - var responseExpected = TestUtils.givenRecordResponseWriteFullResponse(handles, + var responseExpected = TestUtils.givenWriteResponseFull(handles, FdoType.SOURCE_SYSTEM); given(service.createRecords(requests)).willReturn(responseExpected); @@ -381,7 +375,7 @@ void testCreateAnnotationsBatch() throws Exception { List requests = new ArrayList<>(); handles.forEach(handle -> requests.add( genCreateRecordRequest(givenAnnotationRequestObject(), FdoType.ANNOTATION))); - var responseExpected = TestUtils.givenRecordResponseWriteFullResponse(handles, + var responseExpected = TestUtils.givenWriteResponseFull(handles, FdoType.ANNOTATION); given(service.createRecords(requests)).willReturn(responseExpected); @@ -401,7 +395,7 @@ void testCreateMappingBatch() throws Exception { handles.forEach(handle -> requests.add( genCreateRecordRequest(givenDataMappingRequestObject(), FdoType.DATA_MAPPING))); - var responseExpected = TestUtils.givenRecordResponseWriteFullResponse(handles, + var responseExpected = TestUtils.givenWriteResponseFull(handles, FdoType.DATA_MAPPING); given(service.createRecords(requests)).willReturn(responseExpected); @@ -413,94 +407,58 @@ void testCreateMappingBatch() throws Exception { assertThat(responseReceived.getBody()).isEqualTo(responseExpected); } - private JsonNode givenJsonNode(String id, String type, JsonNode attributes) { - ObjectNode node = mapper.createObjectNode(); - node.put(NODE_ID, id); - node.put(NODE_TYPE, type); - node.set(NODE_ATTRIBUTES, attributes); - return node; - } - @Test void testUpdateRecord() throws Exception { // Given - var updateAttributes = genUpdateRequestAltLoc(); - ObjectNode updateRequestNode = mapper.createObjectNode(); - updateRequestNode.set(NODE_DATA, - givenJsonNode(HANDLE, FdoType.HANDLE.getDigitalObjectType(), updateAttributes)); - - var responseExpected = givenRecordResponseWriteAltLoc(List.of(HANDLE)); - given(service.updateRecords(List.of(updateRequestNode), true)).willReturn( - responseExpected); + var request = givenUpdateRequest(); // When - var responseReceived = controller.updateRecord(PREFIX, SUFFIX, updateRequestNode, + var result = controller.updateRecord(PREFIX, SUFFIX, request.get(0), authentication); // Then - assertThat(responseReceived.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(responseReceived.getBody()).isEqualTo(responseExpected); + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); + then(service).should().updateRecords(request, true); } @Test void testUpdateRecordBadRequest() { - // Given - var updateAttributes = genUpdateRequestAltLoc(); - ObjectNode updateRequestNode = mapper.createObjectNode(); - updateRequestNode.set("data", - givenJsonNode(HANDLE_ALT, FdoType.HANDLE.getDigitalObjectType(), updateAttributes)); + var request = MAPPER.createObjectNode() + .set(NODE_DATA, MAPPER.createObjectNode() + .put(NODE_TYPE, FdoType.HANDLE.getDigitalObjectType()) + .put(NODE_ID, HANDLE_ALT) + .set(NODE_ATTRIBUTES, MAPPER.valueToTree(givenHandleRecordRequestObjectUpdate()))); // Then assertThrowsExactly(InvalidRequestException.class, - () -> controller.updateRecord(PREFIX, SUFFIX, updateRequestNode, authentication)); + () -> controller.updateRecord(PREFIX, SUFFIX, request, authentication)); } @Test void testUpdateRecordBatch() throws Exception { // Given - var handles = List.of(HANDLE, HANDLE_ALT); - List updateRequestList = new ArrayList<>(); - var responseExpected = givenRecordResponseWriteAltLoc(handles); - handles.forEach(h -> { - var updateAttributes = genUpdateRequestAltLoc(); - ObjectNode updateRequestNode = mapper.createObjectNode(); - updateRequestNode.set("data", - givenJsonNode(HANDLE, FdoType.HANDLE.getDigitalObjectType(), updateAttributes)); - updateRequestList.add(updateRequestNode.deepCopy()); - }); - given(service.updateRecords(updateRequestList, true)).willReturn(responseExpected); + var request = givenUpdateRequest(); // When - var responseReceived = controller.updateRecords(updateRequestList, authentication); + var responseReceived = controller.updateRecords(request, authentication); // Then assertThat(responseReceived.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(responseReceived.getBody()).isEqualTo(responseExpected); + then(service).should().updateRecords(request, true); } @Test void testRollbackUpdate() throws Exception { // Given - var handles = List.of(HANDLE, HANDLE_ALT); - List updateRequestList = new ArrayList<>(); - var responseExpected = givenRecordResponseWriteAltLoc(handles); - handles.forEach(h -> { - var updateAttributes = genUpdateRequestAltLoc(); - ObjectNode updateRequestNode = mapper.createObjectNode(); - updateRequestNode.set("data", - givenJsonNode(HANDLE, FdoType.HANDLE.getDigitalObjectType(), updateAttributes)); - updateRequestList.add(updateRequestNode.deepCopy()); - }); - - given(service.updateRecords(updateRequestList, false)).willReturn(responseExpected); + var request = givenUpdateRequest(); // When - var responseReceived = controller.rollbackHandleUpdate(updateRequestList, authentication); + var responseReceived = controller.rollbackHandleUpdate(request, authentication); // Then assertThat(responseReceived.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(responseReceived.getBody()).isEqualTo(responseExpected); + then(service).should().updateRecords(request, false); } @Test @@ -548,9 +506,9 @@ void testRollbackHandlesBadRequest() { @Test void testArchiveRecord() throws Exception { // Given - var responseExpected = givenRecordResponseWriteArchive(List.of(HANDLE)); + var responseExpected = TestUtils.givenWriteResponseFull(List.of(HANDLE), FdoType.TOMBSTONE); var archiveRequest = givenArchiveRequest(); - given(service.tombstoneRecordBatch(List.of(archiveRequest))).willReturn( + given(service.tombstoneRecords(List.of(archiveRequest))).willReturn( responseExpected); // When @@ -578,8 +536,8 @@ void testArchiveRecordBatch() throws Exception { var handles = List.of(HANDLE, HANDLE_ALT); List archiveRequestList = new ArrayList<>(); handles.forEach(h -> archiveRequestList.add(givenArchiveRequest())); - var responseExpected = givenRecordResponseWriteArchive(handles); - given(service.tombstoneRecordBatch(archiveRequestList)).willReturn(responseExpected); + var responseExpected = TestUtils.givenWriteResponseFull(handles, FdoType.TOMBSTONE); + given(service.tombstoneRecords(archiveRequestList)).willReturn(responseExpected); // When var responseReceived = controller.archiveRecords(archiveRequestList, authentication); @@ -599,22 +557,8 @@ private JsonNode givenArchiveRequest() { return archiveRequest; } - - @Test - void testArchiveRecordBadRequest() { - // Given - var archiveAttributes = genTombstoneRequest(); - ObjectNode archiveRequestNode = mapper.createObjectNode(); - archiveRequestNode.set("data", - givenJsonNode(HANDLE_ALT, FdoType.HANDLE.getDigitalObjectType(), archiveAttributes)); - - // Then - assertThrowsExactly(InvalidRequestException.class, - () -> controller.updateRecord(PREFIX, SUFFIX, archiveRequestNode, authentication)); - } - @Test - void testPiDResolutionException() throws Exception { + void testPidResolutionException() throws Exception { // Given DoiRecordRequest request = givenDoiRecordRequestObject(); ObjectNode requestNode = genCreateRecordRequest(request, FdoType.DOI); diff --git a/src/test/java/eu/dissco/core/handlemanager/domain/JsonSchemaValidatorTest.java b/src/test/java/eu/dissco/core/handlemanager/domain/JsonSchemaValidatorTest.java index 4ab38a95..309ebff6 100644 --- a/src/test/java/eu/dissco/core/handlemanager/domain/JsonSchemaValidatorTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/domain/JsonSchemaValidatorTest.java @@ -1,30 +1,16 @@ package eu.dissco.core.handlemanager.domain; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MAS_NAME; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.MEDIA_HOST; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.ORGANISATION_ID; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PID_ISSUER; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.REFERENT_NAME; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SOURCE_DATA_STANDARD; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SOURCE_SYSTEM_NAME; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.SPECIMEN_HOST; -import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TARGET_TYPE; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.TOMBSTONED_TEXT; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ATTRIBUTES; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_DATA; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ID; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_TYPE; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE; import static eu.dissco.core.handlemanager.testUtils.TestUtils.LOC_TESTVAL; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.MAPPER; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.MEDIA_HOST_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.PID_ISSUER_TESTVAL_OTHER; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.REFERENT_DOI_NAME_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.SPECIMEN_HOST_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genCreateRecordRequest; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genTombstoneRequest; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genTombstoneRequestBatch; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genUpdateRequestAltLoc; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDataMappingRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaRequestObject; @@ -34,6 +20,7 @@ import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMasRecordRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenOrganisationRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenSourceSystemRequestObject; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenTombstoneRequest; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; @@ -45,14 +32,10 @@ import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.StructuralType; import eu.dissco.core.handlemanager.domain.validation.JsonSchemaValidator; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; -import java.util.List; -import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.junit.jupiter.MockitoExtension; @@ -190,113 +173,13 @@ void testPostMasRequest() { } @Test - void testHandlePatchRequest() { - // Given - var request = givenUpdateRequest(FdoType.HANDLE, PID_ISSUER.get(), - PID_ISSUER_TESTVAL_OTHER); - - // Then - assertDoesNotThrow(() -> schemaValidator.validatePatchRequest(request)); - } - - @Test - void testDoiPatchRequest() { - // Given - var request = givenUpdateRequest(FdoType.DOI, REFERENT_NAME.get(), - REFERENT_DOI_NAME_TESTVAL); - - // Then - assertDoesNotThrow(() -> schemaValidator.validatePatchRequest(request)); - } - - @Test - void testDigitalSpecimenPatchRequest() { - // Given - var request = givenUpdateRequest(FdoType.DIGITAL_SPECIMEN, SPECIMEN_HOST.get(), - SPECIMEN_HOST_TESTVAL); - - // Then - assertDoesNotThrow(() -> schemaValidator.validatePatchRequest(request)); - } - - @Test - void testDigitalMediaPatchRequest() { - // Given - var request = givenUpdateRequest(FdoType.DIGITAL_MEDIA, MEDIA_HOST.get(), MEDIA_HOST_TESTVAL); - - // Then - assertDoesNotThrow(() -> schemaValidator.validatePatchRequest(request)); - } - - @Test - void testAnnotationPatchRequest() { - // Given - var request = givenUpdateRequest(FdoType.ANNOTATION, TARGET_TYPE.get(), - "Annotation"); - - // Then - assertDoesNotThrow(() -> schemaValidator.validatePatchRequest(request)); - } - - @Test - void testOrganisationPatchRequest() { - // Given - var request = givenUpdateRequest(FdoType.ORGANISATION, ORGANISATION_ID.get(), "new"); - - // Then - assertDoesNotThrow(() -> schemaValidator.validatePatchRequest(request)); - } - - @Test - void testMappingPatchRequest() { - // Given - var request = givenUpdateRequest(FdoType.DATA_MAPPING, SOURCE_DATA_STANDARD.get(), "new"); - - // Then - assertDoesNotThrow(() -> schemaValidator.validatePatchRequest(request)); - } - - @Test - void testSourceSystemPatchRequest() { - // Given - var request = givenUpdateRequest(FdoType.SOURCE_SYSTEM, SOURCE_SYSTEM_NAME.get(), "new"); - - // Then - assertDoesNotThrow(() -> schemaValidator.validatePatchRequest(request)); - } - - @Test - void testMasPatchRequest() { + void testTombstoneRequest() { // Given - var request = givenUpdateRequest(FdoType.MAS, MAS_NAME.get(), "new"); + var request = givenTombstoneRequest().get(0); // Then - assertDoesNotThrow(() -> schemaValidator.validatePatchRequest(request)); - } - - private ObjectNode givenUpdateRequest(FdoType type, String key, String val) { - ObjectNode request = MAPPER.createObjectNode(); - ObjectNode data = MAPPER.createObjectNode(); - var attributes = (ObjectNode) genUpdateRequestAltLoc(); - attributes.put(key, val); - - data.put(NODE_TYPE, type.getDigitalObjectType()); - data.put(NODE_ID, HANDLE); - data.set(NODE_ATTRIBUTES, attributes); - request.set("data", data); - return request; - } - - @Test - void testTombstoneRequest() { - var data = MAPPER.createObjectNode(); - data.put(NODE_ID, HANDLE); - var attributes = genTombstoneRequest(); - data.set(NODE_ATTRIBUTES, attributes); - var request = MAPPER.createObjectNode(); - request.set(NODE_DATA, data); - assertDoesNotThrow(() -> schemaValidator.validatePutRequest(request)); + } @ParameterizedTest @@ -412,48 +295,10 @@ void testBadPostDigitalMediaRequestMissingProperty() { assertThat(e.getMessage()).contains(MISSING_MSG).contains(missingAttribute); } - @Test - void testBadPatchRequest() { - // Given - ObjectNode request = MAPPER.createObjectNode(); - request.set("data", MAPPER.createObjectNode() - .put(NODE_TYPE, "bad type") - .put(NODE_ID, HANDLE) - .set(NODE_ATTRIBUTES, genUpdateRequestAltLoc())); - - // Then - Exception e = assertThrowsExactly(InvalidRequestException.class, - () -> schemaValidator.validatePatchRequest(request)); - assertThat(e.getMessage()).contains(ENUM_MSG).contains(NODE_TYPE); - } - - @ParameterizedTest - @MethodSource("provideFdoTypes") - void testBadPatchRequestUnknownProperty(FdoType recordType) { - // Given - var request = givenUpdateRequest(recordType, UNKNOWN_ATTRIBUTE, UNKNOWN_VAL); - - // Then - Exception e = assertThrowsExactly(InvalidRequestException.class, - () -> schemaValidator.validatePatchRequest(request)); - - assertThat(e.getMessage()).contains(UNRECOGNIZED_MSG).contains(UNKNOWN_ATTRIBUTE); - } - - private static Stream provideFdoTypes() { - return Stream.of( - Arguments.of(FdoType.HANDLE), - Arguments.of(FdoType.DOI), - Arguments.of(FdoType.DIGITAL_SPECIMEN), - Arguments.of(FdoType.DIGITAL_MEDIA) - ); - - } - @Test void testBadArchiveRequest() { // Given - var request = genTombstoneRequestBatch(List.of(HANDLE)).get(0); + var request = givenTombstoneRequest().get(0); ((ObjectNode) request.get(NODE_DATA)).remove(NODE_TYPE); ((ObjectNode) request.get(NODE_DATA)).remove(NODE_ID); @@ -468,8 +313,7 @@ void testBadArchiveRequest() { @Test void testBadArchiveRequestMissingProperty() { // Given - var request = genTombstoneRequestBatch(List.of(HANDLE)).get(0); - ((ObjectNode) request.get(NODE_DATA)).remove(NODE_TYPE); + var request = givenTombstoneRequest().get(0); ((ObjectNode) request.get(NODE_DATA).get(NODE_ATTRIBUTES)).remove(TOMBSTONED_TEXT.get()); // When diff --git a/src/test/java/eu/dissco/core/handlemanager/repository/MongoRepositoryIT.java b/src/test/java/eu/dissco/core/handlemanager/repository/MongoRepositoryIT.java index 638e7e2d..3b10e786 100644 --- a/src/test/java/eu/dissco/core/handlemanager/repository/MongoRepositoryIT.java +++ b/src/test/java/eu/dissco/core/handlemanager/repository/MongoRepositoryIT.java @@ -1,20 +1,45 @@ package eu.dissco.core.handlemanager.repository; +import static com.mongodb.client.model.Filters.eq; +import static com.mongodb.client.model.Filters.in; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.CREATED; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE_ALT; import static eu.dissco.core.handlemanager.testUtils.TestUtils.MAPPER; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.PREFIX; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.PRIMARY_MEDIA_ID_TESTVAL; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaFdoRecord; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenFdoRecord; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenHandleFdoRecord; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMongoDocument; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenUpdatedFdoRecord; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertThrows; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; +import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; +import eu.dissco.core.handlemanager.domain.fdo.FdoProfile; +import eu.dissco.core.handlemanager.domain.fdo.FdoType; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoAttribute; +import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoRecord; +import java.util.List; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.testcontainers.containers.MongoDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.utility.DockerImageName; @Testcontainers -public class MongoRepositoryIT { +class MongoRepositoryIT { + + private static final String SPECIMEN_ID = PREFIX + "/SPECIMEN"; + private static final String MEDIA_ID = PREFIX + "/MEDIA"; private static final DockerImageName MONGODB = DockerImageName.parse("mongo:6.0.4"); @@ -23,14 +48,15 @@ public class MongoRepositoryIT { private static final MongoDBContainer CONTAINER = new MongoDBContainer(MONGODB); private MongoDatabase database; private MongoClient client; - private PidRepository repository; + private MongoCollection collection; + private MongoRepository repository; @BeforeEach void prepareDocumentStore() { client = MongoClients.create(CONTAINER.getConnectionString()); database = client.getDatabase("dissco"); - var collection = database.getCollection("handles"); - repository = new PidRepository(collection, MAPPER); + collection = database.getCollection("handles"); + repository = new MongoRepository(collection, MAPPER); } @AfterEach @@ -39,5 +65,187 @@ void disposeDocumentStore() { client.close(); } + @Test + void testGetHandleRecordsHandle() throws Exception { + // Given + populateMongoDB(); + var expected = List.of(givenHandleFdoRecord(HANDLE)); + + // When + var result = repository.getHandleRecords(List.of(HANDLE)); + + // Then + assertThat(result).isEqualTo(expected); + } + + @Test + void testGetHandleRecordsSpecimen() throws Exception { + // Given + populateMongoDB(); + var expected = List.of(givenDigitalSpecimenFdoRecord(SPECIMEN_ID)); + + // When + var result = repository.getHandleRecords(List.of(SPECIMEN_ID)); + + // Then + assertThat(result).isEqualTo(expected); + } + + @Test + void testGetHandleRecordsMedia() throws Exception { + // Given + populateMongoDB(); + var expected = List.of(givenDigitalMediaFdoRecord(MEDIA_ID)); + + // When + var result = repository.getHandleRecords(List.of(MEDIA_ID)); + + // Then + assertThat(result).isEqualTo(expected); + } + + @Test + void testGetHandleRecordsNotFound() throws Exception { + // Given + populateMongoDB(); + + // When + var result = repository.getHandleRecords(List.of(HANDLE_ALT)); + + // Then + assertThat(result).isEmpty(); + } + + @Test + void testGetExistingHandles() throws Exception { + // Given + populateMongoDB(); + + // When + var result = repository.getExistingHandles(List.of(HANDLE)); + + // Then + assertThat(result).isEqualTo(List.of(HANDLE)); + } + + @Test + void testGetExistingHandlesNotFound() throws Exception { + // Given + populateMongoDB(); + + // When + var result = repository.getExistingHandles(List.of(HANDLE_ALT)); + + // Then + assertThat(result).isEmpty(); + } + + @Test + void testPostHandleRecords() throws Exception { + // Given + var fdoRecord = givenDigitalSpecimenFdoRecord(HANDLE); + var expected = givenMongoDocument(fdoRecord); + + // When + repository.postHandleRecords(List.of(expected)); + var result = collection.find(eq("_id", HANDLE)).first(); + + // Then + assertThat(result).isEqualTo(expected); + } + + @Test + void testUpdateHandleRecords() throws Exception { + // Given + var specimenDoc = givenMongoDocument(givenDigitalSpecimenFdoRecord(HANDLE)); + collection.insertOne(specimenDoc); + var expected = givenMongoDocument(givenUpdatedFdoRecord(FdoType.DIGITAL_SPECIMEN, + NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL)); + + // When + repository.updateHandleRecords(List.of(expected)); + var result = collection.find(eq("_id", HANDLE)).first(); + + // Then + assertThat(result).isEqualTo(expected); + } + + @Test + void testSearchByPrimaryLocalIdSpecimen() throws Exception { + // Given + populateMongoDB(); + var expected = List.of(givenDigitalSpecimenFdoRecord(SPECIMEN_ID)); + + // When + var result = repository.searchByPrimaryLocalId(FdoProfile.NORMALISED_SPECIMEN_OBJECT_ID.get(), + List.of(NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL)); + + // Then + assertThat(result).isEqualTo(expected); + } + + @Test + void testSearchByPrimaryLocalIdMedia() throws Exception { + // Given + populateMongoDB(); + var expected = List.of(givenDigitalMediaFdoRecord(MEDIA_ID)); + + // When + var result = repository.searchByPrimaryLocalId(FdoProfile.PRIMARY_MEDIA_ID.get(), + List.of(PRIMARY_MEDIA_ID_TESTVAL)); + + // Then + assertThat(result).isEqualTo(expected); + } + + @Test + void testRollbackHandles() throws Exception { + // Given + populateMongoDB(); + var idList = List.of(HANDLE, SPECIMEN_ID, MEDIA_ID); + + // When + repository.rollbackHandles(idList); + var result = collection.find(in("_id", idList)); + + // Then + assertThat(result).isEmpty(); + } + + @Test + void testRollbackHandlesFromPhysicalId() throws Exception { + // Given + populateMongoDB(); + + // When + repository.rollbackHandles(FdoProfile.NORMALISED_SPECIMEN_OBJECT_ID.get(), + List.of(NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL)); + var result = collection.find(in("_id", SPECIMEN_ID)); + + // Then + assertThat(result).isEmpty(); + } + + @Test + void testFdoTypeNotFound() throws Exception { + // Given + var fdoRecord = new FdoRecord(HANDLE, FdoType.HANDLE, + List.of(new FdoAttribute(FdoProfile.FDO_RECORD_LICENSE, CREATED, "License")), null); + collection.insertOne(givenMongoDocument(fdoRecord)); + var handleList = List.of(HANDLE); + + // When / Then + assertThrows(IllegalStateException.class, () -> repository.getHandleRecords(handleList)); + } + + private void populateMongoDB() throws Exception { + var handleDoc = givenMongoDocument(givenHandleFdoRecord(HANDLE)); + var specimenDoc = givenMongoDocument(givenDigitalSpecimenFdoRecord(SPECIMEN_ID)); + var mediaDoc = givenMongoDocument(givenDigitalMediaFdoRecord(MEDIA_ID)); + collection.insertOne(handleDoc); + collection.insertOne(specimenDoc); + collection.insertOne(mediaDoc); + } + } \ No newline at end of file diff --git a/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java index d46dc627..de44fc99 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java @@ -7,8 +7,6 @@ import static eu.dissco.core.handlemanager.testUtils.TestUtils.NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.PRIMARY_MEDIA_ID_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genCreateRecordRequest; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genObjectNodeAttributeRecord; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genUpdateRequestBatch; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalMediaRequestObjectUpdate; @@ -16,8 +14,10 @@ import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenRequestObjectNullOptionals; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDigitalSpecimenRequestObjectUpdate; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMongoDocument; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseWriteSmallResponse; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenUpdateRequest; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenUpdatedFdoRecord; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenWriteResponseIdsOnly; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.jsonFormatFdoRecord; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; @@ -38,7 +38,7 @@ import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.UnprocessableEntityException; import eu.dissco.core.handlemanager.properties.ProfileProperties; -import eu.dissco.core.handlemanager.repository.PidRepository; +import eu.dissco.core.handlemanager.repository.MongoRepository; import java.time.Clock; import java.time.Instant; import java.time.ZoneOffset; @@ -65,7 +65,7 @@ class DoiServiceTest { @Mock private DataCiteService dataCiteService; @Mock - private PidRepository mongoRepository; + private MongoRepository mongoRepository; private PidService service; private MockedStatic mockedStatic; private MockedStatic mockedClock; @@ -99,9 +99,9 @@ void testCreateDigitalSpecimen() throws Exception { var request = genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), FdoType.DIGITAL_SPECIMEN); var fdoRecord = givenDigitalSpecimenFdoRecord(HANDLE); - var responseExpected = givenRecordResponseWriteSmallResponse(List.of(fdoRecord), + var responseExpected = givenWriteResponseIdsOnly(List.of(fdoRecord), FdoType.DIGITAL_SPECIMEN, DOI_DOMAIN); - var dataCiteEvent = new DataCiteEvent(genObjectNodeAttributeRecord(fdoRecord.attributes()), + var dataCiteEvent = new DataCiteEvent(jsonFormatFdoRecord(fdoRecord.attributes()), EventType.CREATE); given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); given(fdoRecordService.prepareNewDigitalSpecimenRecord(any(), any(), any())).willReturn( @@ -121,9 +121,9 @@ void testCreateDigitalMedia() throws Exception { // Given var request = genCreateRecordRequest(givenDigitalMediaRequestObject(), FdoType.DIGITAL_MEDIA); var digitalMedia = givenDigitalMediaFdoRecord(HANDLE); - var responseExpected = givenRecordResponseWriteSmallResponse(List.of(digitalMedia), + var responseExpected = givenWriteResponseIdsOnly(List.of(digitalMedia), FdoType.DIGITAL_MEDIA, DOI_DOMAIN); - var dataCiteEvent = new DataCiteEvent(genObjectNodeAttributeRecord(digitalMedia.attributes()), + var dataCiteEvent = new DataCiteEvent(jsonFormatFdoRecord(digitalMedia.attributes()), EventType.CREATE); given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); given(fdoRecordService.prepareNewDigitalMediaRecord(any(), any(), any())).willReturn( @@ -162,19 +162,18 @@ void testUpdateDigitalSpecimen() throws Exception { // Given var previousVersion = givenDigitalSpecimenFdoRecord(HANDLE); var request = MAPPER.valueToTree(givenDigitalSpecimenRequestObjectUpdate()); - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN, request); + var updateRequest = givenUpdateRequest(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN, request); var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.DIGITAL_SPECIMEN, NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL); var expectedDocument = givenMongoDocument(updatedAttributeRecord); - var responseExpected = givenRecordResponseWriteSmallResponse(List.of(updatedAttributeRecord), + var responseExpected = givenWriteResponseIdsOnly(List.of(updatedAttributeRecord), FdoType.DIGITAL_SPECIMEN, DOI_DOMAIN); var expectedEvent = new DataCiteEvent( - (genObjectNodeAttributeRecord(updatedAttributeRecord.attributes())), + (jsonFormatFdoRecord(updatedAttributeRecord.attributes())), EventType.UPDATE); given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); given(fdoRecordService.prepareUpdatedDigitalSpecimenRecord(any(), any(), any(), anyBoolean())).willReturn(updatedAttributeRecord); - given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(DOI_DOMAIN); // When @@ -182,7 +181,7 @@ void testUpdateDigitalSpecimen() throws Exception { // Then assertThat(responseReceived).isEqualTo(responseExpected); - then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); + then(mongoRepository).should().updateHandleRecords(List.of(expectedDocument)); then(dataCiteService).should().publishToDataCite(expectedEvent, FdoType.DIGITAL_SPECIMEN); } @@ -191,19 +190,18 @@ void testUpdateDigitalMedia() throws Exception { // Given var previousVersion = givenDigitalMediaFdoRecord(HANDLE); var request = MAPPER.valueToTree(givenDigitalMediaRequestObjectUpdate()); - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DIGITAL_MEDIA, request); + var updateRequest = givenUpdateRequest(List.of(HANDLE), FdoType.DIGITAL_MEDIA, request); var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.DIGITAL_MEDIA, PRIMARY_MEDIA_ID_TESTVAL); var expectedDocument = givenMongoDocument(updatedAttributeRecord); - var responseExpected = givenRecordResponseWriteSmallResponse(List.of(updatedAttributeRecord), + var responseExpected = givenWriteResponseIdsOnly(List.of(updatedAttributeRecord), FdoType.DIGITAL_MEDIA, DOI_DOMAIN); var expectedEvent = new DataCiteEvent( - (genObjectNodeAttributeRecord(updatedAttributeRecord.attributes())), + (jsonFormatFdoRecord(updatedAttributeRecord.attributes())), EventType.UPDATE); given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); given(fdoRecordService.prepareUpdatedDigitalMediaRecord(any(), any(), any(), anyBoolean())).willReturn(updatedAttributeRecord); - given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(DOI_DOMAIN); // When @@ -211,14 +209,14 @@ void testUpdateDigitalMedia() throws Exception { // Then assertThat(responseReceived).isEqualTo(responseExpected); - then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); + then(mongoRepository).should().updateHandleRecords(List.of(expectedDocument)); then(dataCiteService).should().publishToDataCite(expectedEvent, FdoType.DIGITAL_MEDIA); } @Test void testUpdateInvalidType() throws Exception { // Given - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.HANDLE, + var updateRequest = givenUpdateRequest(List.of(HANDLE), FdoType.HANDLE, MAPPER.valueToTree(givenDigitalSpecimenRequestObjectUpdate())); // When Then @@ -230,7 +228,7 @@ void testUpdateInvalidType() throws Exception { void testUpdateRecordLocationDataCiteFails() throws Exception { // Given var requestAttributes = MAPPER.valueToTree(givenDigitalSpecimenRequestObjectUpdate()); - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN, + var updateRequest = givenUpdateRequest(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN, requestAttributes); var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.DIGITAL_SPECIMEN, NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL); @@ -238,7 +236,6 @@ void testUpdateRecordLocationDataCiteFails() throws Exception { var expectedDocument = givenMongoDocument(updatedAttributeRecord); given(fdoRecordService.prepareUpdatedDigitalSpecimenRecord(any(), any(), eq(previousVersion), eq(true))).willReturn(updatedAttributeRecord); - given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); doThrow(JsonProcessingException.class).when(dataCiteService).publishToDataCite(any(), any()); @@ -247,7 +244,7 @@ void testUpdateRecordLocationDataCiteFails() throws Exception { () -> service.updateRecords(updateRequest, true)); // Then - then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); + then(mongoRepository).should().updateHandleRecords(List.of(expectedDocument)); then(mongoRepository).shouldHaveNoMoreInteractions(); } diff --git a/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java index 0014a23c..a98d024d 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java @@ -48,13 +48,13 @@ import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMasFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMasRecordRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMasRecordRequestObjectUpdate; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenMongoDocument; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenOrganisationFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenOrganisationRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenOrganisationRequestObjectUpdate; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenSourceSystemFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenSourceSystemRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenSourceSystemRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenTombstoneFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenTombstoneRecordRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenUpdatedFdoRecord; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; @@ -78,14 +78,9 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Collections; -import java.util.List; -import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; @@ -284,7 +279,7 @@ void testPrepareNewDigitalSpecimenRecordMin() throws Exception { void testPrepareNewSpecimenRecordFull() throws Exception { var request = givenDigitalSpecimenRequestObject(); var expected = new FdoRecord(HANDLE, FdoType.DIGITAL_SPECIMEN, - genDigitalSpecimenAttributes(HANDLE, request), + genDigitalSpecimenAttributes(HANDLE, request, CREATED), NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL); // When @@ -505,7 +500,8 @@ void testPrepareUpdatedOrganisationRecord() throws Exception { void testPrepareTombstoneRecordNoRelatedIds() throws Exception { var previousVersion = givenHandleFdoRecord(HANDLE); var request = new TombstoneRecordRequest(TOMBSTONE_TEXT_TESTVAL, null); - var expected = new FdoRecord(HANDLE, FdoType.HANDLE, genTombstoneAttributes(request), null); + var expected = new FdoRecord(HANDLE, FdoType.HANDLE.HANDLE, genTombstoneAttributes(request), + null); // When var result = fdoRecordService.prepareTombstoneRecord(request, UPDATED, previousVersion); @@ -521,7 +517,8 @@ void testPrepareTombstoneRecordNoRelatedIds() throws Exception { void testPrepareTombstoneRecordEmptyRelatedIds() throws Exception { var previousVersion = givenHandleFdoRecord(HANDLE); var request = new TombstoneRecordRequest(TOMBSTONE_TEXT_TESTVAL, Collections.emptyList()); - var expected = new FdoRecord(HANDLE, FdoType.HANDLE, genTombstoneAttributes(request), null); + var expected = new FdoRecord(HANDLE, FdoType.HANDLE.HANDLE, genTombstoneAttributes(request), + null); // When var result = fdoRecordService.prepareTombstoneRecord(request, UPDATED, previousVersion); @@ -537,7 +534,7 @@ void testPrepareTombstoneRecordEmptyRelatedIds() throws Exception { void testPrepareTombstoneRecordFull() throws Exception { var previousVersion = givenHandleFdoRecord(HANDLE); var request = givenTombstoneRecordRequestObject(); - var expected = new FdoRecord(HANDLE, FdoType.HANDLE, genTombstoneAttributes(request), null); + var expected = givenTombstoneFdoRecord(); // When var result = fdoRecordService.prepareTombstoneRecord(request, UPDATED, previousVersion); @@ -549,25 +546,4 @@ void testPrepareTombstoneRecordFull() throws Exception { assertThat(result.handle()).isEqualTo(expected.handle()); } - @ParameterizedTest - @MethodSource("fdoRecords") - void testToMongoDbDocumentHandle(FdoRecord fdoRecord) throws Exception { - // Given - var expected = List.of(givenMongoDocument(fdoRecord)); - - // When - var result = fdoRecordService.toMongoDbDocument(List.of(fdoRecord)); - - // Then - assertThat(result).isEqualTo(expected); - } - - private static Stream fdoRecords() throws Exception { - return Stream.of(Arguments.of(givenHandleFdoRecord(HANDLE)), - Arguments.of(givenDigitalSpecimenFdoRecord(HANDLE)), - Arguments.of(givenDigitalMediaFdoRecord(HANDLE)), - Arguments.of(givenAnnotationFdoRecord(HANDLE, true)), - Arguments.of(givenAnnotationFdoRecord(HANDLE, false))); - } - } diff --git a/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java index ae1e6467..2de08f25 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java @@ -1,5 +1,6 @@ package eu.dissco.core.handlemanager.service; +import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.NORMALISED_SPECIMEN_OBJECT_ID; import static eu.dissco.core.handlemanager.domain.fdo.FdoProfile.PID_STATUS; import static eu.dissco.core.handlemanager.testUtils.TestUtils.CREATED; import static eu.dissco.core.handlemanager.testUtils.TestUtils.HANDLE; @@ -11,7 +12,6 @@ import static eu.dissco.core.handlemanager.testUtils.TestUtils.PID_STATUS_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.PRIMARY_MEDIA_ID_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genCreateRecordRequest; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.genUpdateRequestBatch; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationRequestObjectUpdate; @@ -37,13 +37,16 @@ import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenOrganisationFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenOrganisationRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenOrganisationRequestObjectUpdate; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseRead; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseWriteFullResponse; -import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseWriteSmallResponse; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenReadResponse; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenSourceSystemFdoRecord; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenSourceSystemRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenSourceSystemRequestObjectUpdate; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenTombstoneFdoRecord; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenTombstoneRequest; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenUpdateRequest; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenUpdatedFdoRecord; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenWriteResponseFull; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenWriteResponseIdsOnly; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -60,7 +63,8 @@ import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.properties.ProfileProperties; -import eu.dissco.core.handlemanager.repository.PidRepository; +import eu.dissco.core.handlemanager.repository.MongoRepository; +import eu.dissco.core.handlemanager.testUtils.TestUtils; import java.nio.charset.StandardCharsets; import java.time.Clock; import java.time.Instant; @@ -88,7 +92,7 @@ class HandleServiceTest { @Mock private ProfileProperties profileProperties; @Mock - PidRepository mongoRepository; + MongoRepository mongoRepository; private PidService service; private List handles; private MockedStatic mockedStatic; @@ -128,12 +132,9 @@ void destroy() { void testCreateAnnotationNoHash() throws Exception { var request = genCreateRecordRequest(givenAnnotationRequestObject(), FdoType.ANNOTATION); var fdoRecord = givenAnnotationFdoRecord(HANDLE, false); - var expected = givenRecordResponseWriteFullResponse(List.of(HANDLE), FdoType.ANNOTATION); - var expectedDocument = givenMongoDocument(fdoRecord); + var expected = TestUtils.givenWriteResponseFull(List.of(HANDLE), FdoType.ANNOTATION); given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); given(fdoRecordService.prepareNewAnnotationRecord(any(), any(), any())).willReturn(fdoRecord); - given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( - List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -147,13 +148,10 @@ void testCreateAnnotationNoHash() throws Exception { void testCreateAnnotationIncludeHash() throws Exception { var request = genCreateRecordRequest(givenAnnotationRequestObject(), FdoType.ANNOTATION); var fdoRecord = givenAnnotationFdoRecord(HANDLE, true); - var expected = givenRecordResponseWriteSmallResponse(List.of(fdoRecord), FdoType.ANNOTATION, + var expected = givenWriteResponseIdsOnly(List.of(fdoRecord), FdoType.ANNOTATION, HANDLE_DOMAIN); - var expectedDocument = givenMongoDocument(fdoRecord); given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); given(fdoRecordService.prepareNewAnnotationRecord(any(), any(), any())).willReturn(fdoRecord); - given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( - List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -169,15 +167,14 @@ void testUpdateAnnotation() throws Exception { // Given var previousVersion = givenAnnotationFdoRecord(HANDLE, false); var request = MAPPER.valueToTree(givenAnnotationRequestObjectUpdate()); - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.ANNOTATION, request); + var updateRequest = givenUpdateRequest(List.of(HANDLE), FdoType.ANNOTATION, request); var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.ANNOTATION, null); var expectedDocument = givenMongoDocument(updatedAttributeRecord); - var expected = givenRecordResponseWriteSmallResponse(List.of(updatedAttributeRecord), + var expected = givenWriteResponseIdsOnly(List.of(updatedAttributeRecord), FdoType.ANNOTATION, HANDLE_DOMAIN); given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); given(fdoRecordService.prepareUpdatedAnnotationRecord(any(), any(), any(), anyBoolean())).willReturn(updatedAttributeRecord); - given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -185,7 +182,7 @@ void testUpdateAnnotation() throws Exception { // Then assertThat(result).isEqualTo(expected); - then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); + then(mongoRepository).should().updateHandleRecords(List.of(expectedDocument)); } @Test @@ -193,14 +190,11 @@ void testCreateDigitalSpecimen() throws Exception { var request = genCreateRecordRequest(givenDigitalSpecimenRequestObject(), FdoType.DIGITAL_SPECIMEN); var fdoRecord = givenDigitalSpecimenFdoRecord(HANDLE); - var expected = givenRecordResponseWriteSmallResponse(List.of(fdoRecord), + var expected = givenWriteResponseIdsOnly(List.of(fdoRecord), FdoType.DIGITAL_SPECIMEN, HANDLE_DOMAIN); - var expectedDocument = givenMongoDocument(fdoRecord); given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); given(fdoRecordService.prepareNewDigitalSpecimenRecord(any(), any(), any())).willReturn( fdoRecord); - given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( - List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -228,16 +222,15 @@ void testUpdateDigitalSpecimen() throws Exception { // Given var previousVersion = givenDigitalSpecimenFdoRecord(HANDLE); var request = MAPPER.valueToTree(givenDigitalSpecimenRequestObjectUpdate()); - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN, request); + var updateRequest = givenUpdateRequest(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN, request); var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.DIGITAL_SPECIMEN, NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL); var expectedDocument = givenMongoDocument(updatedAttributeRecord); - var expected = givenRecordResponseWriteSmallResponse(List.of(updatedAttributeRecord), + var expected = givenWriteResponseIdsOnly(List.of(updatedAttributeRecord), FdoType.DIGITAL_SPECIMEN, HANDLE_DOMAIN); given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); given(fdoRecordService.prepareUpdatedDigitalSpecimenRecord(any(), any(), any(), anyBoolean())).willReturn(updatedAttributeRecord); - given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -245,19 +238,16 @@ void testUpdateDigitalSpecimen() throws Exception { // Then assertThat(result).isEqualTo(expected); - then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); + then(mongoRepository).should().updateHandleRecords(List.of(expectedDocument)); } @Test void testCreateDoi() throws Exception { var request = genCreateRecordRequest(givenDoiRecordRequestObject(), FdoType.DOI); var fdoRecord = givenDoiFdoRecord(HANDLE); - var expected = givenRecordResponseWriteFullResponse(List.of(HANDLE), FdoType.DOI); - var expectedDocument = givenMongoDocument(fdoRecord); + var expected = TestUtils.givenWriteResponseFull(List.of(HANDLE), FdoType.DOI); given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); given(fdoRecordService.prepareNewDoiRecord(any(), any(), any(), any())).willReturn(fdoRecord); - given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( - List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -272,14 +262,13 @@ void testUpdateDoiRecord() throws Exception { // Given var previousVersion = givenDoiFdoRecord(HANDLE); var request = MAPPER.valueToTree(givenDoiRecordRequestObjectUpdate()); - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DOI, request); + var updateRequest = givenUpdateRequest(List.of(HANDLE), FdoType.DOI, request); var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.DOI, null); var expectedDocument = givenMongoDocument(updatedAttributeRecord); - var expected = givenRecordResponseWriteFullResponse(updatedAttributeRecord); + var expected = givenWriteResponseFull(updatedAttributeRecord); given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); given(fdoRecordService.prepareUpdatedDoiRecord(any(), any(), any(), any(), anyBoolean())).willReturn(updatedAttributeRecord); - given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -287,20 +276,17 @@ void testUpdateDoiRecord() throws Exception { // Then assertThat(result).isEqualTo(expected); - then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); + then(mongoRepository).should().updateHandleRecords(List.of(expectedDocument)); } @Test void testCreateHandle() throws Exception { var request = genCreateRecordRequest(givenHandleRecordRequestObject(), FdoType.HANDLE); var fdoRecord = givenHandleFdoRecord(HANDLE); - var expected = givenRecordResponseWriteFullResponse(List.of(HANDLE), FdoType.HANDLE); - var expectedDocument = givenMongoDocument(fdoRecord); + var expected = TestUtils.givenWriteResponseFull(List.of(HANDLE), FdoType.HANDLE); given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); given(fdoRecordService.prepareNewHandleRecord(any(), any(), any(), any())).willReturn( fdoRecord); - given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( - List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -315,14 +301,13 @@ void testUpdateHandleRecord() throws Exception { // Given var previousVersion = givenHandleFdoRecord(HANDLE); var request = MAPPER.valueToTree(givenHandleRecordRequestObjectUpdate()); - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.HANDLE, request); + var updateRequest = givenUpdateRequest(List.of(HANDLE), FdoType.HANDLE, request); var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.HANDLE, null); var expectedDocument = givenMongoDocument(updatedAttributeRecord); - var expected = givenRecordResponseWriteFullResponse(updatedAttributeRecord); + var expected = givenWriteResponseFull(updatedAttributeRecord); given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); given(fdoRecordService.prepareUpdatedHandleRecord(any(), any(), any(), any(), anyBoolean())).willReturn(updatedAttributeRecord); - given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -330,7 +315,7 @@ void testUpdateHandleRecord() throws Exception { // Then assertThat(result).isEqualTo(expected); - then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); + then(mongoRepository).should().updateHandleRecords(List.of(expectedDocument)); } @Test @@ -341,7 +326,7 @@ void testUpdateHandleRecordInternalDuplicates() throws Exception { new FdoAttribute(PID_STATUS, CREATED, PidStatus.TOMBSTONED.name())); var previousVersion = new FdoRecord(HANDLE_ALT, FdoType.HANDLE, attributes, null); var request = MAPPER.valueToTree(givenHandleRecordRequestObjectUpdate()); - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.HANDLE, request); + var updateRequest = givenUpdateRequest(List.of(HANDLE), FdoType.HANDLE, request); given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); // When / Then @@ -352,7 +337,7 @@ void testUpdateHandleRecordInternalDuplicates() throws Exception { void testUpdateHandleRecordNotWritableInternalDuplicates() throws Exception { // Given var request = MAPPER.valueToTree(givenHandleRecordRequestObjectUpdate()); - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.HANDLE, request); + var updateRequest = givenUpdateRequest(List.of(HANDLE), FdoType.HANDLE, request); // When / Then assertThrows(InvalidRequestException.class, () -> service.updateRecords(updateRequest, true)); @@ -362,7 +347,7 @@ void testUpdateHandleRecordNotWritableInternalDuplicates() throws Exception { void testUpdateHandleRecordNotFound() throws Exception { // Given var request = MAPPER.valueToTree(givenHandleRecordRequestObjectUpdate()); - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.HANDLE, request); + var updateRequest = givenUpdateRequest(List.of(HANDLE), FdoType.HANDLE, request); given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(Collections.emptyList()); // When @@ -374,12 +359,9 @@ void testUpdateHandleRecordNotFound() throws Exception { void testCreateDataMapping() throws Exception { var request = genCreateRecordRequest(givenDataMappingRequestObject(), FdoType.DATA_MAPPING); var fdoRecord = givenDataMappingFdoRecord(HANDLE); - var expected = givenRecordResponseWriteFullResponse(List.of(HANDLE), FdoType.DATA_MAPPING); - var expectedDocument = givenMongoDocument(fdoRecord); + var expected = TestUtils.givenWriteResponseFull(List.of(HANDLE), FdoType.DATA_MAPPING); given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); given(fdoRecordService.prepareNewDataMappingRecord(any(), any(), any())).willReturn(fdoRecord); - given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( - List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -394,14 +376,13 @@ void testUpdateDataMapping() throws Exception { // Given var previousVersion = givenDataMappingFdoRecord(HANDLE); var request = MAPPER.valueToTree(givenDataMappingRequestObjectUpdate()); - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DATA_MAPPING, request); + var updateRequest = givenUpdateRequest(List.of(HANDLE), FdoType.DATA_MAPPING, request); var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.DATA_MAPPING, null); var expectedDocument = givenMongoDocument(updatedAttributeRecord); - var expected = givenRecordResponseWriteFullResponse(updatedAttributeRecord); + var expected = givenWriteResponseFull(updatedAttributeRecord); given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); given(fdoRecordService.prepareUpdatedDataMappingRecord(any(), any(), any(), anyBoolean())).willReturn(updatedAttributeRecord); - given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -409,19 +390,16 @@ void testUpdateDataMapping() throws Exception { // Then assertThat(result).isEqualTo(expected); - then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); + then(mongoRepository).should().updateHandleRecords(List.of(expectedDocument)); } @Test void testCreateMas() throws Exception { var request = genCreateRecordRequest(givenMasRecordRequestObject(), FdoType.MAS); var fdoRecord = givenMasFdoRecord(HANDLE); - var expected = givenRecordResponseWriteFullResponse(List.of(HANDLE), FdoType.MAS); - var expectedDocument = givenMongoDocument(fdoRecord); + var expected = TestUtils.givenWriteResponseFull(List.of(HANDLE), FdoType.MAS); given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); given(fdoRecordService.prepareNewMasRecord(any(), any(), any())).willReturn(fdoRecord); - given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( - List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -436,14 +414,13 @@ void testUpdateMas() throws Exception { // Given var previousVersion = givenMasFdoRecord(HANDLE); var request = MAPPER.valueToTree(givenMasRecordRequestObjectUpdate()); - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.MAS, request); + var updateRequest = givenUpdateRequest(List.of(HANDLE), FdoType.MAS, request); var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.MAS, null); var expectedDocument = givenMongoDocument(updatedAttributeRecord); - var expected = givenRecordResponseWriteFullResponse(updatedAttributeRecord); + var expected = givenWriteResponseFull(updatedAttributeRecord); given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); given(fdoRecordService.prepareUpdatedMasRecord(any(), any(), any(), anyBoolean())).willReturn( updatedAttributeRecord); - given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -451,20 +428,17 @@ void testUpdateMas() throws Exception { // Then assertThat(result).isEqualTo(expected); - then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); + then(mongoRepository).should().updateHandleRecords(List.of(expectedDocument)); } @Test void testCreateDigitalMedia() throws Exception { var request = genCreateRecordRequest(givenDigitalMediaRequestObject(), FdoType.DIGITAL_MEDIA); var fdoRecord = givenDigitalMediaFdoRecord(HANDLE); - var expected = givenRecordResponseWriteSmallResponse(List.of(fdoRecord), FdoType.DIGITAL_MEDIA, + var expected = givenWriteResponseIdsOnly(List.of(fdoRecord), FdoType.DIGITAL_MEDIA, HANDLE_DOMAIN); - var expectedDocument = givenMongoDocument(fdoRecord); given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); given(fdoRecordService.prepareNewDigitalMediaRecord(any(), any(), any())).willReturn(fdoRecord); - given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( - List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -478,16 +452,15 @@ void testCreateDigitalMedia() throws Exception { void testUpdateDigitalMedia() throws Exception { var previousVersion = givenDigitalMediaFdoRecord(HANDLE); var request = MAPPER.valueToTree(givenDigitalMediaRequestObjectUpdate()); - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.DIGITAL_MEDIA, request); + var updateRequest = givenUpdateRequest(List.of(HANDLE), FdoType.DIGITAL_MEDIA, request); var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.DIGITAL_MEDIA, PRIMARY_MEDIA_ID_TESTVAL); var expectedDocument = givenMongoDocument(updatedAttributeRecord); - var responseExpected = givenRecordResponseWriteSmallResponse(List.of(updatedAttributeRecord), + var responseExpected = givenWriteResponseIdsOnly(List.of(updatedAttributeRecord), FdoType.DIGITAL_MEDIA, HANDLE_DOMAIN); given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); given(fdoRecordService.prepareUpdatedDigitalMediaRecord(any(), any(), any(), anyBoolean())).willReturn(updatedAttributeRecord); - given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -495,19 +468,16 @@ void testUpdateDigitalMedia() throws Exception { // Then assertThat(responseReceived).isEqualTo(responseExpected); - then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); + then(mongoRepository).should().updateHandleRecords(List.of(expectedDocument)); } @Test void testCreateSourceSystem() throws Exception { var request = genCreateRecordRequest(givenSourceSystemRequestObject(), FdoType.SOURCE_SYSTEM); var fdoRecord = givenSourceSystemFdoRecord(HANDLE); - var expected = givenRecordResponseWriteFullResponse(List.of(HANDLE), FdoType.SOURCE_SYSTEM); - var expectedDocument = givenMongoDocument(fdoRecord); + var expected = TestUtils.givenWriteResponseFull(List.of(HANDLE), FdoType.SOURCE_SYSTEM); given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); given(fdoRecordService.prepareNewSourceSystemRecord(any(), any(), any())).willReturn(fdoRecord); - given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( - List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -522,14 +492,13 @@ void testUpdateSourceSystem() throws Exception { // Given var previousVersion = givenMasFdoRecord(HANDLE); var request = MAPPER.valueToTree(givenSourceSystemRequestObjectUpdate()); - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.SOURCE_SYSTEM, request); + var updateRequest = givenUpdateRequest(List.of(HANDLE), FdoType.SOURCE_SYSTEM, request); var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.SOURCE_SYSTEM, null); var expectedDocument = givenMongoDocument(updatedAttributeRecord); - var expected = givenRecordResponseWriteFullResponse(updatedAttributeRecord); + var expected = givenWriteResponseFull(updatedAttributeRecord); given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); given(fdoRecordService.prepareUpdatedSourceSystemRecord(any(), any(), any(), anyBoolean())).willReturn(updatedAttributeRecord); - given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -537,19 +506,16 @@ void testUpdateSourceSystem() throws Exception { // Then assertThat(result).isEqualTo(expected); - then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); + then(mongoRepository).should().updateHandleRecords(List.of(expectedDocument)); } @Test void testCreateOrganisation() throws Exception { var request = genCreateRecordRequest(givenOrganisationRequestObject(), FdoType.ORGANISATION); var fdoRecord = givenOrganisationFdoRecord(HANDLE); - var expected = givenRecordResponseWriteFullResponse(List.of(HANDLE), FdoType.ORGANISATION); - var expectedDocument = givenMongoDocument(fdoRecord); + var expected = TestUtils.givenWriteResponseFull(List.of(HANDLE), FdoType.ORGANISATION); given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); given(fdoRecordService.prepareNewOrganisationRecord(any(), any(), any())).willReturn(fdoRecord); - given(fdoRecordService.toMongoDbDocument(List.of(fdoRecord))).willReturn( - List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -564,14 +530,13 @@ void testUpdateOrganisation() throws Exception { // Given var previousVersion = givenOrganisationFdoRecord(HANDLE); var request = MAPPER.valueToTree(givenOrganisationRequestObjectUpdate()); - var updateRequest = genUpdateRequestBatch(List.of(HANDLE), FdoType.ORGANISATION, request); + var updateRequest = givenUpdateRequest(List.of(HANDLE), FdoType.ORGANISATION, request); var updatedAttributeRecord = givenUpdatedFdoRecord(FdoType.ORGANISATION, null); var expectedDocument = givenMongoDocument(updatedAttributeRecord); - var expected = givenRecordResponseWriteFullResponse(updatedAttributeRecord); + var expected = givenWriteResponseFull(updatedAttributeRecord); given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); given(fdoRecordService.prepareUpdatedOrganisationRecord(any(), any(), any(), anyBoolean())).willReturn(updatedAttributeRecord); - given(fdoRecordService.toMongoDbDocument(any())).willReturn(List.of(expectedDocument)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -579,13 +544,33 @@ void testUpdateOrganisation() throws Exception { // Then assertThat(result).isEqualTo(expected); - then(mongoRepository).should().updateHandleRecord(List.of(expectedDocument)); + then(mongoRepository).should().updateHandleRecords(List.of(expectedDocument)); + } + + @Test + void testTombstoneRecords() throws Exception { + // Given + var request = givenTombstoneRequest(); + var previousVersion = givenHandleFdoRecord(HANDLE); + var fdoRecord = givenTombstoneFdoRecord(); + var expectedDocument = givenMongoDocument(fdoRecord); + var expected = givenWriteResponseFull(List.of(HANDLE), FdoType.TOMBSTONE); + given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn(List.of(previousVersion)); + given(fdoRecordService.prepareTombstoneRecord(any(), any(), any())).willReturn(fdoRecord); + given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); + + // When + var result = service.tombstoneRecords(request); + + // Then + assertThat(result).isEqualTo(expected); + then(mongoRepository).should().updateHandleRecords(List.of(expectedDocument)); } @Test void testResolveSingleRecord() throws Exception { // Given - var expected = givenRecordResponseRead(List.of(HANDLE), PATH, FdoType.HANDLE, HANDLE_DOMAIN); + var expected = givenReadResponse(List.of(HANDLE), PATH, FdoType.HANDLE, HANDLE_DOMAIN); given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn( List.of(givenHandleFdoRecord(HANDLE))); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -600,7 +585,7 @@ void testResolveSingleRecord() throws Exception { @Test void testResolveBatchRecord() throws Exception { // Given - var expected = givenRecordResponseRead(List.of(HANDLE), PATH, FdoType.HANDLE, HANDLE_DOMAIN); + var expected = givenReadResponse(List.of(HANDLE), PATH, FdoType.HANDLE, HANDLE_DOMAIN); given(mongoRepository.getHandleRecords(List.of(HANDLE))).willReturn( List.of(givenHandleFdoRecord(HANDLE))); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -625,7 +610,7 @@ void testResolveBatchRecordNotFound() throws Exception { @Test void testSearchByPhysicalSpecimenId() throws Exception { // Given - var expected = givenRecordResponseWriteFullResponse(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN); + var expected = TestUtils.givenWriteResponseFull(List.of(HANDLE), FdoType.DIGITAL_SPECIMEN); given(mongoRepository.searchByPrimaryLocalId(any(), any())).willReturn( List.of(givenDigitalSpecimenFdoRecord(HANDLE))); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -678,5 +663,42 @@ void testDifferentTypes() { assertThrows(UnsupportedOperationException.class, () -> service.createRecords(requests)); } + @Test + void testRollbackHandles() { + // Given + + // When + service.rollbackHandles(List.of(HANDLE)); + + // Then + then(mongoRepository).should().rollbackHandles(List.of(HANDLE)); + } + + @Test + void testRollbackHandlesFromPhysId() { + // Given + + // When + service.rollbackHandlesFromPhysId(List.of(NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL)); + + // Then + then(mongoRepository).should().rollbackHandles(NORMALISED_SPECIMEN_OBJECT_ID.get(), + List.of(NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL)); + } + + @Test + void testInternalDuplicates() { + // Given + var attributes = MAPPER.valueToTree(givenHandleRecordRequestObject()); + var request = givenUpdateRequest(List.of(HANDLE, HANDLE, HANDLE_ALT), FdoType.ORGANISATION, + attributes); + + // When + var e = assertThrows(InvalidRequestException.class, () -> service.updateRecords(request, true)); + + // Then + assertThat(e.getMessage()).contains(HANDLE); + } + } diff --git a/src/test/java/eu/dissco/core/handlemanager/service/PidNameGeneratorServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/PidNameGeneratorServiceTest.java index 1e31c995..cc6825a6 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/PidNameGeneratorServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/PidNameGeneratorServiceTest.java @@ -8,7 +8,7 @@ import static org.mockito.Mockito.lenient; import eu.dissco.core.handlemanager.properties.ApplicationProperties; -import eu.dissco.core.handlemanager.repository.PidRepository; +import eu.dissco.core.handlemanager.repository.MongoRepository; import java.util.ArrayList; import java.util.List; import java.util.Random; @@ -22,7 +22,7 @@ class PidNameGeneratorServiceTest { @Mock - private PidRepository pidRepository; + private MongoRepository mongoRepository; @Mock private Random random; @@ -35,7 +35,8 @@ class PidNameGeneratorServiceTest { @BeforeEach void setup() { - this.pidNameGeneratorService = new PidNameGeneratorService(applicationProperties, pidRepository, + this.pidNameGeneratorService = new PidNameGeneratorService(applicationProperties, + mongoRepository, random); lenient().when(applicationProperties.getMaxHandles()).thenReturn(MAX_HANDLES); } @@ -91,7 +92,7 @@ void testDbCollision() { var expectedHandle1 = PREFIX + "/BBB-BBB-BBB"; var expectedHandle2 = PREFIX + "/ABB-BBB-BBB"; given(random.nextInt(anyInt())).willReturn(0, 1); - given(pidRepository.getExistingHandles(anyList())) + given(mongoRepository.getExistingHandles(anyList())) .willReturn(List.of(expectedHandle1)) .willReturn(new ArrayList<>()); given(applicationProperties.getPrefix()).willReturn(PREFIX); diff --git a/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java b/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java index 166d1190..312b4169 100644 --- a/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java +++ b/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java @@ -74,6 +74,7 @@ import static eu.dissco.core.handlemanager.domain.fdo.FdoType.ANNOTATION; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.DIGITAL_MEDIA; import static eu.dissco.core.handlemanager.domain.fdo.FdoType.DIGITAL_SPECIMEN; +import static eu.dissco.core.handlemanager.domain.fdo.FdoType.TOMBSTONE; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ATTRIBUTES; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_DATA; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ID; @@ -168,7 +169,6 @@ public class TestUtils { // Generated Attributes public static final String PID_STATUS_TESTVAL = PidStatus.ACTIVE.name(); public static final String REFERENT_DOI_NAME_TESTVAL = PREFIX + "/" + SUFFIX; - //DOIs //Digital Specimens public static final String ROR_IDENTIFIER = "0x123"; @@ -217,69 +217,46 @@ private TestUtils() { // Handle Attribute Lists public static FdoRecord givenHandleFdoRecord(String handle) throws Exception { - return new FdoRecord(handle, FdoType.HANDLE, genHandleRecordAttributes(handle), null); + return new FdoRecord(handle, FdoType.HANDLE, + genHandleRecordAttributes(handle, CREATED, FdoType.HANDLE), null); } - public static List genHandleRecordAttributes(String handle) throws Exception { - return genHandleRecordAttributes(handle, FdoType.HANDLE); - } - - public static List genHandleRecordAttributes(String handle, FdoType fdoType) - throws Exception { - return genHandleRecordAttributes(handle, CREATED, fdoType); - } - - public static List genHandleRecordAttributes(String handle, Instant timestamp, FdoType fdoType) throws Exception { List fdoAttributes = new ArrayList<>(); var request = givenHandleRecordRequestObject(); var loc = setLocations(request.getLocations(), handle, fdoType); fdoAttributes.add(new FdoAttribute(LOC, CREATED, loc)); - // 1: FDO Profile fdoAttributes.add(new FdoAttribute(FDO_PROFILE, timestamp, fdoType.getFdoProfile())); - // 2: FDO Record License fdoAttributes.add(new FdoAttribute(FDO_RECORD_LICENSE, timestamp, "https://creativecommons.org/publicdomain/zero/1.0/")); - // 3: DigitalObjectType fdoAttributes.add( new FdoAttribute(DIGITAL_OBJECT_TYPE, timestamp, fdoType.getDigitalObjectType())); - // 4: DigitalObjectName fdoAttributes.add( new FdoAttribute(DIGITAL_OBJECT_NAME, timestamp, fdoType.getDigitalObjectName())); - // 5: Pid fdoAttributes.add(new FdoAttribute(PID, timestamp, fdoType.getDomain() + handle)); - // 6: PidIssuer fdoAttributes.add(new FdoAttribute(PID_ISSUER, timestamp, request.getPidIssuer())); - // 7: pidIssuerName fdoAttributes.add(new FdoAttribute(PID_ISSUER_NAME, timestamp, PID_ISSUER_TESTVAL_OTHER)); - // 8: issuedForAgent fdoAttributes.add(new FdoAttribute(ISSUED_FOR_AGENT, timestamp, request.getIssuedForAgent())); - // 9: issuedForAgentName fdoAttributes.add(new FdoAttribute(ISSUED_FOR_AGENT_NAME, timestamp, ISSUED_FOR_AGENT_TESTVAL)); - // 10: pidRecordIssueDate fdoAttributes.add(new FdoAttribute(PID_RECORD_ISSUE_DATE, timestamp, ISSUE_DATE_TESTVAL)); - // 11: pidRecordIssueNumber fdoAttributes.add(new FdoAttribute(PID_RECORD_ISSUE_NUMBER, timestamp, "1")); - // 12: structuralType fdoAttributes.add( new FdoAttribute(STRUCTURAL_TYPE, timestamp, STRUCTURAL_TYPE_TESTVAL.toString())); - // 13: PidStatus fdoAttributes.add(new FdoAttribute(PID_STATUS, timestamp, PID_STATUS_TESTVAL)); - // 100 ADMIN fdoAttributes.add(new FdoAttribute(timestamp, PREFIX)); @@ -317,39 +294,20 @@ public static FdoRecord givenUpdatedFdoRecord(FdoType fdoType, String primaryLoc return new FdoRecord(HANDLE, fdoType, attributesWithUpdatedTimeStamp, primaryLocalId); } - public static List genUpdateRecordAttributesAltLoc(String handle) - throws ParserConfigurationException, TransformerException { - var locAlt = setLocations(LOC_ALT_TESTVAL, handle, FdoType.HANDLE); - return List.of(new FdoAttribute(LOC, CREATED, locAlt)); - } - public static FdoRecord givenDoiFdoRecord(String handle) throws Exception { - return new FdoRecord(handle, FdoType.DOI, genDoiRecordAttributes(handle, FdoType.DOI), null); - } - - public static List genDoiRecordAttributes(String handle, FdoType type) - throws Exception { - return genDoiRecordAttributes(handle, type, givenDoiRecordRequestObject()); - } - - public static List genDoiRecordAttributes(String handle, FdoType type, - DoiRecordRequest request) throws Exception { - return genDoiRecordAttributes(handle, CREATED, type, request); + return new FdoRecord(handle, FdoType.DOI, + genDoiRecordAttributes(handle, CREATED, FdoType.DOI, givenDoiRecordRequestObject()), null); } public static List genDoiRecordAttributes(String handle, Instant timestamp, FdoType type, DoiRecordRequest request) throws Exception { var fdoRecord = genHandleRecordAttributes(handle, timestamp, type); - // 40: referentType fdoRecord.add(new FdoAttribute(REFERENT_TYPE, timestamp, request.getReferentType())); - // 41: referentDoiName fdoRecord.add(new FdoAttribute(REFERENT_DOI_NAME, timestamp, REFERENT_DOI_NAME_TESTVAL)); - // 42: referentName fdoRecord.add(new FdoAttribute(REFERENT_NAME, timestamp, request.getReferentName())); - // 43: primaryReferentType fdoRecord.add( new FdoAttribute(PRIMARY_REFERENT_TYPE, timestamp, request.getPrimaryReferentType())); @@ -358,16 +316,11 @@ public static List genDoiRecordAttributes(String handle, Instant t public static FdoRecord givenDigitalSpecimenFdoRecord(String handle) throws Exception { return new FdoRecord(handle, FdoType.DIGITAL_SPECIMEN, - genDigitalSpecimenAttributes(handle, givenDigitalSpecimenRequestObjectNullOptionals()), + genDigitalSpecimenAttributes(handle, givenDigitalSpecimenRequestObjectNullOptionals(), + CREATED), NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL); } - public static List genDigitalSpecimenAttributes(String handle, - DigitalSpecimenRequest request) throws Exception { - return genDigitalSpecimenAttributes(handle, request, CREATED); - - } - public static List genDigitalSpecimenAttributes(String handle, DigitalSpecimenRequest request, Instant timestamp) throws Exception { List fdoRecord = genDoiRecordAttributes(handle, timestamp, @@ -559,6 +512,11 @@ public static FdoRecord givenDataMappingFdoRecord(String handle) throws Exceptio return new FdoRecord(handle, FdoType.DATA_MAPPING, genMappingAttributes(handle, CREATED), null); } + public static FdoRecord givenTombstoneFdoRecord() throws Exception { + return new FdoRecord(HANDLE, FdoType.HANDLE, + genTombstoneAttributes(givenTombstoneRecordRequestObject()), null); + } + public static List genAnnotationAttributes(String handle, boolean includeHash) throws Exception { return genAnnotationAttributes(handle, CREATED, includeHash); @@ -797,12 +755,26 @@ public static TombstoneRecordRequest givenTombstoneRecordRequestObject() { List.of(new HasRelatedPid(HANDLE_ALT, "Media ID"))); } - public static JsonApiWrapperRead givenRecordResponseRead(List handles, String path, + // Misc + + public static FdoAttribute getField(List fdoAttributes, FdoProfile targetField) { + for (var attribute : fdoAttributes) { + if (attribute.getIndex() == targetField.index()) { + return attribute; + } + } + log.error("Unable to find field {} in record {}", targetField, fdoAttributes); + throw new IllegalStateException(); + } + + // Json api Responses + + public static JsonApiWrapperRead givenReadResponse(List handles, String path, FdoType recordType, String domain) throws Exception { List dataNodes = new ArrayList<>(); for (String handle : handles) { var testDbRecord = genAttributes(recordType, handle); - JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); + JsonNode recordAttributes = jsonFormatFdoRecord(testDbRecord); var pidLink = new JsonApiLinks(domain + handle); dataNodes.add( new JsonApiDataLinks(handle, recordType.getDigitalObjectType(), recordAttributes, @@ -812,24 +784,24 @@ public static JsonApiWrapperRead givenRecordResponseRead(List handles, S return new JsonApiWrapperRead(responseLink, dataNodes); } - public static JsonApiWrapperWrite givenRecordResponseWriteFullResponse(List handles, - FdoType recordType) throws Exception { + public static JsonApiWrapperWrite givenWriteResponseFull(List handles, + FdoType fdoType) throws Exception { List dataNodes = new ArrayList<>(); for (var handle : handles) { - var testDbRecord = genAttributes(recordType, handle); - JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); - + var testDbRecord = genAttributes(fdoType, handle); + JsonNode recordAttributes = jsonFormatFdoRecord(testDbRecord); var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); + fdoType = fdoType == TOMBSTONE ? FdoType.HANDLE : fdoType; dataNodes.add( - new JsonApiDataLinks(handle, recordType.getDigitalObjectType(), recordAttributes, + new JsonApiDataLinks(handle, fdoType.getDigitalObjectType(), recordAttributes, pidLink)); } return new JsonApiWrapperWrite(dataNodes); } - public static JsonApiWrapperWrite givenRecordResponseWriteFullResponse( + public static JsonApiWrapperWrite givenWriteResponseFull( FdoRecord fdoRecord) { - JsonNode recordAttributes = genObjectNodeAttributeRecord(fdoRecord.attributes()); + JsonNode recordAttributes = jsonFormatFdoRecord(fdoRecord.attributes()); var pidLink = new JsonApiLinks(HANDLE_DOMAIN + HANDLE); var dataNodes = List.of( new JsonApiDataLinks(HANDLE, fdoRecord.fdoType().getDigitalObjectType(), recordAttributes, @@ -837,7 +809,7 @@ public static JsonApiWrapperWrite givenRecordResponseWriteFullResponse( return new JsonApiWrapperWrite(dataNodes); } - public static JsonApiWrapperWrite givenRecordResponseWriteSmallResponse( + public static JsonApiWrapperWrite givenWriteResponseIdsOnly( List fdoRecords, FdoType fdoType, String domain) { List dataNodes = new ArrayList<>(); List fdoSublist; @@ -850,18 +822,14 @@ public static JsonApiWrapperWrite givenRecordResponseWriteSmallResponse( fdoSublist = List.of(getField(fdoRecord.attributes(), ANNOTATION_HASH)); } } - case DIGITAL_SPECIMEN -> { - fdoSublist = List.of(getField(fdoRecord.attributes(), NORMALISED_SPECIMEN_OBJECT_ID)); - } - case DIGITAL_MEDIA -> { - fdoSublist = List.of(getField(fdoRecord.attributes(), PRIMARY_MEDIA_ID), - getField(fdoRecord.attributes(), LINKED_DO_PID)); - } - default -> { - fdoSublist = fdoRecord.attributes(); - } + case DIGITAL_SPECIMEN -> + fdoSublist = List.of(getField(fdoRecord.attributes(), NORMALISED_SPECIMEN_OBJECT_ID)); + case DIGITAL_MEDIA -> + fdoSublist = List.of(getField(fdoRecord.attributes(), PRIMARY_MEDIA_ID), + getField(fdoRecord.attributes(), LINKED_DO_PID)); + default -> fdoSublist = fdoRecord.attributes(); } - var recordAttributes = genObjectNodeAttributeRecord(fdoSublist); + var recordAttributes = jsonFormatFdoRecord(fdoSublist); var pidLink = new JsonApiLinks(domain + fdoRecord.handle()); dataNodes.add( new JsonApiDataLinks(fdoRecord.handle(), fdoType.getDigitalObjectType(), recordAttributes, @@ -870,96 +838,10 @@ public static JsonApiWrapperWrite givenRecordResponseWriteSmallResponse( return new JsonApiWrapperWrite(dataNodes); } - public static FdoAttribute getField(List fdoAttributes, FdoProfile targetField) { - for (var attribute : fdoAttributes) { - if (attribute.getIndex() == targetField.index()) { - return attribute; - } - } - log.error("Unable to find field {} in record {}", targetField, fdoAttributes); - throw new IllegalStateException(); - } - - - public static JsonApiWrapperWrite givenRecordResponseWriteGeneric(List handles, - FdoType fdoType) throws Exception { - List dataNodes = new ArrayList<>(); - - for (var handle : handles) { - var testDbRecord = genAttributes(fdoType, handle); - JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); - - var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); - dataNodes.add(new JsonApiDataLinks(handle, "PID", recordAttributes, pidLink)); - } - return new JsonApiWrapperWrite(dataNodes); - } - - public static JsonApiWrapperWrite givenRecordResponseWrite(List handles, - FdoType attributeType, String recordType) throws Exception { - List dataNodes = new ArrayList<>(); - for (var handle : handles) { - var testDbRecord = genAttributes(attributeType, handle); - JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); - var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); - dataNodes.add(new JsonApiDataLinks(handle, recordType, recordAttributes, pidLink)); - } - return new JsonApiWrapperWrite(dataNodes); - } - - public static JsonApiWrapperWrite givenRecordResponseWriteAltLoc(List handles) - throws Exception { - return givenRecordResponseWriteAltLoc(handles, FdoType.HANDLE); - } - - public static JsonApiWrapperWrite givenRecordResponseWriteAltLoc(List handles, - FdoType recordType) throws Exception { - List dataNodes = new ArrayList<>(); - - for (var handle : handles) { - var testDbRecord = genUpdateRecordAttributesAltLoc(handle); - JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); - - var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); - dataNodes.add( - new JsonApiDataLinks(handle, recordType.getDigitalObjectType(), recordAttributes, - pidLink)); - } - return new JsonApiWrapperWrite(dataNodes); - } - - public static JsonApiWrapperWrite givenRecordResponseNullAttributes(List handles, - FdoType type) { - List dataNodes = new ArrayList<>(); - for (var handle : handles) { - var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); - dataNodes.add(new JsonApiDataLinks(handle, type.getDigitalObjectType(), null, pidLink)); - } - return new JsonApiWrapperWrite(dataNodes); - } - - - public static JsonApiWrapperWrite givenRecordResponseWriteArchive(List handles) - throws Exception { - List dataNodes = new ArrayList<>(); - - for (var handle : handles) { - List testDbRecord = Collections.emptyList(); // todo - JsonNode recordAttributes = genObjectNodeAttributeRecord(testDbRecord); - - var pidLink = new JsonApiLinks(HANDLE_DOMAIN + handle); - dataNodes.add( - new JsonApiDataLinks(handle, FdoType.TOMBSTONE.getDigitalObjectType(), recordAttributes, - pidLink)); - } - return new JsonApiWrapperWrite(dataNodes); - } - public static List genAttributes(FdoType fdoType, String handle) throws Exception { return genAttributes(fdoType, handle, CREATED); } - public static List genAttributes(FdoType fdoType, String handle, Instant timestamp) throws Exception { switch (fdoType) { @@ -988,6 +870,9 @@ public static List genAttributes(FdoType fdoType, String handle, I case MAS -> { return genMasAttributes(handle, timestamp); } + case TOMBSTONE -> { + return genTombstoneAttributes(givenTombstoneRecordRequestObject()); + } default -> { log.warn("Default type"); return genHandleRecordAttributes(handle, timestamp, FdoType.HANDLE); @@ -995,64 +880,38 @@ public static List genAttributes(FdoType fdoType, String handle, I } } - public static List genUpdateRequestBatch(List handles, FdoType type, - JsonNode requestAttributes) { - ObjectMapper mapper = new ObjectMapper(); - ObjectNode requestNodeRoot = mapper.createObjectNode(); - ObjectNode requestNodeData = mapper.createObjectNode(); - List requestNodeList = new ArrayList<>(); - - for (var handle : handles) { - requestNodeData.put("type", type.getDigitalObjectType()); - requestNodeData.put("id", handle); - requestNodeData.set("attributes", requestAttributes); - requestNodeRoot.set("data", requestNodeData); - - requestNodeList.add(requestNodeRoot.deepCopy()); - - requestNodeData.removeAll(); - requestNodeRoot.removeAll(); - } - return requestNodeList; + public static List givenUpdateRequest() { + return givenUpdateRequest(List.of(HANDLE), FdoType.HANDLE, + MAPPER.valueToTree(givenHandleRecordRequestObjectUpdate())); } - public static List genTombstoneRequestBatch(List handles) { - ObjectMapper mapper = new ObjectMapper(); - ObjectNode requestNodeRoot = mapper.createObjectNode(); - ObjectNode requestNodeData = mapper.createObjectNode(); - List requestNodeList = new ArrayList<>(); - - for (String handle : handles) { - requestNodeData.put(NODE_TYPE, FdoType.HANDLE.getDigitalObjectType()); - requestNodeData.put(NODE_ID, handle); - requestNodeData.set(NODE_ATTRIBUTES, genTombstoneRequest()); - requestNodeRoot.set(NODE_DATA, requestNodeData); - - requestNodeList.add(requestNodeRoot.deepCopy()); + public static JsonNode givenUpdateRequestSingle(FdoType fdoType, Object request) { + return givenUpdateRequest(List.of(HANDLE), fdoType, MAPPER.valueToTree(request)).get(0); + } - requestNodeData.removeAll(); - requestNodeRoot.removeAll(); + public static List givenUpdateRequest(List handles, FdoType type, + JsonNode requestAttributes) { + var requestNodeList = new ArrayList(); + for (var handle : handles) { + requestNodeList.add(MAPPER.createObjectNode() + .set(NODE_DATA, MAPPER.createObjectNode() + .put(NODE_TYPE, type.getDigitalObjectType()) + .put(NODE_ID, handle) + .set(NODE_ATTRIBUTES, requestAttributes))); } - return requestNodeList; } - public static JsonNode genUpdateRequestAltLoc() { - ObjectMapper mapper = new ObjectMapper(); - ObjectNode rootNode = mapper.createObjectNode(); - rootNode.putArray("locations").add(LOC_ALT_TESTVAL[0]); - return rootNode; - } - - public static JsonNode genTombstoneRequest() { - ObjectMapper mapper = new ObjectMapper(); - ObjectNode rootNode = mapper.createObjectNode(); - rootNode.put(TOMBSTONED_TEXT.get(), TOMBSTONE_TEXT_TESTVAL); - return rootNode; + public static List givenTombstoneRequest() { + var request = MAPPER.createObjectNode() + .set(NODE_DATA, MAPPER.createObjectNode() + .put(NODE_ID, HANDLE) + .set(NODE_ATTRIBUTES, MAPPER.valueToTree(givenTombstoneRecordRequestObject()))); + return List.of(request); } // Handle Attributes as ObjectNode - public static JsonNode genObjectNodeAttributeRecord(List dbRecord) { + public static JsonNode jsonFormatFdoRecord(List dbRecord) { ObjectMapper mapper = new ObjectMapper(); ObjectNode rootNode = mapper.createObjectNode(); for (var row : dbRecord) { From 1c004d26d789f1b6daf96ebe04fe4e79aa0b6c14 Mon Sep 17 00:00:00 2001 From: southeo Date: Wed, 17 Jul 2024 13:45:19 +0200 Subject: [PATCH 07/15] Remove jsonschema2pojo --- .../repsitoryobjects/HandleAttribute.java | 61 ------------------- .../handlemanager/domain/FdoProfileTest.java | 6 -- .../handlemanager/testUtils/TestUtils.java | 2 +- 3 files changed, 1 insertion(+), 68 deletions(-) delete mode 100644 src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/HandleAttribute.java delete mode 100644 src/test/java/eu/dissco/core/handlemanager/domain/FdoProfileTest.java diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/HandleAttribute.java b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/HandleAttribute.java deleted file mode 100644 index dffc52e4..00000000 --- a/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/HandleAttribute.java +++ /dev/null @@ -1,61 +0,0 @@ -package eu.dissco.core.handlemanager.domain.repsitoryobjects; - -import eu.dissco.core.handlemanager.domain.fdo.FdoProfile; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Objects; -import lombok.Data; - -@Data -public class HandleAttribute { - - private final int index; - private final byte[] handle; - private final String type; - private final byte[] data; - - public HandleAttribute(int index, byte[] handle, String type, byte[] data) { - this.index = index; - this.handle = handle; - this.type = type; - this.data = data; - } - - public HandleAttribute(FdoProfile fdoAttribute, byte[] handle, String data) { - this.index = fdoAttribute.index(); - this.handle = handle; - this.data = data.getBytes(StandardCharsets.UTF_8); - this.type = fdoAttribute.get(); - } - - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - HandleAttribute that = (HandleAttribute) o; - return index == that.index && Objects.equals(type, that.type) - && Arrays.equals(data, that.data); - } - - @Override - public int hashCode() { - int result = Objects.hash(index, type); - result = 31 * result + Arrays.hashCode(data); - return result; - } - - @Override - public String toString() { - return "HandleAttribute{" + - "handle=" + new String(handle, StandardCharsets.UTF_8) + - ", index=" + index + - ", type='" + type + '\'' + - ", data=" + new String(data, StandardCharsets.UTF_8) + - '}'; - } -} \ No newline at end of file diff --git a/src/test/java/eu/dissco/core/handlemanager/domain/FdoProfileTest.java b/src/test/java/eu/dissco/core/handlemanager/domain/FdoProfileTest.java deleted file mode 100644 index 0e560a07..00000000 --- a/src/test/java/eu/dissco/core/handlemanager/domain/FdoProfileTest.java +++ /dev/null @@ -1,6 +0,0 @@ -package eu.dissco.core.handlemanager.domain; - -class FdoProfileTest { - - -} diff --git a/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java b/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java index 312b4169..240895aa 100644 --- a/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java +++ b/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java @@ -96,6 +96,7 @@ import eu.dissco.core.handlemanager.domain.fdo.OrganisationRequest; import eu.dissco.core.handlemanager.domain.fdo.SourceSystemRequest; import eu.dissco.core.handlemanager.domain.fdo.TombstoneRecordRequest; +import eu.dissco.core.handlemanager.domain.fdo.vocabulary.PidStatus; import eu.dissco.core.handlemanager.domain.fdo.vocabulary.annotation.Motivation; import eu.dissco.core.handlemanager.domain.fdo.vocabulary.media.LinkedDigitalObjectType; import eu.dissco.core.handlemanager.domain.fdo.vocabulary.specimen.BaseTypeOfSpecimen; @@ -117,7 +118,6 @@ import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoAttribute; import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoRecord; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; -import eu.dissco.core.handlemanager.schema.Mas.PidStatus; import java.io.IOException; import java.io.StringWriter; import java.nio.charset.StandardCharsets; From 7d4ffcc6d236d5d014e65111c364239673710b1b Mon Sep 17 00:00:00 2001 From: southeo Date: Wed, 17 Jul 2024 13:59:37 +0200 Subject: [PATCH 08/15] trivy --- .github/workflows/.trivyignore | 6 +++--- pom.xml | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/.trivyignore b/.github/workflows/.trivyignore index 546adac9..6d205aed 100644 --- a/.github/workflows/.trivyignore +++ b/.github/workflows/.trivyignore @@ -1,3 +1,3 @@ -# April 17 -# Spring boot needs to update its version of spring -CVE-2024-22262 \ No newline at end of file +# July 17 +# Spring boot needs to update its version of Apache Tomcat +CVE-2024-34750 \ No newline at end of file diff --git a/pom.xml b/pom.xml index ff4abb49..f3b075cf 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ spring-boot-starter-parent org.springframework.boot - 3.2.4 + 3.3.1 handle-manager 0.0.1-SNAPSHOT @@ -28,7 +28,6 @@ https://sonarcloud.io dissco - 6.2.3 4.28.0 From 729e8834b7718f1ce08966970764cc60db6be99a Mon Sep 17 00:00:00 2001 From: southeo Date: Wed, 17 Jul 2024 14:25:37 +0200 Subject: [PATCH 09/15] sonar --- pom.xml | 6 ++-- .../controller/PidController.java | 4 +-- .../vocabulary/specimen/StructuralType.java | 2 +- .../domain/repsitoryobjects/FdoAttribute.java | 4 +-- .../controller/PidControllerTest.java | 10 +++--- .../service/FdoRecordServiceTest.java | 5 --- .../handlemanager/testUtils/TestUtils.java | 36 +++++++++++-------- 7 files changed, 34 insertions(+), 33 deletions(-) diff --git a/pom.xml b/pom.xml index f3b075cf..af20c071 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ spring-boot-starter-parent org.springframework.boot - 3.3.1 + 3.3.1 handle-manager 0.0.1-SNAPSHOT @@ -18,10 +18,10 @@ 17 UTF-8 - 1.17.6 + 1.19.8 4.3 5.2.0 - 4.11.1 + 5.1.1 4.10.0 ../app-it/target/site/jacoco-aggregate/jacoco.xml diff --git a/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java b/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java index 64f8eb04..79aa4185 100644 --- a/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java +++ b/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java @@ -54,7 +54,7 @@ public class PidController { @GetMapping("/{prefix}/{suffix}") public ResponseEntity resolvePid(@PathVariable("prefix") String prefix, @PathVariable("suffix") String suffix, HttpServletRequest r) throws PidResolutionException { - String path = applicationProperties.getUiUrl() + r.getRequestURI(); + String path = applicationProperties.getUiUrl() + "/" + r.getRequestURI(); String handle = prefix + "/" + suffix; if (prefix.equals(applicationProperties.getPrefix())) { @@ -71,7 +71,7 @@ public ResponseEntity resolvePid(@PathVariable("prefix") Str public ResponseEntity resolvePids( @RequestParam List handles, HttpServletRequest r) throws InvalidRequestException { - String path = applicationProperties.getUiUrl() + r.getRequestURI(); + String path = applicationProperties.getUiUrl() + "/" + r.getRequestURI(); if (handles.size() > applicationProperties.getMaxHandles()) { throw new InvalidRequestException( diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/specimen/StructuralType.java b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/specimen/StructuralType.java index 4c561aa8..2f4d8036 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/specimen/StructuralType.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/specimen/StructuralType.java @@ -7,7 +7,7 @@ public enum StructuralType { @JsonProperty("digital") DIGITAL("digital"), @JsonProperty("physical") PHYSICAL("physical"), @JsonProperty("performance") PERFORM("performance"), - @JsonProperty("performance") ABSTRACT("abstraction"); + @JsonProperty("abstraction") ABSTRACT("abstraction"); private final String state; diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoAttribute.java b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoAttribute.java index 9885ed91..6cc63afd 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoAttribute.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoAttribute.java @@ -36,7 +36,7 @@ public FdoAttribute(Instant timestamp, String prefix) { } @JsonCreator - public FdoAttribute(Integer index, String type, Integer ttl, Double timestamp, Document data) { + public FdoAttribute(Integer index, String type, Integer ttl, Instant timestamp, Document data) { this.index = index; this.type = type; var value = data.get("value", Object.class); @@ -50,7 +50,7 @@ public FdoAttribute(Integer index, String type, Integer ttl, Double timestamp, D this.data = new AdminHandleData(prefix); } this.ttl = ttl; - this.timestamp = Instant.ofEpochSecond(timestamp.longValue()); + this.timestamp = timestamp; } @JsonIgnore diff --git a/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java b/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java index 3e0d9cfe..7dfa6944 100644 --- a/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java @@ -11,6 +11,7 @@ import static eu.dissco.core.handlemanager.testUtils.TestUtils.PREFIX; import static eu.dissco.core.handlemanager.testUtils.TestUtils.PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.SUFFIX; +import static eu.dissco.core.handlemanager.testUtils.TestUtils.UI_URL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.genCreateRecordRequest; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenAnnotationRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDataMappingRequestObject; @@ -72,7 +73,6 @@ class PidControllerTest { private PidController controller; - private final String SANDBOX_URI = "https://sandbox.dissco.tech"; @Mock private ApplicationProperties applicationProperties; @@ -84,13 +84,13 @@ void setup() { @Test void testResolveSingleHandle() throws Exception { // Given - String path = SANDBOX_URI + PREFIX + "/" + SUFFIX; + String path = UI_URL + "/" + PREFIX + "/" + SUFFIX; MockHttpServletRequest r = new MockHttpServletRequest(); r.setRequestURI(PREFIX + "/" + SUFFIX); var responseExpected = givenReadResponse(List.of(HANDLE), path, FdoType.HANDLE, HANDLE_DOMAIN); - given(applicationProperties.getUiUrl()).willReturn(SANDBOX_URI); + given(applicationProperties.getUiUrl()).willReturn(UI_URL); given(service.resolveSingleRecord(HANDLE, path)).willReturn(responseExpected); given(applicationProperties.getPrefix()).willReturn(PREFIX); @@ -149,7 +149,7 @@ void testSearchByPhysicalIdCombined() throws Exception { @Test void testResolveBatchHandle() throws Exception { // Given - String path = SANDBOX_URI + "view"; + String path = UI_URL + "/view"; MockHttpServletRequest r = new MockHttpServletRequest(); r.setRequestURI("view"); @@ -157,7 +157,7 @@ void testResolveBatchHandle() throws Exception { var responseExpected = givenReadResponse(handleString, path, FdoType.HANDLE, HANDLE_DOMAIN); - given(applicationProperties.getUiUrl()).willReturn(SANDBOX_URI); + given(applicationProperties.getUiUrl()).willReturn(UI_URL); given(applicationProperties.getMaxHandles()).willReturn(1000); given(service.resolveBatchRecord(anyList(), eq(path))).willReturn(responseExpected); diff --git a/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java index a98d024d..be57caa3 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java @@ -74,15 +74,12 @@ import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.properties.ApplicationProperties; import eu.dissco.core.handlemanager.web.PidResolver; -import java.time.Clock; -import java.time.Instant; import java.util.ArrayList; import java.util.Collections; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; @@ -99,8 +96,6 @@ class FdoRecordServiceTest { private ApplicationProperties applicationProperties; @Mock Environment environment; - private MockedStatic mockedStatic; - private MockedStatic mockedClock; @BeforeEach void init() throws PidResolutionException { diff --git a/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java b/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java index 240895aa..c2b6df24 100644 --- a/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java +++ b/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java @@ -83,7 +83,10 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.node.ObjectNode; +import eu.dissco.core.handlemanager.configuration.InstantDeserializer; +import eu.dissco.core.handlemanager.configuration.InstantSerializer; import eu.dissco.core.handlemanager.domain.fdo.AnnotationRequest; import eu.dissco.core.handlemanager.domain.fdo.DataMappingRequest; import eu.dissco.core.handlemanager.domain.fdo.DigitalMediaRequest; @@ -165,7 +168,6 @@ public class TestUtils { public static final String REFERENT_NAME_TESTVAL = "Bird nest"; public static final String PRIMARY_REFERENT_TYPE_TESTVAL = "materialSample"; - // Generated Attributes public static final String PID_STATUS_TESTVAL = PidStatus.ACTIVE.name(); public static final String REFERENT_DOI_NAME_TESTVAL = PREFIX + "/" + SUFFIX; @@ -180,36 +182,40 @@ public class TestUtils { public static final Motivation MOTIVATION_TESTVAL = Motivation.EDITING; public static final UUID ANNOTATION_HASH_TESTVAL = UUID.fromString( "550e8400-e29b-41d4-a716-446655440000"); - // Media Objects public static final String MEDIA_HOST_TESTVAL = SPECIMEN_HOST_TESTVAL; public static final String MEDIA_HOST_NAME_TESTVAL = SPECIMEN_HOST_NAME_TESTVAL; public static final LinkedDigitalObjectType LINKED_DIGITAL_OBJECT_TYPE_TESTVAL = LinkedDigitalObjectType.SPECIMEN; public static final String LINKED_DO_PID_TESTVAL = HANDLE; - public static final String LICENSE_NAME_TESTVAL = "CC0 1.0 Universal (CC0 1.0) Public Domain Dedication"; public static final String PRIMARY_MEDIA_ID_TESTVAL = "https://images.com/ABC"; // Mappings public static final String SOURCE_DATA_STANDARD_TESTVAL = "dwc"; // MAS public static final String MAS_NAME_TESTVAL = "Plant Organ detection"; - + // Tombstone Record vals + public static final String TOMBSTONE_TEXT_TESTVAL = "pid was deleted"; + // Misc public static final String API_URL = "https://sandbox.dissco.tech/api/v1"; public static final String UI_URL = "https://sandbox.dissco.tech"; public static final String PATH = UI_URL + HANDLE; public static final String ORCHESTRATION_URL = "https://orchestration.dissco.tech/api/v1"; public static final String PTR_TYPE_DOI = "doi"; - public final static String PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL = "BOTANICAL.QRS.123"; - public final static String NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL = + public static final String PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL = "BOTANICAL.QRS.123"; + public static final String NORMALISED_PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL = PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL + ":" + ROR_IDENTIFIER; - public final static String EXTERNAL_PID = "21.T11148/d8de0819e144e4096645"; - - // Tombstone Record vals - public final static String TOMBSTONE_TEXT_TESTVAL = "pid was deleted"; - // Pid Type Record vals - public static ObjectMapper MAPPER = new ObjectMapper().findAndRegisterModules(); - - public static TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance(); - public static DocumentBuilderFactory DOC_BUILDER_FACTORY = DocumentBuilderFactory.newInstance(); + public static final String EXTERNAL_PID = "21.T11148/d8de0819e144e4096645"; + public static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance(); + public static final DocumentBuilderFactory DOC_BUILDER_FACTORY = DocumentBuilderFactory.newInstance(); + public static final ObjectMapper MAPPER; + + static { + var mapper = new ObjectMapper().findAndRegisterModules(); + SimpleModule dateModule = new SimpleModule(); + dateModule.addSerializer(Instant.class, new InstantSerializer()); + dateModule.addDeserializer(Instant.class, new InstantDeserializer()); + mapper.registerModule(dateModule); + MAPPER = mapper; + } private TestUtils() { throw new IllegalStateException("Utility class"); From d23bf661336723897748082c30e308394b3137b9 Mon Sep 17 00:00:00 2001 From: southeo Date: Thu, 18 Jul 2024 09:02:56 +0200 Subject: [PATCH 10/15] sonar --- .../controller/PidController.java | 14 +- .../domain/repsitoryobjects/FdoAttribute.java | 5 +- .../{PutRequest.java => PatchRequest.java} | 2 +- .../domain/requests/PostRequestData.java | 2 +- .../domain/requests/PutRequestData.java | 4 +- .../domain/requests/TombstoneRequest.java | 5 + .../domain/requests/TombstoneRequestData.java | 12 ++ .../validation/JsonSchemaValidator.java | 147 +++++++++--------- .../service/FdoRecordService.java | 136 ++++------------ .../domain/JsonSchemaValidatorTest.java | 2 +- .../handlemanager/testUtils/TestUtils.java | 125 ++++----------- 11 files changed, 174 insertions(+), 280 deletions(-) rename src/main/java/eu/dissco/core/handlemanager/domain/requests/{PutRequest.java => PatchRequest.java} (58%) create mode 100644 src/main/java/eu/dissco/core/handlemanager/domain/requests/TombstoneRequest.java create mode 100644 src/main/java/eu/dissco/core/handlemanager/domain/requests/TombstoneRequestData.java diff --git a/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java b/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java index 79aa4185..a8722e02 100644 --- a/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java +++ b/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java @@ -54,11 +54,11 @@ public class PidController { @GetMapping("/{prefix}/{suffix}") public ResponseEntity resolvePid(@PathVariable("prefix") String prefix, @PathVariable("suffix") String suffix, HttpServletRequest r) throws PidResolutionException { - String path = applicationProperties.getUiUrl() + "/" + r.getRequestURI(); + String link = applicationProperties.getUiUrl() + "/" + r.getRequestURI(); String handle = prefix + "/" + suffix; if (prefix.equals(applicationProperties.getPrefix())) { - var node = service.resolveSingleRecord(handle, path); + var node = service.resolveSingleRecord(handle, link); return ResponseEntity.status(HttpStatus.OK).body(node); } throw new PidResolutionException( @@ -71,7 +71,7 @@ public ResponseEntity resolvePid(@PathVariable("prefix") Str public ResponseEntity resolvePids( @RequestParam List handles, HttpServletRequest r) throws InvalidRequestException { - String path = applicationProperties.getUiUrl() + "/" + r.getRequestURI(); + String link = applicationProperties.getUiUrl() + "/" + r.getRequestURI(); if (handles.size() > applicationProperties.getMaxHandles()) { throw new InvalidRequestException( @@ -79,7 +79,7 @@ public ResponseEntity resolvePids( + applicationProperties.getMaxHandles()); } - return ResponseEntity.status(HttpStatus.OK).body(service.resolveBatchRecord(handles, path)); + return ResponseEntity.status(HttpStatus.OK).body(service.resolveBatchRecord(handles, link)); } @Operation(summary = "Given a physical identifier (i.e. local identifier), resolve PID record") @@ -119,7 +119,7 @@ public ResponseEntity updateRecord(@PathVariable("prefix") Authentication authentication) throws InvalidRequestException, UnprocessableEntityException { log.info("Received single update request for PID {}/{} from user {}", prefix, suffix, authentication.getName()); - schemaValidator.validatePutRequest(request); + schemaValidator.validatePatchRequest(request); var handle = (prefix + "/" + suffix); var handleData = request.get(NODE_DATA).get(NODE_ID).asText(); if (!handle.equals(handleData)) { @@ -137,7 +137,7 @@ public ResponseEntity updateRecords(@RequestBody List rollbackHandleUpdate( log.info("Validating rollback update request from user {}", authentication.getName()); for (JsonNode request : requests) { - schemaValidator.validatePutRequest(request); + schemaValidator.validatePatchRequest(request); } var handles = requests.stream().map(r -> r.get(NODE_DATA).get(NODE_ID).asText()) .limit(LOG_LIMIT).toList(); diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoAttribute.java b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoAttribute.java index 6cc63afd..18655358 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoAttribute.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/repsitoryobjects/FdoAttribute.java @@ -19,11 +19,12 @@ public class FdoAttribute { int ttl; Instant timestamp; - public FdoAttribute(FdoProfile fdoAttribute, Instant timestamp, String value) { + public FdoAttribute(FdoProfile fdoAttribute, Instant timestamp, Object value) { this.index = fdoAttribute.index(); this.type = fdoAttribute.get(); this.timestamp = timestamp; - this.data = new StringHandleData(value); + value = value == null ? "" : value; + this.data = new StringHandleData(value.toString()); this.ttl = 86400; } diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/requests/PutRequest.java b/src/main/java/eu/dissco/core/handlemanager/domain/requests/PatchRequest.java similarity index 58% rename from src/main/java/eu/dissco/core/handlemanager/domain/requests/PutRequest.java rename to src/main/java/eu/dissco/core/handlemanager/domain/requests/PatchRequest.java index 0b51d4f2..081dc237 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/requests/PutRequest.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/requests/PatchRequest.java @@ -2,6 +2,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; -public record PutRequest(@JsonProperty(required = true) PutRequestData data) { +public record PatchRequest(@JsonProperty(required = true) PutRequestData data) { } diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/requests/PostRequestData.java b/src/main/java/eu/dissco/core/handlemanager/domain/requests/PostRequestData.java index bfd85bf6..18ad8881 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/requests/PostRequestData.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/requests/PostRequestData.java @@ -6,7 +6,7 @@ import eu.dissco.core.handlemanager.domain.fdo.FdoType; public record PostRequestData( - @JsonProperty(required = true) + //@JsonProperty(required = true) @JsonPropertyDescription("type of object") FdoType type, @JsonProperty(required = true) diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/requests/PutRequestData.java b/src/main/java/eu/dissco/core/handlemanager/domain/requests/PutRequestData.java index 100e2296..1fca81a3 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/requests/PutRequestData.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/requests/PutRequestData.java @@ -3,9 +3,11 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; import com.fasterxml.jackson.databind.JsonNode; +import eu.dissco.core.handlemanager.domain.fdo.FdoType; public record PutRequestData( @JsonProperty(required = true) @JsonPropertyDescription("DiSSCo Identifier of object") String id, - @JsonProperty(required = true) JsonNode attributes) { + @JsonProperty(required = true) JsonNode attributes, + FdoType type) { } diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/requests/TombstoneRequest.java b/src/main/java/eu/dissco/core/handlemanager/domain/requests/TombstoneRequest.java new file mode 100644 index 00000000..2eb6de11 --- /dev/null +++ b/src/main/java/eu/dissco/core/handlemanager/domain/requests/TombstoneRequest.java @@ -0,0 +1,5 @@ +package eu.dissco.core.handlemanager.domain.requests; + +public record TombstoneRequest(TombstoneRequestData data) { + +} diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/requests/TombstoneRequestData.java b/src/main/java/eu/dissco/core/handlemanager/domain/requests/TombstoneRequestData.java new file mode 100644 index 00000000..5bc52403 --- /dev/null +++ b/src/main/java/eu/dissco/core/handlemanager/domain/requests/TombstoneRequestData.java @@ -0,0 +1,12 @@ +package eu.dissco.core.handlemanager.domain.requests; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.fasterxml.jackson.databind.JsonNode; + +public record TombstoneRequestData( + @JsonProperty(required = true) @JsonPropertyDescription("DiSSCo Identifier of object") String id, + @JsonProperty(required = true) JsonNode attributes +) { + +} diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/validation/JsonSchemaValidator.java b/src/main/java/eu/dissco/core/handlemanager/domain/validation/JsonSchemaValidator.java index 260d9396..bafbf895 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/validation/JsonSchemaValidator.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/validation/JsonSchemaValidator.java @@ -1,5 +1,6 @@ package eu.dissco.core.handlemanager.domain.validation; +import static eu.dissco.core.handlemanager.domain.fdo.FdoType.TOMBSTONE; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_ATTRIBUTES; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_DATA; import static eu.dissco.core.handlemanager.domain.jsonapi.JsonApiFields.NODE_TYPE; @@ -28,8 +29,9 @@ import eu.dissco.core.handlemanager.domain.fdo.OrganisationRequest; import eu.dissco.core.handlemanager.domain.fdo.SourceSystemRequest; import eu.dissco.core.handlemanager.domain.fdo.TombstoneRecordRequest; +import eu.dissco.core.handlemanager.domain.requests.PatchRequest; import eu.dissco.core.handlemanager.domain.requests.PostRequest; -import eu.dissco.core.handlemanager.domain.requests.PutRequest; +import eu.dissco.core.handlemanager.domain.requests.TombstoneRequest; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import jakarta.validation.constraints.NotEmpty; import java.util.Arrays; @@ -46,31 +48,33 @@ public class JsonSchemaValidator { // JsonNodes private JsonNode postReqJsonNode; + private JsonNode patchReqJsonNode; private JsonNode putReqJsonNode; - private JsonNode handlePostReqJsonNode; - private JsonNode doiPostReqJsonNode; - private JsonNode digitalSpecimenPostReqJsonNode; - private JsonNode digitalMediaPostReqJsonNode; + private JsonNode handleJsonNode; + private JsonNode doiJsonNode; + private JsonNode digitalSpecimenJsonNode; + private JsonNode digitalMediaJsonNode; private JsonNode tombstoneReqJsonNode; - private JsonNode annotationPostReqJsonNode; - private JsonNode mappingPostReqJsonNode; - private JsonNode sourceSystemPostReqJsonNode; - private JsonNode organisationPostReqJsonNode; - private JsonNode masPostReqJsonNode; + private JsonNode annotationJsonNode; + private JsonNode mappingJsonNode; + private JsonNode sourceSystemJsonNode; + private JsonNode organisationJsonNode; + private JsonNode masJsonNode; // Schemas private JsonSchema postReqSchema; + private JsonSchema patchReqSchema; private JsonSchema putReqSchema; - private JsonSchema handlePostReqSchema; - private JsonSchema doiPostReqSchema; - private JsonSchema digitalSpecimenPostReqSchema; - private JsonSchema digitalMediaPostReqSchema; + private JsonSchema handleSchema; + private JsonSchema doiSchema; + private JsonSchema digitalSpecimenSchema; + private JsonSchema digitalMediaSchema; private JsonSchema tombstoneReqSchema; - private JsonSchema annotationPostReqSchema; - private JsonSchema mappingPostReqSchema; - private JsonSchema sourceSystemPostReqSchema; - private JsonSchema organisationPostReqSchema; - private JsonSchema masPostReqSchema; + private JsonSchema annotationSchema; + private JsonSchema dataMappingSchema; + private JsonSchema sourceSystemSchema; + private JsonSchema organisationSchema; + private JsonSchema masSchema; public JsonSchemaValidator() { setPostRequestAttributesJsonNodes(); @@ -80,16 +84,16 @@ public JsonSchemaValidator() { private void setPostRequestAttributesJsonNodes() { var schemaGenerator = new SchemaGenerator(jacksonModuleSchemaConfig()); - handlePostReqJsonNode = schemaGenerator.generateSchema(HandleRecordRequest.class); - doiPostReqJsonNode = schemaGenerator.generateSchema(DoiRecordRequest.class); - digitalSpecimenPostReqJsonNode = schemaGenerator.generateSchema(DigitalSpecimenRequest.class); - digitalMediaPostReqJsonNode = schemaGenerator.generateSchema(DigitalMediaRequest.class); + handleJsonNode = schemaGenerator.generateSchema(HandleRecordRequest.class); + doiJsonNode = schemaGenerator.generateSchema(DoiRecordRequest.class); + digitalSpecimenJsonNode = schemaGenerator.generateSchema(DigitalSpecimenRequest.class); + digitalMediaJsonNode = schemaGenerator.generateSchema(DigitalMediaRequest.class); tombstoneReqJsonNode = schemaGenerator.generateSchema(TombstoneRecordRequest.class); - annotationPostReqJsonNode = schemaGenerator.generateSchema(AnnotationRequest.class); - mappingPostReqJsonNode = schemaGenerator.generateSchema(DataMappingRequest.class); - sourceSystemPostReqJsonNode = schemaGenerator.generateSchema(SourceSystemRequest.class); - organisationPostReqJsonNode = schemaGenerator.generateSchema(OrganisationRequest.class); - masPostReqJsonNode = schemaGenerator.generateSchema(MasRequest.class); + annotationJsonNode = schemaGenerator.generateSchema(AnnotationRequest.class); + mappingJsonNode = schemaGenerator.generateSchema(DataMappingRequest.class); + sourceSystemJsonNode = schemaGenerator.generateSchema(SourceSystemRequest.class); + organisationJsonNode = schemaGenerator.generateSchema(OrganisationRequest.class); + masJsonNode = schemaGenerator.generateSchema(MasRequest.class); } private SchemaGeneratorConfig jacksonModuleSchemaConfig() { @@ -132,7 +136,8 @@ private SchemaGeneratorConfig jacksonModuleSchemaConfig() { private void setRequestJsonNodes() { var schemaGenerator = new SchemaGenerator(requestSchemaConfig()); postReqJsonNode = schemaGenerator.generateSchema(PostRequest.class); - putReqJsonNode = schemaGenerator.generateSchema(PutRequest.class); + patchReqJsonNode = schemaGenerator.generateSchema(PatchRequest.class); + putReqJsonNode = schemaGenerator.generateSchema(TombstoneRequest.class); } public SchemaGeneratorConfig requestSchemaConfig() { @@ -163,74 +168,72 @@ public SchemaGeneratorConfig requestSchemaConfig() { private void setJsonSchemas() { JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - postReqSchema = factory.getSchema(postReqJsonNode); + patchReqSchema = factory.getSchema(patchReqJsonNode); putReqSchema = factory.getSchema(putReqJsonNode); - - handlePostReqSchema = factory.getSchema(handlePostReqJsonNode); - doiPostReqSchema = factory.getSchema(doiPostReqJsonNode); - digitalSpecimenPostReqSchema = factory.getSchema(digitalSpecimenPostReqJsonNode); - digitalMediaPostReqSchema = factory.getSchema(digitalMediaPostReqJsonNode); - annotationPostReqSchema = factory.getSchema(annotationPostReqJsonNode); - mappingPostReqSchema = factory.getSchema(mappingPostReqJsonNode); - sourceSystemPostReqSchema = factory.getSchema(sourceSystemPostReqJsonNode); - organisationPostReqSchema = factory.getSchema(organisationPostReqJsonNode); - masPostReqSchema = factory.getSchema(masPostReqJsonNode); + handleSchema = factory.getSchema(handleJsonNode); + doiSchema = factory.getSchema(doiJsonNode); + digitalSpecimenSchema = factory.getSchema(digitalSpecimenJsonNode); + digitalMediaSchema = factory.getSchema(digitalMediaJsonNode); + annotationSchema = factory.getSchema(annotationJsonNode); + dataMappingSchema = factory.getSchema(mappingJsonNode); + sourceSystemSchema = factory.getSchema(sourceSystemJsonNode); + organisationSchema = factory.getSchema(organisationJsonNode); + masSchema = factory.getSchema(masJsonNode); tombstoneReqSchema = factory.getSchema(tombstoneReqJsonNode); - } public void validatePostRequest(JsonNode requestRoot) throws InvalidRequestException { var validationErrors = postReqSchema.validate(requestRoot); if (!validationErrors.isEmpty()) { - throw new InvalidRequestException(setErrorMessage(validationErrors, "POST")); + throw new InvalidRequestException(setErrorMessage(validationErrors, "CREATE")); } + var fdoType = FdoType.fromString(requestRoot.get(NODE_DATA).get(NODE_TYPE).asText()); + validateAttributes(requestRoot, fdoType); + } - FdoType type = FdoType.fromString(requestRoot.get(NODE_DATA).get(NODE_TYPE).asText()); - var attributes = requestRoot.get(NODE_DATA).get(NODE_ATTRIBUTES); - switch (type) { - case HANDLE -> validateRequestAttributes(attributes, handlePostReqSchema, type); - case DOI -> validateRequestAttributes(attributes, doiPostReqSchema, type); - case DIGITAL_SPECIMEN -> - validateRequestAttributes(attributes, digitalSpecimenPostReqSchema, type); - case DIGITAL_MEDIA -> validateRequestAttributes(attributes, digitalMediaPostReqSchema, type); - case ANNOTATION -> validateRequestAttributes(attributes, annotationPostReqSchema, type); - case DATA_MAPPING -> validateRequestAttributes(attributes, mappingPostReqSchema, type); - case SOURCE_SYSTEM -> validateRequestAttributes(attributes, sourceSystemPostReqSchema, type); - case ORGANISATION -> validateRequestAttributes(attributes, organisationPostReqSchema, type); - case MAS -> validateRequestAttributes(attributes, masPostReqSchema, type); - default -> - throw new InvalidRequestException("Invalid Request. Reason: Invalid type: " + type); + public void validatePatchRequest(JsonNode requestRoot) throws InvalidRequestException { + var validationErrors = patchReqSchema.validate(requestRoot); + if (!validationErrors.isEmpty()) { + throw new InvalidRequestException( + setErrorMessage(validationErrors, "UPDATE")); } + var fdoType = FdoType.fromString(requestRoot.get(NODE_DATA).get(NODE_TYPE).asText()); + validateAttributes(requestRoot, fdoType); } public void validatePutRequest(JsonNode requestRoot) throws InvalidRequestException { var validationErrors = putReqSchema.validate(requestRoot); if (!validationErrors.isEmpty()) { throw new InvalidRequestException( - setErrorMessage(validationErrors, "PUT (tombstone)")); + setErrorMessage(validationErrors, "TOMBSTONE")); } - var attributes = requestRoot.get(NODE_DATA).get(NODE_ATTRIBUTES); - validateTombstoneRequestAttributes(attributes); + validateAttributes(requestRoot, TOMBSTONE); } - - private void validateTombstoneRequestAttributes(JsonNode requestAttributes) + private void validateAttributes(JsonNode requestRoot, FdoType fdoType) throws InvalidRequestException { - var validationErrors = tombstoneReqSchema.validate(requestAttributes); - if (!validationErrors.isEmpty()) { - throw new InvalidRequestException( - setErrorMessage(validationErrors, FdoType.TOMBSTONE.getDigitalObjectName(), - requestAttributes)); + var requestAttributes = requestRoot.get(NODE_DATA).get(NODE_ATTRIBUTES); + JsonSchema schema; + switch (fdoType) { + case HANDLE -> schema = handleSchema; + case DOI -> schema = doiSchema; + case DIGITAL_SPECIMEN -> schema = digitalSpecimenSchema; + case DIGITAL_MEDIA -> schema = digitalMediaSchema; + case ANNOTATION -> schema = annotationSchema; + case DATA_MAPPING -> schema = dataMappingSchema; + case SOURCE_SYSTEM -> schema = sourceSystemSchema; + case ORGANISATION -> schema = organisationSchema; + case MAS -> schema = masSchema; + case TOMBSTONE -> schema = tombstoneReqSchema; + default -> + throw new InvalidRequestException("Invalid Request. Reason: Invalid type: " + fdoType); } - } - - private void validateRequestAttributes(JsonNode requestAttributes, JsonSchema schema, - FdoType type) throws InvalidRequestException { var validationErrors = schema.validate(requestAttributes); if (!validationErrors.isEmpty()) { throw new InvalidRequestException( - setErrorMessage(validationErrors, type.getDigitalObjectName(), requestAttributes)); + setErrorMessage(validationErrors, fdoType.getDigitalObjectName(), + requestAttributes)); } } diff --git a/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java b/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java index 36ca9afa..db0312e9 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java @@ -252,8 +252,7 @@ private ArrayList prepareHandleAttributesFromRequest( getObjectName(request.getIssuedForAgent(), null))); // 12: Structural Type handleAttributeList.add( - new FdoAttribute(STRUCTURAL_TYPE, timestamp, - request.getStructuralType().toString())); + new FdoAttribute(STRUCTURAL_TYPE, timestamp, request.getStructuralType())); return handleAttributeList; } @@ -366,17 +365,10 @@ private List prepareAnnotationAttributesFromRequest( handleAttributeList.add( new FdoAttribute(TARGET_TYPE, timestamp, request.getTargetType())); // 502 Motivation - handleAttributeList.add(new FdoAttribute(MOTIVATION, timestamp, - request.getMotivation().toString())); + handleAttributeList.add(new FdoAttribute(MOTIVATION, timestamp, request.getMotivation())); // 503 Annotation Hash - if (request.getAnnotationHash() != null) { - handleAttributeList.add(new FdoAttribute(ANNOTATION_HASH, timestamp, - request.getAnnotationHash().toString())); - } else { - handleAttributeList.add(new FdoAttribute(ANNOTATION_HASH, timestamp, - null)); - } - + handleAttributeList.add( + new FdoAttribute(ANNOTATION_HASH, timestamp, request.getAnnotationHash())); return handleAttributeList; } @@ -463,75 +455,33 @@ private List prepareDigitalSpecimenAttributesFromRequest( null)); } // 208 Topic Origin - if (request.getTopicOrigin() != null) { - handleAttributeList.add( - new FdoAttribute(TOPIC_ORIGIN, timestamp, request.getTopicOrigin().toString())); - } else { - handleAttributeList.add(new FdoAttribute(TOPIC_ORIGIN, timestamp, null)); - } + handleAttributeList.add(new FdoAttribute(TOPIC_ORIGIN, timestamp, request.getTopicOrigin())); // 209 Topic Domain - if (request.getTopicDomain() != null) { - handleAttributeList.add( - new FdoAttribute(TOPIC_DOMAIN, timestamp, request.getTopicDomain().toString())); - } else { - handleAttributeList.add(new FdoAttribute(TOPIC_DOMAIN, timestamp, null)); - } + handleAttributeList.add( + new FdoAttribute(TOPIC_DOMAIN, timestamp, request.getTopicDomain())); // 210 Topic Discipline - if (request.getTopicDiscipline() != null) { - handleAttributeList.add(new FdoAttribute(TOPIC_DISCIPLINE, timestamp, - request.getTopicDiscipline().toString())); - } else { - handleAttributeList.add(new FdoAttribute(TOPIC_DISCIPLINE, timestamp, null)); - } + handleAttributeList.add(new FdoAttribute(TOPIC_DISCIPLINE, timestamp, + request.getTopicDiscipline())); // 211 Topic Category - if (request.getTopicCategory() != null) { - handleAttributeList.add(new FdoAttribute(TOPIC_CATEGORY, timestamp, - request.getTopicCategory().toString())); - } else { - handleAttributeList.add(new FdoAttribute(TOPIC_CATEGORY, timestamp, null)); - } + handleAttributeList.add(new FdoAttribute(TOPIC_CATEGORY, timestamp, + request.getTopicCategory())); // 212 Living or Preserved - if (request.getLivingOrPreserved() != null) { - handleAttributeList.add(new FdoAttribute(LIVING_OR_PRESERVED, timestamp, - request.getLivingOrPreserved().toString())); - } else { - handleAttributeList.add(new FdoAttribute(LIVING_OR_PRESERVED, timestamp, null)); - } + handleAttributeList.add(new FdoAttribute(LIVING_OR_PRESERVED, timestamp, + request.getLivingOrPreserved())); // 213 Base Type of Specimen - if (request.getBaseTypeOfSpecimen() != null) { - handleAttributeList.add(new FdoAttribute(BASE_TYPE_OF_SPECIMEN, timestamp, - request.getBaseTypeOfSpecimen().toString())); - } else { - handleAttributeList.add(new FdoAttribute(BASE_TYPE_OF_SPECIMEN, timestamp, null)); - } + handleAttributeList.add(new FdoAttribute(BASE_TYPE_OF_SPECIMEN, timestamp, + request.getBaseTypeOfSpecimen())); // 214 Information Artefact Type - if (request.getInformationArtefactType() != null) { - handleAttributeList.add(new FdoAttribute(INFORMATION_ARTEFACT_TYPE, timestamp, - request.getInformationArtefactType().toString())); - } else { - handleAttributeList.add(new FdoAttribute(INFORMATION_ARTEFACT_TYPE, timestamp, null)); - } + handleAttributeList.add(new FdoAttribute(INFORMATION_ARTEFACT_TYPE, timestamp, + request.getInformationArtefactType())); // 215 Material Sample Type - if (request.getMaterialSampleType() != null) { - handleAttributeList.add(new FdoAttribute(MATERIAL_SAMPLE_TYPE, timestamp, - request.getMaterialSampleType().toString())); - } else { - handleAttributeList.add(new FdoAttribute(MATERIAL_SAMPLE_TYPE, timestamp, null)); - } + handleAttributeList.add(new FdoAttribute(MATERIAL_SAMPLE_TYPE, timestamp, + request.getMaterialSampleType())); // 216 Material or Digital Entity - if (request.getMaterialOrDigitalEntity() != null) { - handleAttributeList.add(new FdoAttribute(MATERIAL_OR_DIGITAL_ENTITY, timestamp, - request.getMaterialOrDigitalEntity().toString())); - } else { - handleAttributeList.add(new FdoAttribute(MATERIAL_OR_DIGITAL_ENTITY, timestamp, null)); - } + handleAttributeList.add(new FdoAttribute(MATERIAL_OR_DIGITAL_ENTITY, timestamp, + request.getMaterialOrDigitalEntity())); // 217 Marked as Type - if (request.getMarkedAsType() != null) { - handleAttributeList.add(new FdoAttribute(MARKED_AS_TYPE, timestamp, - String.valueOf(request.getMarkedAsType()))); - } else { - handleAttributeList.add(new FdoAttribute(MARKED_AS_TYPE, timestamp, null)); - } + handleAttributeList.add(new FdoAttribute(MARKED_AS_TYPE, timestamp, request.getMarkedAsType())); // 218 Was Derived From Entity handleAttributeList.add( new FdoAttribute(WAS_DERIVED_FROM_ENTITY, timestamp, @@ -606,8 +556,7 @@ private List prepareDigitalMediaAttributesFromRequest( new FdoAttribute(LINKED_DO_PID, timestamp, request.getLinkedDigitalObjectPid())); // 405 Linked Digital Object Type handleAttributeList.add( - new FdoAttribute(LINKED_DO_TYPE, timestamp, - request.getLinkedDigitalObjectType().toString())); + new FdoAttribute(LINKED_DO_TYPE, timestamp, request.getLinkedDigitalObjectType())); // 406 Linked Attribute handleAttributeList.add( new FdoAttribute(LINKED_ATTRIBUTE, timestamp, request.getLinkedAttribute())); @@ -615,38 +564,23 @@ private List prepareDigitalMediaAttributesFromRequest( handleAttributeList.add( new FdoAttribute(PRIMARY_MEDIA_ID, timestamp, request.getPrimaryMediaId())); // 408 Primary Media Object Id Type - if (request.getPrimaryMediaObjectIdType() != null) { - handleAttributeList.add( - new FdoAttribute(PRIMARY_MO_ID_TYPE, timestamp, - request.getPrimaryMediaObjectIdType().toString())); - } else { - handleAttributeList.add( - new FdoAttribute(PRIMARY_MO_ID_TYPE, timestamp, null)); - } + handleAttributeList.add( + new FdoAttribute(PRIMARY_MO_ID_TYPE, timestamp, + request.getPrimaryMediaObjectIdType())); // 409 Primary Media Object Id Name handleAttributeList.add( new FdoAttribute(PRIMARY_MO_ID_NAME, timestamp, request.getPrimaryMediaObjectIdName())); // 410 dcterms:type - if (request.getDcTermsType() != null) { - handleAttributeList.add( - new FdoAttribute(DCTERMS_TYPE, timestamp, request.getDcTermsType().toString())); - } else { - handleAttributeList.add( - new FdoAttribute(DCTERMS_TYPE, timestamp, null)); - } + handleAttributeList.add( + new FdoAttribute(DCTERMS_TYPE, timestamp, request.getDcTermsType())); // 411 dcterms:subject handleAttributeList.add( new FdoAttribute(DCTERMS_SUBJECT, timestamp, request.getDctermsSubject())); // 412 dcterms:format - if (request.getDctermsFormat() != null) { - handleAttributeList.add( - new FdoAttribute(DCTERMS_FORMAT, timestamp, - request.getDctermsFormat().toString())); - } else { - handleAttributeList.add( - new FdoAttribute(DCTERMS_FORMAT, timestamp, null)); - } + handleAttributeList.add( + new FdoAttribute(DCTERMS_FORMAT, timestamp, + request.getDctermsFormat())); // 413 Derived from Entity handleAttributeList.add( new FdoAttribute(DERIVED_FROM_ENTITY, timestamp, request.getDerivedFromEntity())); @@ -662,12 +596,8 @@ private List prepareDigitalMediaAttributesFromRequest( handleAttributeList.add( new FdoAttribute(RIGHTSHOLDER_PID, timestamp, request.getRightsholderPid())); // 418 RightsholderPidType - if (request.getRightsholderPidType() != null) { - handleAttributeList.add(new FdoAttribute(RIGHTSHOLDER_PID_TYPE, timestamp, - request.getRightsholderPidType().toString())); - } else { - handleAttributeList.add(new FdoAttribute(RIGHTSHOLDER_PID_TYPE, timestamp, null)); - } + handleAttributeList.add(new FdoAttribute(RIGHTSHOLDER_PID_TYPE, timestamp, + request.getRightsholderPidType())); // 419 dcterms:conformsTo handleAttributeList.add( new FdoAttribute(DC_TERMS_CONFORMS, timestamp, request.getDctermsConformsTo())); diff --git a/src/test/java/eu/dissco/core/handlemanager/domain/JsonSchemaValidatorTest.java b/src/test/java/eu/dissco/core/handlemanager/domain/JsonSchemaValidatorTest.java index 309ebff6..3aedcc1c 100644 --- a/src/test/java/eu/dissco/core/handlemanager/domain/JsonSchemaValidatorTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/domain/JsonSchemaValidatorTest.java @@ -304,7 +304,7 @@ void testBadArchiveRequest() { // When Exception e = assertThrowsExactly(InvalidRequestException.class, - () -> schemaValidator.validatePutRequest(request)); + () -> schemaValidator.validatePatchRequest(request)); // Then assertThat(e.getMessage()).contains(MISSING_MSG).contains(NODE_ID); diff --git a/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java b/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java index c2b6df24..cf8a0c18 100644 --- a/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java +++ b/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java @@ -260,7 +260,7 @@ public static List genHandleRecordAttributes(String handle, Instan fdoAttributes.add(new FdoAttribute(PID_RECORD_ISSUE_NUMBER, timestamp, "1")); // 12: structuralType fdoAttributes.add( - new FdoAttribute(STRUCTURAL_TYPE, timestamp, STRUCTURAL_TYPE_TESTVAL.toString())); + new FdoAttribute(STRUCTURAL_TYPE, timestamp, STRUCTURAL_TYPE_TESTVAL)); // 13: PidStatus fdoAttributes.add(new FdoAttribute(PID_STATUS, timestamp, PID_STATUS_TESTVAL)); // 100 ADMIN @@ -340,7 +340,7 @@ public static List genDigitalSpecimenAttributes(String handle, request.getPrimarySpecimenObjectId())); // 203: primarySpecimenObjectIdType fdoRecord.add(new FdoAttribute(PRIMARY_SPECIMEN_OBJECT_ID_TYPE, timestamp, - request.getPrimarySpecimenObjectIdType().toString())); + request.getPrimarySpecimenObjectIdType())); // 204: primarySpecimenObjectIdName fdoRecord.add(new FdoAttribute(PRIMARY_SPECIMEN_OBJECT_ID_NAME, timestamp, request.getPrimarySpecimenObjectIdName())); @@ -358,73 +358,33 @@ public static List genDigitalSpecimenAttributes(String handle, fdoRecord.add(new FdoAttribute(OTHER_SPECIMEN_IDS, timestamp, null)); } // 208: topicOrigin - if (request.getTopicOrigin() != null) { - fdoRecord.add(new FdoAttribute(TOPIC_ORIGIN, timestamp, request.getTopicOrigin().toString())); - } else { - fdoRecord.add(new FdoAttribute(TOPIC_ORIGIN, timestamp, null)); - } + fdoRecord.add(new FdoAttribute(TOPIC_ORIGIN, timestamp, request.getTopicOrigin())); // 209: topicDomain - if (request.getTopicDomain() != null) { - fdoRecord.add(new FdoAttribute(TOPIC_DOMAIN, timestamp, request.getTopicDomain().toString())); - } else { - fdoRecord.add(new FdoAttribute(TOPIC_DOMAIN, timestamp, null)); - } + fdoRecord.add(new FdoAttribute(TOPIC_DOMAIN, timestamp, request.getTopicDomain())); // 210: topicDiscipline - if (request.getTopicDiscipline() != null) { - fdoRecord.add( - new FdoAttribute(TOPIC_DISCIPLINE, timestamp, request.getTopicDiscipline().toString())); - } else { - fdoRecord.add(new FdoAttribute(TOPIC_DISCIPLINE, timestamp, null)); - } + fdoRecord.add( + new FdoAttribute(TOPIC_DISCIPLINE, timestamp, request.getTopicDiscipline())); // 211: topicCategory - if (request.getTopicCategory() != null) { - fdoRecord.add( - new FdoAttribute(TOPIC_CATEGORY, timestamp, request.getTopicCategory().toString())); - } else { - fdoRecord.add(new FdoAttribute(TOPIC_CATEGORY, timestamp, null)); - } + fdoRecord.add( + new FdoAttribute(TOPIC_CATEGORY, timestamp, request.getTopicCategory())); // 212: livingOrPreserved - if (request.getLivingOrPreserved() != null) { - fdoRecord.add(new FdoAttribute(LIVING_OR_PRESERVED, timestamp, - request.getLivingOrPreserved().toString())); - } else { - fdoRecord.add(new FdoAttribute(LIVING_OR_PRESERVED, timestamp, null)); - } + fdoRecord.add(new FdoAttribute(LIVING_OR_PRESERVED, timestamp, + request.getLivingOrPreserved())); // 213: baseTypeOfSpecimen - if (request.getBaseTypeOfSpecimen() != null) { - fdoRecord.add(new FdoAttribute(BASE_TYPE_OF_SPECIMEN, timestamp, - request.getBaseTypeOfSpecimen().toString())); - } else { - fdoRecord.add(new FdoAttribute(BASE_TYPE_OF_SPECIMEN, timestamp, null)); - } + fdoRecord.add(new FdoAttribute(BASE_TYPE_OF_SPECIMEN, timestamp, + request.getBaseTypeOfSpecimen())); // 214: informationArtefactType - if (request.getInformationArtefactType() != null) { - fdoRecord.add(new FdoAttribute(INFORMATION_ARTEFACT_TYPE, timestamp, - request.getInformationArtefactType().toString())); - } else { - fdoRecord.add(new FdoAttribute(INFORMATION_ARTEFACT_TYPE, timestamp, null)); - } + fdoRecord.add(new FdoAttribute(INFORMATION_ARTEFACT_TYPE, timestamp, + request.getInformationArtefactType())); // 215: materialSampleType - if (request.getMaterialSampleType() != null) { - fdoRecord.add(new FdoAttribute(MATERIAL_SAMPLE_TYPE, timestamp, - request.getMaterialSampleType().toString())); - } else { - fdoRecord.add(new FdoAttribute(MATERIAL_SAMPLE_TYPE, timestamp, null)); - } + fdoRecord.add(new FdoAttribute(MATERIAL_SAMPLE_TYPE, timestamp, + request.getMaterialSampleType())); // 216: materialOrDigitalEntity - if (request.getMaterialOrDigitalEntity() != null) { - fdoRecord.add(new FdoAttribute(MATERIAL_OR_DIGITAL_ENTITY, timestamp, - request.getMaterialOrDigitalEntity().toString())); - } else { - fdoRecord.add(new FdoAttribute(MATERIAL_OR_DIGITAL_ENTITY, timestamp, null)); - } + fdoRecord.add(new FdoAttribute(MATERIAL_OR_DIGITAL_ENTITY, timestamp, + request.getMaterialOrDigitalEntity())); // 217: markedAsType - if (request.getMarkedAsType() != null) { - fdoRecord.add( - new FdoAttribute(MARKED_AS_TYPE, timestamp, request.getMarkedAsType().toString())); - } else { - fdoRecord.add(new FdoAttribute(MARKED_AS_TYPE, timestamp, null)); - } + fdoRecord.add( + new FdoAttribute(MARKED_AS_TYPE, timestamp, request.getMarkedAsType())); // 218: wasDerivedFromEntity fdoRecord.add(new FdoAttribute(WAS_DERIVED_FROM_ENTITY, timestamp, String.valueOf(request.getDerivedFromEntity() != null))); @@ -447,48 +407,29 @@ public static List genDigitalMediaAttributes(String handle, } else { fdoRecord.add(new FdoAttribute(MEDIA_HOST_NAME, timestamp, request.getMediaHostName())); } - if (request.getDctermsFormat() != null) { - fdoRecord.add( - new FdoAttribute(DCTERMS_FORMAT, timestamp, request.getDctermsFormat().toString())); - } else { - fdoRecord.add(new FdoAttribute(DCTERMS_FORMAT, timestamp, null)); - } + fdoRecord.add( + new FdoAttribute(DCTERMS_FORMAT, timestamp, request.getDctermsFormat())); fdoRecord.add(new FdoAttribute(IS_DERIVED_FROM_SPECIMEN, timestamp, - request.getIsDerivedFromSpecimen().toString())); + request.getIsDerivedFromSpecimen())); fdoRecord.add(new FdoAttribute(LINKED_DO_PID, timestamp, request.getLinkedDigitalObjectPid())); - if (request.getLinkedDigitalObjectType() != null) { - fdoRecord.add(new FdoAttribute(LINKED_DO_TYPE, timestamp, - request.getLinkedDigitalObjectType().toString())); - } else { - fdoRecord.add(new FdoAttribute(LINKED_DO_TYPE, timestamp, null)); - } + fdoRecord.add(new FdoAttribute(LINKED_DO_TYPE, timestamp, + request.getLinkedDigitalObjectType())); + fdoRecord.add(new FdoAttribute(LINKED_ATTRIBUTE, timestamp, request.getLinkedAttribute())); fdoRecord.add(new FdoAttribute(PRIMARY_MEDIA_ID, timestamp, request.getPrimaryMediaId())); - if (request.getPrimaryMediaObjectIdType() != null) { - fdoRecord.add(new FdoAttribute(PRIMARY_MO_ID_TYPE, timestamp, - request.getPrimaryMediaObjectIdType().toString())); - } else { - fdoRecord.add(new FdoAttribute(PRIMARY_MO_ID_TYPE, timestamp, null)); - } + fdoRecord.add( + new FdoAttribute(PRIMARY_MO_ID_TYPE, timestamp, request.getPrimaryMediaObjectIdType())); fdoRecord.add( new FdoAttribute(PRIMARY_MO_ID_NAME, timestamp, request.getPrimaryMediaObjectIdName())); - if (request.getDcTermsType() != null) { - fdoRecord.add(new FdoAttribute(DCTERMS_TYPE, timestamp, request.getDcTermsType().toString())); - } else { - fdoRecord.add(new FdoAttribute(DCTERMS_TYPE, timestamp, null)); - } + fdoRecord.add(new FdoAttribute(DCTERMS_TYPE, timestamp, request.getDcTermsType())); fdoRecord.add(new FdoAttribute(DCTERMS_SUBJECT, timestamp, request.getDctermsSubject())); fdoRecord.add(new FdoAttribute(DERIVED_FROM_ENTITY, timestamp, request.getDerivedFromEntity())); fdoRecord.add(new FdoAttribute(LICENSE_NAME, timestamp, request.getLicenseName())); fdoRecord.add(new FdoAttribute(LICENSE_URL, timestamp, request.getLicenseUrl())); fdoRecord.add(new FdoAttribute(RIGHTSHOLDER_NAME, timestamp, request.getRightsholderName())); fdoRecord.add(new FdoAttribute(RIGHTSHOLDER_PID, timestamp, request.getRightsholderPid())); - if (request.getRightsholderPidType() != null) { - fdoRecord.add(new FdoAttribute(RIGHTSHOLDER_PID_TYPE, timestamp, - request.getRightsholderPidType().toString())); - } else { - fdoRecord.add(new FdoAttribute(RIGHTSHOLDER_PID_TYPE, timestamp, null)); - } + fdoRecord.add(new FdoAttribute(RIGHTSHOLDER_PID_TYPE, timestamp, + request.getRightsholderPidType())); fdoRecord.add(new FdoAttribute(DC_TERMS_CONFORMS, timestamp, request.getDctermsConformsTo())); return fdoRecord; } @@ -536,11 +477,11 @@ public static List genAnnotationAttributes(String handle, Instant // 501 TargetType fdoRecord.add(new FdoAttribute(TARGET_TYPE, timestamp, TARGET_TYPE_TESTVAL)); // 502 motivation - fdoRecord.add(new FdoAttribute(MOTIVATION, timestamp, MOTIVATION_TESTVAL.toString())); + fdoRecord.add(new FdoAttribute(MOTIVATION, timestamp, MOTIVATION_TESTVAL)); // 503 AnnotationHash if (includeHash) { fdoRecord.add( - new FdoAttribute(ANNOTATION_HASH, timestamp, ANNOTATION_HASH_TESTVAL.toString())); + new FdoAttribute(ANNOTATION_HASH, timestamp, ANNOTATION_HASH_TESTVAL)); } else { fdoRecord.add(new FdoAttribute(ANNOTATION_HASH, timestamp, null)); } From 157372c68d2793b2a4cd6647fcff1edf7a14165b Mon Sep 17 00:00:00 2001 From: southeo Date: Thu, 18 Jul 2024 09:19:04 +0200 Subject: [PATCH 11/15] Update pidStatus to TOMBSTONED --- .../dissco/core/handlemanager/service/FdoRecordService.java | 3 +++ .../java/eu/dissco/core/handlemanager/service/PidService.java | 2 +- .../core/handlemanager/service/FdoRecordServiceTest.java | 4 ++-- .../eu/dissco/core/handlemanager/testUtils/TestUtils.java | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java b/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java index db0312e9..ebb4c9d1 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java @@ -673,7 +673,10 @@ private List prepareTombstoneAttributes(TombstoneRecordRequest req var handleAttributeList = new ArrayList<>(previousVersion.attributes()); var previousIssueNum = getField(previousVersion.attributes(), PID_RECORD_ISSUE_NUMBER); var newIssueNum = incrementIssueNumber(previousIssueNum, timestamp); + var previousStatus = getField(previousVersion.attributes(), PID_STATUS); + var newStatus = new FdoAttribute(PID_STATUS, timestamp, PidStatus.TOMBSTONED); handleAttributeList.set(handleAttributeList.indexOf(previousIssueNum), newIssueNum); + handleAttributeList.set(handleAttributeList.indexOf(previousStatus), newStatus); // 30: Tombstoned Text handleAttributeList.add( new FdoAttribute(TOMBSTONED_TEXT, timestamp, request.getTombstonedText())); diff --git a/src/main/java/eu/dissco/core/handlemanager/service/PidService.java b/src/main/java/eu/dissco/core/handlemanager/service/PidService.java index a61c5439..8196c455 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/PidService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/PidService.java @@ -245,7 +245,7 @@ protected void checkHandlesWritable(List previousVersions) var pidStatus = getField(fdoRecord.attributes(), PID_STATUS).getValue(); if (PidStatus.TOMBSTONED.name().equals(pidStatus)) { log.error("Attempting to update a FDO record that has been archived"); - throw new InvalidRequestException(); + throw new InvalidRequestException("This PID has already been archived. It is read-only"); } } } diff --git a/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java index be57caa3..7119efe2 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java @@ -495,7 +495,7 @@ void testPrepareUpdatedOrganisationRecord() throws Exception { void testPrepareTombstoneRecordNoRelatedIds() throws Exception { var previousVersion = givenHandleFdoRecord(HANDLE); var request = new TombstoneRecordRequest(TOMBSTONE_TEXT_TESTVAL, null); - var expected = new FdoRecord(HANDLE, FdoType.HANDLE.HANDLE, genTombstoneAttributes(request), + var expected = new FdoRecord(HANDLE, FdoType.HANDLE, genTombstoneAttributes(request), null); // When @@ -512,7 +512,7 @@ void testPrepareTombstoneRecordNoRelatedIds() throws Exception { void testPrepareTombstoneRecordEmptyRelatedIds() throws Exception { var previousVersion = givenHandleFdoRecord(HANDLE); var request = new TombstoneRecordRequest(TOMBSTONE_TEXT_TESTVAL, Collections.emptyList()); - var expected = new FdoRecord(HANDLE, FdoType.HANDLE.HANDLE, genTombstoneAttributes(request), + var expected = new FdoRecord(HANDLE, FdoType.HANDLE, genTombstoneAttributes(request), null); // When diff --git a/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java b/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java index cf8a0c18..b7441cba 100644 --- a/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java +++ b/src/test/java/eu/dissco/core/handlemanager/testUtils/TestUtils.java @@ -529,7 +529,8 @@ public static List genTombstoneAttributes(TombstoneRecordRequest r fdoRecord.add(new FdoAttribute(TOMBSTONED_TEXT, UPDATED, request.getTombstonedText())); fdoRecord.set(fdoRecord.indexOf(new FdoAttribute(PID_RECORD_ISSUE_NUMBER, CREATED, "1")), new FdoAttribute(PID_RECORD_ISSUE_NUMBER, UPDATED, "2")); - + fdoRecord.set(fdoRecord.indexOf(new FdoAttribute(PID_STATUS, CREATED, PidStatus.ACTIVE)), + new FdoAttribute(PID_STATUS, UPDATED, PidStatus.TOMBSTONED)); // 31: hasRelatedPID if (request.getHasRelatedPID() != null && !request.getHasRelatedPID().isEmpty()) { fdoRecord.add(new FdoAttribute(HAS_RELATED_PID, UPDATED, From f22b98b70666e8c2e03d3128543aa49e55429a3a Mon Sep 17 00:00:00 2001 From: southeo Date: Thu, 18 Jul 2024 09:19:28 +0200 Subject: [PATCH 12/15] Update pidStatus to TOMBSTONED --- .../core/handlemanager/domain/fdo/vocabulary/PidStatus.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/PidStatus.java b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/PidStatus.java index 37e16293..5404ee99 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/PidStatus.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/fdo/vocabulary/PidStatus.java @@ -4,5 +4,6 @@ public enum PidStatus { TOMBSTONED, ACTIVE, DRAFT, + FAILED, TEST } From 1a7b0f565969736265302cad779751731780d176 Mon Sep 17 00:00:00 2001 From: southeo Date: Thu, 18 Jul 2024 09:23:11 +0200 Subject: [PATCH 13/15] Update pidStatus to TOMBSTONED --- .../handlemanager/exceptions/InvalidRequestException.java | 4 ---- .../eu/dissco/core/handlemanager/service/HandleService.java | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/eu/dissco/core/handlemanager/exceptions/InvalidRequestException.java b/src/main/java/eu/dissco/core/handlemanager/exceptions/InvalidRequestException.java index 1d82a299..061409cb 100644 --- a/src/main/java/eu/dissco/core/handlemanager/exceptions/InvalidRequestException.java +++ b/src/main/java/eu/dissco/core/handlemanager/exceptions/InvalidRequestException.java @@ -8,8 +8,4 @@ public InvalidRequestException(String s) { super(s); } - public InvalidRequestException() { - super(); - } - } diff --git a/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java b/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java index c17e9554..9533033b 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java @@ -111,7 +111,7 @@ public JsonApiWrapperWrite updateRecords(List requests, boolean increm return new JsonApiWrapperWrite(formatFdoRecord(fdoRecords, fdoType)); } catch (JsonProcessingException e) { log.error("An error has occurred processing JSON data", e); - throw new InvalidRequestException(); + throw new InvalidRequestException("Unable to parse FDO Record"); } } From 0c385d6209ff271adcf5bb494eaa60a2d7b7b829 Mon Sep 17 00:00:00 2001 From: southeo Date: Thu, 18 Jul 2024 10:07:43 +0200 Subject: [PATCH 14/15] format --- .../eu/dissco/core/handlemanager/properties/MongoProperties.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/eu/dissco/core/handlemanager/properties/MongoProperties.java b/src/main/java/eu/dissco/core/handlemanager/properties/MongoProperties.java index f2e21f1a..fcd53cf0 100644 --- a/src/main/java/eu/dissco/core/handlemanager/properties/MongoProperties.java +++ b/src/main/java/eu/dissco/core/handlemanager/properties/MongoProperties.java @@ -16,5 +16,4 @@ public class MongoProperties { @NotBlank private String database; - } From 28678d9ffc96247018236d4d62b12bedb4fa34ac Mon Sep 17 00:00:00 2001 From: southeo Date: Fri, 19 Jul 2024 11:57:38 +0200 Subject: [PATCH 15/15] code reivw --- pom.xml | 2 +- .../FdoAttributeDeserializer.java | 19 ----------------- .../domain/requests/PostRequestData.java | 1 - .../handlemanager/service/DoiService.java | 2 +- .../handlemanager/service/HandleService.java | 2 +- .../service/PidNameGeneratorService.java | 4 ++-- .../handlemanager/service/PidService.java | 1 - .../repository/MongoRepositoryIT.java | 2 +- .../handlemanager/service/DoiServiceTest.java | 7 ++++--- .../service/HandleServiceTest.java | 21 ++++++++++--------- .../service/PidNameGeneratorServiceTest.java | 19 +++++++++-------- 11 files changed, 31 insertions(+), 49 deletions(-) delete mode 100644 src/main/java/eu/dissco/core/handlemanager/configuration/FdoAttributeDeserializer.java diff --git a/pom.xml b/pom.xml index af20c071..4ea42527 100644 --- a/pom.xml +++ b/pom.xml @@ -23,12 +23,12 @@ 5.2.0 5.1.1 4.10.0 + 4.35.0 ../app-it/target/site/jacoco-aggregate/jacoco.xml https://sonarcloud.io dissco - 4.28.0 diff --git a/src/main/java/eu/dissco/core/handlemanager/configuration/FdoAttributeDeserializer.java b/src/main/java/eu/dissco/core/handlemanager/configuration/FdoAttributeDeserializer.java deleted file mode 100644 index b5562247..00000000 --- a/src/main/java/eu/dissco/core/handlemanager/configuration/FdoAttributeDeserializer.java +++ /dev/null @@ -1,19 +0,0 @@ -package eu.dissco.core.handlemanager.configuration; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import eu.dissco.core.handlemanager.domain.repsitoryobjects.FdoAttribute; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class FdoAttributeDeserializer extends JsonDeserializer { - - - @Override - public FdoAttribute deserialize(JsonParser jsonParser, - DeserializationContext deserializationContext) { - return null; - } - -} diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/requests/PostRequestData.java b/src/main/java/eu/dissco/core/handlemanager/domain/requests/PostRequestData.java index 18ad8881..ecafde3e 100644 --- a/src/main/java/eu/dissco/core/handlemanager/domain/requests/PostRequestData.java +++ b/src/main/java/eu/dissco/core/handlemanager/domain/requests/PostRequestData.java @@ -6,7 +6,6 @@ import eu.dissco.core.handlemanager.domain.fdo.FdoType; public record PostRequestData( - //@JsonProperty(required = true) @JsonPropertyDescription("type of object") FdoType type, @JsonProperty(required = true) diff --git a/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java b/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java index 6a4d6066..731c8477 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java @@ -44,7 +44,7 @@ public DoiService(FdoRecordService fdoRecordService, @Override public JsonApiWrapperWrite createRecords(List requests) throws InvalidRequestException, UnprocessableEntityException { - var handles = hf.genHandleList(requests.size()).iterator(); + var handles = hf.generateNewHandles(requests.size()).iterator(); var requestAttributes = requests.stream() .map(request -> request.get(NODE_DATA).get(NODE_ATTRIBUTES)).toList(); var type = getObjectTypeFromJsonNode(requests); diff --git a/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java b/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java index 9533033b..29c95674 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java @@ -48,7 +48,7 @@ public HandleService(FdoRecordService fdoRecordService, // Pid Record Creation @Override public JsonApiWrapperWrite createRecords(List requests) throws InvalidRequestException { - var handles = hf.genHandleList(requests.size()).iterator(); + var handles = hf.generateNewHandles(requests.size()).iterator(); var requestAttributes = requests.stream() .map(request -> request.get(NODE_DATA).get(NODE_ATTRIBUTES)).toList(); var fdoType = getObjectTypeFromJsonNode(requests); diff --git a/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java b/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java index c8cc3689..126aa912 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/PidNameGeneratorService.java @@ -28,13 +28,13 @@ public class PidNameGeneratorService { private final Random random; - public List genHandleList(int h) { + public Set generateNewHandles(int h) { if (h > applicationProperties.getMaxHandles()) { log.warn("Max number of handles exceeded. Generating maximum {} handles instead", applicationProperties.getMaxHandles()); h = applicationProperties.getMaxHandles(); } - return new ArrayList<>(genHandleHashSet(h)); + return genHandleHashSet(h); } private Set genHandleHashSet(int h) { diff --git a/src/main/java/eu/dissco/core/handlemanager/service/PidService.java b/src/main/java/eu/dissco/core/handlemanager/service/PidService.java index 8196c455..24deab6e 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/PidService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/PidService.java @@ -353,7 +353,6 @@ private void verifyObjectsAreNew(List normalisedIds) var existingHandles = mongoRepository .searchByPrimaryLocalId(NORMALISED_SPECIMEN_OBJECT_ID.get(), normalisedIds); if (!existingHandles.isEmpty()) { - log.error("Unable to create new handles, as "); var handleMap = existingHandles.stream() .collect(Collectors.toMap( FdoRecord::handle, diff --git a/src/test/java/eu/dissco/core/handlemanager/repository/MongoRepositoryIT.java b/src/test/java/eu/dissco/core/handlemanager/repository/MongoRepositoryIT.java index 3b10e786..d57156ba 100644 --- a/src/test/java/eu/dissco/core/handlemanager/repository/MongoRepositoryIT.java +++ b/src/test/java/eu/dissco/core/handlemanager/repository/MongoRepositoryIT.java @@ -42,7 +42,7 @@ class MongoRepositoryIT { private static final String MEDIA_ID = PREFIX + "/MEDIA"; private static final DockerImageName MONGODB = - DockerImageName.parse("mongo:6.0.4"); + DockerImageName.parse("mongo:7.0.12"); @Container private static final MongoDBContainer CONTAINER = new MongoDBContainer(MONGODB); diff --git a/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java index de44fc99..1a7ad4f2 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java @@ -43,6 +43,7 @@ import java.time.Instant; import java.time.ZoneOffset; import java.util.List; +import java.util.Set; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -103,7 +104,7 @@ void testCreateDigitalSpecimen() throws Exception { FdoType.DIGITAL_SPECIMEN, DOI_DOMAIN); var dataCiteEvent = new DataCiteEvent(jsonFormatFdoRecord(fdoRecord.attributes()), EventType.CREATE); - given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(pidNameGeneratorService.generateNewHandles(1)).willReturn(Set.of(HANDLE)); given(fdoRecordService.prepareNewDigitalSpecimenRecord(any(), any(), any())).willReturn( fdoRecord); given(profileProperties.getDomain()).willReturn(DOI_DOMAIN); @@ -125,7 +126,7 @@ void testCreateDigitalMedia() throws Exception { FdoType.DIGITAL_MEDIA, DOI_DOMAIN); var dataCiteEvent = new DataCiteEvent(jsonFormatFdoRecord(digitalMedia.attributes()), EventType.CREATE); - given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(pidNameGeneratorService.generateNewHandles(1)).willReturn(Set.of(HANDLE)); given(fdoRecordService.prepareNewDigitalMediaRecord(any(), any(), any())).willReturn( digitalMedia); given(profileProperties.getDomain()).willReturn(DOI_DOMAIN); @@ -145,7 +146,7 @@ void testCreateDigitalSpecimenDataCiteFails() throws Exception { (JsonNode) genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), FdoType.DIGITAL_SPECIMEN)); var digitalSpecimen = givenDigitalSpecimenFdoRecord(HANDLE); - given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(pidNameGeneratorService.generateNewHandles(1)).willReturn(Set.of(HANDLE)); given(fdoRecordService.prepareNewDigitalSpecimenRecord(any(), any(), any())).willReturn( digitalSpecimen); doThrow(JsonProcessingException.class).when(dataCiteService).publishToDataCite(any(), any()); diff --git a/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java index 2de08f25..db6d85b8 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java @@ -72,6 +72,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Set; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -133,7 +134,7 @@ void testCreateAnnotationNoHash() throws Exception { var request = genCreateRecordRequest(givenAnnotationRequestObject(), FdoType.ANNOTATION); var fdoRecord = givenAnnotationFdoRecord(HANDLE, false); var expected = TestUtils.givenWriteResponseFull(List.of(HANDLE), FdoType.ANNOTATION); - given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(pidNameGeneratorService.generateNewHandles(1)).willReturn(Set.of(HANDLE)); given(fdoRecordService.prepareNewAnnotationRecord(any(), any(), any())).willReturn(fdoRecord); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -150,7 +151,7 @@ void testCreateAnnotationIncludeHash() throws Exception { var fdoRecord = givenAnnotationFdoRecord(HANDLE, true); var expected = givenWriteResponseIdsOnly(List.of(fdoRecord), FdoType.ANNOTATION, HANDLE_DOMAIN); - given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(pidNameGeneratorService.generateNewHandles(1)).willReturn(Set.of(HANDLE)); given(fdoRecordService.prepareNewAnnotationRecord(any(), any(), any())).willReturn(fdoRecord); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -192,7 +193,7 @@ void testCreateDigitalSpecimen() throws Exception { var fdoRecord = givenDigitalSpecimenFdoRecord(HANDLE); var expected = givenWriteResponseIdsOnly(List.of(fdoRecord), FdoType.DIGITAL_SPECIMEN, HANDLE_DOMAIN); - given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(pidNameGeneratorService.generateNewHandles(1)).willReturn(Set.of(HANDLE)); given(fdoRecordService.prepareNewDigitalSpecimenRecord(any(), any(), any())).willReturn( fdoRecord); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -246,7 +247,7 @@ void testCreateDoi() throws Exception { var request = genCreateRecordRequest(givenDoiRecordRequestObject(), FdoType.DOI); var fdoRecord = givenDoiFdoRecord(HANDLE); var expected = TestUtils.givenWriteResponseFull(List.of(HANDLE), FdoType.DOI); - given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(pidNameGeneratorService.generateNewHandles(1)).willReturn(Set.of(HANDLE)); given(fdoRecordService.prepareNewDoiRecord(any(), any(), any(), any())).willReturn(fdoRecord); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -284,7 +285,7 @@ void testCreateHandle() throws Exception { var request = genCreateRecordRequest(givenHandleRecordRequestObject(), FdoType.HANDLE); var fdoRecord = givenHandleFdoRecord(HANDLE); var expected = TestUtils.givenWriteResponseFull(List.of(HANDLE), FdoType.HANDLE); - given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(pidNameGeneratorService.generateNewHandles(1)).willReturn(Set.of(HANDLE)); given(fdoRecordService.prepareNewHandleRecord(any(), any(), any(), any())).willReturn( fdoRecord); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -360,7 +361,7 @@ void testCreateDataMapping() throws Exception { var request = genCreateRecordRequest(givenDataMappingRequestObject(), FdoType.DATA_MAPPING); var fdoRecord = givenDataMappingFdoRecord(HANDLE); var expected = TestUtils.givenWriteResponseFull(List.of(HANDLE), FdoType.DATA_MAPPING); - given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(pidNameGeneratorService.generateNewHandles(1)).willReturn(Set.of(HANDLE)); given(fdoRecordService.prepareNewDataMappingRecord(any(), any(), any())).willReturn(fdoRecord); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -398,7 +399,7 @@ void testCreateMas() throws Exception { var request = genCreateRecordRequest(givenMasRecordRequestObject(), FdoType.MAS); var fdoRecord = givenMasFdoRecord(HANDLE); var expected = TestUtils.givenWriteResponseFull(List.of(HANDLE), FdoType.MAS); - given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(pidNameGeneratorService.generateNewHandles(1)).willReturn(Set.of(HANDLE)); given(fdoRecordService.prepareNewMasRecord(any(), any(), any())).willReturn(fdoRecord); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -437,7 +438,7 @@ void testCreateDigitalMedia() throws Exception { var fdoRecord = givenDigitalMediaFdoRecord(HANDLE); var expected = givenWriteResponseIdsOnly(List.of(fdoRecord), FdoType.DIGITAL_MEDIA, HANDLE_DOMAIN); - given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(pidNameGeneratorService.generateNewHandles(1)).willReturn(Set.of(HANDLE)); given(fdoRecordService.prepareNewDigitalMediaRecord(any(), any(), any())).willReturn(fdoRecord); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -476,7 +477,7 @@ void testCreateSourceSystem() throws Exception { var request = genCreateRecordRequest(givenSourceSystemRequestObject(), FdoType.SOURCE_SYSTEM); var fdoRecord = givenSourceSystemFdoRecord(HANDLE); var expected = TestUtils.givenWriteResponseFull(List.of(HANDLE), FdoType.SOURCE_SYSTEM); - given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(pidNameGeneratorService.generateNewHandles(1)).willReturn(Set.of(HANDLE)); given(fdoRecordService.prepareNewSourceSystemRecord(any(), any(), any())).willReturn(fdoRecord); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -514,7 +515,7 @@ void testCreateOrganisation() throws Exception { var request = genCreateRecordRequest(givenOrganisationRequestObject(), FdoType.ORGANISATION); var fdoRecord = givenOrganisationFdoRecord(HANDLE); var expected = TestUtils.givenWriteResponseFull(List.of(HANDLE), FdoType.ORGANISATION); - given(pidNameGeneratorService.genHandleList(1)).willReturn(List.of(HANDLE)); + given(pidNameGeneratorService.generateNewHandles(1)).willReturn(Set.of(HANDLE)); given(fdoRecordService.prepareNewOrganisationRecord(any(), any(), any())).willReturn(fdoRecord); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); diff --git a/src/test/java/eu/dissco/core/handlemanager/service/PidNameGeneratorServiceTest.java b/src/test/java/eu/dissco/core/handlemanager/service/PidNameGeneratorServiceTest.java index cc6825a6..0ff597bb 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/PidNameGeneratorServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/PidNameGeneratorServiceTest.java @@ -12,6 +12,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; +import java.util.Set; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -44,27 +45,27 @@ void setup() { @Test void testSingleBatchGen() { // Given - String expectedHandle = PREFIX + "/AAA-AAA-AAA"; + var expected = Set.of(PREFIX + "/AAA-AAA-AAA"); given(random.nextInt(anyInt())).willReturn(0); given(applicationProperties.getPrefix()).willReturn(PREFIX); // When - String generatedHandle = pidNameGeneratorService.genHandleList(1).get(0); + var result = pidNameGeneratorService.generateNewHandles(1); // Then - assertThat(generatedHandle).isEqualTo(expectedHandle); + assertThat(result).isEqualTo(expected); } @Test void testBatchGen() { // Given - var expected = List.of(PREFIX + "/ABB-BBB-BBB", PREFIX + "/BBB-BBB-BBB"); + var expected = Set.of(PREFIX + "/ABB-BBB-BBB", PREFIX + "/BBB-BBB-BBB"); given(random.nextInt(anyInt())).willReturn(0, 1); given(applicationProperties.getPrefix()).willReturn(PREFIX); // When - var handleList = pidNameGeneratorService.genHandleList(2); + var handleList = pidNameGeneratorService.generateNewHandles(2); // Then assertThat(handleList).isEqualTo(expected); @@ -72,7 +73,7 @@ void testBatchGen() { @Test void testInternalCollision() { - var expected = List.of(PREFIX + "/AAA-AAA-AAA", PREFIX + "/BBB-BBB-BBB"); + var expected = Set.of(PREFIX + "/AAA-AAA-AAA", PREFIX + "/BBB-BBB-BBB"); given(random.nextInt(anyInt())).willReturn(0, 0, 0, 0, 0, 0, 0, 0, 0)// First .willReturn(0, 0, 0, 0, 0, 0, 0, 0, 0) // Collision @@ -80,7 +81,7 @@ void testInternalCollision() { given(applicationProperties.getPrefix()).willReturn(PREFIX); // When - var result = pidNameGeneratorService.genHandleList(2); + var result = pidNameGeneratorService.generateNewHandles(2); // Then assertThat(expected).isEqualTo(result); @@ -98,7 +99,7 @@ void testDbCollision() { given(applicationProperties.getPrefix()).willReturn(PREFIX); // When - var result = pidNameGeneratorService.genHandleList(2); + var result = pidNameGeneratorService.generateNewHandles(2); // Then assertThat(result).hasSameElementsAs(List.of(expectedHandle1, expectedHandle2)); @@ -107,7 +108,7 @@ void testDbCollision() { @Test void testInvalidNumberOfHandles() { // When - var tooFew = pidNameGeneratorService.genHandleList(-1); + var tooFew = pidNameGeneratorService.generateNewHandles(-1); // Then assertThat(tooFew).isEmpty();