diff --git a/src/main/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesEditor.java b/src/main/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesEditor.java index 54b9293..ba5ad2f 100644 --- a/src/main/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesEditor.java +++ b/src/main/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesEditor.java @@ -187,7 +187,7 @@ private void moveFiles() throws IOException, DataverseException { log.debug("Start moving files {} for deposit {}", editFiles.getMoveFiles().size(), depositId); for (var move : editFiles.getMoveFiles()) { var fileMeta = filesInDatasetCache.get(move.getFrom()); - fileMeta = filesInDatasetCache.getMovedFile(move.getTo(), fileMeta); + fileMeta = filesInDatasetCache.createFileMetaForMovedFile(move.getTo(), fileMeta); dataverseService.updateFileMetadata(fileMeta.getDataFile().getId(), fileMeta); filesInDatasetCache.remove(move.getFrom()); filesInDatasetCache.put(fileMeta); // auto-rename is done by getMovedFile diff --git a/src/main/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesInDatasetCache.java b/src/main/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesInDatasetCache.java index cc73e82..fe9eead 100644 --- a/src/main/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesInDatasetCache.java +++ b/src/main/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesInDatasetCache.java @@ -28,8 +28,7 @@ /** *

- * Keeps track of the FileMeta objects of files in a dataset. The cache is initialized by downloading the files from the dataset. If you try to initialize the cache again, an IllegalStateException is - * thrown. The client is responsible for adding, removing, and updating the cache. + * Keeps track of the FileMeta objects of files in a dataset. The cache is initialized by downloading the files from the dataset. *

*/ @Slf4j @@ -72,7 +71,15 @@ public void put(@NonNull FileMeta fileMeta) { filesInDataset.put(getPath(fileMeta), fileMeta); } - public FileMeta getMovedFile(@NonNull String toPath, @NonNull FileMeta fileMeta) { + /** + * A move operation is in fact a file metadata update operation in which the directory label and label are updated. This method allows to calculate the file metadata for the moved file in the + * dataset. The filepath will be auto-renamed if it is in the renamedFiles map, so the local path from the bag is used. + * + * @param toPath new filepath before auto-rename + * @param fileMeta the FileMeta object for the file in the dataset after the move + * @return the FileMeta object for the moved file in the dataset + */ + public FileMeta createFileMetaForMovedFile(@NonNull String toPath, @NonNull FileMeta fileMeta) { var newPath = autoRenamePath(toPath); var dataversePath = new DataversePath(newPath); fileMeta.setDirectoryLabel(dataversePath.getDirectoryLabel()); @@ -80,22 +87,23 @@ public FileMeta getMovedFile(@NonNull String toPath, @NonNull FileMeta fileMeta) return fileMeta; } - private String autoRename(@NonNull FileMeta fileMeta) { - var filepath = getPath(fileMeta); - var renamedFilepath = autoRenamedFiles.get(filepath); - if (renamedFilepath != null) { - filepath = renamedFilepath; - var dataversePath = new DataversePath(renamedFilepath); - fileMeta.setDirectoryLabel(dataversePath.getDirectoryLabel()); - fileMeta.setLabel(dataversePath.getLabel()); - } - return filepath; - } - + /** + * Removes the FileMeta object for the given filepath. The filepath will be auto-renamed if it is in the renamedFiles map, so the local path from the bag is used. + * + * @param filepath before auto-rename + */ public void remove(@NonNull String filepath) { filesInDataset.remove(autoRenamePath(filepath)); } + /** + * Download the file metadata from the dataset with the given persistent identifier, initializing the cache. This method can only be called once. Subsequent calls will throw an exception. + * + * @param pid the persistent identifier of the dataset + * @throws IOException if an I/O error occurs + * @throws DataverseException if the Dataverse API returns an error + * @throws IllegalStateException if the cache is already initialized + */ public void downloadFromDataset(@NonNull String pid) throws IOException, DataverseException { if (initialized) { throw new IllegalStateException("Cache already initialized"); diff --git a/src/test/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesEditorTest.java b/src/test/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesEditorTest.java index 6fb4dfd..5987fd7 100644 --- a/src/test/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesEditorTest.java +++ b/src/test/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesEditorTest.java @@ -97,4 +97,6 @@ public void deleteFiles_throws_exception_when_file_not_found() throws Exception .hasMessage("File to delete not found in dataset: file4"); } + + } diff --git a/src/test/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesInDatasetCacheNonNullTest.java b/src/test/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesInDatasetCacheNonNullTest.java new file mode 100644 index 0000000..cb7c76b --- /dev/null +++ b/src/test/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesInDatasetCacheNonNullTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2024 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.dvingest.core.bagprocessor; + +import nl.knaw.dans.dvingest.core.service.DataverseService; +import nl.knaw.dans.lib.dataverse.model.file.FileMeta; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class FilesInDatasetCacheNonNullTest { + private final DataverseService dataverseServiceMock = Mockito.mock(DataverseService.class); + + @Test + public void constructor_throws_exception_when_dataverseService_is_null() { + assertThatThrownBy(() -> new FilesInDatasetCache(null, Map.of())) + .isInstanceOf(NullPointerException.class); + } + + @Test + public void constructor_throws_exception_when_autoRenamedFiles_is_null() { + assertThatThrownBy(() -> new FilesInDatasetCache(dataverseServiceMock, null)) + .isInstanceOf(NullPointerException.class); + } + + @Test + public void get_throws_exception_when_filepath_is_null() { + var filesInDatasetCache = new FilesInDatasetCache(dataverseServiceMock, Map.of()); + assertThatThrownBy(() -> filesInDatasetCache.get(null)) + .isInstanceOf(NullPointerException.class); + } + + @Test + public void put_throws_exception_when_fileMeta_is_null() { + var filesInDatasetCache = new FilesInDatasetCache(dataverseServiceMock, Map.of()); + assertThatThrownBy(() -> filesInDatasetCache.put(null)) + .isInstanceOf(NullPointerException.class); + } + + @Test + public void createFileMetaForMovedFile_throws_exception_when_toPath_is_null() { + var filesInDatasetCache = new FilesInDatasetCache(dataverseServiceMock, Map.of()); + var fileMeta = new FileMeta(); + assertThatThrownBy(() -> filesInDatasetCache.createFileMetaForMovedFile(null, fileMeta)) + .isInstanceOf(NullPointerException.class); + } + + @Test + public void createFileMetaForMovedFile_throws_exception_when_fileMeta_is_null() { + var filesInDatasetCache = new FilesInDatasetCache(dataverseServiceMock, Map.of()); + assertThatThrownBy(() -> filesInDatasetCache.createFileMetaForMovedFile("path", null)) + .isInstanceOf(NullPointerException.class); + } + + @Test + public void remove_throws_exception_when_filepath_is_null() { + var filesInDatasetCache = new FilesInDatasetCache(dataverseServiceMock, Map.of()); + assertThatThrownBy(() -> filesInDatasetCache.remove(null)) + .isInstanceOf(NullPointerException.class); + } + + @Test + public void downloadFromDataset_throws_exception_when_pid_is_null() { + var filesInDatasetCache = new FilesInDatasetCache(dataverseServiceMock, Map.of()); + assertThatThrownBy(() -> filesInDatasetCache.downloadFromDataset(null)) + .isInstanceOf(NullPointerException.class); + } +} \ No newline at end of file diff --git a/src/test/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesInDatasetCacheTest.java b/src/test/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesInDatasetCacheTest.java index 2ed5781..6520f21 100644 --- a/src/test/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesInDatasetCacheTest.java +++ b/src/test/java/nl/knaw/dans/dvingest/core/bagprocessor/FilesInDatasetCacheTest.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2024 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.dvingest.core.bagprocessor; import nl.knaw.dans.dvingest.core.service.DataverseService; @@ -9,6 +24,7 @@ import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class FilesInDatasetCacheTest { private final DataverseService dataverseServiceMock = Mockito.mock(DataverseService.class); @@ -34,19 +50,106 @@ public void get_returns_fileMeta_by_filepath() { } @Test - public void put_auto_renames_filepath() { + public void get_returns_null_for_nonexistent_filepath() { + // Given + var filesInDatasetCache = new FilesInDatasetCache(dataverseServiceMock, Map.of()); + + // When + var result = filesInDatasetCache.get("nonexistent/filepath"); + + // Then + assertThat(result).isNull(); + } + + @Test + public void put_adds_fileMeta_to_cache() { + // Given + var filesInDatasetCache = new FilesInDatasetCache(dataverseServiceMock, Map.of()); + var fileMeta = new FileMeta(); + fileMeta.setLabel("label"); + fileMeta.setDirectoryLabel("directoryLabel"); + + // When + filesInDatasetCache.put(fileMeta); + + // Then + assertThat(filesInDatasetCache.get("directoryLabel/label")).isEqualTo(fileMeta); + } + + @Test + public void remove_deletes_fileMeta_from_cache() { + // Given + var filesInDatasetCache = new FilesInDatasetCache(dataverseServiceMock, Map.of()); + var fileMeta = new FileMeta(); + fileMeta.setLabel("label"); + fileMeta.setDirectoryLabel("directoryLabel"); + filesInDatasetCache.put(fileMeta); + + // When + filesInDatasetCache.remove("directoryLabel/label"); + + // Then + assertThat(filesInDatasetCache.get("directoryLabel/label")).isNull(); + } + + @Test + public void downloadFromDataset_initializes_cache() throws Exception { // Given - var filesInDatasetCache = new FilesInDatasetCache(dataverseServiceMock, Map.of("directoryLabel/label", "newDirectoryLabel/newLabel")); var fileMeta = new FileMeta(); fileMeta.setLabel("label"); fileMeta.setDirectoryLabel("directoryLabel"); + Mockito.when(dataverseServiceMock.getFiles("pid")).thenReturn(java.util.List.of(fileMeta)); + var filesInDatasetCache = new FilesInDatasetCache(dataverseServiceMock, Map.of()); // When + filesInDatasetCache.downloadFromDataset("pid"); + + // Then + assertThat(filesInDatasetCache.get("directoryLabel/label")).isEqualTo(fileMeta); + } + + @Test + public void downloadFromDataset_throws_exception_if_already_initialized() throws Exception { + // Given + var filesInDatasetCache = new FilesInDatasetCache(dataverseServiceMock, Map.of()); + filesInDatasetCache.downloadFromDataset("pid"); + + // When / Then + assertThatThrownBy(() -> filesInDatasetCache.downloadFromDataset("pid")) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Cache already initialized"); + } + + @Test + public void createFileMetaForMovedFile_updates_fileMeta_with_new_path() { + // Given + var filesInDatasetCache = new FilesInDatasetCache(dataverseServiceMock, Map.of("oldPath/file.txt", "newPath/file.txt")); + var fileMeta = new FileMeta(); + fileMeta.setLabel("file.txt"); + fileMeta.setDirectoryLabel("oldPath"); + + // When + var updatedFileMeta = filesInDatasetCache.createFileMetaForMovedFile("newPath/file.txt", fileMeta); + + // Then + assertThat(updatedFileMeta.getLabel()).isEqualTo("file.txt"); + assertThat(updatedFileMeta.getDirectoryLabel()).isEqualTo("newPath"); + } + + @Test + public void get_returns_fileMeta_by_old_name_after_rename() { + // Given + var filesInDatasetCache = new FilesInDatasetCache(dataverseServiceMock, Map.of("oldPath/file.txt", "newPath/file.txt")); + var fileMeta = new FileMeta(); + fileMeta.setLabel("file.txt"); + fileMeta.setDirectoryLabel("newPath"); filesInDatasetCache.put(fileMeta); - var returnedFileMeta = filesInDatasetCache.get("newDirectoryLabel/newLabel"); + + // When + var result = filesInDatasetCache.get("oldPath/file.txt"); // Then - assertThat(returnedFileMeta).isEqualTo(fileMeta); + assertThat(result).isEqualTo(fileMeta); } -} +} \ No newline at end of file