From 973c62a6756303ed75396cafd9a60d79f516018c Mon Sep 17 00:00:00 2001 From: straumat Date: Sat, 20 Jan 2024 18:45:53 +0100 Subject: [PATCH] Added methods to delete or check if a file exists to content service #461 --- .../core/provider/storage/ContentService.java | 15 ++++++ .../LocalFileServiceImplementation.java | 20 +++++++- .../storage/S3ServiceImplementation.java | 50 ++++++++++++++++--- .../core/service/LocalFileServiceTest.java | 34 +++++++++++++ .../storage/S3ServiceImplementationTest.java | 7 +++ 5 files changed, 119 insertions(+), 7 deletions(-) diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/ContentService.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/ContentService.java index e507aa223..2b34af7d5 100644 --- a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/ContentService.java +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/ContentService.java @@ -13,4 +13,19 @@ public interface ContentService { */ void storeFile(byte[] fileContent, String fileName); + /** + * Check if a file exists. + * + * @param fileName file name + * @return true if file exists, false otherwise + */ + boolean fileExists(String fileName); + + /** + * Delete a file. + * + * @param fileName file name + */ + void deleteFile(String fileName); + } diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/LocalFileServiceImplementation.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/LocalFileServiceImplementation.java index 477601931..32b672b73 100644 --- a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/LocalFileServiceImplementation.java +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/LocalFileServiceImplementation.java @@ -6,12 +6,14 @@ import io.undertow.server.handlers.resource.PathResourceManager; import io.undertow.server.handlers.resource.ResourceHandler; import jakarta.annotation.PreDestroy; +import lombok.NonNull; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Hex; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import javax.xml.bind.DatatypeConverter; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.Files; @@ -100,7 +102,7 @@ public void onDestroy() throws Exception { @Override @SuppressWarnings("checkstyle:DesignForExtension") public void storeFile(final byte[] fileContent, - final String fileName) { + @NonNull final String fileName) { try { Files.write(fileSystem.getPath(".").resolve(fileName), fileContent); } catch (Exception e) { @@ -108,6 +110,22 @@ public void storeFile(final byte[] fileContent, } } + @Override + @SuppressWarnings("checkstyle:DesignForExtension") + public boolean fileExists(@NonNull final String fileName) { + return Files.exists(fileSystem.getPath(".").resolve(fileName)); + } + + @Override + @SuppressWarnings("checkstyle:DesignForExtension") + public void deleteFile(@NonNull final String fileName) { + try { + Files.delete(fileSystem.getPath(".").resolve(fileName)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + /** * Returns the sha256 value calculated with the parameter. * diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/S3ServiceImplementation.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/S3ServiceImplementation.java index 5bfb498cb..fd075afdf 100644 --- a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/S3ServiceImplementation.java +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/S3ServiceImplementation.java @@ -2,6 +2,9 @@ import io.minio.MinioClient; import io.minio.PutObjectArgs; +import io.minio.RemoveObjectArgs; +import io.minio.StatObjectArgs; +import io.minio.errors.ErrorResponseException; import lombok.NonNull; import org.apache.tika.Tika; import org.royllo.explorer.core.util.base.BaseService; @@ -41,17 +44,22 @@ public void updateS3Parameters(final S3Parameters newS3Parameters) { this.s3Parameters = newS3Parameters; } - @Override - public void storeFile(final byte[] fileContent, @NonNull final String fileName) { - // Creating a client. - MinioClient minioClient = MinioClient.builder() + /** + * Get Minio client. + * + * @return Minio client. + */ + private MinioClient getMinioClient() { + return MinioClient.builder() .endpoint(s3Parameters.getEndpointURL()) .credentials(s3Parameters.getAccessKey(), s3Parameters.getSecretKey()) .build(); + } - // Adding the file to the bucket. + @Override + public void storeFile(final byte[] fileContent, @NonNull final String fileName) { try { - minioClient.putObject(PutObjectArgs.builder() + getMinioClient().putObject(PutObjectArgs.builder() .bucket(s3Parameters.getBucketName()) .object(fileName).stream(new ByteArrayInputStream(fileContent), fileContent.length, -1) .contentType(new Tika().detect(fileContent)) @@ -61,4 +69,34 @@ public void storeFile(final byte[] fileContent, @NonNull final String fileName) } } + @Override + public boolean fileExists(@NonNull final String fileName) { + try { + getMinioClient().statObject(StatObjectArgs.builder() + .bucket(s3Parameters.getBucketName()) + .object(fileName) + .build()); + return true; + } catch (ErrorResponseException e) { + return false; + } catch (Exception e) { + logger.error("Error checking if file exists {} in S3: {}", fileName, e.getMessage()); + throw new RuntimeException(e.getMessage()); + } + } + + @Override + public void deleteFile(@NonNull final String fileName) { + try { + getMinioClient().removeObject( + RemoveObjectArgs.builder() + .bucket(s3Parameters.getBucketName()) + .object(fileName) + .build()); + } catch (Exception e) { + logger.error("Error deleting file {} in S3: {}", fileName, e.getMessage()); + throw new RuntimeException(e.getMessage()); + } + } + } diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/LocalFileServiceTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/LocalFileServiceTest.java index e025ebb9d..701ad02dc 100644 --- a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/LocalFileServiceTest.java +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/LocalFileServiceTest.java @@ -14,6 +14,7 @@ import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.royllo.explorer.core.provider.storage.LocalFileServiceImplementation.WEB_SERVER_HOST; @@ -82,4 +83,37 @@ public void storeAndGetFile() { } } + + @Test + @DisplayName("File exists") + public void fileExists() { + // The file should not exist. + assertFalse(localFileServiceImplementation.fileExists("fileExistsTest.txt")); + + // We create the file. + localFileServiceImplementation.storeFile("Hello World!".getBytes(), "fileExistsTest.txt"); + + // The file should now exist. + assertTrue(localFileServiceImplementation.fileExists("fileExistsTest.txt")); + } + + @Test + @DisplayName("Delete file") + public void deleteFile() { + // The file should not exist. + assertFalse(localFileServiceImplementation.fileExists("fileDeleteTest.txt")); + + // We create the file. + localFileServiceImplementation.storeFile("Hello World!".getBytes(), "fileDeleteTest.txt"); + + // The file should now exist. + assertTrue(localFileServiceImplementation.fileExists("fileDeleteTest.txt")); + + // We delete the file. + localFileServiceImplementation.deleteFile("fileDeleteTest.txt"); + + // The file should not exist anymore. + assertFalse(localFileServiceImplementation.fileExists("fileDeleteTest.txt")); + } + } diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/integration/storage/S3ServiceImplementationTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/integration/storage/S3ServiceImplementationTest.java index 2607ebb3c..aaee602cd 100644 --- a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/integration/storage/S3ServiceImplementationTest.java +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/integration/storage/S3ServiceImplementationTest.java @@ -34,6 +34,7 @@ import java.security.NoSuchAlgorithmException; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -99,6 +100,7 @@ public void s3ImplementationTest() throws DecoderException { } // Adding the file. + assertFalse(contentService.fileExists("test.txt")); contentService.storeFile("test".getBytes(), "test.txt"); // Adding the file (again) - Checking there is no exception. @@ -107,6 +109,7 @@ public void s3ImplementationTest() throws DecoderException { // Checking that the file now exists. try { // File exists ? + assertTrue(contentService.fileExists("test.txt")); minioClient.statObject(StatObjectArgs.builder().bucket(S3_BUCKET_NAME).object("test.txt").build()); // Retrieving the file. @@ -121,6 +124,10 @@ public void s3ImplementationTest() throws DecoderException { fail("The file should now exist"); } + // Testing file deletion. + assertTrue(contentService.fileExists("test.txt")); + contentService.deleteFile("test.txt"); + assertFalse(contentService.fileExists("test.txt")); } @BeforeAll