From ae06dab2eb21caf7fd9e7a140df073504c952047 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Sat, 30 Nov 2024 21:25:13 +0100 Subject: [PATCH 1/2] wip --- pom.xml | 1 + .../DdValidateDansBagApplication.java | 8 +-- .../core/service/FileServiceImpl.java | 12 +++- .../core/service/RuleEngineService.java | 2 + .../core/service/RuleEngineServiceImpl.java | 57 ++++++++++++++++++- .../ValidateLocalDirApiResource.java | 42 ++++++++++++++ .../ValidateOkYamlMessageBodyWriter.java | 50 ---------------- .../resources/ValidateZipApiResource.java | 47 +++++++++++++++ .../ValidateResourceIntegrationTest.java | 30 ---------- 9 files changed, 160 insertions(+), 89 deletions(-) create mode 100644 src/main/java/nl/knaw/dans/validatedansbag/resources/ValidateLocalDirApiResource.java delete mode 100644 src/main/java/nl/knaw/dans/validatedansbag/resources/ValidateOkYamlMessageBodyWriter.java create mode 100644 src/main/java/nl/knaw/dans/validatedansbag/resources/ValidateZipApiResource.java diff --git a/pom.xml b/pom.xml index 12174752..4759491f 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,7 @@ nl.knaw.dans.validatedansbag.DdValidateDansBagApplication + 0.5.1-SNAPSHOT diff --git a/src/main/java/nl/knaw/dans/validatedansbag/DdValidateDansBagApplication.java b/src/main/java/nl/knaw/dans/validatedansbag/DdValidateDansBagApplication.java index 773d8f58..cf72965a 100644 --- a/src/main/java/nl/knaw/dans/validatedansbag/DdValidateDansBagApplication.java +++ b/src/main/java/nl/knaw/dans/validatedansbag/DdValidateDansBagApplication.java @@ -16,7 +16,6 @@ package nl.knaw.dans.validatedansbag; -import io.dropwizard.configuration.FileConfigurationSourceProvider; import io.dropwizard.core.Application; import io.dropwizard.core.setup.Bootstrap; import io.dropwizard.core.setup.Environment; @@ -45,8 +44,9 @@ import nl.knaw.dans.validatedansbag.core.validator.PolygonListValidatorImpl; import nl.knaw.dans.validatedansbag.health.XmlSchemaHealthCheck; import nl.knaw.dans.validatedansbag.resources.IllegalArgumentExceptionMapper; -import nl.knaw.dans.validatedansbag.resources.ValidateOkYamlMessageBodyWriter; +import nl.knaw.dans.validatedansbag.resources.ValidateLocalDirApiResource; import nl.knaw.dans.validatedansbag.resources.ValidateResource; +import nl.knaw.dans.validatedansbag.resources.ValidateZipApiResource; import nl.knaw.dans.vaultcatalog.client.invoker.ApiClient; import nl.knaw.dans.vaultcatalog.client.resources.DefaultApi; @@ -55,7 +55,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; @@ -125,7 +124,8 @@ public void run(final DdValidateDansBagConfiguration configuration, final Enviro environment.jersey().register(new IllegalArgumentExceptionMapper()); environment.jersey().register(new ValidateResource(ruleEngineService, fileService)); - environment.jersey().register(new ValidateOkYamlMessageBodyWriter()); + environment.jersey().register(new ValidateZipApiResource(ruleEngineService, fileService)); + environment.jersey().register(new ValidateLocalDirApiResource(ruleEngineService)); environment.healthChecks().register("xml-schemas", new XmlSchemaHealthCheck(xmlSchemaValidator)); } diff --git a/src/main/java/nl/knaw/dans/validatedansbag/core/service/FileServiceImpl.java b/src/main/java/nl/knaw/dans/validatedansbag/core/service/FileServiceImpl.java index 1a90acec..53c6326d 100644 --- a/src/main/java/nl/knaw/dans/validatedansbag/core/service/FileServiceImpl.java +++ b/src/main/java/nl/knaw/dans/validatedansbag/core/service/FileServiceImpl.java @@ -32,9 +32,17 @@ public class FileServiceImpl implements FileService { private final Path baseFolder; + private final Path tempFolder; public FileServiceImpl(Path baseFolder) { - this.baseFolder = baseFolder; + this.baseFolder = baseFolder.normalize().toAbsolutePath(); + + try { + this.tempFolder = Files.createDirectories(this.baseFolder.resolve("temp")); + } + catch (IOException e) { + throw new RuntimeException("Could not create temp directory", e); + } } @Override @@ -84,7 +92,7 @@ public CharBuffer readFileContents(Path path, Charset charset) throws IOExceptio @Override public Path extractZipFile(InputStream inputStream) throws IOException { - var tempPath = Files.createTempDirectory(this.baseFolder, "bag-"); + var tempPath = Files.createTempDirectory(this.tempFolder, "bag-"); try (var input = new ZipInputStream(inputStream)) { var entry = input.getNextEntry(); diff --git a/src/main/java/nl/knaw/dans/validatedansbag/core/service/RuleEngineService.java b/src/main/java/nl/knaw/dans/validatedansbag/core/service/RuleEngineService.java index 59aeb5ca..53b42a81 100644 --- a/src/main/java/nl/knaw/dans/validatedansbag/core/service/RuleEngineService.java +++ b/src/main/java/nl/knaw/dans/validatedansbag/core/service/RuleEngineService.java @@ -15,6 +15,7 @@ */ package nl.knaw.dans.validatedansbag.core.service; +import nl.knaw.dans.validatedansbag.api.ValidateOkDto; import nl.knaw.dans.validatedansbag.core.engine.DepositType; import nl.knaw.dans.validatedansbag.core.engine.RuleValidationResult; @@ -25,4 +26,5 @@ public interface RuleEngineService { List validateBag(Path path, DepositType depositType) throws Exception; + ValidateOkDto validateBag(Path path, DepositType depositType, String bagLocation) throws Exception; } diff --git a/src/main/java/nl/knaw/dans/validatedansbag/core/service/RuleEngineServiceImpl.java b/src/main/java/nl/knaw/dans/validatedansbag/core/service/RuleEngineServiceImpl.java index d593ee52..be23d1e8 100644 --- a/src/main/java/nl/knaw/dans/validatedansbag/core/service/RuleEngineServiceImpl.java +++ b/src/main/java/nl/knaw/dans/validatedansbag/core/service/RuleEngineServiceImpl.java @@ -16,6 +16,8 @@ package nl.knaw.dans.validatedansbag.core.service; import lombok.extern.slf4j.Slf4j; +import nl.knaw.dans.validatedansbag.api.ValidateOkDto; +import nl.knaw.dans.validatedansbag.api.ValidateOkRuleViolationsInnerDto; import nl.knaw.dans.validatedansbag.core.BagNotFoundException; import nl.knaw.dans.validatedansbag.core.engine.DepositType; import nl.knaw.dans.validatedansbag.core.engine.NumberedRule; @@ -25,6 +27,7 @@ import java.nio.file.Path; import java.util.List; +import java.util.stream.Collectors; @Slf4j public class RuleEngineServiceImpl implements RuleEngineService { @@ -34,8 +37,8 @@ public class RuleEngineServiceImpl implements RuleEngineService { private final NumberedRule[] ruleSet; public RuleEngineServiceImpl(RuleEngine ruleEngine, - FileService fileService, - NumberedRule[] ruleSet) { + FileService fileService, + NumberedRule[] ruleSet) { this.ruleEngine = ruleEngine; this.fileService = fileService; this.ruleSet = ruleSet; @@ -54,10 +57,58 @@ public List validateBag(Path path, DepositType depositType return ruleEngine.validateRules(path, this.ruleSet, depositType); } + @Override + public ValidateOkDto validateBag(Path path, DepositType depositType, String bagLocation) throws Exception { + log.info("Validating bag on path '{}'", path); + + if (!fileService.isReadable(path)) { + log.warn("Path {} could not not be found or is not readable", path); + throw new BagNotFoundException(String.format("Bag on path '%s' could not be found or read", path)); + } + + var results = ruleEngine.validateRules(path, this.ruleSet, depositType); + var isValid = results.stream().noneMatch(r -> r.getStatus().equals(RuleValidationResult.RuleValidationResultStatus.FAILURE)); + + var result = new ValidateOkDto(); + result.setBagLocation(bagLocation); + result.setIsCompliant(isValid); + result.setName(path.getFileName().toString()); + result.setProfileVersion("1.1.0"); + result.setInformationPackageType(toInfoPackageType(depositType)); + result.setRuleViolations(results.stream() + .filter(r -> r.getStatus().equals(RuleValidationResult.RuleValidationResultStatus.FAILURE)) + .map(rule -> { + var ret = new ValidateOkRuleViolationsInnerDto(); + ret.setRule(rule.getNumber()); + + var message = new StringBuilder(); + + if (rule.getErrorMessage() != null) { + message.append(rule.getErrorMessage()); + } + + ret.setViolation(message.toString()); + return ret; + }) + .collect(Collectors.toList())); + + log.debug("Validation result: {}", result); + + return result; + } + + private ValidateOkDto.InformationPackageTypeEnum toInfoPackageType(DepositType value) { + if (DepositType.MIGRATION.equals(value)) { + return ValidateOkDto.InformationPackageTypeEnum.MIGRATION; + } + return ValidateOkDto.InformationPackageTypeEnum.DEPOSIT; + } + public void validateRuleConfiguration() { try { this.ruleEngine.validateRuleConfiguration(this.ruleSet); - } catch (RuleEngineConfigurationException e) { + } + catch (RuleEngineConfigurationException e) { throw new RuntimeException("Rule configuration is not valid", e); } } diff --git a/src/main/java/nl/knaw/dans/validatedansbag/resources/ValidateLocalDirApiResource.java b/src/main/java/nl/knaw/dans/validatedansbag/resources/ValidateLocalDirApiResource.java new file mode 100644 index 00000000..e9375992 --- /dev/null +++ b/src/main/java/nl/knaw/dans/validatedansbag/resources/ValidateLocalDirApiResource.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.validatedansbag.resources; + +import lombok.AllArgsConstructor; +import nl.knaw.dans.validatedansbag.api.ValidateCommandDto; +import nl.knaw.dans.validatedansbag.core.engine.DepositType; +import nl.knaw.dans.validatedansbag.core.service.RuleEngineService; + +import javax.ws.rs.core.Response; +import java.nio.file.Path; + +@AllArgsConstructor +public class ValidateLocalDirApiResource implements ValidateLocalDirApi { + private final RuleEngineService ruleEngineService; + + @Override + public Response validateLocalDirPost(ValidateCommandDto validateCommandDto) { + try { + var result = ruleEngineService.validateBag(Path.of(validateCommandDto.getBagLocation()), + DepositType.valueOf(validateCommandDto.getPackageType().toString()), + validateCommandDto.getBagLocation()); + return Response.ok(result).build(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/nl/knaw/dans/validatedansbag/resources/ValidateOkYamlMessageBodyWriter.java b/src/main/java/nl/knaw/dans/validatedansbag/resources/ValidateOkYamlMessageBodyWriter.java deleted file mode 100644 index 25453c8b..00000000 --- a/src/main/java/nl/knaw/dans/validatedansbag/resources/ValidateOkYamlMessageBodyWriter.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2022 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package nl.knaw.dans.validatedansbag.resources; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import nl.knaw.dans.validatedansbag.api.ValidateOkDto; - -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.ext.MessageBodyWriter; -import javax.ws.rs.ext.Provider; -import java.io.IOException; -import java.io.OutputStream; -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; - -@Provider -@Produces(MediaType.TEXT_PLAIN) -public class ValidateOkYamlMessageBodyWriter implements MessageBodyWriter { - private final ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()); - - @Override - public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { - return type == ValidateOkDto.class; - } - - @Override - public void writeTo(ValidateOkDto ValidateOkDtoResult, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, - OutputStream entityStream) throws IOException, WebApplicationException { - var result = objectMapper.writeValueAsString(ValidateOkDtoResult); - entityStream.write(result.getBytes(StandardCharsets.UTF_8)); - } -} diff --git a/src/main/java/nl/knaw/dans/validatedansbag/resources/ValidateZipApiResource.java b/src/main/java/nl/knaw/dans/validatedansbag/resources/ValidateZipApiResource.java new file mode 100644 index 00000000..3089c1d5 --- /dev/null +++ b/src/main/java/nl/knaw/dans/validatedansbag/resources/ValidateZipApiResource.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.validatedansbag.resources; + +import lombok.AllArgsConstructor; +import nl.knaw.dans.validatedansbag.core.engine.DepositType; +import nl.knaw.dans.validatedansbag.core.service.FileService; +import nl.knaw.dans.validatedansbag.core.service.RuleEngineService; + +import javax.ws.rs.core.Response; +import java.io.File; +import java.io.FileInputStream; + +@AllArgsConstructor +public class ValidateZipApiResource implements ValidateZipApi { + private final RuleEngineService ruleEngineService; + private final FileService fileService; + + @Override + public Response validateZipPost(File body) { + try (var inputStream = new FileInputStream(body)) { + var dir = fileService.extractZipFile(inputStream); + var bagDir = fileService.getFirstDirectory(dir); + if (bagDir.isEmpty()) { + return Response.status(Response.Status.BAD_REQUEST).entity("No bag directory found in zip file").build(); + } + var result = ruleEngineService.validateBag(bagDir.get(), DepositType.DEPOSIT, "ZIP"); + return Response.ok(result).build(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/nl/knaw/dans/validatedansbag/resources/ValidateResourceIntegrationTest.java b/src/test/java/nl/knaw/dans/validatedansbag/resources/ValidateResourceIntegrationTest.java index 860ffef9..5f9c7a22 100644 --- a/src/test/java/nl/knaw/dans/validatedansbag/resources/ValidateResourceIntegrationTest.java +++ b/src/test/java/nl/knaw/dans/validatedansbag/resources/ValidateResourceIntegrationTest.java @@ -91,7 +91,6 @@ public boolean isValidLicense(String license) { static { EXT = ResourceExtension.builder() .addProvider(MultiPartFeature.class) - .addProvider(ValidateOkYamlMessageBodyWriter.class) .addResource(buildValidateResource()) .build(); } @@ -367,35 +366,6 @@ void validateFormData_should_not_throw_internal_server_error_on_incomplete_manif .endsWith("original-metadata.zip] is in the payload directory but isn't listed in any manifest!"); } - @Test - void validateZipFile_should_return_a_textual_representation_when_requested() throws Exception { - var inputStream = Files.newInputStream(Path.of(baseTestFolder, "/zips/invalid-sha1.zip")); - - var embargoResultJson = """ - { - "status": "OK", - "data": { - "message": "24" - } - }"""; - var maxEmbargoDurationResult = new MockedDataverseResponse(embargoResultJson, DataMessage.class); - Mockito.when(dataverseService.getMaxEmbargoDurationInMonths()) - .thenReturn(maxEmbargoDurationResult); - - var response = EXT.target("/validate") - .request() - .header("accept", "text/plain") - .header("Authorization", basicUsernamePassword("user001", "user001")) - .post(Entity.entity(inputStream, MediaType.valueOf("application/zip")), String.class); - - assertTrue(response.contains("Bag location:")); - assertTrue(response.contains("Name:")); - assertTrue(response.contains("Profile version:")); - assertTrue(response.contains("Information package type:")); - assertTrue(response.contains("Is compliant:")); - assertTrue(response.contains("Rule violations:")); - } - @Test void validateFormData_with_invalid_path_should_return_400_error() { var data = new ValidateCommandDto(); From cf6d3bb5ae87db4b54c45f496618b4dea26947d5 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Sun, 1 Dec 2024 20:16:00 +0100 Subject: [PATCH 2/2] Removed old API --- .../DdValidateDansBagApplication.java | 1 - .../core/engine/RuleEngineImpl.java | 20 +- .../core/service/RuleEngineServiceImpl.java | 2 +- .../ValidateLocalDirApiResource.java | 5 + ...teLocalDirApiResourceIntegrationTest.java} | 730 +++++++++--------- .../resources/ValidateResourceTest.java | 168 ---- 6 files changed, 363 insertions(+), 563 deletions(-) rename src/test/java/nl/knaw/dans/validatedansbag/resources/{ValidateResourceIntegrationTest.java => ValidateLocalDirApiResourceIntegrationTest.java} (51%) delete mode 100644 src/test/java/nl/knaw/dans/validatedansbag/resources/ValidateResourceTest.java diff --git a/src/main/java/nl/knaw/dans/validatedansbag/DdValidateDansBagApplication.java b/src/main/java/nl/knaw/dans/validatedansbag/DdValidateDansBagApplication.java index cf72965a..43fb5e48 100644 --- a/src/main/java/nl/knaw/dans/validatedansbag/DdValidateDansBagApplication.java +++ b/src/main/java/nl/knaw/dans/validatedansbag/DdValidateDansBagApplication.java @@ -123,7 +123,6 @@ public void run(final DdValidateDansBagConfiguration configuration, final Enviro configuration.getDataverse() != null ? ruleSets.getDataStationSet() : ruleSets.getVaasSet()); environment.jersey().register(new IllegalArgumentExceptionMapper()); - environment.jersey().register(new ValidateResource(ruleEngineService, fileService)); environment.jersey().register(new ValidateZipApiResource(ruleEngineService, fileService)); environment.jersey().register(new ValidateLocalDirApiResource(ruleEngineService)); diff --git a/src/main/java/nl/knaw/dans/validatedansbag/core/engine/RuleEngineImpl.java b/src/main/java/nl/knaw/dans/validatedansbag/core/engine/RuleEngineImpl.java index 18a272ea..dfcce130 100644 --- a/src/main/java/nl/knaw/dans/validatedansbag/core/engine/RuleEngineImpl.java +++ b/src/main/java/nl/knaw/dans/validatedansbag/core/engine/RuleEngineImpl.java @@ -60,7 +60,7 @@ public List validateRules(Path bag, NumberedRule[] rules, // create a copy, because we will modify this list var remainingRules = new ArrayList<>(rulesToExecute); - while (remainingRules.size() > 0) { + while (!remainingRules.isEmpty()) { var toRemove = new HashSet(); for (var rule : remainingRules) { @@ -82,19 +82,11 @@ else if (canBeExecuted(rule, ruleResults)) { var response = rule.getRule().validate(bag); log.trace("Task result: {}", response.getStatus()); - RuleValidationResult ruleValidationResult = null; - - switch (response.getStatus()) { - case SUCCESS: - ruleValidationResult = new RuleValidationResult(number, RuleValidationResult.RuleValidationResultStatus.SUCCESS); - break; - case SKIP_DEPENDENCIES: - ruleValidationResult = new RuleValidationResult(number, RuleValidationResult.RuleValidationResultStatus.SUCCESS, true); - break; - case ERROR: - ruleValidationResult = new RuleValidationResult(number, RuleValidationResult.RuleValidationResultStatus.FAILURE, formatErrorMessages(response.getErrorMessages())); - break; - } + RuleValidationResult ruleValidationResult = switch (response.getStatus()) { + case SUCCESS -> new RuleValidationResult(number, RuleValidationResult.RuleValidationResultStatus.SUCCESS); + case SKIP_DEPENDENCIES -> new RuleValidationResult(number, RuleValidationResult.RuleValidationResultStatus.SUCCESS, true); + case ERROR -> new RuleValidationResult(number, RuleValidationResult.RuleValidationResultStatus.FAILURE, formatErrorMessages(response.getErrorMessages())); + }; ruleResults.put(number, ruleValidationResult); diff --git a/src/main/java/nl/knaw/dans/validatedansbag/core/service/RuleEngineServiceImpl.java b/src/main/java/nl/knaw/dans/validatedansbag/core/service/RuleEngineServiceImpl.java index be23d1e8..ff43d0bf 100644 --- a/src/main/java/nl/knaw/dans/validatedansbag/core/service/RuleEngineServiceImpl.java +++ b/src/main/java/nl/knaw/dans/validatedansbag/core/service/RuleEngineServiceImpl.java @@ -73,7 +73,7 @@ public ValidateOkDto validateBag(Path path, DepositType depositType, String bagL result.setBagLocation(bagLocation); result.setIsCompliant(isValid); result.setName(path.getFileName().toString()); - result.setProfileVersion("1.1.0"); + result.setProfileVersion("1.2.0"); result.setInformationPackageType(toInfoPackageType(depositType)); result.setRuleViolations(results.stream() .filter(r -> r.getStatus().equals(RuleValidationResult.RuleValidationResultStatus.FAILURE)) diff --git a/src/main/java/nl/knaw/dans/validatedansbag/resources/ValidateLocalDirApiResource.java b/src/main/java/nl/knaw/dans/validatedansbag/resources/ValidateLocalDirApiResource.java index e9375992..8dd723a3 100644 --- a/src/main/java/nl/knaw/dans/validatedansbag/resources/ValidateLocalDirApiResource.java +++ b/src/main/java/nl/knaw/dans/validatedansbag/resources/ValidateLocalDirApiResource.java @@ -17,10 +17,12 @@ import lombok.AllArgsConstructor; import nl.knaw.dans.validatedansbag.api.ValidateCommandDto; +import nl.knaw.dans.validatedansbag.core.BagNotFoundException; import nl.knaw.dans.validatedansbag.core.engine.DepositType; import nl.knaw.dans.validatedansbag.core.service.RuleEngineService; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; import java.nio.file.Path; @AllArgsConstructor @@ -35,6 +37,9 @@ public Response validateLocalDirPost(ValidateCommandDto validateCommandDto) { validateCommandDto.getBagLocation()); return Response.ok(result).build(); } + catch (BagNotFoundException e) { + return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build(); + } catch (Exception e) { throw new RuntimeException(e); } diff --git a/src/test/java/nl/knaw/dans/validatedansbag/resources/ValidateResourceIntegrationTest.java b/src/test/java/nl/knaw/dans/validatedansbag/resources/ValidateLocalDirApiResourceIntegrationTest.java similarity index 51% rename from src/test/java/nl/knaw/dans/validatedansbag/resources/ValidateResourceIntegrationTest.java rename to src/test/java/nl/knaw/dans/validatedansbag/resources/ValidateLocalDirApiResourceIntegrationTest.java index 5f9c7a22..202f7569 100644 --- a/src/test/java/nl/knaw/dans/validatedansbag/resources/ValidateResourceIntegrationTest.java +++ b/src/test/java/nl/knaw/dans/validatedansbag/resources/ValidateLocalDirApiResourceIntegrationTest.java @@ -53,7 +53,6 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.Map; @@ -61,19 +60,18 @@ import java.util.Set; import java.util.stream.Collectors; -import static nl.knaw.dans.validatedansbag.resources.util.TestUtil.basicUsernamePassword; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(DropwizardExtensionsSupport.class) -class ValidateResourceIntegrationTest { +class ValidateLocalDirApiResourceIntegrationTest { public static final ResourceExtension EXT; private static final DataverseService dataverseService = Mockito.mock(DataverseService.class); private static final XmlSchemaValidator xmlSchemaValidator = Mockito.mock(XmlSchemaValidator.class); - private static final String baseTestFolder = Objects.requireNonNull(Objects.requireNonNull(ValidateResourceIntegrationTest.class.getClassLoader().getResource("")).getPath()); + private static final String baseTestFolder = Objects.requireNonNull(Objects.requireNonNull(ValidateLocalDirApiResourceIntegrationTest.class.getClassLoader().getResource("")).getPath()); private static final LicenseValidator licenseValidator = new LicenseValidator() { @@ -83,19 +81,19 @@ public boolean isValidUri(String license) { } @Override - public boolean isValidLicense(String license) { + public boolean isValidLicense(String license) { return true; } }; static { EXT = ResourceExtension.builder() - .addProvider(MultiPartFeature.class) - .addResource(buildValidateResource()) - .build(); + .addProvider(MultiPartFeature.class) + .addResource(buildValidateResource()) + .build(); } - static ValidateResource buildValidateResource() { + static ValidateLocalDirApiResource buildValidateResource() { var fileService = new FileServiceImpl(Path.of(baseTestFolder)); var bagItMetadataReader = new BagItMetadataReaderImpl(); var xmlReader = new XmlReaderImpl(); @@ -106,18 +104,18 @@ static ValidateResource buildValidateResource() { var vaultService = Mockito.mock(VaultCatalogClient.class); var organizationIdentifierPrefixValidator = new OrganizationIdentifierPrefixValidatorImpl( - List.of("u1:", "u2:") + List.of("u1:", "u2:") ); // set up the engine and the service that has a default set of rules var ruleEngine = new RuleEngineImpl(); var ruleSets = new RuleSets( - dataverseService, fileService, filesXmlService, originalFilepathsService, xmlReader, - bagItMetadataReader, xmlSchemaValidator, licenseValidator, identifierValidator, polygonListValidator, organizationIdentifierPrefixValidator, - vaultService, Map.of(), Map.of()); + dataverseService, fileService, filesXmlService, originalFilepathsService, xmlReader, + bagItMetadataReader, xmlSchemaValidator, licenseValidator, identifierValidator, polygonListValidator, organizationIdentifierPrefixValidator, + vaultService, Map.of(), Map.of()); var ruleEngineService = new RuleEngineServiceImpl(ruleEngine, fileService, ruleSets.getDataStationSet()); - return new ValidateResource(ruleEngineService, fileService); + return new ValidateLocalDirApiResource(ruleEngineService); } @BeforeEach @@ -133,27 +131,25 @@ void validateFormData_should_have_validation_errors_with_invalid_bag() throws IO var data = new ValidateCommandDto(); data.setBagLocation(filename); data.setPackageType(ValidateCommandDto.PackageTypeEnum.DEPOSIT); - var multipart = new FormDataMultiPart() - .field("command", data, MediaType.APPLICATION_JSON_TYPE); var embargoResultJson = """ - { - "status": "OK", - "data": { - "message": "24" - } - }"""; + { + "status": "OK", + "data": { + "message": "24" + } + }"""; var maxEmbargoDurationResult = new MockedDataverseResponse(embargoResultJson, DataMessage.class); Mockito.when(dataverseService.getMaxEmbargoDurationInMonths()) - .thenReturn(maxEmbargoDurationResult); + .thenReturn(maxEmbargoDurationResult); - var response = EXT.target("/validate") - .register(MultiPartFeature.class) - .request() - .post(Entity.entity(multipart, multipart.getMediaType()), ValidateOkDto.class); + var response = EXT.target("/validateLocalDir") + .register(MultiPartFeature.class) + .request() + .post(Entity.entity(data, MediaType.APPLICATION_JSON_TYPE), ValidateOkDto.class); assertFalse(response.getIsCompliant()); - assertEquals("1.1.0", response.getProfileVersion()); + assertEquals("1.2.0", response.getProfileVersion()); assertEquals(ValidateOkDto.InformationPackageTypeEnum.DEPOSIT, response.getInformationPackageType()); assertEquals(filename, response.getBagLocation()); assertFalse(response.getRuleViolations().isEmpty()); @@ -166,16 +162,14 @@ void validateFormData_should_return_500_when_xml_errors_occur() throws Exception var data = new ValidateCommandDto(); data.setBagLocation(filename); data.setPackageType(ValidateCommandDto.PackageTypeEnum.DEPOSIT); - var multipart = new FormDataMultiPart() - .field("command", data, MediaType.APPLICATION_JSON_TYPE); Mockito.when(xmlSchemaValidator.validateDocument(Mockito.any(), Mockito.anyString())) - .thenThrow(new SAXException("Something is broken")); + .thenThrow(new SAXException("Something is broken")); - try (var response = EXT.target("/validate") - .register(MultiPartFeature.class) - .request() - .post(Entity.entity(multipart, multipart.getMediaType()), Response.class)) { + try (var response = EXT.target("/validateLocalDir") + .register(MultiPartFeature.class) + .request() + .post(Entity.entity(data, MediaType.APPLICATION_JSON_TYPE), Response.class)) { assertEquals(500, response.getStatus()); } @@ -189,71 +183,68 @@ void validateFormData_should_validate_ok_with_valid_bag_and_original_filepaths() data.setBagLocation(filename); data.setPackageType(ValidateCommandDto.PackageTypeEnum.MIGRATION); - var multipart = new FormDataMultiPart() - .field("command", data, MediaType.APPLICATION_JSON_TYPE); - var searchResultsJson = """ - { - "status": "OK", - "data": { - "q": "NBN:urn:nbn:nl:ui:13-025de6e2-bdcf-4622-b134-282b4c590f42", - "total_count": 1, - "start": 0, - "spelling_alternatives": {}, - "items": [ - { - "name": "Manual Test", - "type": "dataset", - "url": "https://doi.org/10.5072/FK2/QZZSST", - "global_id": "doi:10.5072/FK2/QZZSST" - } - ], - "count_in_response": 1 + { + "status": "OK", + "data": { + "q": "NBN:urn:nbn:nl:ui:13-025de6e2-bdcf-4622-b134-282b4c590f42", + "total_count": 1, + "start": 0, + "spelling_alternatives": {}, + "items": [ + { + "name": "Manual Test", + "type": "dataset", + "url": "https://doi.org/10.5072/FK2/QZZSST", + "global_id": "doi:10.5072/FK2/QZZSST" } - }"""; + ], + "count_in_response": 1 + } + }"""; var dataverseRoleAssignmentsJson = """ + { + "status": "OK", + "data": [ { - "status": "OK", - "data": [ - { - "id": 6, - "assignee": "@user001", - "roleId": 11, - "_roleAlias": "datasetcreator", - "definitionPointId": 2 - } - ] - }"""; + "id": 6, + "assignee": "@user001", + "roleId": 11, + "_roleAlias": "datasetcreator", + "definitionPointId": 2 + } + ] + }"""; var embargoResultJson = """ - { - "status": "OK", - "data": { - "message": "24" - } - }"""; + { + "status": "OK", + "data": { + "message": "24" + } + }"""; var swordTokenResult = new MockedDataverseResponse(searchResultsJson, SearchResult.class); var dataverseRoleAssignmentsResult = new MockedDataverseResponse>(dataverseRoleAssignmentsJson, List.class, RoleAssignmentReadOnly.class); var maxEmbargoDurationResult = new MockedDataverseResponse(embargoResultJson, DataMessage.class); Mockito.when(dataverseService.searchBySwordToken(Mockito.anyString())) - .thenReturn(swordTokenResult); + .thenReturn(swordTokenResult); Mockito.when(dataverseService.getDataverseRoleAssignments(Mockito.anyString())) - .thenReturn(dataverseRoleAssignmentsResult); + .thenReturn(dataverseRoleAssignmentsResult); Mockito.when(dataverseService.getMaxEmbargoDurationInMonths()) - .thenReturn(maxEmbargoDurationResult); + .thenReturn(maxEmbargoDurationResult); - var response = EXT.target("/validate") - .register(MultiPartFeature.class) - .request() - .post(Entity.entity(multipart, multipart.getMediaType()), ValidateOkDto.class); + var response = EXT.target("/validateLocalDir") + .register(MultiPartFeature.class) + .request() + .post(Entity.entity(data, MediaType.APPLICATION_JSON_TYPE), ValidateOkDto.class); assertTrue(response.getIsCompliant()); - assertEquals("1.1.0", response.getProfileVersion()); + assertEquals("1.2.0", response.getProfileVersion()); assertEquals(ValidateOkDto.InformationPackageTypeEnum.MIGRATION, response.getInformationPackageType()); assertEquals(filename, response.getBagLocation()); assertEquals(0, response.getRuleViolations().size()); @@ -267,29 +258,26 @@ void validateFormData_should_have_validation_errors_with_invalid_bag_and_origina data.setBagLocation(filename); data.setPackageType(ValidateCommandDto.PackageTypeEnum.MIGRATION); - var multipart = new FormDataMultiPart() - .field("command", data, MediaType.APPLICATION_JSON_TYPE); - var embargoResultJson = """ - { - "status": "OK", - "data": { - "message": "24" - } - }"""; + { + "status": "OK", + "data": { + "message": "24" + } + }"""; var maxEmbargoDurationResult = new MockedDataverseResponse(embargoResultJson, DataMessage.class); Mockito.when(dataverseService.getMaxEmbargoDurationInMonths()) - .thenReturn(maxEmbargoDurationResult); + .thenReturn(maxEmbargoDurationResult); - var response = EXT.target("/validate") - .register(MultiPartFeature.class) - .request() - .post(Entity.entity(multipart, multipart.getMediaType()), ValidateOkDto.class); + var response = EXT.target("/validateLocalDir") + .register(MultiPartFeature.class) + .request() + .post(Entity.entity(data, MediaType.APPLICATION_JSON_TYPE), ValidateOkDto.class); assertFalse(response.getIsCompliant()); - assertEquals("1.1.0", response.getProfileVersion()); + assertEquals("1.2.0", response.getProfileVersion()); assertEquals(ValidateOkDto.InformationPackageTypeEnum.MIGRATION, response.getInformationPackageType()); assertEquals(filename, response.getBagLocation()); } @@ -301,27 +289,25 @@ void validateFormData_should_have_validation_errors_with_not_allowed_original_me var data = new ValidateCommandDto(); data.setBagLocation(filename); data.setPackageType(ValidateCommandDto.PackageTypeEnum.DEPOSIT); - var multipart = new FormDataMultiPart() - .field("command", data, MediaType.APPLICATION_JSON_TYPE); var embargoResultJson = """ - { - "status": "OK", - "data": { - "message": "24" - } - }"""; + { + "status": "OK", + "data": { + "message": "24" + } + }"""; var maxEmbargoDurationResult = new MockedDataverseResponse(embargoResultJson, DataMessage.class); Mockito.when(dataverseService.getMaxEmbargoDurationInMonths()) - .thenReturn(maxEmbargoDurationResult); + .thenReturn(maxEmbargoDurationResult); - var response = EXT.target("/validate") - .register(MultiPartFeature.class) - .request() - .post(Entity.entity(multipart, multipart.getMediaType()), ValidateOkDto.class); + var response = EXT.target("/validateLocalDir") + .register(MultiPartFeature.class) + .request() + .post(Entity.entity(data, MediaType.APPLICATION_JSON_TYPE), ValidateOkDto.class); assertFalse(response.getIsCompliant()); - assertEquals("1.1.0", response.getProfileVersion()); + assertEquals("1.2.0", response.getProfileVersion()); assertEquals(ValidateOkDto.InformationPackageTypeEnum.DEPOSIT, response.getInformationPackageType()); assertEquals(filename, response.getBagLocation()); assertThat(response.getRuleViolations().size()).isEqualTo(1); @@ -337,33 +323,31 @@ void validateFormData_should_not_throw_internal_server_error_on_incomplete_manif var data = new ValidateCommandDto(); data.setBagLocation(filename); data.setPackageType(ValidateCommandDto.PackageTypeEnum.DEPOSIT); - var multipart = new FormDataMultiPart() - .field("command", data, MediaType.APPLICATION_JSON_TYPE); var embargoResultJson = """ - { - "status": "OK", - "data": { - "message": "24" - } - }"""; + { + "status": "OK", + "data": { + "message": "24" + } + }"""; var maxEmbargoDurationResult = new MockedDataverseResponse(embargoResultJson, DataMessage.class); Mockito.when(dataverseService.getMaxEmbargoDurationInMonths()) - .thenReturn(maxEmbargoDurationResult); + .thenReturn(maxEmbargoDurationResult); - var response = EXT.target("/validate") - .register(MultiPartFeature.class) - .request() - .post(Entity.entity(multipart, multipart.getMediaType()), ValidateOkDto.class); + var response = EXT.target("/validateLocalDir") + .register(MultiPartFeature.class) + .request() + .post(Entity.entity(data, MediaType.APPLICATION_JSON_TYPE), ValidateOkDto.class); assertFalse(response.getIsCompliant()); - assertEquals("1.1.0", response.getProfileVersion()); + assertEquals("1.2.0", response.getProfileVersion()); assertEquals(ValidateOkDto.InformationPackageTypeEnum.DEPOSIT, response.getInformationPackageType()); assertEquals(filename, response.getBagLocation()); assertThat(response.getRuleViolations().size()).isEqualTo(1); assertThat(response.getRuleViolations().get(0).getRule()).isEqualTo("1.1.1"); assertThat(response.getRuleViolations().get(0).getViolation()) - .endsWith("original-metadata.zip] is in the payload directory but isn't listed in any manifest!"); + .endsWith("original-metadata.zip] is in the payload directory but isn't listed in any manifest!"); } @Test @@ -372,13 +356,10 @@ void validateFormData_with_invalid_path_should_return_400_error() { data.setBagLocation(baseTestFolder + "/some/non/existing/filename"); data.setPackageType(ValidateCommandDto.PackageTypeEnum.DEPOSIT); - var multipart = new FormDataMultiPart() - .field("command", data, MediaType.APPLICATION_JSON_TYPE); - - try (var response = EXT.target("/validate") - .register(MultiPartFeature.class) - .request() - .post(Entity.entity(multipart, multipart.getMediaType()), Response.class)) { + try (var response = EXT.target("/validateLocalDir") + .register(MultiPartFeature.class) + .request() + .post(Entity.entity(data, MediaType.APPLICATION_JSON_TYPE), Response.class)) { assertEquals(400, response.getStatus()); } @@ -392,101 +373,98 @@ void validateFormData_with_HasOrganizationalIdentifier_should_validate_if_result data.setBagLocation(filename); data.setPackageType(ValidateCommandDto.PackageTypeEnum.DEPOSIT); - var multipart = new FormDataMultiPart() - .field("command", data, MediaType.APPLICATION_JSON_TYPE); - var searchResultsJson = """ - { - "status": "OK", - "data": { - "q": "NBN:urn:nbn:nl:ui:13-025de6e2-bdcf-4622-b134-282b4c590f42", - "total_count": 1, - "start": 0, - "spelling_alternatives": {}, - "items": [ - { - "name": "Manual Test", - "type": "dataset", - "url": "https://doi.org/10.5072/FK2/QZZSST", - "global_id": "doi:10.5072/FK2/QZZSST" - } - ], - "count_in_response": 1 + { + "status": "OK", + "data": { + "q": "NBN:urn:nbn:nl:ui:13-025de6e2-bdcf-4622-b134-282b4c590f42", + "total_count": 1, + "start": 0, + "spelling_alternatives": {}, + "items": [ + { + "name": "Manual Test", + "type": "dataset", + "url": "https://doi.org/10.5072/FK2/QZZSST", + "global_id": "doi:10.5072/FK2/QZZSST" } - }"""; + ], + "count_in_response": 1 + } + }"""; var latestVersionJson = """ - { - "status": "OK", - "data": { - "id": 2, - "identifier": "FK2/QZZSST", - "persistentUrl": "https://doi.org/10.5072/FK2/QZZSST", - "latestVersion": { - "id": 2, - "datasetId": 2, - "datasetPersistentId": "doi:10.5072/FK2/QZZSST", - "storageIdentifier": "file://10.5072/FK2/QZZSST", - "fileAccessRequest": false, - "metadataBlocks": { - "dansDataVaultMetadata": { - "displayName": "Data Vault Metadata", - "name": "dansDataVaultMetadata", - "fields": [ - { - "typeName": "dansSwordToken", - "multiple": false, - "typeClass": "primitive", - "value": "urn:uuid:34632f71-11f8-48d8-9bf3-79551ad22b5e" - }, - { - "typeName": "dansOtherId", - "multiple": false, - "typeClass": "primitive", - "value": "u1:organizational-identifier" - } - ] + { + "status": "OK", + "data": { + "id": 2, + "identifier": "FK2/QZZSST", + "persistentUrl": "https://doi.org/10.5072/FK2/QZZSST", + "latestVersion": { + "id": 2, + "datasetId": 2, + "datasetPersistentId": "doi:10.5072/FK2/QZZSST", + "storageIdentifier": "file://10.5072/FK2/QZZSST", + "fileAccessRequest": false, + "metadataBlocks": { + "dansDataVaultMetadata": { + "displayName": "Data Vault Metadata", + "name": "dansDataVaultMetadata", + "fields": [ + { + "typeName": "dansSwordToken", + "multiple": false, + "typeClass": "primitive", + "value": "urn:uuid:34632f71-11f8-48d8-9bf3-79551ad22b5e" + }, + { + "typeName": "dansOtherId", + "multiple": false, + "typeClass": "primitive", + "value": "u1:organizational-identifier" } - } + ] } } - }"""; + } + } + }"""; var dataverseRoleAssignmentsJson = """ + { + "status": "OK", + "data": [ { - "status": "OK", - "data": [ - { - "id": 6, - "assignee": "@user001", - "roleId": 11, - "_roleAlias": "datasetcreator", - "definitionPointId": 2 - } - ] - }"""; + "id": 6, + "assignee": "@user001", + "roleId": 11, + "_roleAlias": "datasetcreator", + "definitionPointId": 2 + } + ] + }"""; var datasetRoleAssignmentsJson = """ + { + "status": "OK", + "data": [ { - "status": "OK", - "data": [ - { - "id": 6, - "assignee": "@user001", - "roleId": 11, - "_roleAlias": "dataseteditor", - "definitionPointId": 2 - } - ] - }"""; + "id": 6, + "assignee": "@user001", + "roleId": 11, + "_roleAlias": "dataseteditor", + "definitionPointId": 2 + } + ] + }"""; var embargoResultJson = """ - { - "status": "OK", - "data": { - "message": "24" - } - }"""; + { + "status": "OK", + "data": { + "message": "24" + } + }"""; var searchResult = new MockedDataverseResponse(searchResultsJson, SearchResult.class); var latestVersionResult = new MockedDataverseResponse(latestVersionJson, DatasetLatestVersion.class); @@ -496,27 +474,27 @@ void validateFormData_with_HasOrganizationalIdentifier_should_validate_if_result var maxEmbargoDurationResult = new MockedDataverseResponse(embargoResultJson, DataMessage.class); Mockito.when(dataverseService.getMaxEmbargoDurationInMonths()) - .thenReturn(maxEmbargoDurationResult); + .thenReturn(maxEmbargoDurationResult); Mockito.when(dataverseService.getDataverseRoleAssignments(Mockito.anyString())) - .thenReturn(dataverseRoleAssignmentsResult); + .thenReturn(dataverseRoleAssignmentsResult); Mockito.when(dataverseService.getDatasetRoleAssignments(Mockito.anyString())) - .thenReturn(datasetRoleAssignmentsResult); + .thenReturn(datasetRoleAssignmentsResult); Mockito.when(dataverseService.searchDatasetsByOrganizationalIdentifier(Mockito.anyString())) - .thenReturn(searchResult); + .thenReturn(searchResult); Mockito.when(dataverseService.getDataset(Mockito.anyString())) - .thenReturn(latestVersionResult); + .thenReturn(latestVersionResult); Mockito.when(dataverseService.searchBySwordToken(Mockito.anyString())) - .thenReturn(swordTokenResult); + .thenReturn(swordTokenResult); - var response = EXT.target("/validate") - .register(MultiPartFeature.class) - .request() - .post(Entity.entity(multipart, multipart.getMediaType()), ValidateOkDto.class); + var response = EXT.target("/validateLocalDir") + .register(MultiPartFeature.class) + .request() + .post(Entity.entity(data, MediaType.APPLICATION_JSON_TYPE), ValidateOkDto.class); assertTrue(response.getIsCompliant()); assertEquals("bag-with-is-version-of", response.getName()); @@ -530,96 +508,93 @@ void validateFormData_with_HasOrganizationalIdentifier_should_not_validate_if_re data.setBagLocation(filename); data.setPackageType(ValidateCommandDto.PackageTypeEnum.DEPOSIT); - var multipart = new FormDataMultiPart() - .field("command", data, MediaType.APPLICATION_JSON_TYPE); - var searchResultsJson = """ - { - "status": "OK", - "data": { - "q": "NBN:urn:nbn:nl:ui:13-025de6e2-bdcf-4622-b134-282b4c590f42", - "total_count": 1, - "start": 0, - "spelling_alternatives": {}, - "items": [ - { - "name": "Manual Test", - "type": "dataset", - "url": "https://doi.org/10.5072/FK2/QZZSST", - "global_id": "doi:10.5072/FK2/QZZSST" - } - ], - "count_in_response": 1 + { + "status": "OK", + "data": { + "q": "NBN:urn:nbn:nl:ui:13-025de6e2-bdcf-4622-b134-282b4c590f42", + "total_count": 1, + "start": 0, + "spelling_alternatives": {}, + "items": [ + { + "name": "Manual Test", + "type": "dataset", + "url": "https://doi.org/10.5072/FK2/QZZSST", + "global_id": "doi:10.5072/FK2/QZZSST" } - }"""; + ], + "count_in_response": 1 + } + }"""; // Violation will be caused by: "value": "urn:uuid:wrong-uuid" var latestVersionJson = """ - { - "status": "OK", - "data": { - "id": 2, - "identifier": "FK2/QZZSST", - "persistentUrl": "https://doi.org/10.5072/FK2/QZZSST", - "latestVersion": { - "id": 2, - "datasetId": 2, - "datasetPersistentId": "doi:10.5072/FK2/QZZSST", - "storageIdentifier": "file://10.5072/FK2/QZZSST", - "fileAccessRequest": false, - "metadataBlocks": { - "dansDataVaultMetadata": { - "displayName": "Data Vault Metadata", - "name": "dansDataVaultMetadata", - "fields": [ - { - "typeName": "dansOtherId", - "multiple": false, - "typeClass": "primitive", - "value": "urn:uuid:wrong-uuid" - } - ] + { + "status": "OK", + "data": { + "id": 2, + "identifier": "FK2/QZZSST", + "persistentUrl": "https://doi.org/10.5072/FK2/QZZSST", + "latestVersion": { + "id": 2, + "datasetId": 2, + "datasetPersistentId": "doi:10.5072/FK2/QZZSST", + "storageIdentifier": "file://10.5072/FK2/QZZSST", + "fileAccessRequest": false, + "metadataBlocks": { + "dansDataVaultMetadata": { + "displayName": "Data Vault Metadata", + "name": "dansDataVaultMetadata", + "fields": [ + { + "typeName": "dansOtherId", + "multiple": false, + "typeClass": "primitive", + "value": "urn:uuid:wrong-uuid" } - } + ] } } - }"""; + } + } + }"""; var dataverseRoleAssignmentsJson = """ + { + "status": "OK", + "data": [ { - "status": "OK", - "data": [ - { - "id": 6, - "assignee": "@user001", - "roleId": 11, - "_roleAlias": "datasetcreator", - "definitionPointId": 2 - } - ] - }"""; + "id": 6, + "assignee": "@user001", + "roleId": 11, + "_roleAlias": "datasetcreator", + "definitionPointId": 2 + } + ] + }"""; var datasetRoleAssignmentsJson = """ + { + "status": "OK", + "data": [ { - "status": "OK", - "data": [ - { - "id": 6, - "assignee": "@user001", - "roleId": 11, - "_roleAlias": "dataseteditor", - "definitionPointId": 2 - } - ] - }"""; + "id": 6, + "assignee": "@user001", + "roleId": 11, + "_roleAlias": "dataseteditor", + "definitionPointId": 2 + } + ] + }"""; var embargoResultJson = """ - { - "status": "OK", - "data": { - "message": "24" - } - }"""; + { + "status": "OK", + "data": { + "message": "24" + } + }"""; var searchResult = new MockedDataverseResponse(searchResultsJson, SearchResult.class); var latestVersionResult = new MockedDataverseResponse(latestVersionJson, DatasetLatestVersion.class); @@ -629,30 +604,30 @@ void validateFormData_with_HasOrganizationalIdentifier_should_not_validate_if_re var maxEmbargoDurationResult = new MockedDataverseResponse(embargoResultJson, DataMessage.class); Mockito.when(dataverseService.getMaxEmbargoDurationInMonths()) - .thenReturn(maxEmbargoDurationResult); + .thenReturn(maxEmbargoDurationResult); Mockito.when(dataverseService.searchDatasetsByOrganizationalIdentifier(Mockito.anyString())) - .thenReturn(searchResult); + .thenReturn(searchResult); Mockito.when(dataverseService.getDataset(Mockito.anyString())) - .thenReturn(latestVersionResult); + .thenReturn(latestVersionResult); Mockito.when(dataverseService.searchBySwordToken(Mockito.anyString())) - .thenReturn(swordTokenResult); + .thenReturn(swordTokenResult); Mockito.when(dataverseService.getDataverseRoleAssignments(Mockito.anyString())) - .thenReturn(dataverseRoleAssignmentsResult); + .thenReturn(dataverseRoleAssignmentsResult); Mockito.when(dataverseService.getDatasetRoleAssignments(Mockito.anyString())) - .thenReturn(datasetRoleAssignmentsResult); + .thenReturn(datasetRoleAssignmentsResult); - var response = EXT.target("/validate") - .register(MultiPartFeature.class) - .request() - .post(Entity.entity(multipart, multipart.getMediaType()), ValidateOkDto.class); + var response = EXT.target("/validateLocalDir") + .register(MultiPartFeature.class) + .request() + .post(Entity.entity(data, MediaType.APPLICATION_JSON_TYPE), ValidateOkDto.class); var failed = response.getRuleViolations().stream() - .map(ValidateOkRuleViolationsInnerDto::getRule).collect(Collectors.toSet()); + .map(ValidateOkRuleViolationsInnerDto::getRule).collect(Collectors.toSet()); assertEquals(Set.of("4.1(b)"), failed); assertFalse(response.getIsCompliant()); @@ -667,82 +642,79 @@ void validateFormData_should_yield_violation_errors_if_swordToken_does_not_match data.setBagLocation(filename); data.setPackageType(ValidateCommandDto.PackageTypeEnum.DEPOSIT); - var multipart = new FormDataMultiPart() - .field("command", data, MediaType.APPLICATION_JSON_TYPE); - var searchResultsJson = """ - { - "status": "OK", - "data": { - "q": "NBN:urn:nbn:nl:ui:13-025de6e2-bdcf-4622-b134-282b4c590f42", - "total_count": 1, - "start": 0, - "spelling_alternatives": {}, - "items": [ - { - "name": "Manual Test", - "type": "dataset", - "url": "https://doi.org/10.5072/FK2/QZZSST", - "global_id": "doi:10.5072/FK2/QZZSST" - } - ], - "count_in_response": 1 + { + "status": "OK", + "data": { + "q": "NBN:urn:nbn:nl:ui:13-025de6e2-bdcf-4622-b134-282b4c590f42", + "total_count": 1, + "start": 0, + "spelling_alternatives": {}, + "items": [ + { + "name": "Manual Test", + "type": "dataset", + "url": "https://doi.org/10.5072/FK2/QZZSST", + "global_id": "doi:10.5072/FK2/QZZSST" } - }"""; + ], + "count_in_response": 1 + } + }"""; // returns 0 items, causing the rule to be violated var swordTokenJson = """ - { - "status": "OK", - "data": { - "q": "NBN:urn:nbn:nl:ui:13-025de6e2-bdcf-4622-b134-282b4c590f42", - "total_count": 1, - "start": 0, - "spelling_alternatives": {}, - "items": [ - ], - "count_in_response": 0 - } - }"""; + { + "status": "OK", + "data": { + "q": "NBN:urn:nbn:nl:ui:13-025de6e2-bdcf-4622-b134-282b4c590f42", + "total_count": 1, + "start": 0, + "spelling_alternatives": {}, + "items": [ + ], + "count_in_response": 0 + } + }"""; var latestVersionJson = """ - { - "status": "OK", - "data": { - "id": 2, - "identifier": "FK2/QZZSST", - "persistentUrl": "https://doi.org/10.5072/FK2/QZZSST", - "latestVersion": { - "id": 2, - "datasetId": 2, - "datasetPersistentId": "doi:10.5072/FK2/QZZSST", - "storageIdentifier": "file://10.5072/FK2/QZZSST", - "fileAccessRequest": false, - "metadataBlocks": { - "dansDataVaultMetadata": { - "displayName": "Data Vault Metadata", - "name": "dansDataVaultMetadata", - "fields": [ - { - "typeName": "dansSwordToken", - "multiple": false, - "typeClass": "primitive", - "value": "urn:uuid:34632f71-11f8-48d8-9bf3-79551ad22b5e" - } - ] + { + "status": "OK", + "data": { + "id": 2, + "identifier": "FK2/QZZSST", + "persistentUrl": "https://doi.org/10.5072/FK2/QZZSST", + "latestVersion": { + "id": 2, + "datasetId": 2, + "datasetPersistentId": "doi:10.5072/FK2/QZZSST", + "storageIdentifier": "file://10.5072/FK2/QZZSST", + "fileAccessRequest": false, + "metadataBlocks": { + "dansDataVaultMetadata": { + "displayName": "Data Vault Metadata", + "name": "dansDataVaultMetadata", + "fields": [ + { + "typeName": "dansSwordToken", + "multiple": false, + "typeClass": "primitive", + "value": "urn:uuid:34632f71-11f8-48d8-9bf3-79551ad22b5e" } - } + ] } } - }"""; + } + } + }"""; var embargoResultJson = """ - { - "status": "OK", - "data": { - "message": "24" - } - }"""; + { + "status": "OK", + "data": { + "message": "24" + } + }"""; var searchResult = new MockedDataverseResponse(searchResultsJson, SearchResult.class); var latestVersionResult = new MockedDataverseResponse(latestVersionJson, DatasetLatestVersion.class); @@ -750,24 +722,24 @@ void validateFormData_should_yield_violation_errors_if_swordToken_does_not_match var maxEmbargoDurationResult = new MockedDataverseResponse(embargoResultJson, DataMessage.class); Mockito.when(dataverseService.getMaxEmbargoDurationInMonths()) - .thenReturn(maxEmbargoDurationResult); + .thenReturn(maxEmbargoDurationResult); Mockito.when(dataverseService.searchDatasetsByOrganizationalIdentifier(Mockito.anyString())) - .thenReturn(searchResult); + .thenReturn(searchResult); Mockito.when(dataverseService.getDataset(Mockito.anyString())) - .thenReturn(latestVersionResult); + .thenReturn(latestVersionResult); Mockito.when(dataverseService.searchBySwordToken(Mockito.anyString())) - .thenReturn(swordTokenResult); + .thenReturn(swordTokenResult); - var response = EXT.target("/validate") - .register(MultiPartFeature.class) - .request() - .post(Entity.entity(multipart, multipart.getMediaType()), ValidateOkDto.class); + var response = EXT.target("/validateLocalDir") + .register(MultiPartFeature.class) + .request() + .post(Entity.entity(data, MediaType.APPLICATION_JSON_TYPE), ValidateOkDto.class); var failed = response.getRuleViolations().stream() - .map(ValidateOkRuleViolationsInnerDto::getRule).collect(Collectors.toSet()); + .map(ValidateOkRuleViolationsInnerDto::getRule).collect(Collectors.toSet()); assertEquals(Set.of("4.1(a)", "4.1(b)"), failed); assertFalse(response.getIsCompliant()); diff --git a/src/test/java/nl/knaw/dans/validatedansbag/resources/ValidateResourceTest.java b/src/test/java/nl/knaw/dans/validatedansbag/resources/ValidateResourceTest.java deleted file mode 100644 index 7618e7e5..00000000 --- a/src/test/java/nl/knaw/dans/validatedansbag/resources/ValidateResourceTest.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2022 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package nl.knaw.dans.validatedansbag.resources; - -import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; -import io.dropwizard.testing.junit5.ResourceExtension; -import nl.knaw.dans.validatedansbag.api.ValidateCommandDto; -import nl.knaw.dans.validatedansbag.api.ValidateOkDto; -import nl.knaw.dans.validatedansbag.core.BagNotFoundException; -import nl.knaw.dans.validatedansbag.core.service.FileService; -import nl.knaw.dans.validatedansbag.core.service.RuleEngineService; -import org.glassfish.jersey.media.multipart.FormDataMultiPart; -import org.glassfish.jersey.media.multipart.MultiPartFeature; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mockito; - -import javax.ws.rs.client.Entity; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.nio.file.Path; -import java.util.Optional; -import java.util.zip.ZipError; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -@ExtendWith(DropwizardExtensionsSupport.class) -class ValidateResourceTest { - private final RuleEngineService ruleEngineService = Mockito.mock(RuleEngineService.class); - private final FileService fileService = Mockito.mock(FileService.class); - - public final ResourceExtension EXT = ResourceExtension.builder() - .addProvider(MultiPartFeature.class) - .addResource(new ValidateResource(ruleEngineService, fileService)) - .build(); - - @BeforeEach - void setup() { - Mockito.reset(fileService); - Mockito.reset(ruleEngineService); - } - - @Test - void validateFormData_should_have_no_interactions_with_fileService_and_match_properties() { - var data = new ValidateCommandDto(); - data.setBagLocation("it/is/here"); - data.setPackageType(ValidateCommandDto.PackageTypeEnum.DEPOSIT); - - var multipart = new FormDataMultiPart() - .field("command", data, MediaType.APPLICATION_JSON_TYPE); - - Path testPath = Path.of("it/is/here"); - Mockito.doReturn(testPath).when(fileService).getSecurePath(testPath); - - var response = EXT.target("/validate") - .register(MultiPartFeature.class) - .request() - .post(Entity.entity(multipart, multipart.getMediaType()), ValidateOkDto.class); - - assertEquals("it/is/here", response.getBagLocation()); - assertEquals("here", response.getName()); - assertEquals(ValidateOkDto.InformationPackageTypeEnum.DEPOSIT, response.getInformationPackageType()); - } - - @Test - void validateFormData_should_create_zipFile_on_disk() throws Exception { - var data = new ValidateCommandDto(); - data.setBagLocation(null); - data.setPackageType(ValidateCommandDto.PackageTypeEnum.DEPOSIT); - - var multipart = new FormDataMultiPart() - .field("command", data, MediaType.APPLICATION_JSON_TYPE) - .field("zip", new ByteArrayInputStream(new byte[4]), MediaType.valueOf("application/zip")); - - Mockito.doReturn(Path.of("/tmp/bag-1")) - .when(fileService) - .extractZipFile(Mockito.any(InputStream.class)); - - Mockito.doReturn(Optional.of(Path.of("bagdir"))) - .when(fileService) - .getFirstDirectory(Mockito.any()); - - var response = EXT.target("/validate") - .register(MultiPartFeature.class) - .request() - .post(Entity.entity(multipart, multipart.getMediaType()), ValidateOkDto.class); - - Mockito.verify(fileService).extractZipFile(Mockito.any(InputStream.class)); - - assertEquals("bagdir", response.getName()); - } - - @Test - void validateFormData_should_return_400_when_file_does_not_exist() throws Exception { - var data = new ValidateCommandDto(); - data.setBagLocation("some/path"); - data.setPackageType(ValidateCommandDto.PackageTypeEnum.DEPOSIT); - - var multipart = new FormDataMultiPart() - .field("command", data, MediaType.APPLICATION_JSON_TYPE); - - Mockito.doThrow(BagNotFoundException.class) - .when(ruleEngineService) - .validateBag(Mockito.any(), Mockito.any()); - - try (var response = EXT.target("/validate") - .register(MultiPartFeature.class) - .request() - .post(Entity.entity(multipart, multipart.getMediaType()), Response.class)) { - - assertEquals(400, response.getStatus()); - } - } - - @Test - void validateZipFile_should_interact_with_fileService() throws Exception { - var zip = Entity.entity(new ByteArrayInputStream(new byte[4]), MediaType.valueOf("application/zip")); - - Mockito.doReturn(Path.of("/tmp/bag-1")) - .when(fileService) - .extractZipFile(Mockito.any(InputStream.class)); - - Mockito.doReturn(Optional.of(Path.of("bagdir"))) - .when(fileService) - .getFirstDirectory(Mockito.any()); - - var response = EXT.target("/validate") - .request() - .post(zip, ValidateOkDto.class); - - Mockito.verify(fileService).extractZipFile(Mockito.any(InputStream.class)); - - assertEquals("bagdir", response.getName()); - } - - @Test - void validateZipFile_should_return_500_when_file_is_invalid() throws Exception { - var zip = Entity.entity(new ByteArrayInputStream(new byte[4]), MediaType.valueOf("application/zip")); - - Mockito.doThrow(ZipError.class) - .when(fileService) - .extractZipFile(Mockito.any(InputStream.class)); - - try (var response = EXT.target("/validate") - .register(MultiPartFeature.class) - .request() - .post(zip, Response.class)) { - - assertEquals(500, response.getStatus()); - } - } -} \ No newline at end of file