You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by rc...@apache.org on 2020/03/16 09:37:29 UTC

[james-project] branch master updated (151db97 -> e76b529)

This is an automated email from the ASF dual-hosted git repository.

rcordier pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git.


    from 151db97  JAMES-3074 UidValidity sanitizing at the IMAP level
     new 3578c0b  [Refactoring] integrate filePrefix directly with string
     new e76b529  JAMES-3072: MailboxesBackup ExportService

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 pom.xml                                            |   6 +
 .../james/blob/export/api/BlobExportMechanism.java |   4 +
 .../export/file/LocalFileBlobExportMechanism.java  |   3 +-
 .../file/LocalFileBlobExportMechanismTest.java     |   3 +-
 .../james/webadmin/vault/routes/ExportService.java |   3 +-
 server/protocols/webadmin/webadmin-mailbox/pom.xml |  34 +++
 .../james/webadmin/service/ExportService.java      | 136 ++++++++++
 .../james/webadmin/service/ExportServiceTest.java  | 273 +++++++++++++++++++++
 .../linshare/LinshareBlobExportMechanismTest.java  |   5 +-
 9 files changed, 459 insertions(+), 8 deletions(-)
 create mode 100644 server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/service/ExportService.java
 create mode 100644 server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/service/ExportServiceTest.java


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[james-project] 01/02: [Refactoring] integrate filePrefix directly with string

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rcordier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 3578c0b0e5798c133c3d4a8f0e68460e7ac2fe8c
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Wed Feb 19 10:10:01 2020 +0700

    [Refactoring] integrate filePrefix directly with string
---
 .../java/org/apache/james/blob/export/api/BlobExportMechanism.java   | 4 ++++
 .../james/blob/export/file/LocalFileBlobExportMechanismTest.java     | 3 +--
 .../java/org/apache/james/webadmin/vault/routes/ExportService.java   | 3 +--
 .../org/apache/james/linshare/LinshareBlobExportMechanismTest.java   | 5 ++---
 4 files changed, 8 insertions(+), 7 deletions(-)

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 d9fffa5..4bf04e3 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
@@ -49,6 +49,10 @@ public interface BlobExportMechanism {
         default FileExtensionStage noFileCustomPrefix() {
             return filePrefix(Optional.empty());
         }
+
+        default FileExtensionStage filePrefix(String prefix) {
+            return filePrefix(Optional.of(prefix));
+        }
     }
 
     @FunctionalInterface
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 51c86bc..dc1262c 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
@@ -28,7 +28,6 @@ import static org.mockito.Mockito.when;
 import java.io.FileInputStream;
 import java.net.InetAddress;
 import java.nio.charset.StandardCharsets;
-import java.util.Optional;
 
 import javax.mail.Message;
 import javax.mail.internet.InternetAddress;
@@ -208,7 +207,7 @@ class LocalFileBlobExportMechanismTest {
         testee.blobId(blobId)
             .with(MailAddressFixture.RECIPIENT1)
             .explanation("The content of a deleted message vault had been shared with you.")
-            .filePrefix(Optional.of(filePrefix))
+            .filePrefix(filePrefix)
             .fileExtension(FileExtension.ZIP)
             .export();
 
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 1ba4ed2..9af103c 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
@@ -22,7 +22,6 @@ package org.apache.james.webadmin.vault.routes;
 import static org.apache.james.blob.api.BlobStore.StoragePolicy.LOW_COST;
 
 import java.io.IOException;
-import java.util.Optional;
 import java.util.function.Predicate;
 
 import javax.inject.Inject;
@@ -78,7 +77,7 @@ class ExportService {
         blobExport.blobId(blobId)
             .with(exportToAddress)
             .explanation(exportMessage(username))
-            .filePrefix(Optional.of(String.format("deleted-message-of-%s_", username.asString())))
+            .filePrefix(String.format("deleted-message-of-%s_", username.asString()))
             .fileExtension(FileExtension.ZIP)
             .export();
     }
diff --git a/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareBlobExportMechanismTest.java b/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareBlobExportMechanismTest.java
index 3148e50..5f5d03f 100644
--- a/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareBlobExportMechanismTest.java
+++ b/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareBlobExportMechanismTest.java
@@ -27,7 +27,6 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.nio.charset.StandardCharsets;
-import java.util.Optional;
 
 import org.apache.james.blob.api.BlobId;
 import org.apache.james.blob.api.HashBlobId;
@@ -76,7 +75,7 @@ class LinshareBlobExportMechanismTest {
         testee.blobId(blobId)
             .with(new MailAddress(USER_2.getUsername()))
             .explanation(EXPLANATION)
-            .filePrefix(Optional.of(filePrefix))
+            .filePrefix(filePrefix)
             .fileExtension(FILE_TEXT_EXTENSION)
             .export();
 
@@ -124,7 +123,7 @@ class LinshareBlobExportMechanismTest {
         testee.blobId(blobId)
             .with(new MailAddress(USER_2.getUsername()))
             .explanation(EXPLANATION)
-            .filePrefix(Optional.of(filePrefix))
+            .filePrefix(filePrefix)
             .fileExtension(FILE_TEXT_EXTENSION)
             .export();
 


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[james-project] 02/02: JAMES-3072: MailboxesBackup ExportService

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rcordier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit e76b529a99d5c948807b401d02c51667f69273b5
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Wed Feb 19 11:51:31 2020 +0700

    JAMES-3072: MailboxesBackup ExportService
---
 pom.xml                                            |   6 +
 .../export/file/LocalFileBlobExportMechanism.java  |   3 +-
 server/protocols/webadmin/webadmin-mailbox/pom.xml |  34 +++
 .../james/webadmin/service/ExportService.java      | 136 ++++++++++
 .../james/webadmin/service/ExportServiceTest.java  | 273 +++++++++++++++++++++
 5 files changed, 451 insertions(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index f83608b..869e985 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1125,6 +1125,12 @@
             </dependency>
             <dependency>
                 <groupId>${james.groupId}</groupId>
+                <artifactId>blob-export-file</artifactId>
+                <type>test-jar</type>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${james.groupId}</groupId>
                 <artifactId>blob-export-guice</artifactId>
                 <version>${project.version}</version>
             </dependency>
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 c88d065..a2968ea 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
@@ -93,8 +93,9 @@ public class LocalFileBlobExportMechanism implements BlobExportMechanism {
     private final DNSService dnsService;
     private final Configuration configuration;
 
+    @VisibleForTesting
     @Inject
-    LocalFileBlobExportMechanism(MailetContext mailetContext, BlobStore blobStore, FileSystem fileSystem, DNSService dnsService, Configuration configuration) {
+    public LocalFileBlobExportMechanism(MailetContext mailetContext, BlobStore blobStore, FileSystem fileSystem, DNSService dnsService, Configuration configuration) {
         this.mailetContext = mailetContext;
         this.blobStore = blobStore;
         this.fileSystem = fileSystem;
diff --git a/server/protocols/webadmin/webadmin-mailbox/pom.xml b/server/protocols/webadmin/webadmin-mailbox/pom.xml
index d189e00..cb3c161 100644
--- a/server/protocols/webadmin/webadmin-mailbox/pom.xml
+++ b/server/protocols/webadmin/webadmin-mailbox/pom.xml
@@ -101,6 +101,40 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>backup</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>backup</artifactId>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>blob-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>blob-export-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>blob-export-file</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>blob-export-file</artifactId>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>blob-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-json</artifactId>
             <scope>test</scope>
             <type>test-jar</type>
diff --git a/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/service/ExportService.java b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/service/ExportService.java
new file mode 100644
index 0000000..e2f96ee
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/service/ExportService.java
@@ -0,0 +1,136 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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 org.apache.james.webadmin.service;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+import javax.inject.Inject;
+
+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.Username;
+import org.apache.james.mailbox.backup.MailboxBackup;
+import org.apache.james.task.Task;
+import org.apache.james.user.api.UsersRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.fge.lambdas.Throwing;
+
+import reactor.core.publisher.Mono;
+import reactor.core.scheduler.Schedulers;
+
+public class ExportService {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ExportService.class);
+    private static final String EXPLANATION = "The backup of your mailboxes has been exported to you";
+    private static final String FILE_PREFIX = "mailbox-backup-";
+
+    private final MailboxBackup mailboxBackup;
+    private final BlobStore blobStore;
+    private final BlobExportMechanism blobExport;
+    private final UsersRepository usersRepository;
+
+    @Inject
+    ExportService(MailboxBackup mailboxBackup, BlobStore blobStore, BlobExportMechanism blobExport, UsersRepository usersRepository) {
+        this.mailboxBackup = mailboxBackup;
+        this.blobStore = blobStore;
+        this.blobExport = blobExport;
+        this.usersRepository = usersRepository;
+    }
+
+    public Mono<Task.Result> export(Username username) {
+        return Mono.usingWhen(
+            Mono.fromCallable(() -> zipMailboxesContent(username)),
+            inputStream -> export(username, inputStream),
+            this::closeResource,
+            (inputStream, throwable) -> closeResource(inputStream),
+            this::closeResource
+        );
+    }
+
+    private InputStream zipMailboxesContent(Username username) throws IOException {
+        PipedOutputStream out = new PipedOutputStream();
+        PipedInputStream in = new PipedInputStream(out);
+
+        writeUserMailboxesContent(username, out)
+            .subscribeOn(Schedulers.elastic())
+            .subscribe();
+
+        return in;
+    }
+
+    private Mono<Task.Result> export(Username username, InputStream inputStream) {
+        return Mono.usingWhen(
+                blobStore.save(blobStore.getDefaultBucketName(), inputStream, BlobStore.StoragePolicy.LOW_COST),
+                blobId -> export(username, blobId),
+                this::deleteBlob)
+            .thenReturn(Task.Result.COMPLETED)
+            .onErrorResume(e -> {
+                LOGGER.error("Error exporting mailboxes of user: {}", username.asString(), e);
+                return Mono.just(Task.Result.PARTIAL);
+            });
+    }
+
+    private Mono<Void> export(Username username, BlobId blobId) {
+        return Mono.fromRunnable(Throwing.runnable(() ->
+            blobExport.blobId(blobId)
+                .with(usersRepository.getMailAddressFor(username))
+                .explanation(EXPLANATION)
+                .filePrefix(FILE_PREFIX + username.asString() + "-")
+                .fileExtension(FileExtension.ZIP)
+                .export())
+            .sneakyThrow());
+    }
+
+    private Mono<Void> deleteBlob(BlobId blobId) {
+        return Mono.from(blobStore.delete(blobStore.getDefaultBucketName(), blobId))
+            .onErrorResume(e -> {
+                LOGGER.error("Error deleting Blob with blobId: {}", blobId.asString(), e);
+                return Mono.empty();
+            });
+    }
+
+    private Mono<Void> writeUserMailboxesContent(Username username, PipedOutputStream out) {
+        return Mono.usingWhen(
+            Mono.fromCallable(() -> out),
+            outputStream -> Mono.fromRunnable(Throwing.runnable(() -> mailboxBackup.backupAccount(username, outputStream))),
+            this::closeResource,
+            (outputStream, throwable) -> closeResource(outputStream)
+                .doFinally(any -> LOGGER.error("Error while backing up mailboxes for user {}", username.asString(), throwable)),
+            this::closeResource);
+    }
+
+    private Mono<Void> closeResource(Closeable resource) {
+        return Mono.fromRunnable(() -> {
+            try {
+                resource.close();
+            } catch (IOException e) {
+                LOGGER.error("Error while closing resource", e);
+            }
+        });
+    }
+}
diff --git a/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/service/ExportServiceTest.java b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/service/ExportServiceTest.java
new file mode 100644
index 0000000..ce15b01
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/service/ExportServiceTest.java
@@ -0,0 +1,273 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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 org.apache.james.webadmin.service;
+
+import static org.apache.james.mailbox.DefaultMailboxes.INBOX;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import javax.mail.MessagingException;
+
+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.ObjectNotFoundException;
+import org.apache.james.blob.export.api.BlobExportMechanism;
+import org.apache.james.blob.export.api.FileExtension;
+import org.apache.james.blob.export.file.FileSystemExtension;
+import org.apache.james.blob.export.file.LocalFileBlobExportMechanism;
+import org.apache.james.blob.memory.MemoryBlobStore;
+import org.apache.james.blob.memory.MemoryDumbBlobStore;
+import org.apache.james.core.Domain;
+import org.apache.james.core.Username;
+import org.apache.james.dnsservice.api.DNSService;
+import org.apache.james.domainlist.memory.MemoryDomainList;
+import org.apache.james.filesystem.api.FileSystem;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.backup.ArchiveService;
+import org.apache.james.mailbox.backup.DefaultMailboxBackup;
+import org.apache.james.mailbox.backup.MailArchivesLoader;
+import org.apache.james.mailbox.backup.MailboxBackup;
+import org.apache.james.mailbox.backup.ZipAssert;
+import org.apache.james.mailbox.backup.ZipMailArchiveRestorer;
+import org.apache.james.mailbox.backup.zip.ZipArchivesLoader;
+import org.apache.james.mailbox.backup.zip.Zipper;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.inmemory.InMemoryMailboxManager;
+import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
+import org.apache.james.mailbox.model.ComposedMessageId;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.task.Task;
+import org.apache.james.user.memory.MemoryUsersRepository;
+import org.apache.mailet.base.MailAddressFixture;
+import org.apache.mailet.base.test.FakeMailContext;
+import org.assertj.core.api.SoftAssertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mockito;
+
+import com.google.common.base.Strings;
+import com.google.common.io.Files;
+
+import reactor.core.publisher.Mono;
+
+@ExtendWith(FileSystemExtension.class)
+class ExportServiceTest {
+
+    private static final String JAMES_HOST = "james-host";
+    private static final Domain DOMAIN = Domain.of("domain.tld");
+    private static final Username BOB = Username.fromLocalPartWithDomain("bob", DOMAIN);
+    private static final Username UNKNOWN_USER = Username.fromLocalPartWithDomain("unknown", DOMAIN);
+    private static final String PASSWORD = "password";
+    private static final String CORRESPONDING_FILE_HEADER = "corresponding-file";
+    private static final String MESSAGE_CONTENT = "MIME-Version: 1.0\r\n" +
+        "Subject: test\r\n" +
+        "Content-Type: text/plain; charset=UTF-8\r\n" +
+        "\r\n" +
+        "testmail";
+    private static final String TWELVE_MEGABYTES_STRING = Strings.repeat("0123456789\r\n", 1024 * 1024);
+    private static final String FILE_PREFIX = "mailbox-backup-";
+    private static final BlobId.Factory FACTORY = new HashBlobId.Factory();
+
+    private ExportService testee;
+    private InMemoryMailboxManager mailboxManager;
+    private MailboxSession bobSession;
+    private BlobStore blobStore;
+    private FakeMailContext mailetContext;
+
+    @BeforeEach
+    void setUp(FileSystem fileSystem) throws Exception {
+        mailboxManager = InMemoryIntegrationResources.defaultResources().getMailboxManager();
+        bobSession = mailboxManager.createSystemSession(BOB);
+
+        MailboxBackup backup = createMailboxBackup();
+        DNSService dnsService = createDnsService();
+        blobStore = Mockito.spy(new MemoryBlobStore(FACTORY, new MemoryDumbBlobStore()));
+        mailetContext = FakeMailContext.builder().postmaster(MailAddressFixture.POSTMASTER_AT_JAMES).build();
+        BlobExportMechanism blobExport = new LocalFileBlobExportMechanism(mailetContext, blobStore, fileSystem, dnsService,
+            LocalFileBlobExportMechanism.Configuration.DEFAULT_CONFIGURATION);
+        MemoryUsersRepository usersRepository = createUsersRepository(dnsService);
+
+        testee = new ExportService(backup, blobStore, blobExport, usersRepository);
+    }
+
+    private MemoryUsersRepository createUsersRepository(DNSService dnsService) throws Exception {
+        MemoryDomainList domainList = new MemoryDomainList(dnsService);
+        MemoryUsersRepository usersRepository = MemoryUsersRepository.withVirtualHosting(domainList);
+
+        domainList.addDomain(DOMAIN);
+        usersRepository.addUser(BOB, PASSWORD);
+        return usersRepository;
+    }
+
+    private DNSService createDnsService() throws UnknownHostException {
+        InetAddress localHost = mock(InetAddress.class);
+        when(localHost.getHostName()).thenReturn(JAMES_HOST);
+        DNSService dnsService = mock(DNSService.class);
+        when(dnsService.getLocalHost()).thenReturn(localHost);
+        return dnsService;
+    }
+
+    private DefaultMailboxBackup createMailboxBackup() {
+        ArchiveService archiveService = new Zipper();
+        MailArchivesLoader archiveLoader = new ZipArchivesLoader();
+        ZipMailArchiveRestorer archiveRestorer = new ZipMailArchiveRestorer(mailboxManager, archiveLoader);
+        return new DefaultMailboxBackup(mailboxManager, archiveService, archiveRestorer);
+    }
+
+    private String getFileUrl() throws MessagingException {
+        return mailetContext.getSentMails().get(0).getMsg().getHeader(CORRESPONDING_FILE_HEADER)[0];
+    }
+
+    @Test
+    void exportUserMailboxesDataShouldReturnCompletedWhenUserDoesNotExist() {
+        assertThat(testee.export(UNKNOWN_USER).block())
+            .isEqualTo(Task.Result.COMPLETED);
+    }
+
+    @Test
+    void exportUserMailboxesDataShouldReturnCompletedWhenExistingUserWithoutMailboxes() {
+        assertThat(testee.export(BOB).block())
+            .isEqualTo(Task.Result.COMPLETED);
+    }
+
+    @Test
+    void exportUserMailboxesDataShouldReturnCompletedWhenExistingUser() throws Exception {
+        createAMailboxWithAMail(MESSAGE_CONTENT);
+
+        assertThat(testee.export(BOB).block())
+            .isEqualTo(Task.Result.COMPLETED);
+    }
+
+    private ComposedMessageId createAMailboxWithAMail(String message) throws MailboxException {
+        MailboxPath bobInboxPath = MailboxPath.inbox(BOB);
+        mailboxManager.createMailbox(bobInboxPath, bobSession);
+        return mailboxManager.getMailbox(bobInboxPath, bobSession)
+            .appendMessage(MessageManager.AppendCommand.builder()
+                    .build(message),
+                bobSession);
+    }
+
+    @Test
+    void exportUserMailboxesDataShouldProduceAnEmptyZipWhenUserDoesNotExist() throws Exception {
+        testee.export(UNKNOWN_USER).block();
+
+        ZipAssert.assertThatZip(new FileInputStream(getFileUrl()))
+            .hasNoEntry();
+    }
+
+    @Test
+    void exportUserMailboxesDataShouldProduceAnEmptyZipWhenExistingUserWithoutAnyMailboxes() throws Exception {
+        testee.export(BOB).block();
+
+        ZipAssert.assertThatZip(new FileInputStream(getFileUrl()))
+            .hasNoEntry();
+    }
+
+    @Test
+    void exportUserMailboxesDataShouldProduceAZipWithEntry() throws Exception {
+        ComposedMessageId id = createAMailboxWithAMail(MESSAGE_CONTENT);
+
+        testee.export(BOB).block();
+
+        ZipAssert.assertThatZip(new FileInputStream(getFileUrl()))
+            .containsOnlyEntriesMatching(
+                ZipAssert.EntryChecks.hasName(INBOX + "/").isDirectory(),
+                ZipAssert.EntryChecks.hasName(id.getMessageId().serialize()).hasStringContent(MESSAGE_CONTENT));
+    }
+
+    @Test
+    void exportUserMailboxesDataShouldProduceAFileWithExpectedExtension() throws Exception {
+        createAMailboxWithAMail(MESSAGE_CONTENT);
+
+        testee.export(BOB).block();
+
+        assertThat(Files.getFileExtension(getFileUrl())).isEqualTo(FileExtension.ZIP.getExtension());
+    }
+
+    @Test
+    void exportUserMailboxesDataShouldProduceAFileWithExpectedName() throws Exception {
+        createAMailboxWithAMail(MESSAGE_CONTENT);
+
+        testee.export(BOB).block();
+
+        File file = new File(getFileUrl());
+
+        assertThat(file.getName()).startsWith(FILE_PREFIX + BOB.asString());
+    }
+
+    @Test
+    void exportUserMailboxesWithSizableDataShouldProduceAFile() throws Exception {
+        ComposedMessageId id = createAMailboxWithAMail(TWELVE_MEGABYTES_STRING);
+
+        testee.export(BOB).block();
+
+        ZipAssert.assertThatZip(new FileInputStream(getFileUrl()))
+            .containsOnlyEntriesMatching(
+                ZipAssert.EntryChecks.hasName(INBOX + "/").isDirectory(),
+                ZipAssert.EntryChecks.hasName(id.getMessageId().serialize()).hasStringContent(TWELVE_MEGABYTES_STRING));
+    }
+
+    @Test
+    void exportUserMailboxesDataShouldDeleteBlobAfterCompletion() throws Exception {
+        createAMailboxWithAMail(MESSAGE_CONTENT);
+
+        testee.export(BOB).block();
+
+        String fileName = Files.getNameWithoutExtension(getFileUrl());
+        String blobId = fileName.substring(fileName.lastIndexOf("-") + 1);
+
+        SoftAssertions.assertSoftly(softly -> {
+            assertThatThrownBy(() -> blobStore.read(blobStore.getDefaultBucketName(), FACTORY.from(blobId)))
+                .isInstanceOf(ObjectNotFoundException.class);
+            assertThatThrownBy(() -> blobStore.read(blobStore.getDefaultBucketName(), FACTORY.from(blobId)))
+                .hasMessage(String.format("blob '%s' not found in bucket '%s'", blobId, blobStore.getDefaultBucketName().asString()));
+        });
+    }
+
+    @Test
+    void exportUserMailboxesDataShouldReturnSuccessWhenBlobDeletingFails() throws Exception {
+        createAMailboxWithAMail(MESSAGE_CONTENT);
+
+        doReturn(Mono.error(new RuntimeException()))
+            .when(blobStore)
+            .delete(any(), any());
+
+        Task.Result result = testee.export(BOB).block();
+
+        String fileName = Files.getNameWithoutExtension(getFileUrl());
+        String blobId = fileName.substring(fileName.lastIndexOf("-") + 1);
+
+        blobStore.read(blobStore.getDefaultBucketName(), FACTORY.from(blobId));
+
+        assertThat(result).isEqualTo(Task.Result.COMPLETED);
+    }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org