From 9466907a4b558005e830a3b9905ff2c00ba12518 Mon Sep 17 00:00:00 2001 From: southeo Date: Mon, 4 Dec 2023 10:54:46 +0100 Subject: [PATCH] Feature/performance improvements (#71) * Batch copy * Testing * Assume atomic action * Trivy * Remove unused libraries * Leave refs null * Code review * Test fix * separate specimen only endpoint * separate creation of handles for different object types * separate creation of handles for different object types * Test fix * Test * Remove upsert endpoint * Code review * trivy * fix docker * Update trivy --- .github/workflows/.trivyignore | 12 +- Dockerfile | 2 +- pom.xml | 5 +- .../controller/PidController.java | 31 -- .../requests/UpsertDigitalSpecimen.java | 11 - .../UnprocessableEntityException.java | 4 +- .../handlemanager/service/DoiService.java | 83 ++--- .../service/FdoRecordService.java | 66 ++-- .../handlemanager/service/HandleService.java | 221 +++++++------ .../handlemanager/service/PidService.java | 295 +++++++----------- .../core/handlemanager/web/PidResolver.java | 18 +- .../component/PidResolverTest.java | 9 +- .../controller/PidControllerTest.java | 25 -- .../handlemanager/service/DoiServiceTest.java | 11 +- .../service/FdoRecordServiceTest.java | 60 ++-- .../service/HandleServiceTest.java | 179 +---------- 16 files changed, 349 insertions(+), 683 deletions(-) delete mode 100644 src/main/java/eu/dissco/core/handlemanager/domain/requests/UpsertDigitalSpecimen.java diff --git a/.github/workflows/.trivyignore b/.github/workflows/.trivyignore index 599e90bb..d1ae95ef 100644 --- a/.github/workflows/.trivyignore +++ b/.github/workflows/.trivyignore @@ -1,8 +1,4 @@ -# Date: Nov 14, 2023 -# Notes: Issue with openssl. May need to upgrade alpine image. -CVE-2023-5363 -CVE-2023-5678 - -# Date: Nov 21, 2023 -# Notes: Issue with netty. Spring boot needs to update -CVE-2023-34062 \ No newline at end of file +# Date: Dec 1, 2023 +# Notes: Issue with logback. Spring boot needs to update its version +# Currently forcing logback version +# CVE-2023-6378 diff --git a/Dockerfile b/Dockerfile index 88dd6fdf..dbb3b145 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,4 +16,4 @@ RUN true COPY --chown=java:java --from=builder application/application/ ./ USER 1000 -ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"] \ No newline at end of file +ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"] \ No newline at end of file diff --git a/pom.xml b/pom.xml index e18168c6..ffd7bdef 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ spring-boot-starter-parent org.springframework.boot - 3.1.5 + 3.2.0 handle-manager 0.0.1-SNAPSHOT @@ -19,14 +19,13 @@ 17 UTF-8 1.17.6 - 4.28.0 4.3 5.2.0 4.10.0 + 1.4.12 ../app-it/target/site/jacoco-aggregate/jacoco.xml - 2.0 https://sonarcloud.io dissco 4.28.0 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 1f921795..f13d5f31 100644 --- a/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java +++ b/src/main/java/eu/dissco/core/handlemanager/controller/PidController.java @@ -1,20 +1,15 @@ package eu.dissco.core.handlemanager.controller; -import static eu.dissco.core.handlemanager.domain.FdoProfile.PRIMARY_SPECIMEN_OBJECT_ID; -import static eu.dissco.core.handlemanager.domain.JsonApiFields.NODE_ATTRIBUTES; import static eu.dissco.core.handlemanager.domain.JsonApiFields.NODE_DATA; import static eu.dissco.core.handlemanager.domain.JsonApiFields.NODE_ID; -import static eu.dissco.core.handlemanager.domain.JsonApiFields.NODE_TYPE; -import com.fasterxml.jackson.core.JsonProcessingException; 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.requests.validation.JsonSchemaValidator; -import eu.dissco.core.handlemanager.domain.requests.vocabulary.specimen.ObjectType; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.PidCreationException; import eu.dissco.core.handlemanager.exceptions.PidResolutionException; @@ -166,28 +161,6 @@ public ResponseEntity updateRecords(@RequestBody List upsertRecord(@RequestBody List requests, - Authentication authentication) - throws InvalidRequestException, UnprocessableEntityException, PidResolutionException, JsonProcessingException { - log.info("Validating upsert request from user {}", authentication.getName()); - for (var request : requests) { - schemaValidator.validatePostRequest(request); - if (!request.get(NODE_DATA).get(NODE_TYPE).asText() - .equals(ObjectType.DIGITAL_SPECIMEN.toString())) { - log.error( - "Attempting to upsert invalid type of PID record. Currently only upsert for Digital Specimen is supported."); - throw new InvalidRequestException( - "Invalid type. Upsert endpoint only available for type DigitalSpecimen"); - } - } - var ids = getPhysicalSpecimenIds(requests); - log.info("Received valid upsert request for {} physical specimens: {}... ", requests.size(), - ids); - return ResponseEntity.ok(service.upsertDigitalSpecimens(requests)); - } @Operation(summary = "Archive given record") @PutMapping(value = "/{prefix}/{suffix}") @@ -268,8 +241,4 @@ public ResponseEntity archiveRecords(@RequestBody List getPhysicalSpecimenIds(List requests) { - return requests.stream().map(r -> r.get(NODE_DATA).get(NODE_ATTRIBUTES) - .get(PRIMARY_SPECIMEN_OBJECT_ID.get()).asText()).limit(LOG_LIMIT).toList(); - } } \ No newline at end of file diff --git a/src/main/java/eu/dissco/core/handlemanager/domain/requests/UpsertDigitalSpecimen.java b/src/main/java/eu/dissco/core/handlemanager/domain/requests/UpsertDigitalSpecimen.java deleted file mode 100644 index 54fafcf4..00000000 --- a/src/main/java/eu/dissco/core/handlemanager/domain/requests/UpsertDigitalSpecimen.java +++ /dev/null @@ -1,11 +0,0 @@ -package eu.dissco.core.handlemanager.domain.requests; - -import eu.dissco.core.handlemanager.domain.requests.objects.DigitalSpecimenRequest; - -public record UpsertDigitalSpecimen( - String handle, - String physicalId, - DigitalSpecimenRequest request -) { - -} diff --git a/src/main/java/eu/dissco/core/handlemanager/exceptions/UnprocessableEntityException.java b/src/main/java/eu/dissco/core/handlemanager/exceptions/UnprocessableEntityException.java index d5a83e57..afb00ff5 100644 --- a/src/main/java/eu/dissco/core/handlemanager/exceptions/UnprocessableEntityException.java +++ b/src/main/java/eu/dissco/core/handlemanager/exceptions/UnprocessableEntityException.java @@ -1,8 +1,8 @@ package eu.dissco.core.handlemanager.exceptions; -public class UnprocessableEntityException extends Exception { +public class UnprocessableEntityException extends RuntimeException { - public UnprocessableEntityException(String s){ + public UnprocessableEntityException(String s) { super(s); } 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 46dcf6b3..c6aab982 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/DoiService.java @@ -4,6 +4,8 @@ import static eu.dissco.core.handlemanager.domain.JsonApiFields.NODE_ATTRIBUTES; import static eu.dissco.core.handlemanager.domain.JsonApiFields.NODE_DATA; import static eu.dissco.core.handlemanager.domain.JsonApiFields.NODE_TYPE; +import static eu.dissco.core.handlemanager.domain.requests.vocabulary.specimen.ObjectType.DIGITAL_SPECIMEN; +import static eu.dissco.core.handlemanager.domain.requests.vocabulary.specimen.ObjectType.MEDIA_OBJECT; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -11,21 +13,14 @@ import eu.dissco.core.handlemanager.Profiles; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperWrite; import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; -import eu.dissco.core.handlemanager.domain.requests.objects.DigitalSpecimenRequest; -import eu.dissco.core.handlemanager.domain.requests.objects.MediaObjectRequest; -import eu.dissco.core.handlemanager.domain.requests.vocabulary.specimen.ObjectType; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.PidCreationException; 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 java.nio.charset.StandardCharsets; import java.time.Instant; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Profile; @@ -46,57 +41,28 @@ public DoiService(PidRepository pidRepository, private static final String TYPE_ERROR_MESSAGE = "Error creating DOI for object of Type %s. Only Digital Specimens and Media Objects use DOIs."; @Override - public JsonApiWrapperWrite createRecords( - List requests) - throws PidResolutionException, InvalidRequestException, PidCreationException { - - var handles = hf.genHandleList(requests.size()); - var handleIterator = handles.iterator(); - List digitalSpecimenList = new ArrayList<>(); - - List handleAttributes = new ArrayList<>(); - Map recordTypes = new HashMap<>(); - - for (var request : requests) { - var dataNode = request.get(NODE_DATA); - var thisHandle = handleIterator.next(); - ObjectType type = ObjectType.fromString(dataNode.get(NODE_TYPE).asText()); - recordTypes.put(new String(thisHandle, StandardCharsets.UTF_8), type); - try { - switch (type) { - case DIGITAL_SPECIMEN -> { - var requestObject = mapper.treeToValue(dataNode.get(NODE_ATTRIBUTES), - DigitalSpecimenRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareDigitalSpecimenRecordAttributes(requestObject, - thisHandle, type)); - digitalSpecimenList.add(requestObject); - } - case MEDIA_OBJECT -> { - var requestObject = mapper.treeToValue(dataNode.get(NODE_ATTRIBUTES), - MediaObjectRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareMediaObjectAttributes(requestObject, thisHandle, - type)); - } - default -> throw new InvalidRequestException(String.format( - TYPE_ERROR_MESSAGE, type)); - } - } catch (JsonProcessingException | UnprocessableEntityException e) { - throw new InvalidRequestException( - "An error has occurred parsing a record in request. More information: " - + e.getMessage()); + public JsonApiWrapperWrite createRecords(List requests) + throws InvalidRequestException, PidCreationException { + var handles = hf.genHandleList(requests.size()).iterator(); + var requestAttributes = requests.stream() + .map(request -> request.get(NODE_DATA).get(NODE_ATTRIBUTES)).toList(); + var type = getObjectType(requests); + List handleAttributes; + try { + switch (type) { + case DIGITAL_SPECIMEN -> + handleAttributes = createDigitalSpecimen(requestAttributes, handles); + case MEDIA_OBJECT -> handleAttributes = createMediaObject(requestAttributes, handles); + default -> throw new UnsupportedOperationException( + type + " is not an appropriate Type for DOI endpoint."); } + } catch (JsonProcessingException | PidResolutionException e) { + throw new InvalidRequestException( + "An error has occurred parsing a record in request. More information: " + e.getMessage()); } - - validateDigitalSpecimens(digitalSpecimenList); - - log.info("Persisting new DOIs to db"); - var recordTimestamp = Instant.now().getEpochSecond(); - - pidRepository.postAttributesToDb(recordTimestamp, handleAttributes); - - return new JsonApiWrapperWrite(formatCreateRecords(handleAttributes, recordTypes)); + log.info("Persisting new dois to db"); + pidRepository.postAttributesToDb(Instant.now().getEpochSecond(), handleAttributes); + return new JsonApiWrapperWrite(formatCreateRecords(handleAttributes, type)); } @Override @@ -104,15 +70,14 @@ public JsonApiWrapperWrite updateRecords(List requests, boolean increm throws InvalidRequestException, PidResolutionException, UnprocessableEntityException { var types = requests.stream() .map(request -> request.get(NODE_DATA).get(NODE_TYPE).asText()) - .filter(type -> !type.equals(ObjectType.MEDIA_OBJECT.toString()) - || !type.equals(ObjectType.DIGITAL_SPECIMEN.toString())) + .filter(type -> !type.equals(MEDIA_OBJECT.toString()) + || !type.equals(DIGITAL_SPECIMEN.toString())) .collect(Collectors.toSet()); if (!types.isEmpty()) { throw new InvalidRequestException(String.format(TYPE_ERROR_MESSAGE, types)); } return super.updateRecords(requests, incrementVersion); - } 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 8ef81432..5441932f 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/FdoRecordService.java @@ -69,6 +69,8 @@ import static eu.dissco.core.handlemanager.domain.FdoProfile.TOPIC_DOMAIN; import static eu.dissco.core.handlemanager.domain.FdoProfile.TOPIC_ORIGIN; import static eu.dissco.core.handlemanager.domain.FdoProfile.WAS_DERIVED_FROM_ENTITY; +import static eu.dissco.core.handlemanager.domain.requests.vocabulary.specimen.ObjectType.DIGITAL_SPECIMEN; +import static eu.dissco.core.handlemanager.domain.requests.vocabulary.specimen.ObjectType.MAPPING; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; @@ -161,7 +163,7 @@ public HandleAttribute genHsAdmin(byte[] handle) { public List prepareHandleRecordAttributes(HandleRecordRequest request, byte[] handle, ObjectType type) - throws InvalidRequestException, PidResolutionException, UnprocessableEntityException { + throws InvalidRequestException, PidResolutionException { List fdoRecord = new ArrayList<>(); // 100: Admin Handle @@ -250,7 +252,7 @@ private static void checkHandle(String url) throws InvalidRequestException { public List prepareDoiRecordAttributes(DoiRecordRequest request, byte[] handle, ObjectType type) - throws UnprocessableEntityException, PidResolutionException, InvalidRequestException { + throws PidResolutionException, InvalidRequestException { var fdoRecord = prepareHandleRecordAttributes(request, handle, type); // 40: referentType @@ -273,16 +275,14 @@ public List prepareDoiRecordAttributes(DoiRecordRequest request } public List prepareMediaObjectAttributes(MediaObjectRequest request, - byte[] handle, ObjectType type) - throws UnprocessableEntityException, PidResolutionException, InvalidRequestException { - var fdoRecord = prepareDoiRecordAttributes(request, handle, type); + byte[] handle) + throws PidResolutionException, InvalidRequestException { + var fdoRecord = prepareDoiRecordAttributes(request, handle, ObjectType.MEDIA_OBJECT); fdoRecord.add(new HandleAttribute(MEDIA_HOST, handle, request.getMediaHost())); var mediaHostName = setHostName(request.getMediaHostName(), request.getMediaHost(), handle, MEDIA_HOST_NAME); - if (mediaHostName != null) { - fdoRecord.add(mediaHostName); - } + fdoRecord.add(mediaHostName); if (request.getMediaFormat() != null) { fdoRecord.add(new HandleAttribute(MEDIA_FORMAT, handle, request.getMediaFormat().toString())); } @@ -349,10 +349,9 @@ public List prepareMediaObjectAttributes(MediaObjectRequest req return fdoRecord; } - public List prepareAnnotationAttributes(AnnotationRequest request, byte[] handle, - ObjectType type) - throws UnprocessableEntityException, PidResolutionException, InvalidRequestException { - var fdoRecord = prepareHandleRecordAttributes(request, handle, type); + public List prepareAnnotationAttributes(AnnotationRequest request, byte[] handle) + throws PidResolutionException, InvalidRequestException { + var fdoRecord = prepareHandleRecordAttributes(request, handle, ObjectType.ANNOTATION); // 500 TargetPid fdoRecord.add(new HandleAttribute(TARGET_PID, handle, request.getTargetPid())); @@ -369,25 +368,21 @@ public List prepareAnnotationAttributes(AnnotationRequest reque fdoRecord.add( new HandleAttribute(ANNOTATION_HASH, handle, request.getAnnotationHash().toString())); } - return fdoRecord; } - public List prepareMasRecordAttributes(MasRequest request, byte[] handle, - ObjectType type) - throws UnprocessableEntityException, PidResolutionException, InvalidRequestException { - var fdoRecord = prepareHandleRecordAttributes(request, handle, type); - + public List prepareMasRecordAttributes(MasRequest request, byte[] handle) + throws PidResolutionException, InvalidRequestException { + var fdoRecord = prepareHandleRecordAttributes(request, handle, ObjectType.MAS); fdoRecord.add(new HandleAttribute(MAS_NAME, handle, request.getMachineAnnotationServiceName())); - return fdoRecord; } public List prepareSourceSystemAttributes(SourceSystemRequest request, - byte[] handle, ObjectType type) + byte[] handle) throws UnprocessableEntityException, PidResolutionException, InvalidRequestException { - var fdoRecord = prepareHandleRecordAttributes(request, handle, type); + var fdoRecord = prepareHandleRecordAttributes(request, handle, ObjectType.SOURCE_SYSTEM); // 600 sourceSystemName fdoRecord.add(new HandleAttribute(SOURCE_SYSTEM_NAME, handle, request.getSourceSystemName())); @@ -396,9 +391,9 @@ public List prepareSourceSystemAttributes(SourceSystemRequest r } public List prepareOrganisationAttributes(OrganisationRequest request, - byte[] handle, ObjectType type) + byte[] handle) throws UnprocessableEntityException, PidResolutionException, InvalidRequestException { - var fdoRecord = prepareDoiRecordAttributes(request, handle, type); + var fdoRecord = prepareDoiRecordAttributes(request, handle, ObjectType.ORGANISATION); //101 10320/loc -> must contain ROR var objectLocations = new ArrayList<>(List.of(request.getOrganisationIdentifier())); @@ -424,10 +419,9 @@ public List prepareOrganisationAttributes(OrganisationRequest r return fdoRecord; } - public List prepareMappingAttributes(MappingRequest request, byte[] handle, - ObjectType type) - throws UnprocessableEntityException, PidResolutionException, InvalidRequestException { - var fdoRecord = prepareHandleRecordAttributes(request, handle, type); + public List prepareMappingAttributes(MappingRequest request, byte[] handle) + throws PidResolutionException, InvalidRequestException { + var fdoRecord = prepareHandleRecordAttributes(request, handle, MAPPING); // 700 Source Data Standard fdoRecord.add( @@ -437,9 +431,9 @@ public List prepareMappingAttributes(MappingRequest request, by } public List prepareDigitalSpecimenRecordAttributes( - DigitalSpecimenRequest request, byte[] handle, ObjectType type) - throws UnprocessableEntityException, PidResolutionException, InvalidRequestException { - var fdoRecord = prepareDoiRecordAttributes(request, handle, type); + DigitalSpecimenRequest request, byte[] handle) + throws PidResolutionException, InvalidRequestException { + var fdoRecord = prepareDoiRecordAttributes(request, handle, DIGITAL_SPECIMEN); // 200: Specimen Host fdoRecord.add( @@ -653,7 +647,7 @@ public List prepareTombstoneAttributes(byte[] handle, JsonNode } private HandleAttribute genLandingPage(byte[] handle) - throws UnprocessableEntityException { + throws InvalidRequestException { var landingPage = new String[]{"Placeholder landing page"}; var data = setLocations(landingPage, new String(handle, StandardCharsets.UTF_8), ObjectType.TOMBSTONE); @@ -673,7 +667,7 @@ private JsonNode setLocationXmlFromJson(JsonNode request, String handle, ObjectT requestObjectNode.put(LOC.get(), new String(setLocations(locArr, handle, type), StandardCharsets.UTF_8)); requestObjectNode.remove(LOC_REQUEST); - } catch (IOException | UnprocessableEntityException e) { + } catch (IOException e) { throw new InvalidRequestException( "An error has occurred parsing \"locations\" array. " + e.getMessage()); } @@ -685,13 +679,13 @@ private String getDate() { } public byte[] setLocations(String[] userLocations, String handle, ObjectType type) - throws UnprocessableEntityException { + throws InvalidRequestException { DocumentBuilder documentBuilder; try { documentBuilder = dbf.newDocumentBuilder(); } catch (ParserConfigurationException e) { - throw new UnprocessableEntityException(e.getMessage()); + throw new InvalidRequestException(e.getMessage()); } var doc = documentBuilder.newDocument(); @@ -710,8 +704,8 @@ public byte[] setLocations(String[] userLocations, String handle, ObjectType typ try { return documentToString(doc).getBytes(StandardCharsets.UTF_8); } catch (TransformerException e) { - throw new UnprocessableEntityException( - "An internal error has occurred parsing location data"); + throw new InvalidRequestException( + "An error has occurred parsing location data"); } } 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 5651193c..b92d565b 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/HandleService.java @@ -2,37 +2,31 @@ import static eu.dissco.core.handlemanager.domain.JsonApiFields.NODE_ATTRIBUTES; import static eu.dissco.core.handlemanager.domain.JsonApiFields.NODE_DATA; -import static eu.dissco.core.handlemanager.domain.JsonApiFields.NODE_TYPE; +import static eu.dissco.core.handlemanager.domain.requests.vocabulary.specimen.ObjectType.DOI; +import static eu.dissco.core.handlemanager.domain.requests.vocabulary.specimen.ObjectType.HANDLE; 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.Profiles; import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperWrite; import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; import eu.dissco.core.handlemanager.domain.requests.objects.AnnotationRequest; -import eu.dissco.core.handlemanager.domain.requests.objects.DigitalSpecimenRequest; import eu.dissco.core.handlemanager.domain.requests.objects.DoiRecordRequest; import eu.dissco.core.handlemanager.domain.requests.objects.HandleRecordRequest; import eu.dissco.core.handlemanager.domain.requests.objects.MappingRequest; import eu.dissco.core.handlemanager.domain.requests.objects.MasRequest; -import eu.dissco.core.handlemanager.domain.requests.objects.MediaObjectRequest; import eu.dissco.core.handlemanager.domain.requests.objects.OrganisationRequest; import eu.dissco.core.handlemanager.domain.requests.objects.SourceSystemRequest; -import eu.dissco.core.handlemanager.domain.requests.vocabulary.specimen.ObjectType; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.PidCreationException; 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 java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Iterator; import java.util.List; -import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; @@ -42,110 +36,135 @@ @Profile(Profiles.HANDLE) public class HandleService extends PidService { - public HandleService(PidRepository pidRepository, - FdoRecordService fdoRecordService, PidNameGeneratorService hf, - ObjectMapper mapper, - ProfileProperties profileProperties) { + public HandleService(PidRepository pidRepository, FdoRecordService fdoRecordService, + PidNameGeneratorService hf, ObjectMapper mapper, ProfileProperties profileProperties) { super(pidRepository, fdoRecordService, hf, mapper, profileProperties); } // Pid Record Creation @Override - public JsonApiWrapperWrite createRecords( - List requests) - throws PidResolutionException, InvalidRequestException, PidCreationException { + public JsonApiWrapperWrite createRecords(List requests) + throws InvalidRequestException, PidCreationException { + var handles = hf.genHandleList(requests.size()).iterator(); + var requestAttributes = requests.stream() + .map(request -> request.get(NODE_DATA).get(NODE_ATTRIBUTES)).toList(); + var type = getObjectType(requests); + List handleAttributes; + try { + switch (type) { + case ANNOTATION -> handleAttributes = createAnnotation(requestAttributes, handles); + case DIGITAL_SPECIMEN -> + handleAttributes = createDigitalSpecimen(requestAttributes, handles); + case DOI -> handleAttributes = createDoi(requestAttributes, handles); + case HANDLE -> handleAttributes = createHandle(requestAttributes, handles); + 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); + default -> throw new UnsupportedOperationException("Unrecognized type"); + } + } catch (JsonProcessingException | PidResolutionException e) { + 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)); + } - var recordTimestamp = Instant.now().getEpochSecond(); - var handles = hf.genHandleList(requests.size()); - var handleIterator = handles.iterator(); - List digitalSpecimenList = new ArrayList<>(); + private List createAnnotation(List requestAttributes, + Iterator handleIterator) + throws InvalidRequestException, JsonProcessingException, PidResolutionException { + List handleAttributes = new ArrayList<>(); + for (var request : requestAttributes) { + var thisHandle = handleIterator.next(); + var requestObject = mapper.treeToValue(request, AnnotationRequest.class); + handleAttributes.addAll( + fdoRecordService.prepareAnnotationAttributes(requestObject, thisHandle)); + } + return handleAttributes; + } + private List createDoi(List requestAttributes, + Iterator handleIterator) + throws InvalidRequestException, JsonProcessingException, PidResolutionException { List handleAttributes = new ArrayList<>(); - Map recordTypes = new HashMap<>(); + for (var request : requestAttributes) { + var thisHandle = handleIterator.next(); + var requestObject = mapper.treeToValue(request, DoiRecordRequest.class); + handleAttributes.addAll( + fdoRecordService.prepareDoiRecordAttributes(requestObject, thisHandle, + DOI)); + } + return handleAttributes; + } - for (var request : requests) { - ObjectNode dataNode = (ObjectNode) request.get(NODE_DATA); - ObjectType type = ObjectType.fromString(dataNode.get(NODE_TYPE).asText()); + private List createHandle(List requestAttributes, + Iterator handleIterator) + throws InvalidRequestException, JsonProcessingException, PidResolutionException { + List handleAttributes = new ArrayList<>(); + for (var request : requestAttributes) { var thisHandle = handleIterator.next(); - recordTypes.put(new String(thisHandle, StandardCharsets.UTF_8), type); - try { - switch (type) { - case HANDLE -> { - var requestObject = mapper.treeToValue(dataNode.get(NODE_ATTRIBUTES), - HandleRecordRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareHandleRecordAttributes(requestObject, thisHandle, - type)); - } - case DOI -> { - var requestObject = mapper.treeToValue(dataNode.get(NODE_ATTRIBUTES), - DoiRecordRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareDoiRecordAttributes(requestObject, thisHandle, - type)); - } - case DIGITAL_SPECIMEN -> { - var requestObject = mapper.treeToValue(dataNode.get(NODE_ATTRIBUTES), - DigitalSpecimenRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareDigitalSpecimenRecordAttributes(requestObject, - thisHandle, type)); - digitalSpecimenList.add(requestObject); - } - case MEDIA_OBJECT -> { - var requestObject = mapper.treeToValue(dataNode.get(NODE_ATTRIBUTES), - MediaObjectRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareMediaObjectAttributes(requestObject, thisHandle, - type)); - } - case ANNOTATION -> { - var requestObject = mapper.treeToValue(dataNode.get(NODE_ATTRIBUTES), - AnnotationRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareAnnotationAttributes(requestObject, thisHandle, - type)); - } - case MAPPING -> { - var requestObject = mapper.treeToValue(dataNode.get(NODE_ATTRIBUTES), - MappingRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareMappingAttributes(requestObject, thisHandle, type)); - } - case SOURCE_SYSTEM -> { - var requestObject = mapper.treeToValue(dataNode.get(NODE_ATTRIBUTES), - SourceSystemRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareSourceSystemAttributes(requestObject, thisHandle, - type)); - } - case ORGANISATION -> { - var requestObject = mapper.treeToValue(dataNode.get(NODE_ATTRIBUTES), - OrganisationRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareOrganisationAttributes(requestObject, thisHandle, - type)); - } - case MAS -> { - var requestObject = mapper.treeToValue(dataNode.get(NODE_ATTRIBUTES), MasRequest.class); - handleAttributes.addAll( - fdoRecordService.prepareMasRecordAttributes(requestObject, thisHandle, - type)); - } - default -> throw new InvalidRequestException( - "Invalid request. Unrecognized object type: " + type); - } - } catch (JsonProcessingException | UnprocessableEntityException e) { - throw new InvalidRequestException( - "An error has occurred parsing a record in request. More information: " - + e.getMessage()); - } + var requestObject = mapper.treeToValue(request, HandleRecordRequest.class); + handleAttributes.addAll( + fdoRecordService.prepareHandleRecordAttributes(requestObject, thisHandle, + HANDLE)); } + return handleAttributes; + } - validateDigitalSpecimens(digitalSpecimenList); - log.info("Persisting new handles to db"); - pidRepository.postAttributesToDb(recordTimestamp, handleAttributes); - return new JsonApiWrapperWrite(formatCreateRecords(handleAttributes, recordTypes)); + private List createMapping(List requestAttributes, + Iterator handleIterator) + throws InvalidRequestException, JsonProcessingException, PidResolutionException { + List handleAttributes = new ArrayList<>(); + for (var request : requestAttributes) { + var thisHandle = handleIterator.next(); + var requestObject = mapper.treeToValue(request, MappingRequest.class); + handleAttributes.addAll( + fdoRecordService.prepareMappingAttributes(requestObject, thisHandle)); + } + return handleAttributes; + } + + private List createMas(List requestAttributes, + Iterator handleIterator) + throws InvalidRequestException, JsonProcessingException, PidResolutionException { + List handleAttributes = new ArrayList<>(); + for (var request : requestAttributes) { + var thisHandle = handleIterator.next(); + var requestObject = mapper.treeToValue(request, MasRequest.class); + handleAttributes.addAll( + fdoRecordService.prepareMasRecordAttributes(requestObject, thisHandle + )); + } + return handleAttributes; + } + + private List createOrganisation(List requestAttributes, + Iterator handleIterator) + throws InvalidRequestException, JsonProcessingException, PidResolutionException { + List handleAttributes = new ArrayList<>(); + for (var request : requestAttributes) { + var thisHandle = handleIterator.next(); + var requestObject = mapper.treeToValue(request, OrganisationRequest.class); + handleAttributes.addAll( + fdoRecordService.prepareOrganisationAttributes(requestObject, thisHandle)); + } + return handleAttributes; + } + + private List createSourceSystem(List requestAttributes, + Iterator handleIterator) + throws InvalidRequestException, JsonProcessingException, PidResolutionException { + List handleAttributes = new ArrayList<>(); + for (var request : requestAttributes) { + var thisHandle = handleIterator.next(); + var requestObject = mapper.treeToValue(request, SourceSystemRequest.class); + handleAttributes.addAll( + fdoRecordService.prepareSourceSystemAttributes(requestObject, thisHandle)); + } + return handleAttributes; } } 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 6091226c..79b71991 100644 --- a/src/main/java/eu/dissco/core/handlemanager/service/PidService.java +++ b/src/main/java/eu/dissco/core/handlemanager/service/PidService.java @@ -13,6 +13,7 @@ import static eu.dissco.core.handlemanager.domain.JsonApiFields.NODE_TYPE; import static eu.dissco.core.handlemanager.domain.requests.vocabulary.specimen.ObjectType.ANNOTATION; import static eu.dissco.core.handlemanager.domain.requests.vocabulary.specimen.ObjectType.DIGITAL_SPECIMEN; +import static eu.dissco.core.handlemanager.domain.requests.vocabulary.specimen.ObjectType.MEDIA_OBJECT; import static eu.dissco.core.handlemanager.domain.requests.vocabulary.specimen.ObjectType.TOMBSTONE; import com.fasterxml.jackson.core.JsonProcessingException; @@ -25,8 +26,8 @@ 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.requests.UpsertDigitalSpecimen; import eu.dissco.core.handlemanager.domain.requests.objects.DigitalSpecimenRequest; +import eu.dissco.core.handlemanager.domain.requests.objects.MediaObjectRequest; import eu.dissco.core.handlemanager.domain.requests.vocabulary.specimen.ObjectType; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.exceptions.PidCreationException; @@ -39,11 +40,11 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -89,47 +90,82 @@ private String getPidName(String pidLink) { } protected List formatCreateRecords(List dbRecord, - Map recordTypes) { + ObjectType objectType) { var handleMap = mapRecords(dbRecord); + switch (objectType) { + case ANNOTATION -> { + return formatCreateRecordsAnnotation(handleMap); + } + case DIGITAL_SPECIMEN -> { + return formatCreateRecordsSpecimen(handleMap); + } + case MEDIA_OBJECT -> { + return formatCreateRecordsMedia(handleMap); + } + default -> { + return formatCreateRecordsDefault(handleMap, objectType); + } + } + } + + private List formatCreateRecordsAnnotation( + Map> handleMap) { List dataLinksList = new ArrayList<>(); for (var handleRecord : handleMap.entrySet()) { - var type = recordTypes.get(handleRecord.getKey()); - var subRecord = handleRecord.getValue(); - if (type.equals(ObjectType.MEDIA_OBJECT)) { - subRecord = subRecord.stream().filter( - row -> row.getType().equals(PRIMARY_MEDIA_ID.get()) || row.getType() - .equals(LINKED_DO_PID.get())) - .toList(); - } else if (type.equals(DIGITAL_SPECIMEN)) { - subRecord = subRecord.stream() - .filter(row -> row.getType().equals(PRIMARY_SPECIMEN_OBJECT_ID.get())).toList(); - } else if (type.equals(ANNOTATION)) { - var hashRow = subRecord.stream().filter(row -> row.getType().equals(ANNOTATION_HASH.get())) - .findFirst(); - if (hashRow.isPresent()) { - subRecord = List.of(hashRow.get()); - } - } + 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(); - dataLinksList.add(new JsonApiDataLinks(handleRecord.getKey(), type.toString(), rootNode, - new JsonApiLinks(pidLink))); + dataLinksList.add( + new JsonApiDataLinks(handleRecord.getKey(), ANNOTATION.toString(), rootNode, + new JsonApiLinks(pidLink))); } return dataLinksList; } - private List formatUpsertResponse(List records) { + private List formatCreateRecordsSpecimen( + Map> handleMap) { List dataLinksList = new ArrayList<>(); - for (var row : records) { - if (row.getType().equals(PRIMARY_SPECIMEN_OBJECT_ID.get())) { - String h = new String(row.getHandle(), StandardCharsets.UTF_8); - String pidLink = profileProperties.getDomain() + h; - var node = mapper.createObjectNode(); - node.put(PRIMARY_SPECIMEN_OBJECT_ID.get(), - new String(row.getData(), StandardCharsets.UTF_8)); - dataLinksList.add( - new JsonApiDataLinks(h, DIGITAL_SPECIMEN.toString(), node, new JsonApiLinks(pidLink))); - } + 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(); + dataLinksList.add( + new JsonApiDataLinks(handleRecord.getKey(), DIGITAL_SPECIMEN.toString(), rootNode, + new JsonApiLinks(pidLink))); + } + return dataLinksList; + } + + private List formatCreateRecordsMedia( + Map> handleMap) { + 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(); + dataLinksList.add( + new JsonApiDataLinks(handleRecord.getKey(), MEDIA_OBJECT.toString(), rootNode, + new JsonApiLinks(pidLink))); + } + return dataLinksList; + } + + private List formatCreateRecordsDefault( + Map> handleMap, ObjectType objectType) { + List dataLinksList = new ArrayList<>(); + for (var handleRecord : handleMap.entrySet()) { + var rootNode = jsonFormatSingleRecord(handleRecord.getValue()); + String pidLink = profileProperties.getDomain() + handleRecord.getKey(); + dataLinksList.add( + new JsonApiDataLinks(handleRecord.getKey(), objectType.toString(), rootNode, + new JsonApiLinks(pidLink))); } return dataLinksList; } @@ -233,181 +269,60 @@ public abstract JsonApiWrapperWrite createRecords( List requests) throws PidResolutionException, InvalidRequestException, PidCreationException; - protected Set getPhysicalIdsFromRequests( - List digitalSpecimenRequests) { - return digitalSpecimenRequests.stream() - .map(DigitalSpecimenRequest::getNormalisedPrimarySpecimenObjectId) + protected ObjectType getObjectType(List requests) { + var types = requests.stream() + .map(request -> request.get(NODE_DATA).get(NODE_TYPE).asText()) .collect(Collectors.toSet()); - } - - protected List getPhysIdBytes(Set physIds) { - return physIds.stream() - .map(physId -> physId.getBytes(StandardCharsets.UTF_8)) - .toList(); - } - - // Digital Specimen Validation - protected void validateDigitalSpecimens(List digitalSpecimenList) - throws InvalidRequestException, PidCreationException { - if (!digitalSpecimenList.isEmpty()) { - var requestPhysicalIds = getPhysicalIdsFromRequests(digitalSpecimenList); - verifyNoInternalDuplicatePhysicalSpecimenObjectId(digitalSpecimenList, requestPhysicalIds); - verifyNoRegisteredSpecimens(getPhysIdBytes(requestPhysicalIds)); + var type = types.stream().findFirst(); + if (type.isEmpty() || types.size() != 1) { + throw new UnsupportedOperationException("Requests must all be of the same type"); } - } - - protected void verifyNoInternalDuplicatePhysicalSpecimenObjectId( - List requests, Set physicalIds) - throws InvalidRequestException { - if (physicalIds.size() < requests.size()) { - throw new InvalidRequestException( - "Bad Request. Some PhysicalSpecimenObjectIds are duplicated in request body"); + return ObjectType.fromString(type.get()); + } + + protected ArrayList createDigitalSpecimen(List requestAttributes, + Iterator handleIterator) + throws InvalidRequestException, JsonProcessingException, PidResolutionException { + var handleAttributes = new ArrayList(); + var physicalIds = new ArrayList(); + for (var request : requestAttributes) { + var thisHandle = handleIterator.next(); + var requestObject = mapper.treeToValue(request, DigitalSpecimenRequest.class); + physicalIds.add( + requestObject.getNormalisedPrimarySpecimenObjectId().getBytes(StandardCharsets.UTF_8)); + handleAttributes.addAll( + fdoRecordService.prepareDigitalSpecimenRecordAttributes(requestObject, thisHandle)); } + verifyNoRegisteredSpecimens(physicalIds); + return handleAttributes; } protected void verifyNoRegisteredSpecimens(List physicalIds) - throws PidCreationException { - var registeredSpecimens = pidRepository.searchByNormalisedPhysicalIdentifierFullRecord( + throws PidResolutionException { + var registeredRows = pidRepository.searchByNormalisedPhysicalIdentifier( physicalIds); - if (!registeredSpecimens.isEmpty()) { - var registeredHandles = listHandleNamesReturnedFromQuery(registeredSpecimens); - throw new PidCreationException( + if (!registeredRows.isEmpty()) { + var registeredHandles = registeredRows.stream() + .map(row -> new String(row.getHandle(), StandardCharsets.UTF_8)).toList(); + throw new PidResolutionException( "Unable to create PID records. Some requested records are already registered. Verify the following digital specimens:" + registeredHandles); } } - // Upsert - public JsonApiWrapperWrite upsertDigitalSpecimens(List requests) - throws JsonProcessingException, UnprocessableEntityException, PidResolutionException, - InvalidRequestException { - var digitalSpecimenRequests = jsonNodeToDigitalSpecimenRequest(requests); - - var physicalIds = getPhysicalIdsFromRequests(digitalSpecimenRequests); - var physicalIdsBytes = getPhysIdBytes(physicalIds); - var upsertRequests = getRegisteredSpecimensUpsert(digitalSpecimenRequests, physicalIdsBytes); - var upsertAttributes = prepareUpsertAttributes(upsertRequests); - logUpdates(upsertRequests); - - var createRequests = getCreateRequests(upsertRequests, digitalSpecimenRequests); - var newHandles = hf.genHandleList(createRequests.size()); - if (!newHandles.isEmpty()) { - log.info("Successfully minted {} new identifiers(s)", newHandles.size()); - } - var createAttributes = getCreateAttributes(createRequests, newHandles); - - var allRequests = Stream.concat( - createRequests.stream(), - upsertRequests.stream().map(UpsertDigitalSpecimen::request)).toList(); - verifyNoInternalDuplicatePhysicalSpecimenObjectId(allRequests, physicalIds); - - var recordTimestamp = Instant.now().getEpochSecond(); - - log.info("Persisting upserts to db."); - pidRepository.postAndUpdateHandles(recordTimestamp, createAttributes, upsertAttributes); - - var concatAttributes = concatHandleAttributes(createAttributes, upsertAttributes); - - return new JsonApiWrapperWrite( - formatUpsertResponse(concatAttributes)); - } - - private List jsonNodeToDigitalSpecimenRequest(List requests) - throws JsonProcessingException { - ArrayList digitalSpecimenRequests = new ArrayList<>(); - for (var request : requests) { - digitalSpecimenRequests.add(mapper.treeToValue(request.get(NODE_DATA).get(NODE_ATTRIBUTES), - DigitalSpecimenRequest.class)); - } - return digitalSpecimenRequests; - } - - private List getRegisteredSpecimensUpsert( - List requests, List physicalIds) { - var registeredSpecimensHandleAttributes = new HashSet<>( - pidRepository.searchByNormalisedPhysicalIdentifier(physicalIds)); - if (registeredSpecimensHandleAttributes.isEmpty()) { - return new ArrayList<>(); - } - - ArrayList upsertDigitalSpecimen = new ArrayList<>(); - for (var row : registeredSpecimensHandleAttributes) { - var targetPhysId = new String(row.getData(), StandardCharsets.UTF_8); - var targetRequest = getRequestFromPhysicalId(requests, targetPhysId); - requests.remove(targetRequest); - upsertDigitalSpecimen.add(new UpsertDigitalSpecimen( - new String(row.getHandle(), StandardCharsets.UTF_8), - targetPhysId, - targetRequest - )); - } - return upsertDigitalSpecimen; - } - - private void logUpdates(List upsertRequests) { - var registeredHandles = upsertRequests.stream().map(UpsertDigitalSpecimen::handle).toList(); - if (!registeredHandles.isEmpty()) { - log.debug("Some specimens already have identifiers. Updating the following PID Records {}", - registeredHandles); - } - } - - private DigitalSpecimenRequest getRequestFromPhysicalId(List requests, - String physicalId) { - for (var request : requests) { - if (physicalId.equals(request.getNormalisedPrimarySpecimenObjectId())) { - return request; - } - } - throw new IllegalStateException("Physical Identifier not found"); - } - - private List getCreateRequests(List upsertRequests, - List digitalSpecimenRequests) { - var upsertRequestsSet = upsertRequests - .stream() - .map(UpsertDigitalSpecimen::request).collect(Collectors.toSet()); - return digitalSpecimenRequests - .stream() - .filter(r -> (!upsertRequestsSet.contains(r))) - .toList(); - } - - private List getCreateAttributes( - List digitalSpecimenRequests, List newHandles) - throws UnprocessableEntityException, PidResolutionException, InvalidRequestException { - var handles = new ArrayList<>(newHandles); + protected List createMediaObject(List requestAttributes, + Iterator handleIterator) + throws InvalidRequestException, JsonProcessingException, PidResolutionException { List handleAttributes = new ArrayList<>(); - for (var digitalSpecimenRequest : digitalSpecimenRequests) { + for (var request : requestAttributes) { + var thisHandle = handleIterator.next(); + var requestObject = mapper.treeToValue(request, MediaObjectRequest.class); handleAttributes.addAll( - fdoRecordService.prepareDigitalSpecimenRecordAttributes(digitalSpecimenRequest, - handles.remove(0), DIGITAL_SPECIMEN)); + fdoRecordService.prepareMediaObjectAttributes(requestObject, thisHandle)); } return handleAttributes; } - private List concatHandleAttributes(List createAttributes, - List> upsertAttributes) { - List upsertListFlat = new ArrayList<>(); - for (var upsertRecord : upsertAttributes) { - upsertListFlat.addAll(upsertRecord); - } - return Stream.concat(createAttributes.stream(), upsertListFlat.stream()).toList(); - } - - private List> prepareUpsertAttributes( - List upsertDigitalSpecimens) - throws InvalidRequestException, UnprocessableEntityException, PidResolutionException { - List> upsertAttributes = new ArrayList<>(); - for (var upsertRequest : upsertDigitalSpecimens) { - ArrayList upsertAttributeSingleSpecimen = new ArrayList<>(fdoRecordService - .prepareUpdateAttributes(upsertRequest.handle().getBytes(StandardCharsets.UTF_8), - mapper.valueToTree(upsertRequest.request()), DIGITAL_SPECIMEN)); - upsertAttributes.add(upsertAttributeSingleSpecimen); - } - return upsertAttributes; - } - // Update public JsonApiWrapperWrite updateRecords(List requests, boolean incrementVersion) diff --git a/src/main/java/eu/dissco/core/handlemanager/web/PidResolver.java b/src/main/java/eu/dissco/core/handlemanager/web/PidResolver.java index b0f3bd2a..b6d4bb2c 100644 --- a/src/main/java/eu/dissco/core/handlemanager/web/PidResolver.java +++ b/src/main/java/eu/dissco/core/handlemanager/web/PidResolver.java @@ -25,7 +25,7 @@ public class PidResolver { @Cacheable("pidName") public String getObjectName(String pid) - throws UnprocessableEntityException, PidResolutionException { + throws PidResolutionException { log.info("getting Pid name for: {}", pid); var pidRecord = resolveExternalPid(pid); if (pidRecord.get("name") != null) { @@ -36,23 +36,23 @@ public String getObjectName(String pid) } @Cacheable("Qid") - public String resolveQid(String qid) throws UnprocessableEntityException, PidResolutionException { + public String resolveQid(String qid) throws PidResolutionException { try { var qidRecord = resolveExternalPid(qid); return qidRecord.get("labels").get("en").asText(); - } catch (NullPointerException e){ + } catch (NullPointerException e) { log.error("Given Qid {} resolves, but does not include a name attribute. ", qid); - throw new PidResolutionException("Given QID "+ qid + "resolves but does not include a name"); - } catch (UnprocessableEntityException | PidResolutionException e) { + throw new PidResolutionException("Given QID " + qid + "resolves but does not include a name"); + } catch (PidResolutionException e) { log.error("An error has occurred in resolving a QID ", e); - throw new PidResolutionException("An error has occured in resolving a QID: " + e.getMessage()); + throw new PidResolutionException( + "An error has occured in resolving a QID: " + e.getMessage()); } } - private JsonNode resolveExternalPid(String url) - throws UnprocessableEntityException, PidResolutionException { + throws PidResolutionException { var response = webClient.get() .uri(url) .retrieve() @@ -77,7 +77,7 @@ private JsonNode resolveExternalPid(String url) if (e.getCause().getClass().equals(PidResolutionException.class)) { throw new PidResolutionException(e.getMessage()); } - throw new UnprocessableEntityException(e.getMessage()); + throw new PidResolutionException(e.getMessage()); } } diff --git a/src/test/java/eu/dissco/core/handlemanager/component/PidResolverTest.java b/src/test/java/eu/dissco/core/handlemanager/component/PidResolverTest.java index d35934d2..f9316960 100644 --- a/src/test/java/eu/dissco/core/handlemanager/component/PidResolverTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/component/PidResolverTest.java @@ -7,7 +7,6 @@ import static org.junit.Assert.assertThrows; import eu.dissco.core.handlemanager.exceptions.PidResolutionException; -import eu.dissco.core.handlemanager.exceptions.UnprocessableEntityException; import eu.dissco.core.handlemanager.web.PidResolver; import java.io.IOException; import okhttp3.mockwebserver.MockResponse; @@ -108,7 +107,7 @@ void testResolveQidFails() throws Exception { .addHeader("Content-Type", "application/json")); // then - assertThrows(PidResolutionException.class, ()->pidResolver.resolveQid("qid")); + assertThrows(PidResolutionException.class, () -> pidResolver.resolveQid("qid")); } @Test @@ -119,7 +118,7 @@ void testResolveQidNotFound() throws Exception { .addHeader("Content-Type", "application/json")); // then - assertThrows(PidResolutionException.class, ()->pidResolver.resolveQid("qid")); + assertThrows(PidResolutionException.class, () -> pidResolver.resolveQid("qid")); } @Test @@ -159,7 +158,7 @@ void testOther4xxError() { .addHeader("Content-Type", "application/json")); // Then - assertThrows(UnprocessableEntityException.class, () -> pidResolver.getObjectName(EXTERNAL_PID + assertThrows(PidResolutionException.class, () -> pidResolver.getObjectName(EXTERNAL_PID )); } @@ -193,7 +192,7 @@ void testRetriesFailures() { mockServer.enqueue(new MockResponse().setResponseCode(HttpStatus.GATEWAY_TIMEOUT.value())); // Then - assertThrows(UnprocessableEntityException.class, () -> pidResolver.getObjectName(EXTERNAL_PID)); + assertThrows(PidResolutionException.class, () -> pidResolver.getObjectName(EXTERNAL_PID)); assertThat(mockServer.getRequestCount() - requestCount).isEqualTo(4); } 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 4bad72cf..1498706e 100644 --- a/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/controller/PidControllerTest.java @@ -583,31 +583,6 @@ void testRollbackHandlesBadRequest() { () -> controller.rollbackHandleCreation(request, authentication)); } - - @Test - void testUpsert() throws Exception { - // Given - var request = genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), - RECORD_TYPE_DS); - - // When - var response = controller.upsertRecord(List.of(request), authentication); - - // Then - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); - } - - @Test - void testUpsertBadType() { - // Given - var request = genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), - RECORD_TYPE_MAPPING); - - // Then - assertThrows(InvalidRequestException.class, () -> controller.upsertRecord(List.of(request), - authentication)); - } - @Test void testArchiveRecord() throws Exception { // Given 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 707fce3a..99dcf3a7 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/DoiServiceTest.java @@ -14,16 +14,14 @@ import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenDoiRecordRequestObject; import static eu.dissco.core.handlemanager.testUtils.TestUtils.givenRecordResponseWriteSmallResponse; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.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.BDDMockito.given; import static org.mockito.Mockito.mockStatic; import eu.dissco.core.handlemanager.Profiles; import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; import eu.dissco.core.handlemanager.domain.requests.vocabulary.specimen.ObjectType; -import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; import eu.dissco.core.handlemanager.properties.ProfileProperties; import eu.dissco.core.handlemanager.repository.PidRepository; import java.nio.charset.StandardCharsets; @@ -103,9 +101,7 @@ void testCreateDoiRecordDoiProfile() throws Exception { ObjectType.DIGITAL_SPECIMEN); given(pidNameGeneratorService.genHandleList(1)).willReturn(new ArrayList<>(List.of(handle))); - given(pidRepository.searchByNormalisedPhysicalIdentifierFullRecord(anyList())).willReturn( - new ArrayList<>()); - given(fdoRecordService.prepareDigitalSpecimenRecordAttributes(any(), any(), any())).willReturn( + given(fdoRecordService.prepareDigitalSpecimenRecordAttributes(any(), any())).willReturn( digitalSpecimen); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -124,7 +120,8 @@ void testCreateInvalidType() { new ArrayList<>(List.of(handles.get(0)))); // Then - assertThrows(InvalidRequestException.class, () -> service.createRecords(List.of(request))); + assertThrowsExactly(UnsupportedOperationException.class, + () -> service.createRecords(List.of(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 6b38eabb..9fba2b6f 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/FdoRecordServiceTest.java @@ -285,8 +285,8 @@ void testPrepareMediaObjectAttributesMandatory() throws Exception { var request = givenMediaRequestObject(); // When - var result = fdoRecordService.prepareMediaObjectAttributes(request, handle, - ObjectType.MEDIA_OBJECT); + var result = fdoRecordService.prepareMediaObjectAttributes(request, handle + ); // Then assertThat(result).hasSize(MEDIA_QTY); @@ -309,8 +309,8 @@ void testPrepareMediaObjectAttributesOptional() throws Exception { "license", "c", "d", PrimarySpecimenObjectIdType.LOCAL, "e"); // When - var result = fdoRecordService.prepareMediaObjectAttributes(request, handle, - ObjectType.MEDIA_OBJECT); + var result = fdoRecordService.prepareMediaObjectAttributes(request, handle + ); // Then assertThat(result).hasSize(MEDIA_OPTIONAL_QTY); @@ -332,8 +332,8 @@ void testPrepareMediaObjectFullAttributes() throws Exception { LICENSE_NAME_TESTVAL, "g", "h", "i", PrimarySpecimenObjectIdType.LOCAL, "j"); // When - var result = fdoRecordService.prepareMediaObjectAttributes(request, handle, - ObjectType.MEDIA_OBJECT); + var result = fdoRecordService.prepareMediaObjectAttributes(request, handle + ); // Then @@ -355,8 +355,8 @@ void testPrepareMediaObjectAttributesNamesDontResolve() throws Exception { // Then assertThrows(PidResolutionException.class, - () -> fdoRecordService.prepareMediaObjectAttributes(request, handle, - ObjectType.MEDIA_OBJECT)); + () -> fdoRecordService.prepareMediaObjectAttributes(request, handle + )); } @Test @@ -366,8 +366,8 @@ void testPrepareDigitalSpecimenRecordMandatoryAttributes() throws Exception { var request = givenDigitalSpecimenRequestObjectNullOptionals(); // When - var result = fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle, - ObjectType.DIGITAL_SPECIMEN); + var result = fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle + ); // Then assertThat(result).hasSize(DS_MANDATORY_QTY); @@ -393,8 +393,8 @@ void testPrepareDigitalSpecimenRecordMandatoryAttributesQNumber() throws Excepti null, null, null, null, null, null, null); // When - fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle, - ObjectType.DIGITAL_SPECIMEN); + fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle + ); // Then then(pidResolver).should().resolveQid(qidUrl); @@ -413,8 +413,8 @@ void testPrepareDigitalSpecimenRecordMandatoryAttributesBadSpecimenHost() throws // When assertThrows(PidResolutionException.class, - () -> fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle, - ObjectType.DIGITAL_SPECIMEN)); + () -> fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle + )); } @Test @@ -424,8 +424,8 @@ void testPrepareDigitalSpecimenRecordOptionalAttributes() throws Exception { var request = givenDigitalSpecimenRequestObjectOptionalsInit(); // When - var result = fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle, - ObjectType.DIGITAL_SPECIMEN); + var result = fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle + ); // Then assertThat(result).hasSize(DS_OPTIONAL_QTY); @@ -447,8 +447,8 @@ void testPrepareAnnotationAttributesOptional() throws Exception { var request = givenAnnotationRequestObject(); // When - var result = fdoRecordService.prepareAnnotationAttributes(request, handle, - ObjectType.ANNOTATION); + var result = fdoRecordService.prepareAnnotationAttributes(request, handle + ); // Then assertThat(result).hasSize(ANNOTATION_OPTIONAL_QTY); @@ -479,8 +479,8 @@ void testPrepareAnnotationAttributes() throws Exception { ); // When - var result = fdoRecordService.prepareAnnotationAttributes(request, handle, - ObjectType.ANNOTATION); + var result = fdoRecordService.prepareAnnotationAttributes(request, handle + ); // Then assertThat(result).hasSize(ANNOTATION_MANDATORY_QTY); @@ -498,7 +498,7 @@ void testPrepareMasRecordAttributes() throws Exception { var request = givenMasRecordRequestObject(); // When - var result = fdoRecordService.prepareMasRecordAttributes(request, handle, ObjectType.MAS); + var result = fdoRecordService.prepareMasRecordAttributes(request, handle); // Then assertThat(result).hasSize(HANDLE_QTY + 1); @@ -515,7 +515,7 @@ void testPrepareMappingAttributes() throws Exception { var request = givenMappingRequestObject(); // When - var result = fdoRecordService.prepareMappingAttributes(request, handle, ObjectType.MAPPING); + var result = fdoRecordService.prepareMappingAttributes(request, handle); // Then assertThat(hasCorrectElements(result, HANDLE_FIELDS)).isTrue(); @@ -532,8 +532,8 @@ void testPrepareSourceSystemAttributes() throws Exception { var request = givenSourceSystemRequestObject(); // When - var result = fdoRecordService.prepareSourceSystemAttributes(request, handle, - ObjectType.SOURCE_SYSTEM); + var result = fdoRecordService.prepareSourceSystemAttributes(request, handle + ); // Then assertThat(hasCorrectElements(result, HANDLE_FIELDS)).isTrue(); @@ -550,8 +550,8 @@ void testPrepareOrganisationAttributes() throws Exception { var request = givenOrganisationRequestObject(); // When - var result = fdoRecordService.prepareOrganisationAttributes(request, handle, - ObjectType.ORGANISATION); + var result = fdoRecordService.prepareOrganisationAttributes(request, handle + ); // Then assertThat(hasCorrectElements(result, HANDLE_FIELDS)).isTrue(); @@ -613,8 +613,8 @@ void testSpecimenHostResolvable() throws Exception { null, null, null, null, null, null, null); // When - var result = fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle, - ObjectType.DIGITAL_SPECIMEN); + var result = fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle + ); // Then assertThat(result).hasSize(DS_MANDATORY_QTY); @@ -636,8 +636,8 @@ void testSpecimenHostNotResolvable() throws Exception { // Then assertThrows(PidResolutionException.class, - () -> fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle, - ObjectType.DIGITAL_SPECIMEN)); + () -> fdoRecordService.prepareDigitalSpecimenRecordAttributes(request, handle + )); } @Test 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 80cf8526..c42314a6 100644 --- a/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java +++ b/src/test/java/eu/dissco/core/handlemanager/service/HandleServiceTest.java @@ -4,13 +4,11 @@ import static eu.dissco.core.handlemanager.domain.FdoProfile.LINKED_DO_PID; import static eu.dissco.core.handlemanager.domain.FdoProfile.PRIMARY_MEDIA_ID; import static eu.dissco.core.handlemanager.domain.FdoProfile.PRIMARY_SPECIMEN_OBJECT_ID; -import static eu.dissco.core.handlemanager.domain.FdoProfile.PRIMARY_SPECIMEN_OBJECT_ID_TYPE; 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.HANDLE_URI; import static eu.dissco.core.handlemanager.testUtils.TestUtils.MAPPER; import static eu.dissco.core.handlemanager.testUtils.TestUtils.PID_STATUS_TESTVAL; import static eu.dissco.core.handlemanager.testUtils.TestUtils.PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL; @@ -58,7 +56,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; @@ -67,13 +64,9 @@ import com.fasterxml.jackson.databind.JsonNode; import eu.dissco.core.handlemanager.Profiles; -import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiDataLinks; -import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiLinks; -import eu.dissco.core.handlemanager.domain.jsonapi.JsonApiWrapperWrite; import eu.dissco.core.handlemanager.domain.repsitoryobjects.HandleAttribute; import eu.dissco.core.handlemanager.domain.requests.vocabulary.specimen.ObjectType; import eu.dissco.core.handlemanager.exceptions.InvalidRequestException; -import eu.dissco.core.handlemanager.exceptions.PidCreationException; import eu.dissco.core.handlemanager.exceptions.PidResolutionException; import eu.dissco.core.handlemanager.properties.ProfileProperties; import eu.dissco.core.handlemanager.repository.PidRepository; @@ -357,9 +350,7 @@ void testCreateDigitalSpecimen() throws Exception { ObjectType.DIGITAL_SPECIMEN); given(pidNameGeneratorService.genHandleList(1)).willReturn(new ArrayList<>(List.of(handle))); - given(pidRepository.searchByNormalisedPhysicalIdentifierFullRecord(anyList())).willReturn( - new ArrayList<>()); - given(fdoRecordService.prepareDigitalSpecimenRecordAttributes(any(), any(), any())).willReturn( + given(fdoRecordService.prepareDigitalSpecimenRecordAttributes(any(), any())).willReturn( digitalSpecimen); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -370,6 +361,7 @@ void testCreateDigitalSpecimen() throws Exception { assertThat(responseReceived).isEqualTo(responseExpected); } + @Test void testCreateDigitalSpecimenSpecimenExists() throws Exception { // Given @@ -379,17 +371,15 @@ void testCreateDigitalSpecimenSpecimenExists() throws Exception { List digitalSpecimen = genDigitalSpecimenAttributes(handle); given(pidNameGeneratorService.genHandleList(1)).willReturn(new ArrayList<>(List.of(handle))); - given(pidRepository.searchByNormalisedPhysicalIdentifierFullRecord(anyList())).willReturn( + given(pidRepository.searchByNormalisedPhysicalIdentifier(anyList())).willReturn( digitalSpecimen); // When - Exception e = assertThrows(PidCreationException.class, + Exception e = assertThrows(InvalidRequestException.class, () -> service.createRecords(List.of(request))); // Then - assertThat(e).hasMessage( - "Unable to create PID records. Some requested records are already registered. Verify the following digital specimens:" - + List.of(new String(handle, StandardCharsets.UTF_8))); + assertThat(e.getMessage()).contains(new String(handle, StandardCharsets.UTF_8)); } @Test @@ -409,7 +399,7 @@ void testCreateMediaObjectRecord() throws Exception { ObjectType.MEDIA_OBJECT); given(pidNameGeneratorService.genHandleList(1)).willReturn(new ArrayList<>(List.of(handle))); - given(fdoRecordService.prepareMediaObjectAttributes(any(), any(), any())).willReturn( + given(fdoRecordService.prepareMediaObjectAttributes(any(), any())).willReturn( handleRecordSublist); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -429,7 +419,7 @@ void testCreateMasRecord() throws Exception { List handleRecord = genMasAttributes(handle); given(pidNameGeneratorService.genHandleList(1)).willReturn(new ArrayList<>(List.of(handle))); - given(fdoRecordService.prepareMasRecordAttributes(any(), any(), eq(ObjectType.MAS))).willReturn( + given(fdoRecordService.prepareMasRecordAttributes(any(), any())).willReturn( handleRecord); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -507,7 +497,7 @@ void testCreateDigitalSpecimenBatch() throws Exception { sublist, handles, ObjectType.DIGITAL_SPECIMEN); given(pidNameGeneratorService.genHandleList(handles.size())).willReturn(handles); - given(fdoRecordService.prepareDigitalSpecimenRecordAttributes(any(), any(), any())) + given(fdoRecordService.prepareDigitalSpecimenRecordAttributes(any(), any())) .willReturn(genDigitalSpecimenAttributes(handles.get(0))) .willReturn(genDigitalSpecimenAttributes(handles.get(1))); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -530,7 +520,7 @@ void testCreateAnnotationsBatch() throws Exception { var responseExpected = givenAnnotationResponseWrite(handles); given(pidNameGeneratorService.genHandleList(handles.size())).willReturn(handles); - given(fdoRecordService.prepareAnnotationAttributes(any(), any(), any())) + given(fdoRecordService.prepareAnnotationAttributes(any(), any())) .willReturn(genAnnotationAttributes(handles.get(0), true)) .willReturn(genAnnotationAttributes(handles.get(1), true)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -554,7 +544,7 @@ void testCreateAnnotationsBatchNoHash() throws Exception { var responseExpected = givenRecordResponseWrite(handles, RECORD_TYPE_ANNOTATION); given(pidNameGeneratorService.genHandleList(handles.size())).willReturn(handles); - given(fdoRecordService.prepareAnnotationAttributes(any(), any(), any())) + given(fdoRecordService.prepareAnnotationAttributes(any(), any())) .willReturn(genAnnotationAttributes(handles.get(0), false)) .willReturn(genAnnotationAttributes(handles.get(1), false)); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -576,7 +566,7 @@ void testCreateMappingBatch() throws Exception { var responseExpected = givenRecordResponseWrite(handles, RECORD_TYPE_MAPPING); given(pidNameGeneratorService.genHandleList(handles.size())).willReturn(handles); - given(fdoRecordService.prepareMappingAttributes(any(), any(), any())) + given(fdoRecordService.prepareMappingAttributes(any(), any())) .willReturn(genMappingAttributes(handles.get(0))) .willReturn(genMappingAttributes(handles.get(1))); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -599,7 +589,7 @@ void testCreateSourceSystemBatch() throws Exception { var responseExpected = givenRecordResponseWrite(handles, RECORD_TYPE_SOURCE_SYSTEM); given(pidNameGeneratorService.genHandleList(handles.size())).willReturn(handles); - given(fdoRecordService.prepareSourceSystemAttributes(any(), any(), any())) + given(fdoRecordService.prepareSourceSystemAttributes(any(), any())) .willReturn(genSourceSystemAttributes(handles.get(0))) .willReturn(genSourceSystemAttributes(handles.get(1))); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -625,7 +615,7 @@ void testCreateOrganisationBatch() throws Exception { var responseExpected = givenRecordResponseWrite(handles, RECORD_TYPE_ORGANISATION); given(pidNameGeneratorService.genHandleList(handles.size())).willReturn(handles); - given(fdoRecordService.prepareOrganisationAttributes(any(), any(), any())).willReturn(flatList); + given(fdoRecordService.prepareOrganisationAttributes(any(), any())).willReturn(flatList); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); // When @@ -653,7 +643,7 @@ void testCreateMediaObjectBatch() throws Exception { var responseExpected = givenRecordResponseWriteSmallResponse(sublist, handles, ObjectType.MEDIA_OBJECT); given(pidNameGeneratorService.genHandleList(handles.size())).willReturn(handles); - given(fdoRecordService.prepareMediaObjectAttributes(any(), any(), any())) + given(fdoRecordService.prepareMediaObjectAttributes(any(), any())) .willReturn(genMediaObjectAttributes(handles.get(0))) .willReturn(genMediaObjectAttributes(handles.get(1))); given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); @@ -808,129 +798,6 @@ void testGetHandlesPaged() { assertThat(responseExpectedSecond).isEqualTo(handles); } - @Test - void testUpsertDigitalSpecimens() throws Exception { - // Given - var existingHandle = handles.get(0); - var newHandle = handles.get(1); - var existingPhysId = "Alt ID"; - - var newRecordRequest = givenDigitalSpecimenRequestObjectNullOptionals( - PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL); - var existingRecordRequest = givenDigitalSpecimenRequestObjectNullOptionals(existingPhysId); - List requests = List.of( - genCreateRecordRequest(newRecordRequest, RECORD_TYPE_DS), - genCreateRecordRequest(existingRecordRequest, RECORD_TYPE_DS)); - - var existingRecordAttributes = genDigitalSpecimenAttributes(existingHandle, - existingRecordRequest); - var newRecordAttributes = genDigitalSpecimenAttributes(newHandle, newRecordRequest); - - var expected = new JsonApiWrapperWrite( - List.of(upsertedResponse(getPrimarySpecimenObjectIds(newRecordAttributes), - new String(newHandle)), - upsertedResponse(getPrimarySpecimenObjectIds(existingRecordAttributes), - new String(existingHandle, StandardCharsets.UTF_8)))); - - given(pidRepository.searchByNormalisedPhysicalIdentifier(anyList())).willReturn( - List.of(new HandleAttribute(PRIMARY_SPECIMEN_OBJECT_ID.index(), existingHandle, - PRIMARY_SPECIMEN_OBJECT_ID_TYPE.get(), - (existingRecordRequest.getNormalisedPrimarySpecimenObjectId()).getBytes( - StandardCharsets.UTF_8)))); - given(fdoRecordService.prepareDigitalSpecimenRecordAttributes(eq(newRecordRequest), any(), - any())).willReturn(newRecordAttributes); - given(fdoRecordService.prepareUpdateAttributes(any(), - eq(MAPPER.valueToTree(existingRecordRequest)), any())).willReturn( - existingRecordAttributes); - given(pidNameGeneratorService.genHandleList(anyInt())).willReturn( - new ArrayList<>(List.of(newHandle))); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - - // When - var response = service.upsertDigitalSpecimens(requests); - - // Then - assertThat(response).isEqualTo(expected); - then(pidRepository).should() - .postAndUpdateHandles(CREATED.getEpochSecond(), newRecordAttributes, - List.of(existingRecordAttributes)); - } - - @Test - void testInternalDuplicateSpecimenIds() { - // Given - List requests = List.of( - genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), RECORD_TYPE_DS), - genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), RECORD_TYPE_DS) - ); - var expectedMsg = "Bad Request. Some PhysicalSpecimenObjectIds are duplicated in request body"; - given(pidNameGeneratorService.genHandleList(anyInt())).willReturn(handles); - - // When - var response = assertThrows(InvalidRequestException.class, - () -> service.createRecords(requests)); - - // Then - assertThat(response).hasMessage(expectedMsg); - } - - @Test - void testUpsertDigitalSpecimensOnlyUpdate() throws Exception { - // Given - List requests = List.of( - genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), RECORD_TYPE_DS)); - var existingRecord = genDigitalSpecimenAttributes(handles.get(0)); - - var expected = new JsonApiWrapperWrite( - List.of(upsertedResponse(getPrimarySpecimenObjectIds(existingRecord), HANDLE)) - ); - - given(pidRepository.searchByNormalisedPhysicalIdentifier(anyList())).willReturn( - List.of(new HandleAttribute(PRIMARY_SPECIMEN_OBJECT_ID.index(), handles.get(0), - PRIMARY_SPECIMEN_OBJECT_ID.get(), - (PRIMARY_SPECIMEN_OBJECT_ID_TESTVAL).getBytes( - StandardCharsets.UTF_8)))); - given(fdoRecordService.prepareUpdateAttributes(any(), any(), any())).willReturn( - existingRecord); - given(pidNameGeneratorService.genHandleList(0)).willReturn(new ArrayList<>()); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - - // When - var response = service.upsertDigitalSpecimens(requests); - - // Then - assertThat(response).isEqualTo(expected); - then(pidRepository).should() - .postAndUpdateHandles(CREATED.getEpochSecond(), new ArrayList<>(), - List.of(existingRecord)); - } - - @Test - void testUpsertDigitalSpecimensOnlyCreate() throws Exception { - // Given - List requests = List.of( - genCreateRecordRequest(givenDigitalSpecimenRequestObjectNullOptionals(), RECORD_TYPE_DS)); - var newRecord = genDigitalSpecimenAttributes(handles.get(1)); - var expected = new JsonApiWrapperWrite( - List.of(upsertedResponse(getPrimarySpecimenObjectIds(newRecord), HANDLE_ALT)) - ); - - given(pidRepository.searchByNormalisedPhysicalIdentifier(anyList())).willReturn( - new ArrayList<>()); - given(fdoRecordService.prepareDigitalSpecimenRecordAttributes(any(), any(), any())).willReturn( - newRecord); - given(pidNameGeneratorService.genHandleList(anyInt())).willReturn(List.of(handles.get(0))); - given(profileProperties.getDomain()).willReturn(HANDLE_DOMAIN); - - // When - var response = service.upsertDigitalSpecimens(requests); - - // Then - assertThat(response).isEqualTo(expected); - then(pidRepository).should() - .postAndUpdateHandles(CREATED.getEpochSecond(), newRecord, new ArrayList<>()); - } - @Test void testRollbackHandleCreation() { // Given @@ -959,22 +826,4 @@ void testRollbackHandlesFromPhysId() { then(pidRepository).should().rollbackHandles(List.of(HANDLE)); } - JsonApiDataLinks upsertedResponse(List handleRecord, String handle) - throws Exception { - JsonNode recordAttributes = genObjectNodeAttributeRecord(handleRecord); - var pidLink = new JsonApiLinks(HANDLE_URI + handle); - return new JsonApiDataLinks(handle, ObjectType.DIGITAL_SPECIMEN.toString(), - recordAttributes, pidLink); - } - - private List getPrimarySpecimenObjectIds( - List handleAttributes) { - List primaryIdList = new ArrayList<>(); - for (var row : handleAttributes) { - if (row.getType().equals(PRIMARY_SPECIMEN_OBJECT_ID.get())) { - primaryIdList.add(row); - } - } - return primaryIdList; - } }