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]
