JAMES-1715 Implement SetMailboxes: destroy

Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/9ab23a6a
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/9ab23a6a
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/9ab23a6a

Branch: refs/heads/master
Commit: 9ab23a6a98acd9d4b2bb05f65336a312455476d0
Parents: ebeabde
Author: Antoine Duprat <[email protected]>
Authored: Thu Mar 31 15:36:53 2016 +0200
Committer: Antoine Duprat <[email protected]>
Committed: Tue Apr 5 14:00:40 2016 +0200

----------------------------------------------------------------------
 .../org/apache/james/jmap/MethodsModule.java    |   2 +
 .../integration/SetMailboxesMethodTest.java     | 246 ++++++++++++++++++-
 .../exceptions/MailboxHasChildException.java    |  23 ++
 .../jmap/exceptions/SystemMailboxException.java |  23 ++
 .../SetMailboxesDestructionProcessor.java       | 144 +++++++++++
 .../james/jmap/model/SetMailboxesRequest.java   |  18 +-
 .../james/jmap/model/SetMailboxesResponse.java  |  54 +++-
 .../jmap/methods/SetMailboxesMethodTest.java    |  24 ++
 .../jmap/model/SetMailboxesRequestTest.java     |   9 +-
 .../jmap/model/SetMailboxesResponseTest.java    | 113 +++++++++
 10 files changed, 631 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/9ab23a6a/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/MethodsModule.java
----------------------------------------------------------------------
diff --git 
a/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/MethodsModule.java
 
b/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/MethodsModule.java
index 449d468..f0af8b3 100644
--- 
a/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/MethodsModule.java
+++ 
b/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/MethodsModule.java
@@ -29,6 +29,7 @@ import org.apache.james.jmap.methods.JmapResponseWriter;
 import org.apache.james.jmap.methods.JmapResponseWriterImpl;
 import org.apache.james.jmap.methods.Method;
 import org.apache.james.jmap.methods.SetMailboxesCreationProcessor;
+import org.apache.james.jmap.methods.SetMailboxesDestructionProcessor;
 import org.apache.james.jmap.methods.SetMailboxesMethod;
 import org.apache.james.jmap.methods.SetMailboxesProcessor;
 import org.apache.james.jmap.methods.SetMessagesCreationProcessor;
@@ -71,6 +72,7 @@ public class MethodsModule<Id extends MailboxId> extends 
AbstractModule {
         Multibinder<SetMailboxesProcessor<Id>> setMailboxesProcessor =
             Multibinder.newSetBinder(binder(), 
guiceGenericType.newGenericType(SetMailboxesProcessor.class));
         
setMailboxesProcessor.addBinding().to(guiceGenericType.newGenericType(SetMailboxesCreationProcessor.class));
+        
setMailboxesProcessor.addBinding().to(guiceGenericType.newGenericType(SetMailboxesDestructionProcessor.class));
 
         Multibinder<SetMessagesProcessor<Id>> setMessagesProcessors =
                 Multibinder.newSetBinder(binder(), 
guiceGenericType.newGenericType(SetMessagesProcessor.class));

http://git-wip-us.apache.org/repos/asf/james-project/blob/9ab23a6a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMailboxesMethodTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMailboxesMethodTest.java
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMailboxesMethodTest.java
index 176ec06..830777e 100644
--- 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMailboxesMethodTest.java
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMailboxesMethodTest.java
@@ -23,6 +23,8 @@ import static com.jayway.restassured.RestAssured.given;
 import static com.jayway.restassured.RestAssured.with;
 import static com.jayway.restassured.config.EncoderConfig.encoderConfig;
 import static com.jayway.restassured.config.RestAssuredConfig.newConfig;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.hasEntry;
 import static org.hamcrest.Matchers.hasItems;
@@ -35,6 +37,7 @@ import static 
org.hamcrest.collection.IsMapWithSize.aMapWithSize;
 import org.apache.james.GuiceJamesServer;
 import org.apache.james.jmap.JmapAuthentication;
 import org.apache.james.jmap.api.access.AccessToken;
+import org.apache.james.mailbox.store.mail.model.Mailbox;
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Before;
@@ -495,14 +498,45 @@ public abstract class SetMailboxesMethodTest {
     @Test
     public void 
setMailboxesShouldReturnNotCreatedWhenMailboxNameContainsPathDelimiter() throws 
Exception {
         String requestBody =
+                "[" +
+                    "  [ \"setMailboxes\"," +
+                    "    {" +
+                    "      \"create\": {" +
+                    "        \"create-id01\" : {" +
+                    "          \"name\" : \"A.B.C.D\"" +
+                    "        }" +
+                    "      }" +
+                    "    }," +
+                    "    \"#0\"" +
+                    "  ]" +
+                    "]";
+
+            given()
+                .accept(ContentType.JSON)
+                .contentType(ContentType.JSON)
+                .header("Authorization", this.accessToken.serialize())
+                .body(requestBody)
+            .when()
+                .post("/jmap")
+            .then()
+            .statusCode(200)
+            .body(NAME, equalTo("mailboxesSet"))
+            .body(ARGUMENTS + ".notCreated", aMapWithSize(1))
+            .body(ARGUMENTS + ".notCreated", hasEntry(equalTo("create-id01"), 
Matchers.allOf(
+                        hasEntry(equalTo("type"), equalTo("invalidArguments")),
+                        hasEntry(equalTo("description"), equalTo("The mailbox 
'A.B.C.D' contains an illegal character: '.'")))
+                    ));
+    }
+
+    public void setMailboxesShouldReturnDestroyedMailbox() throws Exception {
+        jmapServer.serverProbe().createMailbox("#private", username, "myBox");
+        Mailbox<?> mailbox = jmapServer.serverProbe().getMailbox("#private", 
username, "myBox");
+        String mailboxId = mailbox.getMailboxId().serialize();
+        String requestBody =
             "[" +
                 "  [ \"setMailboxes\"," +
                 "    {" +
-                "      \"create\": {" +
-                "        \"create-id01\" : {" +
-                "          \"name\" : \"A.B.C.D\"" +
-                "        }" +
-                "      }" +
+                "      \"destroy\": [\"" + mailboxId + "\"]" +
                 "    }," +
                 "    \"#0\"" +
                 "  ]" +
@@ -516,12 +550,204 @@ public abstract class SetMailboxesMethodTest {
         .when()
             .post("/jmap")
         .then()
+            .log().ifValidationFails()
             .statusCode(200)
             .body(NAME, equalTo("mailboxesSet"))
-            .body(ARGUMENTS + ".notCreated", aMapWithSize(1))
-            .body(ARGUMENTS + ".notCreated", hasEntry(equalTo("create-id01"), 
Matchers.allOf(
-                        hasEntry(equalTo("type"), equalTo("invalidArguments")),
-                        hasEntry(equalTo("description"), equalTo("The mailbox 
'A.B.C.D' contains an illegal character: '.'")))
-                    ));
+            .body(ARGUMENTS + ".destroyed", contains(mailboxId));
+    }
+
+    @Test
+    public void setMailboxesShouldDestroyMailbox() throws Exception {
+        jmapServer.serverProbe().createMailbox("#private", username, "myBox");
+        Mailbox<?> mailbox = jmapServer.serverProbe().getMailbox("#private", 
username, "myBox");
+        String requestBody =
+            "[" +
+                "  [ \"setMailboxes\"," +
+                "    {" +
+                "      \"destroy\": [\"" + mailbox.getMailboxId().serialize() 
+ "\"]" +
+                "    }," +
+                "    \"#0\"" +
+                "  ]" +
+                "]";
+
+        given()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON)
+            .header("Authorization", this.accessToken.serialize())
+            .body(requestBody)
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        given()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON)
+            .header("Authorization", this.accessToken.serialize())
+            .body("[[\"getMailboxes\", {}, \"#0\"]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .log().ifValidationFails()
+            .statusCode(200)
+            .body(NAME, equalTo("mailboxes"))
+            .body(ARGUMENTS + ".list", hasSize(1)); // Inbox
+    }
+
+    @Test
+    public void setMailboxesShouldReturnNotDestroyedWhenMailboxDoesntExist() 
throws Exception {
+        String requestBody =
+            "[" +
+                "  [ \"setMailboxes\"," +
+                "    {" +
+                "      \"destroy\": [\"123\"]" +
+                "    }," +
+                "    \"#0\"" +
+                "  ]" +
+                "]";
+
+        given()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON)
+            .header("Authorization", this.accessToken.serialize())
+            .body(requestBody)
+        .when()
+            .post("/jmap")
+        .then()
+            .log().ifValidationFails()
+            .statusCode(200)
+            .body(NAME, equalTo("mailboxesSet"))
+            .body(ARGUMENTS + ".notDestroyed", aMapWithSize(1))
+            .body(ARGUMENTS + ".notDestroyed", hasEntry(equalTo("123"), 
Matchers.allOf(
+                    hasEntry(equalTo("type"), equalTo("notFound")),
+                    hasEntry(equalTo("description"), equalTo("The mailbox 
'123' was not found.")))));
+    }
+
+    @Test
+    public void setMailboxesShouldReturnNotDestroyedWhenMailboxHasChild() 
throws Exception {
+        jmapServer.serverProbe().createMailbox("#private", username, "myBox");
+        jmapServer.serverProbe().createMailbox("#private", username, 
"myBox.child");
+        Mailbox<?> mailbox = jmapServer.serverProbe().getMailbox("#private", 
username, "myBox");
+        String mailboxId = mailbox.getMailboxId().serialize();
+        String requestBody =
+            "[" +
+                "  [ \"setMailboxes\"," +
+                "    {" +
+                "      \"destroy\": [\"" + mailboxId + "\"]" +
+                "    }," +
+                "    \"#0\"" +
+                "  ]" +
+                "]";
+
+        given()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON)
+            .header("Authorization", this.accessToken.serialize())
+            .body(requestBody)
+        .when()
+            .post("/jmap")
+        .then()
+            .log().ifValidationFails()
+            .statusCode(200)
+            .body(NAME, equalTo("mailboxesSet"))
+            .body(ARGUMENTS + ".notDestroyed", aMapWithSize(1))
+            .body(ARGUMENTS + ".notDestroyed", hasEntry(equalTo(mailboxId), 
Matchers.allOf(
+                    hasEntry(equalTo("type"), equalTo("mailboxHasChild")),
+                    hasEntry(equalTo("description"), equalTo("The mailbox '" + 
mailboxId + "' has a child.")))));
+    }
+
+    @Test
+    public void setMailboxesShouldReturnNotDestroyedWhenSystemMailbox() throws 
Exception {
+        Mailbox<?> mailbox = jmapServer.serverProbe().getMailbox("#private", 
username, "inbox");
+        String mailboxId = mailbox.getMailboxId().serialize();
+        String requestBody =
+            "[" +
+                "  [ \"setMailboxes\"," +
+                "    {" +
+                "      \"destroy\": [\"" + mailboxId + "\"]" +
+                "    }," +
+                "    \"#0\"" +
+                "  ]" +
+                "]";
+
+        given()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON)
+            .header("Authorization", this.accessToken.serialize())
+            .body(requestBody)
+        .when()
+            .post("/jmap")
+        .then()
+            .log().ifValidationFails()
+            .statusCode(200)
+            .body(NAME, equalTo("mailboxesSet"))
+            .body(ARGUMENTS + ".notDestroyed", aMapWithSize(1))
+            .body(ARGUMENTS + ".notDestroyed", hasEntry(equalTo(mailboxId), 
Matchers.allOf(
+                    hasEntry(equalTo("type"), equalTo("invalidArguments")),
+                    hasEntry(equalTo("description"), equalTo("The mailbox '" + 
mailboxId + "' is a system mailbox.")))));
+    }
+
+    @Test
+    public void 
setMailboxesShouldReturnDestroyedWhenParentThenChildMailboxes() throws 
Exception {
+        jmapServer.serverProbe().createMailbox("#private", username, "parent");
+        Mailbox<?> parentMailbox = 
jmapServer.serverProbe().getMailbox("#private", username, "parent");
+        String parentMailboxId = parentMailbox.getMailboxId().serialize();
+        jmapServer.serverProbe().createMailbox("#private", username, 
"parent.child");
+        Mailbox<?> childMailbox = 
jmapServer.serverProbe().getMailbox("#private", username, "parent.child");
+        String childMailboxId = childMailbox.getMailboxId().serialize();
+        String requestBody =
+            "[" +
+                "  [ \"setMailboxes\"," +
+                "    {" +
+                "      \"destroy\": [\"" + parentMailboxId + "\",\"" + 
childMailboxId + "\"]" +
+                "    }," +
+                "    \"#0\"" +
+                "  ]" +
+                "]";
+
+        given()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON)
+            .header("Authorization", this.accessToken.serialize())
+            .body(requestBody)
+        .when()
+            .post("/jmap")
+        .then()
+            .log().ifValidationFails()
+            .statusCode(200)
+            .body(NAME, equalTo("mailboxesSet"))
+            .body(ARGUMENTS + ".destroyed", 
containsInAnyOrder(parentMailboxId, childMailboxId));
+    }
+
+    @Test
+    public void 
setMailboxesShouldReturnDestroyedWhenChildThenParentMailboxes() throws 
Exception {
+        jmapServer.serverProbe().createMailbox("#private", username, "parent");
+        Mailbox<?> parentMailbox = 
jmapServer.serverProbe().getMailbox("#private", username, "parent");
+        String parentMailboxId = parentMailbox.getMailboxId().serialize();
+        jmapServer.serverProbe().createMailbox("#private", username, 
"parent.child");
+        Mailbox<?> childMailbox = 
jmapServer.serverProbe().getMailbox("#private", username, "parent.child");
+        String childMailboxId = childMailbox.getMailboxId().serialize();
+        String requestBody =
+            "[" +
+                "  [ \"setMailboxes\"," +
+                "    {" +
+                "      \"destroy\": [\"" + childMailboxId + "\",\"" + 
parentMailboxId + "\"]" +
+                "    }," +
+                "    \"#0\"" +
+                "  ]" +
+                "]";
+
+        given()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON)
+            .header("Authorization", this.accessToken.serialize())
+            .body(requestBody)
+        .when()
+            .post("/jmap")
+        .then()
+            .log().ifValidationFails()
+            .statusCode(200)
+            .body(NAME, equalTo("mailboxesSet"))
+            .body(ARGUMENTS + ".destroyed", 
containsInAnyOrder(parentMailboxId, childMailboxId));
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/9ab23a6a/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/MailboxHasChildException.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/MailboxHasChildException.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/MailboxHasChildException.java
new file mode 100644
index 0000000..6ccdfa8
--- /dev/null
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/MailboxHasChildException.java
@@ -0,0 +1,23 @@
+/****************************************************************
+ * 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.jmap.exceptions;
+
+public class MailboxHasChildException extends Exception {
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/9ab23a6a/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/SystemMailboxException.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/SystemMailboxException.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/SystemMailboxException.java
new file mode 100644
index 0000000..f82cfa1
--- /dev/null
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/SystemMailboxException.java
@@ -0,0 +1,23 @@
+/****************************************************************
+ * 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.jmap.exceptions;
+
+public class SystemMailboxException extends Exception {
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/9ab23a6a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMailboxesDestructionProcessor.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMailboxesDestructionProcessor.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMailboxesDestructionProcessor.java
new file mode 100644
index 0000000..6076fb8
--- /dev/null
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMailboxesDestructionProcessor.java
@@ -0,0 +1,144 @@
+/****************************************************************
+ * 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.jmap.methods;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.apache.james.jmap.exceptions.MailboxHasChildException;
+import org.apache.james.jmap.exceptions.SystemMailboxException;
+import org.apache.james.jmap.model.SetError;
+import org.apache.james.jmap.model.SetMailboxesRequest;
+import org.apache.james.jmap.model.SetMailboxesResponse;
+import org.apache.james.jmap.model.SetMailboxesResponse.Builder;
+import org.apache.james.jmap.model.mailbox.Mailbox;
+import org.apache.james.jmap.model.mailbox.Role;
+import org.apache.james.jmap.utils.MailboxUtils;
+import org.apache.james.jmap.utils.SortingHierarchicalCollections;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.store.mail.model.MailboxId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+
+public class SetMailboxesDestructionProcessor<Id extends MailboxId> implements 
SetMailboxesProcessor<Id> {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(SetMailboxesDestructionProcessor.class);
+
+    private final MailboxManager mailboxManager;
+    private final SortingHierarchicalCollections<Map.Entry<String, Mailbox>, 
String> sortingHierarchicalCollections;
+    private final MailboxUtils<Id> mailboxUtils;
+
+    @Inject
+    @VisibleForTesting
+    SetMailboxesDestructionProcessor(MailboxManager mailboxManager, 
MailboxUtils<Id> mailboxUtils) {
+        this.mailboxManager = mailboxManager;
+        this.sortingHierarchicalCollections =
+            new SortingHierarchicalCollections<>(
+                    Entry::getKey,
+                    x -> x.getValue().getParentId());
+        this.mailboxUtils = mailboxUtils;
+    }
+
+    public SetMailboxesResponse process(SetMailboxesRequest request, 
MailboxSession mailboxSession) {
+        ImmutableMap<String, Mailbox> idToMailbox = 
mapDestroyRequests(request, mailboxSession);
+
+        SetMailboxesResponse.Builder builder = SetMailboxesResponse.builder();
+        
sortingHierarchicalCollections.sortFromLeafToRoot(idToMailbox.entrySet())
+            .forEach(entry -> destroyMailbox(entry, mailboxSession, builder));
+
+        notDestroyedRequests(request, idToMailbox, builder);
+        return builder.build();
+    }
+
+    private ImmutableMap<String, Mailbox> 
mapDestroyRequests(SetMailboxesRequest request, MailboxSession mailboxSession) {
+        ImmutableMap.Builder<String, Mailbox> idToMailboxBuilder = 
ImmutableMap.builder(); 
+        request.getDestroy().stream()
+            .map(id -> mailboxUtils.mailboxFromMailboxId(id, mailboxSession))
+            .filter(Optional::isPresent)
+            .map(Optional::get)
+            .forEach(mailbox -> idToMailboxBuilder.put(mailbox.getId(), 
mailbox));
+        return idToMailboxBuilder.build();
+    }
+
+    private void notDestroyedRequests(SetMailboxesRequest request, 
ImmutableMap<String, Mailbox> idToMailbox, SetMailboxesResponse.Builder 
builder) {
+        request.getDestroy().stream()
+            .filter(id -> !idToMailbox.containsKey(id))
+            .forEach(id -> notDestroy(id, builder));
+    }
+
+    private void destroyMailbox(Entry<String, Mailbox> entry, MailboxSession 
mailboxSession, SetMailboxesResponse.Builder builder) {
+        try {
+            Mailbox mailbox = entry.getValue();
+            preconditions(mailbox, mailboxSession);
+
+            mailboxManager.deleteMailbox(mailboxUtils.getMailboxPath(mailbox, 
mailboxSession), mailboxSession);
+            builder.destroyed(entry.getKey());
+        } catch (MailboxHasChildException e) {
+            builder.notDestroyed(entry.getKey(), SetError.builder()
+                    .type("mailboxHasChild")
+                    .description(String.format("The mailbox '%s' has a 
child.", entry.getKey()))
+                    .build());
+        } catch (SystemMailboxException e) {
+            builder.notDestroyed(entry.getKey(), SetError.builder()
+                    .type("invalidArguments")
+                    .description(String.format("The mailbox '%s' is a system 
mailbox.", entry.getKey()))
+                    .build());
+        } catch (MailboxException e) {
+            String message = String.format("An error occurred when deleting 
the mailbox '%s'", entry.getKey());
+            LOGGER.error(message, e);
+            builder.notDestroyed(entry.getKey(), SetError.builder()
+                    .type("anErrorOccurred")
+                    .description(message)
+                    .build());
+        }
+    }
+
+    private void preconditions(Mailbox mailbox, MailboxSession mailboxSession) 
throws MailboxHasChildException, SystemMailboxException, MailboxException {
+        checkForChild(mailbox.getId(), mailboxSession);
+        checkRole(mailbox.getRole());
+    }
+
+    private void checkForChild(String id, MailboxSession mailboxSession) 
throws MailboxHasChildException, MailboxException {
+        if (mailboxUtils.hasChildren(id, mailboxSession)) {
+            throw new MailboxHasChildException();
+        }
+    }
+
+    private void checkRole(Optional<Role> role) throws SystemMailboxException {
+        if (role.map(Role::isSystemRole).orElse(false)) {
+            throw new SystemMailboxException();
+        }
+    }
+
+    private void notDestroy(String id, Builder builder) {
+        builder.notDestroyed(id, SetError.builder()
+                .type("notFound")
+                .description(String.format("The mailbox '%s' was not found.", 
id))
+                .build());
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/9ab23a6a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMailboxesRequest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMailboxesRequest.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMailboxesRequest.java
index a28549b..96b77f5 100644
--- 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMailboxesRequest.java
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMailboxesRequest.java
@@ -30,6 +30,7 @@ import org.apache.james.jmap.model.mailbox.MailboxRequest;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
 @JsonDeserialize(builder = SetMailboxesRequest.Builder.class)
@@ -42,10 +43,12 @@ public class SetMailboxesRequest implements JmapRequest {
     @JsonPOJOBuilder(withPrefix = "")
     public static class Builder {
 
-        private ImmutableMap.Builder<MailboxCreationId, MailboxRequest> create;
+        private final ImmutableMap.Builder<MailboxCreationId, MailboxRequest> 
create;
+        private final ImmutableList.Builder<String> destroy;
 
         private Builder() {
             create = ImmutableMap.builder();
+            destroy = ImmutableList.builder();
         }
 
         public Builder create(Map<MailboxCreationId, MailboxRequest> requests) 
{
@@ -71,22 +74,29 @@ public class SetMailboxesRequest implements JmapRequest {
         }
         
         public Builder destroy(List<String> deletions) {
-            throw new NotImplementedException();
+            destroy.addAll(deletions);
+            return this;
         }
 
         public SetMailboxesRequest build() {
-            return new SetMailboxesRequest(create.build());
+            return new SetMailboxesRequest(create.build(), destroy.build());
         }
     }
 
     private final ImmutableMap<MailboxCreationId, MailboxRequest> create;
+    private final ImmutableList<String> destroy;
 
     @VisibleForTesting
-    SetMailboxesRequest(ImmutableMap<MailboxCreationId, MailboxRequest> 
create) {
+    SetMailboxesRequest(ImmutableMap<MailboxCreationId, MailboxRequest> 
create, ImmutableList<String> destroy) {
         this.create = create;
+        this.destroy = destroy;
     }
 
     public ImmutableMap<MailboxCreationId, MailboxRequest> getCreate() {
         return create;
     }
+
+    public ImmutableList<String> getDestroy() {
+        return destroy;
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/9ab23a6a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMailboxesResponse.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMailboxesResponse.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMailboxesResponse.java
index caeb42a..6de0f77 100644
--- 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMailboxesResponse.java
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMailboxesResponse.java
@@ -23,7 +23,9 @@ import java.util.Map;
 import org.apache.james.jmap.methods.Method;
 import org.apache.james.jmap.model.mailbox.Mailbox;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
 public class SetMailboxesResponse implements Method.Response {
@@ -36,10 +38,14 @@ public class SetMailboxesResponse implements 
Method.Response {
 
         private final ImmutableMap.Builder<MailboxCreationId, Mailbox> created;
         private final ImmutableMap.Builder<MailboxCreationId, SetError> 
notCreated;
+        private final ImmutableList.Builder<String> destroyed;
+        private final ImmutableMap.Builder<String, SetError> notDestroyed;
 
         private Builder() {
             created = ImmutableMap.builder();
             notCreated = ImmutableMap.builder();
+            destroyed = ImmutableList.builder();
+            notDestroyed = ImmutableMap.builder();
         }
 
         public Builder created(MailboxCreationId creationId, Mailbox mailbox) {
@@ -62,17 +68,41 @@ public class SetMailboxesResponse implements 
Method.Response {
             return this;
         }
 
+        public Builder destroyed(String mailboxId) {
+            destroyed.add(mailboxId);
+            return this;
+        }
+
+        public Builder destroyed(ImmutableList<String> destroyed) {
+            this.destroyed.addAll(destroyed);
+            return this;
+        }
+
+        public Builder notDestroyed(String mailboxId, SetError setError) {
+            notDestroyed.put(mailboxId, setError);
+            return this;
+        }
+
+        public Builder notDestroyed(ImmutableMap<String, SetError> 
notDestroyed) {
+            this.notDestroyed.putAll(notDestroyed);
+            return this;
+        }
+
         public SetMailboxesResponse build() {
-            return new SetMailboxesResponse(created.build(), 
notCreated.build());
+            return new SetMailboxesResponse(created.build(), 
notCreated.build(), destroyed.build(), notDestroyed.build());
         }
     }
 
     private final ImmutableMap<MailboxCreationId, Mailbox> created;
     private final ImmutableMap<MailboxCreationId, SetError> notCreated;
+    private final ImmutableList<String> destroyed;
+    private final ImmutableMap<String, SetError> notDestroyed;
 
-    private SetMailboxesResponse(ImmutableMap<MailboxCreationId, Mailbox> 
created, ImmutableMap<MailboxCreationId, SetError> notCreated) {
+    @VisibleForTesting SetMailboxesResponse(ImmutableMap<MailboxCreationId, 
Mailbox> created, ImmutableMap<MailboxCreationId, SetError> notCreated, 
ImmutableList<String> destroyed, ImmutableMap<String, SetError> notDestroyed) {
         this.created = created;
         this.notCreated = notCreated;
+        this.destroyed = destroyed;
+        this.notDestroyed = notDestroyed;
     }
 
     public ImmutableMap<MailboxCreationId, Mailbox> getCreated() {
@@ -83,21 +113,35 @@ public class SetMailboxesResponse implements 
Method.Response {
         return notCreated;
     }
 
+    public ImmutableList<String> getDestroyed() {
+        return destroyed;
+    }
+
+    public ImmutableMap<String, SetError> getNotDestroyed() {
+        return notDestroyed;
+    }
+
     public SetMailboxesResponse.Builder mergeInto(SetMailboxesResponse.Builder 
responseBuilder) {
         return responseBuilder
-            .created(getCreated());
+            .created(getCreated())
+            .notCreated(getNotCreated())
+            .destroyed(getDestroyed())
+            .notDestroyed(getNotDestroyed());
     }
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(created);
+        return Objects.hashCode(created, notCreated, destroyed, notDestroyed);
     }
 
     @Override
     public boolean equals(Object obj) {
         if (obj instanceof SetMailboxesResponse) {
             SetMailboxesResponse other = (SetMailboxesResponse) obj;
-            return Objects.equal(this.created, other.created);
+            return Objects.equal(this.created, other.created)
+                && Objects.equal(this.notCreated, other.notCreated)
+                && Objects.equal(this.destroyed, other.destroyed)
+                && Objects.equal(this.notDestroyed, other.notDestroyed);
         }
         return false;
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/9ab23a6a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMailboxesMethodTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMailboxesMethodTest.java
 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMailboxesMethodTest.java
index fcf2a2e..f79f90c 100644
--- 
a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMailboxesMethodTest.java
+++ 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMailboxesMethodTest.java
@@ -37,6 +37,7 @@ import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.store.TestId;
 import org.junit.Test;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 
 public class SetMailboxesMethodTest {
@@ -112,4 +113,27 @@ public class SetMailboxesMethodTest {
         assertThat(actual).contains(jmapResponse);
     }
 
+    @Test
+    public void processShouldCallDestructorProcessorWhenCreationRequest() {
+        ImmutableList<String> deletions = ImmutableList.of("1");
+        SetMailboxesRequest destructionRequest = 
SetMailboxesRequest.builder().destroy(deletions).build();
+
+        SetMailboxesResponse destructionResponse = 
SetMailboxesResponse.builder().destroyed(deletions).build();
+        JmapResponse jmapResponse = JmapResponse.builder()
+            .response(destructionResponse)
+            .clientId(ClientId.of("clientId"))
+            .responseName(SetMailboxesMethod.RESPONSE_NAME)
+            .build();
+
+        MailboxSession session = mock(MailboxSession.class);
+        @SuppressWarnings("unchecked")
+        SetMailboxesProcessor<TestId> destructorProcessor = 
mock(SetMailboxesProcessor.class);
+        when(destructorProcessor.process(destructionRequest, 
session)).thenReturn(destructionResponse);
+
+        Stream<JmapResponse> actual =
+            new SetMailboxesMethod<>(ImmutableSet.of(destructorProcessor))
+                    .process(destructionRequest, ClientId.of("clientId"), 
session);
+
+        assertThat(actual).contains(jmapResponse);
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/9ab23a6a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetMailboxesRequestTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetMailboxesRequestTest.java
 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetMailboxesRequestTest.java
index 808f558..bafe9f1 100644
--- 
a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetMailboxesRequestTest.java
+++ 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetMailboxesRequestTest.java
@@ -44,21 +44,18 @@ public class SetMailboxesRequestTest {
         SetMailboxesRequest.builder().update(ImmutableMap.of());
     }
     
-    @Test(expected=NotImplementedException.class)
-    public void builderShouldThrowWhenDestroy() {
-        SetMailboxesRequest.builder().destroy(ImmutableList.of());
-    }
-    
     @Test
     public void builderShouldWork() {
         MailboxCreationId creationId = MailboxCreationId.of("creationId");
         MailboxRequest mailboxRequest = MailboxRequest.builder()
             .name("mailboxRequest")
             .build();
-        SetMailboxesRequest expected = new 
SetMailboxesRequest(ImmutableMap.of(creationId, mailboxRequest));
+        ImmutableList<String> destroy = ImmutableList.of("destroyId");
+        SetMailboxesRequest expected = new 
SetMailboxesRequest(ImmutableMap.of(creationId, mailboxRequest), destroy);
         
         SetMailboxesRequest actual = SetMailboxesRequest.builder()
             .create(creationId, mailboxRequest)
+            .destroy(destroy)
             .build();
         
         assertThat(actual).isEqualToComparingFieldByField(expected);

http://git-wip-us.apache.org/repos/asf/james-project/blob/9ab23a6a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetMailboxesResponseTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetMailboxesResponseTest.java
 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetMailboxesResponseTest.java
new file mode 100644
index 0000000..9cc6281
--- /dev/null
+++ 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetMailboxesResponseTest.java
@@ -0,0 +1,113 @@
+/****************************************************************
+ * 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.jmap.model;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.james.jmap.model.mailbox.Mailbox;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class SetMailboxesResponseTest {
+
+    @Test
+    public void builderShouldWork() {
+        ImmutableMap<MailboxCreationId, Mailbox> created = 
ImmutableMap.of(MailboxCreationId.of("1"),
+            Mailbox.builder()
+                .id("1")
+                .name("myBox")
+                .build());
+        ImmutableMap<MailboxCreationId, SetError> notCreated = 
ImmutableMap.of(MailboxCreationId.of("dead-beef-defec8"), 
SetError.builder().type("created").build());
+        ImmutableList<String> destroyed = ImmutableList.of("2");
+        ImmutableMap<String, SetError> notDestroyed  = ImmutableMap.of("2", 
SetError.builder().type("destroyed").build());
+        SetMailboxesResponse expected = new SetMailboxesResponse(created, 
notCreated, destroyed, notDestroyed);
+
+        SetMailboxesResponse setMessagesResponse = 
SetMailboxesResponse.builder()
+            .created(created)
+            .destroyed(destroyed)
+            .notCreated(notCreated)
+            .notDestroyed(notDestroyed)
+            .build();
+
+        
assertThat(setMessagesResponse).isEqualToComparingFieldByField(expected);
+    }
+
+    @Test
+    public void mergeIntoShouldCopyItemsWhenBuilderIsEmpty() {
+        // Given
+        SetMailboxesResponse.Builder emptyBuilder = 
SetMailboxesResponse.builder();
+        SetMailboxesResponse testee = SetMailboxesResponse.builder()
+                .created(buildMailbox(MailboxCreationId.of("1")))
+                .destroyed("2")
+                
.notCreated(ImmutableMap.of(MailboxCreationId.of("dead-beef-defec8"), 
SetError.builder().type("type").build()))
+                .notDestroyed(ImmutableMap.of("3", 
SetError.builder().type("type").build()))
+                .build();
+
+        // When
+        testee.mergeInto(emptyBuilder);
+        // Then
+        
assertThat(emptyBuilder.build()).isEqualToComparingFieldByField(testee);
+    }
+
+    private ImmutableMap<MailboxCreationId, Mailbox> 
buildMailbox(MailboxCreationId mailboxId) {
+        return ImmutableMap.of(mailboxId, Mailbox.builder()
+                .id(mailboxId.getCreationId())
+                .name(mailboxId.getCreationId())
+                .build());
+    }
+
+    @Test
+    public void mergeIntoShouldMergeCreatedLists() {
+        // Given
+        MailboxCreationId buildersCreatedMessageId = MailboxCreationId.of("1");
+        SetMailboxesResponse.Builder nonEmptyBuilder = 
SetMailboxesResponse.builder()
+                .created(buildMailbox(buildersCreatedMessageId));
+        MailboxCreationId createdMessageId = MailboxCreationId.of("2");
+        SetMailboxesResponse testee = SetMailboxesResponse.builder()
+                .created(buildMailbox(createdMessageId))
+                .build();
+        // When
+        testee.mergeInto(nonEmptyBuilder);
+        SetMailboxesResponse mergedResponse = nonEmptyBuilder.build();
+
+        // Then
+        
assertThat(mergedResponse.getCreated().keySet()).containsExactly(buildersCreatedMessageId,
 createdMessageId);
+    }
+
+    @Test
+    public void mergeIntoShouldMergeDestroyedLists() {
+        // Given
+        String buildersDestroyedMessageId = "1";
+        SetMailboxesResponse.Builder nonEmptyBuilder = 
SetMailboxesResponse.builder()
+                .destroyed(buildersDestroyedMessageId);
+        String destroyedMessageId = "2";
+        SetMailboxesResponse testee = SetMailboxesResponse.builder()
+                .destroyed(destroyedMessageId)
+                .build();
+        // When
+        testee.mergeInto(nonEmptyBuilder);
+        SetMailboxesResponse mergedResponse = nonEmptyBuilder.build();
+
+        // Then
+        
assertThat(mergedResponse.getDestroyed()).containsExactly(buildersDestroyedMessageId,
 destroyedMessageId);
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to