JAMES-1676 setMessages: handle parsing error for each update request

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

Branch: refs/heads/master
Commit: 41aebb7b01d8feee85e148e90792b966518a4ebe
Parents: 958a96e
Author: Raphael Ouazana <raphael.ouaz...@linagora.com>
Authored: Fri Feb 5 10:40:23 2016 +0100
Committer: Fabien Vignon <fvig...@linagora.com>
Committed: Wed Feb 10 17:47:25 2016 +0100

----------------------------------------------------------------------
 .../jmap/methods/SetMessagesMethodTest.java     |  6 +--
 .../james/jmap/methods/SetMessagesMethod.java   | 54 ++++++++++++++++++--
 .../james/jmap/model/SetMessagesRequest.java    | 11 ++--
 3 files changed, 58 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/41aebb7b/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/SetMessagesMethodTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/SetMessagesMethodTest.java
 
b/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/SetMessagesMethodTest.java
index 04921b3..c584f4c 100644
--- 
a/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/SetMessagesMethodTest.java
+++ 
b/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/SetMessagesMethodTest.java
@@ -489,7 +489,6 @@ public abstract class SetMessagesMethodTest {
     }
 
     @Test
-    @Ignore("Unable to deal with invalid types from SetMessages requests 
handler")
     public void setMessagesShouldRejectUpdateWhenPropertyHasWrongType() throws 
MailboxException {
         
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, 
username, "mailbox");
         jmapServer.serverProbe().appendMessage(username, new 
MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
@@ -504,7 +503,6 @@ public abstract class SetMessagesMethodTest {
                 .contentType(ContentType.JSON)
                 .header("Authorization", accessToken.serialize())
                 .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : 
{ \"isUnread\" : \"123\" } } }, \"#0\"]]", messageId))
-                // Does not work, jackson throws InvalidFormatException way 
before SetMessageMethod can deal with !
         .when()
                 .post("/jmap")
         .then()
@@ -513,8 +511,8 @@ public abstract class SetMessagesMethodTest {
                 .body("[0][0]", equalTo("messagesSet"))
                 .body("[0][1].notUpdated", hasKey(messageId))
                 .body("[0][1].notUpdated[\""+messageId+"\"].type", 
equalTo("invalidProperties"))
-                .body("[0][1].notUpdated[\""+messageId+"\"].properties", 
equalTo("isUnread"))
-                .body("[0][1].notUpdated[\""+messageId+"\"].description", 
equalTo("invalid properties"))
+                .body("[0][1].notUpdated[\""+messageId+"\"].properties[0]", 
equalTo("isUnread"))
+                .body("[0][1].notUpdated[\""+messageId+"\"].description", 
startsWith("Can not construct instance of java.lang.Boolean from String value 
'123': only \"true\" or \"false\" recognized"))
                 .body("[0][1].updated", hasSize(0));
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/41aebb7b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesMethod.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesMethod.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesMethod.java
index c849718..d93b65a 100644
--- 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesMethod.java
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesMethod.java
@@ -19,19 +19,23 @@
 
 package org.apache.james.jmap.methods;
 
+import java.io.IOException;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.function.Consumer;
 import java.util.stream.Stream;
+
 import javax.inject.Inject;
 import javax.mail.Flags;
 
 import org.apache.james.jmap.exceptions.MessageNotFoundException;
+import org.apache.james.jmap.json.ObjectMapperFactory;
 import org.apache.james.jmap.model.ClientId;
 import org.apache.james.jmap.model.MessageId;
 import org.apache.james.jmap.model.MessageProperties;
+import org.apache.james.jmap.model.MessageProperties.MessageProperty;
 import org.apache.james.jmap.model.SetError;
 import org.apache.james.jmap.model.SetMessagesRequest;
 import org.apache.james.jmap.model.SetMessagesResponse;
@@ -48,8 +52,14 @@ import 
org.apache.james.mailbox.store.mail.MessageMapper.FetchType;
 import org.apache.james.mailbox.store.mail.model.Mailbox;
 import org.apache.james.mailbox.store.mail.model.MailboxId;
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.util.streams.Collectors;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonMappingException.Reference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -65,11 +75,13 @@ public class SetMessagesMethod<Id extends MailboxId> 
implements Method {
 
     private final MailboxMapperFactory<Id> mailboxMapperFactory;
     private final MailboxSessionMapperFactory<Id> mailboxSessionMapperFactory;
+    private final ObjectMapper jsonParser;
 
     @Inject
-    @VisibleForTesting SetMessagesMethod(MailboxMapperFactory<Id> 
mailboxMapperFactory, MailboxSessionMapperFactory<Id> 
mailboxSessionMapperFactory) {
+    @VisibleForTesting SetMessagesMethod(MailboxMapperFactory<Id> 
mailboxMapperFactory, MailboxSessionMapperFactory<Id> 
mailboxSessionMapperFactory, ObjectMapperFactory objectMapperFactory) {
         this.mailboxMapperFactory = mailboxMapperFactory;
         this.mailboxSessionMapperFactory = mailboxSessionMapperFactory;
+        jsonParser = objectMapperFactory.forParsing();
     }
 
     @Override
@@ -106,11 +118,21 @@ public class SetMessagesMethod<Id extends MailboxId> 
implements Method {
         return responseBuilder.build();
     }
 
-    private void processUpdates(Map<MessageId, UpdateMessagePatch> 
mapOfMessagePatchesById, MailboxSession mailboxSession,
+    private void processUpdates(Map<MessageId, ObjectNode> 
mapOfMessagePatchesById, MailboxSession mailboxSession,
                                 SetMessagesResponse.Builder responseBuilder) {
         mapOfMessagePatchesById.entrySet().stream()
-                .filter(kv -> kv.getValue().isValid())
-                .forEach(kv -> update(kv.getKey(), kv.getValue(), 
mailboxSession, responseBuilder));
+                .forEach(kv -> parseAndUpdate(mailboxSession, responseBuilder, 
kv.getKey(), kv.getValue()));
+    }
+
+    private void parseAndUpdate(MailboxSession mailboxSession, 
SetMessagesResponse.Builder responseBuilder, MessageId messageId, ObjectNode 
json) {
+        try {
+            UpdateMessagePatch updateMessagePatch = 
jsonParser.readValue(json.toString(), UpdateMessagePatch.class);
+            update(messageId, updateMessagePatch, mailboxSession, 
responseBuilder);
+        } catch (JsonMappingException e) {
+            handleInvalidField(messageId, responseBuilder, e);
+        } catch (IOException e) {
+            handleInvalidRequest(messageId, responseBuilder, e);
+        }
     }
 
     private void update(MessageId messageId, UpdateMessagePatch 
updateMessagePatch, MailboxSession mailboxSession, SetMessagesResponse.Builder 
builder){
@@ -127,6 +149,30 @@ public class SetMessagesMethod<Id extends MailboxId> 
implements Method {
         }
     }
 
+    private void handleInvalidField(MessageId messageId, 
SetMessagesResponse.Builder builder, JsonMappingException e) {
+        LOGGER.error("Invalid field in update request", e);
+        builder.notUpdated(ImmutableMap.of(messageId, SetError.builder()
+                .type("invalidProperties")
+                .description(e.getMessage())
+                .properties(fromJsonReferences(e.getPath()))
+                .build()));
+    }
+    
+    private ImmutableSet<MessageProperty> fromJsonReferences(List<Reference> 
references) {
+        return references.stream()
+                .map(Reference::getFieldName)
+                .map(MessageProperty::valueOf)
+                .collect(Collectors.toImmutableSet());
+    }
+
+    private void handleInvalidRequest(MessageId messageId, 
SetMessagesResponse.Builder builder, IOException e) {
+        LOGGER.error("Invalid update request", e);
+        builder.notUpdated(ImmutableMap.of(messageId, SetError.builder()
+                .type("invalidProperties")
+                .description(e.getMessage())
+                .build()));
+    }
+
     private void handleMessageUpdateException(MessageId messageId, 
SetMessagesResponse.Builder builder, MailboxException e) {
         LOGGER.error("An error occurred when updating a message", e);
         builder.notUpdated(ImmutableMap.of(messageId, SetError.builder()

http://git-wip-us.apache.org/repos/asf/james-project/blob/41aebb7b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesRequest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesRequest.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesRequest.java
index 1986df0..b1f3a60 100644
--- 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesRequest.java
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesRequest.java
@@ -28,6 +28,7 @@ import org.apache.james.jmap.methods.JmapRequest;
 
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -45,7 +46,7 @@ public class SetMessagesRequest implements JmapRequest {
         private String accountId;
         private String ifInState;
         private ImmutableList.Builder<Message> create;
-        private ImmutableMap.Builder<MessageId, UpdateMessagePatch> update;
+        private ImmutableMap.Builder<MessageId, ObjectNode> update;
         private ImmutableList.Builder<MessageId> destroy;
 
         private Builder() {
@@ -75,7 +76,7 @@ public class SetMessagesRequest implements JmapRequest {
             return this;
         }
 
-        public Builder update(Map<MessageId, UpdateMessagePatch> updates) {
+        public Builder update(Map<MessageId, ObjectNode> updates) {
             this.update.putAll(updates);
             return this;
         }
@@ -93,10 +94,10 @@ public class SetMessagesRequest implements JmapRequest {
     private final Optional<String> accountId;
     private final Optional<String> ifInState;
     private final List<Message> create;
-    private final Map<MessageId, UpdateMessagePatch> update;
+    private final Map<MessageId, ObjectNode> update;
     private final List<MessageId> destroy;
 
-    @VisibleForTesting SetMessagesRequest(Optional<String> accountId, 
Optional<String> ifInState, List<Message> create, Map<MessageId, 
UpdateMessagePatch> update, List<MessageId> destroy) {
+    @VisibleForTesting SetMessagesRequest(Optional<String> accountId, 
Optional<String> ifInState, List<Message> create, Map<MessageId, ObjectNode> 
update, List<MessageId> destroy) {
         this.accountId = accountId;
         this.ifInState = ifInState;
         this.create = create;
@@ -116,7 +117,7 @@ public class SetMessagesRequest implements JmapRequest {
         return create;
     }
 
-    public Map<MessageId, UpdateMessagePatch> getUpdate() {
+    public Map<MessageId, ObjectNode> getUpdate() {
         return update;
     }
 


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

Reply via email to