james-server-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From adup...@apache.org
Subject [james-project] 06/11: JAMES-2711 Add DeleteAPI to DeletedMessageVaultRoutes
Date Thu, 18 Apr 2019 07:00:21 GMT
This is an automated email from the ASF dual-hosted git repository.

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

commit 0980816d7dc2a0c35d8c3c3d41bbc33148379b27
Author: datph <dphamhoang@linagora.com>
AuthorDate: Mon Apr 8 16:34:50 2019 +0700

    JAMES-2711 Add DeleteAPI to DeletedMessageVaultRoutes
---
 .../routes/DeletedMessagesVaultDeleteTask.java     |  84 +++++++++
 .../vault/routes/DeletedMessagesVaultRoutes.java   |  58 ++++++-
 .../routes/DeletedMessagesVaultRoutesTest.java     | 191 ++++++++++++++++++++-
 3 files changed, 329 insertions(+), 4 deletions(-)

diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultDeleteTask.java
b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultDeleteTask.java
new file mode 100644
index 0000000..9c886c5
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultDeleteTask.java
@@ -0,0 +1,84 @@
+/****************************************************************
+ * 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.vault.routes;
+
+import java.util.Optional;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.task.Task;
+import org.apache.james.task.TaskExecutionDetails;
+import org.apache.james.vault.DeletedMessageVault;
+
+import reactor.core.publisher.Mono;
+
+public class DeletedMessagesVaultDeleteTask implements Task {
+
+    public class AdditionalInformation implements TaskExecutionDetails.AdditionalInformation
{
+
+        private final User user;
+        private final MessageId deleteMessageId;
+
+        AdditionalInformation(User user, MessageId deleteMessageId) {
+            this.user = user;
+            this.deleteMessageId = deleteMessageId;
+        }
+
+        public String getUser() {
+            return user.asString();
+        }
+
+        public String getDeleteMessageId() {
+            return deleteMessageId.serialize();
+        }
+    }
+
+    static final String TYPE = "deletedMessages/delete";
+
+    private final DeletedMessageVault vault;
+    private final User user;
+    private final MessageId messageId;
+
+    DeletedMessagesVaultDeleteTask(DeletedMessageVault vault, User user, MessageId messageId)
{
+        this.vault = vault;
+        this.user = user;
+        this.messageId = messageId;
+    }
+
+    @Override
+    public Result run() {
+        return Mono.from(vault.delete(user, messageId))
+            .doOnError(e -> LOGGER.error("Error while deleting message {} for user {}
in DeletedMessageVault: {}", messageId, user, e))
+            .thenReturn(Result.COMPLETED)
+            .blockOptional()
+            .orElse(Result.PARTIAL);
+    }
+
+    @Override
+    public String type() {
+        return TYPE;
+    }
+
+    @Override
+    public Optional<TaskExecutionDetails.AdditionalInformation> details() {
+        return Optional.of(new AdditionalInformation(user, messageId));
+    }
+
+}
diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutes.java
b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutes.java
index 50f483e..e2faa4c 100644
--- a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutes.java
+++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutes.java
@@ -37,6 +37,7 @@ import org.apache.commons.lang3.NotImplementedException;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.james.core.MailAddress;
 import org.apache.james.core.User;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.task.Task;
 import org.apache.james.task.TaskId;
 import org.apache.james.task.TaskManager;
@@ -157,11 +158,13 @@ public class DeletedMessagesVaultRoutes implements Routes {
     private final JsonExtractor<QueryElement> jsonExtractor;
     private final QueryTranslator queryTranslator;
     private final UsersRepository usersRepository;
+    private final MessageId.Factory messageIdFactory;
 
     @Inject
     @VisibleForTesting
-    DeletedMessagesVaultRoutes(DeletedMessageVault deletedMessageVault, RestoreService vaultRestore,
ExportService vaultExport, JsonTransformer jsonTransformer,
-                               TaskManager taskManager, QueryTranslator queryTranslator,
UsersRepository usersRepository) {
+    DeletedMessagesVaultRoutes(DeletedMessageVault deletedMessageVault, RestoreService vaultRestore,
ExportService vaultExport,
+                               JsonTransformer jsonTransformer, TaskManager taskManager,
QueryTranslator queryTranslator,
+                               UsersRepository usersRepository, MessageId.Factory messageIdFactory)
{
         this.deletedMessageVault = deletedMessageVault;
         this.vaultRestore = vaultRestore;
         this.vaultExport = vaultExport;
@@ -170,6 +173,7 @@ public class DeletedMessagesVaultRoutes implements Routes {
         this.queryTranslator = queryTranslator;
         this.usersRepository = usersRepository;
         this.jsonExtractor = new JsonExtractor<>(QueryElement.class);
+        this.messageIdFactory = messageIdFactory;
     }
 
     @Override
@@ -181,6 +185,7 @@ public class DeletedMessagesVaultRoutes implements Routes {
     public void define(Service service) {
         service.post(RESTORE_PATH, this::userActions, jsonTransformer);
         service.delete(ROOT_PATH, this::deleteWithScope, jsonTransformer);
+        service.delete(DELETE_PATH, this::deleteMessage, jsonTransformer);
     }
 
     @POST
@@ -248,6 +253,41 @@ public class DeletedMessagesVaultRoutes implements Routes {
         return TaskIdDto.respond(response, taskId);
     }
 
+    @DELETE
+    @Path(DELETE_PATH)
+    @ApiOperation(value = "Delete message with messageId")
+    @ApiImplicitParams({
+        @ApiImplicitParam(
+            required = true,
+            name = "user",
+            paramType = "path parameter",
+            dataType = "String",
+            defaultValue = "none",
+            example = "user0@james.org",
+            value = "Compulsory. Needs to be a valid username represent for an user had requested
to restore deleted emails"),
+        @ApiImplicitParam(
+            required = true,
+            name = "messageId",
+            paramType = "path parameter",
+            dataType = "String",
+            defaultValue = "none",
+            value = "Compulsory, Need to be a valid messageId")
+    })
+    @ApiResponses(value = {
+        @ApiResponse(code = HttpStatus.CREATED_201, message = "Task is created", response
= TaskIdDto.class),
+        @ApiResponse(code = HttpStatus.BAD_REQUEST_400, message = "Bad request - user param
is invalid"),
+        @ApiResponse(code = HttpStatus.BAD_REQUEST_400, message = "Bad request - messageId
param is invalid"),
+        @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500, message = "Internal server
error - Something went bad on the server side.")
+    })
+    private TaskIdDto deleteMessage(Request request, Response response) {
+        User user = extractUser(request);
+        validateUserExist(user);
+        MessageId messageId = parseMessageId(request);
+
+        TaskId taskId = taskManager.submit(new DeletedMessagesVaultDeleteTask(deletedMessageVault,
user, messageId));
+        return TaskIdDto.respond(response, taskId);
+    }
+
     private Task generateVaultScopeTask(Request request) {
         VaultScope scope = extractParam(request, SCOPE_QUERY_PARAM, this::getVaultScope);
         if (!scope.equals(VaultScope.EXPIRED)) {
@@ -385,4 +425,18 @@ public class DeletedMessagesVaultRoutes implements Routes {
                     .collect(Guavate.toImmutableList()))))
             .haltError();
     }
+
+    private MessageId parseMessageId(Request request) {
+        String messageIdAsString = request.params(MESSAGE_ID_PARAM);
+        try {
+            return messageIdFactory.fromString(messageIdAsString);
+        } catch (Exception e) {
+            throw ErrorResponder.builder()
+                .statusCode(HttpStatus.BAD_REQUEST_400)
+                .message("Can not deserialize the supplied messageId: " + messageIdAsString)
+                .cause(e)
+                .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
+                .haltError();
+        }
+    }
 }
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 74c3167..9375a6d 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
@@ -31,12 +31,14 @@ import static org.apache.james.vault.DeletedMessageFixture.FINAL_STAGE;
 import static org.apache.james.vault.DeletedMessageFixture.MAILBOX_ID_1;
 import static org.apache.james.vault.DeletedMessageFixture.MAILBOX_ID_2;
 import static org.apache.james.vault.DeletedMessageFixture.MAILBOX_ID_3;
+import static org.apache.james.vault.DeletedMessageFixture.MESSAGE_ID;
 import static org.apache.james.vault.DeletedMessageFixture.SUBJECT;
 import static org.apache.james.vault.DeletedMessageFixture.USER;
 import static org.apache.james.vault.DeletedMessageFixture.USER_2;
 import static org.apache.james.vault.DeletedMessageVaultSearchContract.MESSAGE_ID_GENERATOR;
 import static org.apache.james.webadmin.Constants.SEPARATOR;
 import static org.apache.james.webadmin.WebAdminServer.NO_CONFIGURATION;
+import static org.apache.james.webadmin.vault.routes.DeletedMessagesVaultRoutes.MESSAGE_PATH_PARAM;
 import static org.apache.james.webadmin.vault.routes.DeletedMessagesVaultRoutes.USERS;
 import static org.apache.james.webadmin.vault.routes.DeletedMessagesVaultRoutes.USER_PATH;
 import static org.apache.james.webadmin.vault.routes.RestoreService.RESTORE_MAILBOX_NAME;
@@ -92,6 +94,7 @@ import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
 import org.apache.james.mailbox.model.FetchGroupImpl;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageRange;
 import org.apache.james.mailbox.model.MessageResult;
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
@@ -120,7 +123,6 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
 
-import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
 
 import io.restassured.RestAssured;
@@ -147,6 +149,8 @@ class DeletedMessagesVaultRoutesTest {
         "}";
     private static final Domain DOMAIN = Domain.of("apache.org");
     private static final String BOB_PATH = USERS + SEPARATOR + USER.asString();
+    private static final String DELETED_MESSAGE_PARAM_PATH = MESSAGE_PATH_PARAM + SEPARATOR
+ MESSAGE_ID.serialize();
+    private static final String BOB_DELETE_PATH = BOB_PATH + SEPARATOR + DELETED_MESSAGE_PARAM_PATH;
 
     private WebAdminServer webAdminServer;
     private MemoryDeletedMessagesVault vault;
@@ -178,10 +182,11 @@ class DeletedMessagesVaultRoutesTest {
         exportService = new ExportService(blobExporting, blobStore, zipper, vault);
         QueryTranslator queryTranslator = new QueryTranslator(new InMemoryId.Factory());
         usersRepository = createUsersRepository();
+        MessageId.Factory messageIdFactory = new InMemoryMessageId.Factory();
         webAdminServer = WebAdminUtils.createWebAdminServer(
             new DefaultMetricFactory(),
             new TasksRoutes(taskManager, jsonTransformer),
-            new DeletedMessagesVaultRoutes(vault, vaultRestore, exportService, jsonTransformer,
taskManager, queryTranslator, usersRepository));
+            new DeletedMessagesVaultRoutes(vault, vaultRestore, exportService, jsonTransformer,
taskManager, queryTranslator, usersRepository, messageIdFactory));
 
         webAdminServer.configure(NO_CONFIGURATION);
         webAdminServer.await();
@@ -2089,6 +2094,188 @@ class DeletedMessagesVaultRoutesTest {
         }
     }
 
+    @Nested
+    class DeleteTest {
+
+        @Test
+        void deleteShouldReturnATaskCreated() {
+            when()
+                .delete(BOB_DELETE_PATH)
+            .then()
+                .statusCode(HttpStatus.CREATED_201)
+                .body("taskId", notNullValue());
+        }
+
+        @Test
+        void deleteShouldProduceASuccessfulTaskEvenNoDeletedMessageExisted() {
+            String taskId =
+                with()
+                    .delete(BOB_DELETE_PATH)
+                    .jsonPath()
+                    .get("taskId");
+
+            given()
+                .basePath(TasksRoutes.BASE)
+            .when()
+                .get(taskId + "/await")
+            .then()
+                .body("status", is("completed"))
+                .body("taskId", is(taskId))
+                .body("type", is(DeletedMessagesVaultDeleteTask.TYPE))
+                .body("additionalInformation.user", is(USER.asString()))
+                .body("additionalInformation.deleteMessageId", is(MESSAGE_ID.serialize()))
+                .body("startedDate", is(notNullValue()))
+                .body("submitDate", is(notNullValue()))
+                .body("completedDate", is(notNullValue()));
+        }
+
+        @Test
+        void deleteShouldProduceASuccessfulTask() {
+            vault.append(USER, DELETED_MESSAGE, new ByteArrayInputStream(CONTENT)).block();
+            vault.append(USER, DELETED_MESSAGE_2, new ByteArrayInputStream(CONTENT)).block();
+
+            String taskId =
+                with()
+                    .delete(BOB_DELETE_PATH)
+                    .jsonPath()
+                    .get("taskId");
+
+            given()
+                .basePath(TasksRoutes.BASE)
+            .when()
+                .get(taskId + "/await")
+            .then()
+                .body("status", is("completed"))
+                .body("taskId", is(taskId))
+                .body("type", is(DeletedMessagesVaultDeleteTask.TYPE))
+                .body("additionalInformation.user", is(USER.asString()))
+                .body("additionalInformation.deleteMessageId", is(MESSAGE_ID.serialize()))
+                .body("startedDate", is(notNullValue()))
+                .body("submitDate", is(notNullValue()))
+                .body("completedDate", is(notNullValue()));
+        }
+
+        @Test
+        void deleteShouldNotAppendMessagesToUserMailbox() throws Exception {
+            vault.append(USER, DELETED_MESSAGE, new ByteArrayInputStream(CONTENT)).block();
+            vault.append(USER, DELETED_MESSAGE_2, new ByteArrayInputStream(CONTENT)).block();
+
+            String taskId =
+                with()
+                    .delete(BOB_DELETE_PATH)
+                    .jsonPath()
+                    .get("taskId");
+
+            with()
+                .basePath(TasksRoutes.BASE)
+                .get(taskId + "/await");
+
+            assertThat(hasAnyMail(USER))
+                .isFalse();
+        }
+
+        @Test
+        void deleteShouldDeleteMessagesFromTheVault() throws Exception {
+            vault.append(USER, DELETED_MESSAGE, new ByteArrayInputStream(CONTENT)).block();
+
+            String taskId =
+                with()
+                    .delete(BOB_DELETE_PATH)
+                    .jsonPath()
+                    .get("taskId");
+
+            with()
+                .basePath(TasksRoutes.BASE)
+                .get(taskId + "/await");
+
+            assertThat(Flux.from(vault.search(USER, Query.ALL)).toStream())
+                .isEmpty();
+        }
+
+        @Test
+        void deleteShouldNotDeleteNotMatchMessagesFromTheVault() throws Exception {
+            vault.append(USER, DELETED_MESSAGE_2, new ByteArrayInputStream(CONTENT)).block();
+
+            String taskId =
+                with()
+                    .delete(BOB_DELETE_PATH)
+                    .jsonPath()
+                    .get("taskId");
+
+            with()
+                .basePath(TasksRoutes.BASE)
+                .get(taskId + "/await");
+
+            assertThat(Flux.from(vault.search(USER, Query.ALL)).toStream())
+                .contains(DELETED_MESSAGE_2);
+        }
+
+        @Nested
+        class FailingDeleteTest {
+
+            @Test
+            void deleteShouldProduceAFailedTask() {
+                doReturn(Mono.error(new RuntimeException("mock exception")))
+                    .when(vault)
+                    .delete(any(), any());
+
+                String taskId =
+                    with()
+                        .delete(BOB_DELETE_PATH)
+                        .jsonPath()
+                        .get("taskId");
+
+                given()
+                    .basePath(TasksRoutes.BASE)
+                .when()
+                    .get(taskId + "/await")
+                .then()
+                    .body("status", is("failed"))
+                    .body("taskId", is(taskId))
+                    .body("type", is(DeletedMessagesVaultDeleteTask.TYPE))
+                    .body("additionalInformation.user", is(USER.asString()))
+                    .body("additionalInformation.deleteMessageId", is(MESSAGE_ID.serialize()))
+                    .body("startedDate", is(notNullValue()))
+                    .body("submitDate", is(notNullValue()))
+                    .body("completedDate", is(nullValue()));
+            }
+
+            @Test
+            void deleteShouldReturnInvalidWhenUserIsInvalid() {
+                when()
+                    .delete(USERS + SEPARATOR + "not@valid@user.com" + SEPARATOR + DELETED_MESSAGE_PARAM_PATH)
+                .then()
+                    .statusCode(HttpStatus.BAD_REQUEST_400)
+                    .body("statusCode", is(400))
+                    .body("type", is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+                    .body("message", is(notNullValue()))
+                    .body("details", is(notNullValue()));
+            }
+
+            @Test
+            void deleteShouldReturnNotFoundWhenUserIsNotFoundInSystem() {
+                when()
+                    .delete(USERS + SEPARATOR + USER_2.asString() + SEPARATOR + DELETED_MESSAGE_PARAM_PATH)
+                .then()
+                    .statusCode(HttpStatus.NOT_FOUND_404)
+                    .body("statusCode", is(404))
+                    .body("type", is(ErrorResponder.ErrorType.NOT_FOUND.getType()))
+                    .body("message", is(notNullValue()));
+            }
+
+            @Test
+            void deleteShouldReturnInvalidWhenMessageIdIsInvalid() {
+                when()
+                    .delete(BOB_PATH + SEPARATOR + MESSAGE_PATH_PARAM + SEPARATOR + "invalid")
+                .then()
+                    .statusCode(HttpStatus.BAD_REQUEST_400)
+                    .body("statusCode", is(400))
+                    .body("type", is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+                    .body("message", is(notNullValue()));
+            }
+        }
+    }
+
     private boolean hasAnyMail(User user) throws MailboxException {
         MailboxSession session = mailboxManager.createSystemSession(user.asString());
         int limitToOneMessage = 1;


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


Mime
View raw message