This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 651248aaadcff000e0dbc7f8e117a224c2dc5811 Author: Tran Tien Duc <[email protected]> AuthorDate: Mon Apr 1 13:04:05 2019 +0700 JAMES-2705 Putting .zip extension to archives --- .../apache/james/vault/DeletedMessageZipper.java | 5 +- .../james/vault/DeletedMessageZipperTest.java | 8 +-- server/blob/blob-export-api/pom.xml | 15 ++++++ .../james/blob/export/api/BlobExportMechanism.java | 18 ++++++- ...BlobExportMechanism.java => FileExtension.java} | 57 +++++++++++++++------- .../james/blob/export/api/FileExtensionTest.java} | 52 +++++++++++++------- .../export/file/LocalFileBlobExportMechanism.java | 21 +++++--- .../file/LocalFileBlobExportMechanismTest.java | 52 ++++++++++++++++++++ .../integration/DeletedMessagesVaultTest.java | 9 ++-- .../james/webadmin/vault/routes/ExportService.java | 2 + .../routes/DeletedMessagesVaultRoutesTest.java | 2 +- 11 files changed, 186 insertions(+), 55 deletions(-) diff --git a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageZipper.java b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageZipper.java index e5272af..bf76384 100644 --- a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageZipper.java +++ b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageZipper.java @@ -43,7 +43,8 @@ public class DeletedMessageZipper { Optional<InputStream> load(DeletedMessage deletedMessage); } - static final String ZIPPED_FILE_EXTENSION = ".eml"; + @VisibleForTesting + static final String EML_FILE_EXTENSION = ".eml"; public DeletedMessageZipper() { ExtraFieldUtils.register(MessageIdExtraField.class); @@ -83,7 +84,7 @@ public class DeletedMessageZipper { ZipArchiveEntry archiveEntry = (ZipArchiveEntry) zipOutputStream.createArchiveEntry( new File(messageId.serialize()), - messageId.serialize() + ZIPPED_FILE_EXTENSION); + messageId.serialize() + EML_FILE_EXTENSION); archiveEntry.addExtraField(new MessageIdExtraField(messageId)); archiveEntry.addExtraField(new SizeExtraField(message.getSize())); diff --git a/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/DeletedMessageZipperTest.java b/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/DeletedMessageZipperTest.java index ece8944..d9a33f2 100644 --- a/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/DeletedMessageZipperTest.java +++ b/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/DeletedMessageZipperTest.java @@ -27,7 +27,7 @@ import static org.apache.james.vault.DeletedMessageFixture.DELETED_MESSAGE_2; import static org.apache.james.vault.DeletedMessageFixture.MESSAGE_ID; import static org.apache.james.vault.DeletedMessageFixture.MESSAGE_ID_2; import static org.apache.james.vault.DeletedMessageZipper.DeletedMessageContentLoader; -import static org.apache.james.vault.DeletedMessageZipper.ZIPPED_FILE_EXTENSION; +import static org.apache.james.vault.DeletedMessageZipper.EML_FILE_EXTENSION; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -89,8 +89,8 @@ class DeletedMessageZipperTest { try (ZipAssert zipAssert = assertThatZip(outputStream)) { zipAssert.containsOnlyEntriesMatching( - hasName(MESSAGE_ID.serialize() + ZIPPED_FILE_EXTENSION).hasStringContent(MESSAGE_CONTENT), - hasName(MESSAGE_ID_2.serialize() + ZIPPED_FILE_EXTENSION).hasStringContent(MESSAGE_CONTENT)); + hasName(MESSAGE_ID.serialize() + EML_FILE_EXTENSION).hasStringContent(MESSAGE_CONTENT), + hasName(MESSAGE_ID_2.serialize() + EML_FILE_EXTENSION).hasStringContent(MESSAGE_CONTENT)); } } @@ -102,7 +102,7 @@ class DeletedMessageZipperTest { try (ZipAssert zipAssert = assertThatZip(outputStream)) { zipAssert.containsOnlyEntriesMatching( - hasName(MESSAGE_ID.serialize() + ZIPPED_FILE_EXTENSION) + hasName(MESSAGE_ID.serialize() + EML_FILE_EXTENSION) .containsExtraFields(new MessageIdExtraField(MESSAGE_ID)) .containsExtraFields(new SizeExtraField(CONTENT.length))); } diff --git a/server/blob/blob-export-api/pom.xml b/server/blob/blob-export-api/pom.xml index 634d248..a3336bd 100644 --- a/server/blob/blob-export-api/pom.xml +++ b/server/blob/blob-export-api/pom.xml @@ -41,5 +41,20 @@ <groupId>${james.groupId}</groupId> <artifactId>blob-api</artifactId> </dependency> + <dependency> + <groupId>nl.jqno.equalsverifier</groupId> + <artifactId>equalsverifier</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project> \ No newline at end of file diff --git a/server/blob/blob-export-api/src/main/java/org/apache/james/blob/export/api/BlobExportMechanism.java b/server/blob/blob-export-api/src/main/java/org/apache/james/blob/export/api/BlobExportMechanism.java index 2aa94cf..e88aba1 100644 --- a/server/blob/blob-export-api/src/main/java/org/apache/james/blob/export/api/BlobExportMechanism.java +++ b/server/blob/blob-export-api/src/main/java/org/apache/james/blob/export/api/BlobExportMechanism.java @@ -19,10 +19,13 @@ package org.apache.james.blob.export.api; +import java.util.Optional; + import org.apache.james.blob.api.BlobId; import org.apache.james.core.MailAddress; public interface BlobExportMechanism { + class BlobExportException extends RuntimeException { public BlobExportException(String message, Throwable cause) { super(message, cause); @@ -36,7 +39,20 @@ public interface BlobExportMechanism { @FunctionalInterface interface ExplanationStage { - FinalStage explanation(String explanation); + FileExtensionStage explanation(String explanation); + } + + @FunctionalInterface + interface FileExtensionStage { + FinalStage fileExtension(Optional<FileExtension> extension); + + default FinalStage noFileExtension() { + return fileExtension(Optional.empty()); + } + + default FinalStage fileExtension(FileExtension extension) { + return fileExtension(Optional.of(extension)); + } } @FunctionalInterface diff --git a/server/blob/blob-export-api/src/main/java/org/apache/james/blob/export/api/BlobExportMechanism.java b/server/blob/blob-export-api/src/main/java/org/apache/james/blob/export/api/FileExtension.java similarity index 50% copy from server/blob/blob-export-api/src/main/java/org/apache/james/blob/export/api/BlobExportMechanism.java copy to server/blob/blob-export-api/src/main/java/org/apache/james/blob/export/api/FileExtension.java index 2aa94cf..b0c3000 100644 --- a/server/blob/blob-export-api/src/main/java/org/apache/james/blob/export/api/BlobExportMechanism.java +++ b/server/blob/blob-export-api/src/main/java/org/apache/james/blob/export/api/FileExtension.java @@ -19,30 +19,51 @@ package org.apache.james.blob.export.api; -import org.apache.james.blob.api.BlobId; -import org.apache.james.core.MailAddress; +import java.util.Objects; -public interface BlobExportMechanism { - class BlobExportException extends RuntimeException { - public BlobExportException(String message, Throwable cause) { - super(message, cause); - } +import org.apache.commons.lang3.StringUtils; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; + +public class FileExtension { + static final String ZIP_EXTENSION_STRING = "zip"; + public static final FileExtension ZIP = new FileExtension(ZIP_EXTENSION_STRING); + private static final String EXTENSION_SEPARATOR = "."; + + public static FileExtension of(String extension) { + return new FileExtension(extension); } - @FunctionalInterface - interface ShareeStage { - ExplanationStage with(MailAddress sharee); + private final String extension; + + @VisibleForTesting + FileExtension(String extension) { + this.extension = extension; } - @FunctionalInterface - interface ExplanationStage { - FinalStage explanation(String explanation); + public String appendExtension(String filePath) { + Preconditions.checkArgument(StringUtils.isNotBlank(filePath), "filePath cannot be null or blank"); + + return filePath + EXTENSION_SEPARATOR + extension; } - @FunctionalInterface - interface FinalStage { - void export() throws BlobExportException; + public String getExtension() { + return extension; } - ShareeStage blobId(BlobId blobId); -} + @Override + public final boolean equals(Object o) { + if (o instanceof FileExtension) { + FileExtension that = (FileExtension) o; + + return Objects.equals(this.extension, that.extension); + } + return false; + } + + @Override + public final int hashCode() { + return Objects.hash(extension); + } +} \ No newline at end of file diff --git a/server/blob/blob-export-api/src/main/java/org/apache/james/blob/export/api/BlobExportMechanism.java b/server/blob/blob-export-api/src/test/java/org/apache/james/blob/export/api/FileExtensionTest.java similarity index 50% copy from server/blob/blob-export-api/src/main/java/org/apache/james/blob/export/api/BlobExportMechanism.java copy to server/blob/blob-export-api/src/test/java/org/apache/james/blob/export/api/FileExtensionTest.java index 2aa94cf..6c0823b 100644 --- a/server/blob/blob-export-api/src/main/java/org/apache/james/blob/export/api/BlobExportMechanism.java +++ b/server/blob/blob-export-api/src/test/java/org/apache/james/blob/export/api/FileExtensionTest.java @@ -19,30 +19,46 @@ package org.apache.james.blob.export.api; -import org.apache.james.blob.api.BlobId; -import org.apache.james.core.MailAddress; - -public interface BlobExportMechanism { - class BlobExportException extends RuntimeException { - public BlobExportException(String message, Throwable cause) { - super(message, cause); - } +import static org.apache.james.blob.export.api.FileExtension.ZIP_EXTENSION_STRING; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import nl.jqno.equalsverifier.EqualsVerifier; + +class FileExtensionTest { + + private FileExtension fileExtension; + + @BeforeEach + void beforeEach() { + fileExtension = new FileExtension(ZIP_EXTENSION_STRING); } - @FunctionalInterface - interface ShareeStage { - ExplanationStage with(MailAddress sharee); + @Test + void shouldMatchBeanContract() { + EqualsVerifier.forClass(FileExtension.class) + .verify(); } - @FunctionalInterface - interface ExplanationStage { - FinalStage explanation(String explanation); + @Test + void appendExtensionShouldThrowWhenPassingNullValue() { + assertThatThrownBy(() -> fileExtension.appendExtension(null)) + .isInstanceOf(IllegalArgumentException.class); } - @FunctionalInterface - interface FinalStage { - void export() throws BlobExportException; + @Test + void appendExtensionShouldThrowWhenPassingEmptyStringValue() { + assertThatThrownBy(() -> fileExtension.appendExtension("")) + .isInstanceOf(IllegalArgumentException.class); } - ShareeStage blobId(BlobId blobId); + @Test + void appendExtensionShouldReturnValueEndsWithExtension() { + fileExtension = new FileExtension("tar.gz"); + assertThat(fileExtension.appendExtension("/local/james")) + .endsWith(".tar.gz"); + } } diff --git a/server/blob/blob-export-file/src/main/java/org/apache/james/blob/export/file/LocalFileBlobExportMechanism.java b/server/blob/blob-export-file/src/main/java/org/apache/james/blob/export/file/LocalFileBlobExportMechanism.java index 656dc45..c432575 100644 --- a/server/blob/blob-export-file/src/main/java/org/apache/james/blob/export/file/LocalFileBlobExportMechanism.java +++ b/server/blob/blob-export-file/src/main/java/org/apache/james/blob/export/file/LocalFileBlobExportMechanism.java @@ -22,6 +22,7 @@ package org.apache.james.blob.export.file; import java.io.File; import java.io.IOException; import java.net.UnknownHostException; +import java.util.Optional; import javax.inject.Inject; @@ -30,6 +31,7 @@ import org.apache.commons.lang3.RandomStringUtils; import org.apache.james.blob.api.BlobId; import org.apache.james.blob.api.BlobStore; import org.apache.james.blob.export.api.BlobExportMechanism; +import org.apache.james.blob.export.api.FileExtension; import org.apache.james.core.MailAddress; import org.apache.james.core.builder.MimeMessageBuilder; import org.apache.james.dnsservice.api.DNSService; @@ -73,20 +75,18 @@ public class LocalFileBlobExportMechanism implements BlobExportMechanism { @Override public ShareeStage blobId(BlobId blobId) { - return mailAddress -> explanation -> () -> { - String fileUrl = copyBlobToFile(blobId); + return mailAddress -> explanation -> fileExtension -> () -> { + String fileUrl = copyBlobToFile(blobId, fileExtension); sendMail(mailAddress, fileUrl, explanation); }; } - private String copyBlobToFile(BlobId blobId) { + private String copyBlobToFile(BlobId blobId, Optional<FileExtension> fileExtension) { try { File exportingDirectory = fileSystem.getFile(configuration.exportDirectory); FileUtils.forceMkdir(exportingDirectory); - - String fileName = RandomStringUtils.random(STRING_LENGTH, WITH_LETTERS, !WITH_NUMBERS); - String fileURL = configuration.exportDirectory + "/" + fileName; + String fileURL = generateFileUrl(fileExtension); File file = fileSystem.getFile(fileURL); FileUtils.copyToFile(blobStore.read(blobId), file); @@ -96,6 +96,15 @@ public class LocalFileBlobExportMechanism implements BlobExportMechanism { } } + private String generateFileUrl(Optional<FileExtension> fileExtension) { + String fileName = RandomStringUtils.random(STRING_LENGTH, WITH_LETTERS, !WITH_NUMBERS); + String filePathWithoutExtension = configuration.exportDirectory + "/" + fileName; + + return fileExtension + .map(extension -> extension.appendExtension(filePathWithoutExtension)) + .orElse(filePathWithoutExtension); + } + private void sendMail(MailAddress mailAddress, String fileUrl, String explanation) { try { MimeMessageBuilder mimeMessage = MimeMessageBuilder.mimeMessageBuilder() diff --git a/server/blob/blob-export-file/src/test/java/org/apache/james/blob/export/file/LocalFileBlobExportMechanismTest.java b/server/blob/blob-export-file/src/test/java/org/apache/james/blob/export/file/LocalFileBlobExportMechanismTest.java index 83cd0e6..f1485b9 100644 --- a/server/blob/blob-export-file/src/test/java/org/apache/james/blob/export/file/LocalFileBlobExportMechanismTest.java +++ b/server/blob/blob-export-file/src/test/java/org/apache/james/blob/export/file/LocalFileBlobExportMechanismTest.java @@ -31,10 +31,12 @@ import java.nio.charset.StandardCharsets; import javax.mail.Message; import javax.mail.internet.InternetAddress; +import org.apache.commons.io.FilenameUtils; import org.apache.james.blob.api.BlobId; import org.apache.james.blob.api.BlobStore; import org.apache.james.blob.api.HashBlobId; import org.apache.james.blob.api.ObjectStoreException; +import org.apache.james.blob.export.api.FileExtension; import org.apache.james.blob.memory.MemoryBlobStore; import org.apache.james.dnsservice.api.DNSService; import org.apache.james.filesystem.api.FileSystem; @@ -76,6 +78,7 @@ class LocalFileBlobExportMechanismTest { testee.blobId(blobId) .with(MailAddressFixture.RECIPIENT1) .explanation(explanation) + .noFileExtension() .export(); assertThat(mailetContext.getSentMails()).hasSize(1) @@ -106,6 +109,7 @@ class LocalFileBlobExportMechanismTest { testee.blobId(blobId) .with(MailAddressFixture.RECIPIENT1) .explanation("The content of a deleted message vault had been shared with you.") + .noFileExtension() .export(); assertThat(mailetContext.getSentMails()) @@ -129,7 +133,55 @@ class LocalFileBlobExportMechanismTest { testee.blobId(blobId) .with(MailAddressFixture.RECIPIENT1) .explanation("The content of a deleted message vault had been shared with you.") + .noFileExtension() .export()) .isInstanceOf(ObjectStoreException.class); } + + @Test + void exportingBlobShouldCreateAFileWithoutExtensionWhenNotDeclaringExtension() { + BlobId blobId = blobStore.save(BLOB_CONTENT).block(); + + testee.blobId(blobId) + .with(MailAddressFixture.RECIPIENT1) + .explanation("The content of a deleted message vault had been shared with you.") + .noFileExtension() + .export(); + + assertThat(mailetContext.getSentMails()) + .element(0) + .satisfies(sentMail -> { + try { + String fileUrl = sentMail.getMsg().getHeader(LocalFileBlobExportMechanism.CORRESPONDING_FILE_HEADER)[0]; + assertThat(FilenameUtils.getExtension(fileUrl)) + .isEmpty(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + + @Test + void exportingBlobShouldCreateAFileWithExtensionWhenDeclaringExtension() { + BlobId blobId = blobStore.save(BLOB_CONTENT).block(); + + testee.blobId(blobId) + .with(MailAddressFixture.RECIPIENT1) + .explanation("The content of a deleted message vault had been shared with you.") + .fileExtension(FileExtension.ZIP) + .export(); + + assertThat(mailetContext.getSentMails()) + .element(0) + .satisfies(sentMail -> { + try { + String fileUrl = sentMail.getMsg().getHeader(LocalFileBlobExportMechanism.CORRESPONDING_FILE_HEADER)[0]; + String fileExtensionInString = FilenameUtils.getExtension(fileUrl); + assertThat(FileExtension.of(fileExtensionInString)) + .isEqualTo(FileExtension.ZIP); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } } \ No newline at end of file diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/DeletedMessagesVaultTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/DeletedMessagesVaultTest.java index 6bdfc20..50aa924 100644 --- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/DeletedMessagesVaultTest.java +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/DeletedMessagesVaultTest.java @@ -124,7 +124,6 @@ public abstract class DeletedMessagesVaultTest { .userExportFrom(HOMER) .exportTo(BART) .query(MATCH_ALL_QUERY); - private static final String EML_EXTENSION = ".eml"; private MailboxId otherMailboxId; @@ -468,7 +467,7 @@ public abstract class DeletedMessagesVaultTest { String fileLocation = exportAndGetFileLocationFromLastMail(EXPORT_ALL_HOMER_MESSAGES_TO_BART, bartAccessToken); try (ZipAssert zipAssert = assertThatZip(fileSystem.getResource(fileLocation))) { - zipAssert.containsOnlyEntriesMatching(hasName(messageIdOfHomer + EML_EXTENSION)); + zipAssert.containsOnlyEntriesMatching(hasName(messageIdOfHomer + ".eml")); } } @@ -490,7 +489,7 @@ public abstract class DeletedMessagesVaultTest { String fileLocation = exportAndGetFileLocationFromLastMail(EXPORT_ALL_HOMER_MESSAGES_TO_BART, bartAccessToken); try (ZipAssert zipAssert = assertThatZip(fileSystem.getResource(fileLocation))) { - zipAssert.containsOnlyEntriesMatching(hasName(messageIdOfHomer + EML_EXTENSION)); + zipAssert.containsOnlyEntriesMatching(hasName(messageIdOfHomer + ".eml")); } } @@ -514,7 +513,7 @@ public abstract class DeletedMessagesVaultTest { String fileLocation = exportAndGetFileLocationFromLastMail(EXPORT_ALL_HOMER_MESSAGES_TO_BART, bartAccessToken); try (ZipAssert zipAssert = assertThatZip(fileSystem.getResource(fileLocation))) { - zipAssert.containsOnlyEntriesMatching(hasName(messageIdOfHomer + EML_EXTENSION)); + zipAssert.containsOnlyEntriesMatching(hasName(messageIdOfHomer + ".eml")); } } @@ -542,7 +541,7 @@ public abstract class DeletedMessagesVaultTest { String fileLocation = exportAndGetFileLocationFromLastMail(exportRequest, bartAccessToken); try (ZipAssert zipAssert = assertThatZip(fileSystem.getResource(fileLocation))) { - zipAssert.containsOnlyEntriesMatching(hasName(firstMessageIdOfHomer + EML_EXTENSION)); + zipAssert.containsOnlyEntriesMatching(hasName(firstMessageIdOfHomer + ".eml")); } } diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/ExportService.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/ExportService.java index 8dc77d1..a3ca2ce 100644 --- a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/ExportService.java +++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/ExportService.java @@ -27,6 +27,7 @@ import org.apache.commons.io.FileUtils; import org.apache.james.blob.api.BlobId; import org.apache.james.blob.api.BlobStore; import org.apache.james.blob.export.api.BlobExportMechanism; +import org.apache.james.blob.export.api.FileExtension; import org.apache.james.core.MailAddress; import org.apache.james.core.User; import org.apache.james.vault.DeletedMessage; @@ -65,6 +66,7 @@ class ExportService { blobExport.blobId(blobId) .with(exportToAddress) .explanation(exportMessage(user)) + .fileExtension(FileExtension.ZIP) .export(); } diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java index b08c456..7989b31 100644 --- a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java +++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java @@ -122,7 +122,7 @@ class DeletedMessagesVaultRoutesTest { private class NoopBlobExporting implements BlobExportMechanism { @Override public ShareeStage blobId(BlobId blobId) { - return exportTo -> explanation -> () -> export(exportTo, explanation); + return exportTo -> explanation -> fileExtension -> () -> export(exportTo, explanation); } void export(MailAddress exportTo, String explanation) { --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
