You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2018/12/19 11:24:42 UTC

[01/10] james-project git commit: MAILBOX-359 Added Scala Event Serialization

Repository: james-project
Updated Branches:
  refs/heads/master 0f9024f3a -> 544924a9c


http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxDeletionSerializationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxDeletionSerializationTest.java b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxDeletionSerializationTest.java
index bec317e..cc5da61 100644
--- a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxDeletionSerializationTest.java
+++ b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxDeletionSerializationTest.java
@@ -36,6 +36,7 @@ import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.QuotaRoot;
 import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.TestMessageId;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
@@ -74,7 +75,7 @@ class MailboxDeletionSerializationTest {
         "  }" +
         "}";
 
-    private static final EventSerializer EVENT_SERIALIZER = new EventSerializer(new TestId.Factory());
+    private static final EventSerializer EVENT_SERIALIZER = new EventSerializer(new TestId.Factory(), new TestMessageId.Factory());
 
     @Test
     void mailboxAddedShouldBeWellSerialized() {

http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxRenamedSerializationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxRenamedSerializationTest.java b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxRenamedSerializationTest.java
index c4841c5..557eac7 100644
--- a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxRenamedSerializationTest.java
+++ b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxRenamedSerializationTest.java
@@ -32,6 +32,7 @@ import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.TestMessageId;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
@@ -70,7 +71,7 @@ class MailboxRenamedSerializationTest {
             "  }" +
             "}";
 
-    private static final EventSerializer EVENT_SERIALIZER = new EventSerializer(new TestId.Factory());
+    private static final EventSerializer EVENT_SERIALIZER = new EventSerializer(new TestId.Factory(), new TestMessageId.Factory());
 
     @Test
     void mailboxRenamedShouldBeWellSerialized() {

http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/mailbox/event/json/src/test/java/org/apache/james/event/json/QuotaUsageUpdatedEventSerializationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/test/java/org/apache/james/event/json/QuotaUsageUpdatedEventSerializationTest.java b/mailbox/event/json/src/test/java/org/apache/james/event/json/QuotaUsageUpdatedEventSerializationTest.java
index c2bc207..9b940fb 100644
--- a/mailbox/event/json/src/test/java/org/apache/james/event/json/QuotaUsageUpdatedEventSerializationTest.java
+++ b/mailbox/event/json/src/test/java/org/apache/james/event/json/QuotaUsageUpdatedEventSerializationTest.java
@@ -34,6 +34,7 @@ import org.apache.james.mailbox.MailboxListener;
 import org.apache.james.mailbox.model.Quota;
 import org.apache.james.mailbox.model.QuotaRoot;
 import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.TestMessageId;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
@@ -64,7 +65,7 @@ class QuotaUsageUpdatedEventSerializationTest {
             "}" +
         "}";
 
-    private static final EventSerializer EVENT_SERIALIZER = new EventSerializer(new TestId.Factory());
+    private static final EventSerializer EVENT_SERIALIZER = new EventSerializer(new TestId.Factory(), new TestMessageId.Factory());
 
     @Nested
     class WithUser {


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


[05/10] james-project git commit: MAILBOX-363 MessageMoveEvent equals + hashCode

Posted by bt...@apache.org.
MAILBOX-363 MessageMoveEvent equals + hashCode


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

Branch: refs/heads/master
Commit: 8a91427cd33024aa619f2839ce78b985e5c6b9bd
Parents: a809504
Author: Benoit Tellier <bt...@linagora.com>
Authored: Mon Dec 17 10:44:30 2018 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Wed Dec 19 18:06:37 2018 +0700

----------------------------------------------------------------------
 .../apache/james/mailbox/MessageMoveEvent.java  | 18 +++++++++++
 .../james/mailbox/model/MessageMoves.java       | 25 +++++++++++++++
 .../james/mailbox/MessageMoveEventTest.java     |  7 +++++
 .../james/mailbox/model/MessageMovesTest.java   | 32 ++++++++++++++++++++
 4 files changed, 82 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/8a91427c/mailbox/api/src/main/java/org/apache/james/mailbox/MessageMoveEvent.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MessageMoveEvent.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MessageMoveEvent.java
index 226ae47..5122766 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MessageMoveEvent.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MessageMoveEvent.java
@@ -19,6 +19,7 @@
 package org.apache.james.mailbox;
 
 import java.util.Collection;
+import java.util.Objects;
 
 import org.apache.james.core.User;
 import org.apache.james.mailbox.model.MailboxId;
@@ -114,4 +115,21 @@ public class MessageMoveEvent implements Event {
         return messageMoves.removedMailboxIds()
                 .contains(mailboxId);
     }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof MessageMoveEvent) {
+            MessageMoveEvent that = (MessageMoveEvent) o;
+
+            return Objects.equals(this.user, that.user)
+                && Objects.equals(this.messageMoves, that.messageMoves)
+                && Objects.equals(this.messageIds, that.messageIds);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(user, messageMoves, messageIds);
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/8a91427c/mailbox/api/src/main/java/org/apache/james/mailbox/model/MessageMoves.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MessageMoves.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MessageMoves.java
index 38e387a..a041a25 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MessageMoves.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MessageMoves.java
@@ -20,6 +20,7 @@ package org.apache.james.mailbox.model;
 
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Objects;
 import java.util.Set;
 
 import com.google.common.collect.ImmutableSet;
@@ -84,4 +85,28 @@ public class MessageMoves {
     public Set<MailboxId> removedMailboxIds() {
         return Sets.difference(previousMailboxIds, targetMailboxIds);
     }
+
+    public ImmutableSet<MailboxId> getPreviousMailboxIds() {
+        return previousMailboxIds;
+    }
+
+    public ImmutableSet<MailboxId> getTargetMailboxIds() {
+        return targetMailboxIds;
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof MessageMoves) {
+            MessageMoves that = (MessageMoves) o;
+
+            return Objects.equals(this.previousMailboxIds, that.previousMailboxIds)
+                && Objects.equals(this.targetMailboxIds, that.targetMailboxIds);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(previousMailboxIds, targetMailboxIds);
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/8a91427c/mailbox/api/src/test/java/org/apache/james/mailbox/MessageMoveEventTest.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/MessageMoveEventTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/MessageMoveEventTest.java
index 1d217ee..db85d82 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/MessageMoveEventTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MessageMoveEventTest.java
@@ -31,12 +31,19 @@ import org.assertj.core.api.JUnitSoftAssertions;
 import org.junit.Rule;
 import org.junit.Test;
 
+import nl.jqno.equalsverifier.EqualsVerifier;
+
 public class MessageMoveEventTest {
 
     @Rule
     public JUnitSoftAssertions softly = new JUnitSoftAssertions();
 
     @Test
+    public void shouldRespectBeanContract() {
+        EqualsVerifier.forClass(MessageMoveEvent.class).verify();
+    }
+
+    @Test
     public void builderShouldThrowWhenSessionIsNull() {
         assertThatThrownBy(() -> MessageMoveEvent.builder()
                 .build())

http://git-wip-us.apache.org/repos/asf/james-project/blob/8a91427c/mailbox/api/src/test/java/org/apache/james/mailbox/model/MessageMovesTest.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/model/MessageMovesTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/model/MessageMovesTest.java
new file mode 100644
index 0000000..3a76147
--- /dev/null
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/model/MessageMovesTest.java
@@ -0,0 +1,32 @@
+/****************************************************************
+ * 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.mailbox.model;
+
+import org.apache.james.mailbox.MessageMoveEvent;
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class MessageMovesTest {
+    @Test
+    void shouldRespectBeanContract() {
+        EqualsVerifier.forClass(MessageMoveEvent.class).verify();
+    }
+}
\ No newline at end of file


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


[03/10] james-project git commit: MAILBOX-359 Gathering non related event DTOs into one class

Posted by bt...@apache.org.
MAILBOX-359 Gathering non related event DTOs into one class


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

Branch: refs/heads/master
Commit: a809504323cc1a533af873d6782e7f51d9da3a6b
Parents: 9e5b1bf
Author: tran tien duc <dt...@linagora.com>
Authored: Mon Dec 17 15:39:01 2018 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Wed Dec 19 18:05:58 2018 +0700

----------------------------------------------------------------------
 .../org/apache/james/mailbox/FlagsBuilder.java  |   5 -
 .../apache/james/mailbox/MailboxListener.java   |   3 +-
 .../org/apache/james/event/json/DTOs.scala      | 104 +++++++++++++++----
 .../james/event/json/EventSerializer.scala      |  39 ++++---
 .../apache/james/event/json/MetaDataDTO.scala   |  90 ----------------
 .../event/json/AddedSerializationTest.java      |  20 ++--
 6 files changed, 120 insertions(+), 141 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/a8095043/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java b/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java
index f36189b..7a791ec 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java
@@ -61,11 +61,6 @@ public class FlagsBuilder {
         return this;
     }
 
-    public FlagsBuilder merge(FlagsBuilder flagsBuilder) {
-        internalFlags.add(flagsBuilder.internalFlags);
-        return this;
-    }
-
     public Flags build() {
         return new Flags(internalFlags);
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/a8095043/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
index 60d8aa2..83797ca 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
@@ -24,6 +24,7 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.SortedMap;
 
 import org.apache.james.core.User;
 import org.apache.james.core.quota.QuotaCount;
@@ -451,7 +452,7 @@ public interface MailboxListener {
     class Added extends MetaDataHoldingEvent {
         private final Map<MessageUid, MessageMetaData> added;
 
-        public Added(MailboxSession.SessionId sessionId, User user, MailboxPath path, MailboxId mailboxId, Map<MessageUid, MessageMetaData> uids) {
+        public Added(MailboxSession.SessionId sessionId, User user, MailboxPath path, MailboxId mailboxId, SortedMap<MessageUid, MessageMetaData> uids) {
             super(sessionId, user, path, mailboxId);
             this.added = ImmutableMap.copyOf(uids);
         }

http://git-wip-us.apache.org/repos/asf/james-project/blob/a8095043/mailbox/event/json/src/main/scala/org/apache/james/event/json/DTOs.scala
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/main/scala/org/apache/james/event/json/DTOs.scala b/mailbox/event/json/src/main/scala/org/apache/james/event/json/DTOs.scala
index 3e04d1c..ee4529e 100644
--- a/mailbox/event/json/src/main/scala/org/apache/james/event/json/DTOs.scala
+++ b/mailbox/event/json/src/main/scala/org/apache/james/event/json/DTOs.scala
@@ -1,31 +1,38 @@
-/****************************************************************
- * 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.                                           *
- ****************************************************************/
+/** **************************************************************
+  * 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.event.json
 
+import java.time.Instant
+import java.util.Date
+
+import javax.mail.{Flags => JavaMailFlags}
 import org.apache.james.core.quota.QuotaValue
 import org.apache.james.mailbox.acl.{ACLDiff => JavaACLDiff}
-import org.apache.james.mailbox.model.{MailboxACL, MailboxPath => JavaMailboxPath, Quota => JavaQuota}
+import org.apache.james.mailbox.model.{MailboxACL, MessageId, MailboxPath => JavaMailboxPath, MessageMetaData => JavaMessageMetaData,
+  Quota => JavaQuota}
+import org.apache.james.mailbox.{FlagsBuilder, MessageUid}
 
 import scala.collection.JavaConverters._
 
 object DTOs {
+
   object ACLDiff {
     def fromJava(javaACLDiff: JavaACLDiff): ACLDiff = ACLDiff(
       javaACLDiff.getOldACL.getEntries.asScala.toMap,
@@ -63,4 +70,63 @@ object DTOs {
         .limitsByScope(limits.asJava)
         .build()
   }
+
+  object MessageMetaData {
+    def fromJava(javaMessageMetaData: JavaMessageMetaData): MessageMetaData = DTOs.MessageMetaData(
+      javaMessageMetaData.getUid,
+      javaMessageMetaData.getModSeq,
+      javaMessageMetaData.getFlags,
+      javaMessageMetaData.getSize,
+      javaMessageMetaData.getInternalDate.toInstant,
+      javaMessageMetaData.getMessageId)
+  }
+
+  case class MessageMetaData(uid: MessageUid, modSeq: Long, flags: JavaMailFlags, size: Long, internalDate: Instant, messageId: MessageId) {
+    def toJava: JavaMessageMetaData = new JavaMessageMetaData(uid, modSeq, flags, size, Date.from(internalDate), messageId)
+  }
+
+  object Flags {
+    val ANSWERED = "\\Answered"
+    val DELETED = "\\Deleted"
+    val DRAFT = "\\Draft"
+    val FLAGGED = "\\Flagged"
+    val RECENT = "\\Recent"
+    val SEEN = "\\Seen"
+    val ALL_SYSTEM_FLAGS = List(ANSWERED, DELETED, DRAFT, FLAGGED, RECENT, SEEN)
+
+    def toJavaFlags(serializedFlags: Array[String]): JavaMailFlags = {
+      serializedFlags
+        .map(toJavaMailFlag)
+        .foldLeft(new FlagsBuilder)((builder, flag) => builder.add(flag))
+        .build()
+    }
+
+    def toJavaMailFlag(flag: String): JavaMailFlags = ALL_SYSTEM_FLAGS.contains(flag) match {
+      case true => new FlagsBuilder().add(stringToSystemFlag(flag)).build()
+      case false => new FlagsBuilder().add(flag).build()
+    }
+
+    def fromJavaFlags(flags: JavaMailFlags): Array[String] = {
+      flags.getUserFlags ++ flags.getSystemFlags.map(flag => systemFlagToString(flag))
+    }
+
+    private def stringToSystemFlag(serializedFlag: String): JavaMailFlags.Flag = serializedFlag match {
+      case ANSWERED => JavaMailFlags.Flag.ANSWERED
+      case DELETED => JavaMailFlags.Flag.DELETED
+      case DRAFT => JavaMailFlags.Flag.DRAFT
+      case FLAGGED => JavaMailFlags.Flag.FLAGGED
+      case RECENT => JavaMailFlags.Flag.RECENT
+      case SEEN => JavaMailFlags.Flag.SEEN
+      case _ => throw new IllegalArgumentException(serializedFlag + " is not a system flag")
+    }
+
+    private def systemFlagToString(flag: JavaMailFlags.Flag): String = flag match {
+      case JavaMailFlags.Flag.ANSWERED => ANSWERED
+      case JavaMailFlags.Flag.DELETED => DELETED
+      case JavaMailFlags.Flag.DRAFT => DRAFT
+      case JavaMailFlags.Flag.FLAGGED => FLAGGED
+      case JavaMailFlags.Flag.RECENT => RECENT
+      case JavaMailFlags.Flag.SEEN => SEEN
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/a8095043/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
index a638165..01cb1b8 100644
--- a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
+++ b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
@@ -20,19 +20,22 @@
 package org.apache.james.event.json
 
 import java.time.Instant
-import java.util.Optional
+import java.util.{Optional, TreeMap => JavaTreeMap}
 
 import javax.mail.{Flags => JavaMailFlags}
 import julienrf.json.derived
 import org.apache.james.core.quota.{QuotaCount, QuotaSize, QuotaValue}
 import org.apache.james.core.{Domain, User}
 import org.apache.james.event.json.DTOs.{ACLDiff, MailboxPath, Quota}
-import org.apache.james.event.json.MetaDataDTO.Flags
 import org.apache.james.mailbox.MailboxListener.{Added => JavaAdded, Expunged => JavaExpunged,
   MailboxACLUpdated => JavaMailboxACLUpdated, MailboxAdded => JavaMailboxAdded, MailboxDeletion => JavaMailboxDeletion,
   MailboxRenamed => JavaMailboxRenamed, QuotaUsageUpdatedEvent => JavaQuotaUsageUpdatedEvent}
 import org.apache.james.mailbox.MailboxSession.SessionId
 import org.apache.james.mailbox.model.{MailboxId, MessageId, QuotaRoot, MailboxACL => JavaMailboxACL, Quota => JavaQuota}
+import org.apache.james.event.json.DTOs.{Flags, MailboxPath, Quota}
+import org.apache.james.mailbox.MailboxListener.{Added => JavaAdded, Expunged => JavaExpunged, MailboxAdded => JavaMailboxAdded, MailboxDeletion => JavaMailboxDeletion, MailboxRenamed => JavaMailboxRenamed, QuotaUsageUpdatedEvent => JavaQuotaUsageUpdatedEvent}
+import org.apache.james.mailbox.MailboxSession.SessionId
+import org.apache.james.mailbox.model.{MailboxId, MessageId, QuotaRoot, MessageMetaData => JavaMessageMetaData, Quota => JavaQuota}
 import org.apache.james.mailbox.{MessageUid, Event => JavaEvent}
 import play.api.libs.json.{JsArray, JsError, JsNull, JsNumber, JsObject, JsResult, JsString, JsSuccess, Json, OFormat, Reads, Writes}
 
@@ -68,23 +71,23 @@ private object DTO {
   }
 
   case class Added(sessionId: SessionId, user: User, path: MailboxPath, mailboxId: MailboxId,
-                   added: Map[MessageUid, MetaDataDTO.MessageMetaData]) extends Event {
+                   added: Map[MessageUid, DTOs.MessageMetaData]) extends Event {
     override def toJava: JavaEvent = new JavaAdded(
       sessionId,
       user,
       path.toJava,
       mailboxId,
-      added.map(entry => entry._1 -> entry._2.toJava).asJava)
+      new JavaTreeMap[MessageUid, JavaMessageMetaData](added.mapValues(_.toJava).asJava))
   }
 
   case class Expunged(sessionId: SessionId, user: User, path: MailboxPath, mailboxId: MailboxId,
-                      expunged: Map[MessageUid, MetaDataDTO.MessageMetaData]) extends Event {
+                      expunged: Map[MessageUid, DTOs.MessageMetaData]) extends Event {
     override def toJava: JavaEvent = new JavaExpunged(
       sessionId,
       user,
       path.toJava,
       mailboxId,
-      expunged.map(entry => entry._1 -> entry._2.toJava).asJava)
+      expunged.mapValues(_.toJava).asJava)
   }
 }
 
@@ -96,6 +99,11 @@ private object ScalaConverter {
     aclDiff = ACLDiff.fromJava(event.getAclDiff),
     mailboxId = event.getMailboxId)
 
+  private def toScala[T <: QuotaValue[T]](java: JavaQuota[T]): DTOs.Quota[T] = DTOs.Quota(
+    used = java.getUsed,
+    limit = java.getLimit,
+    limits = java.getLimitByScope.asScala.toMap)
+
   private def toScala(event: JavaMailboxAdded): DTO.MailboxAdded = DTO.MailboxAdded(
     mailboxPath = MailboxPath.fromJava(event.getMailboxPath),
     mailboxId = event.getMailboxId,
@@ -111,7 +119,6 @@ private object ScalaConverter {
     totalDeletedSize = event.getTotalDeletedSize,
     mailboxId = event.getMailboxId)
 
-
   private def toScala(event: JavaMailboxRenamed): DTO.MailboxRenamed = DTO.MailboxRenamed(
     sessionId = event.getSessionId,
     user = event.getUser,
@@ -131,7 +138,7 @@ private object ScalaConverter {
     user = event.getUser,
     path = MailboxPath.fromJava(event.getMailboxPath),
     mailboxId = event.getMailboxId,
-    added = event.getAdded.asScala.map(entry => entry._1 -> MetaDataDTO.MessageMetaData.fromJava(entry._2)).toMap
+    added = event.getAdded.asScala.mapValues(DTOs.MessageMetaData.fromJava).toMap
   )
 
   private def toScala(event: JavaExpunged): DTO.Expunged = DTO.Expunged(
@@ -139,17 +146,17 @@ private object ScalaConverter {
     user = event.getUser,
     path = MailboxPath.fromJava(event.getMailboxPath),
     mailboxId = event.getMailboxId,
-    expunged = event.getExpunged.asScala.map(entry => entry._1 -> MetaDataDTO.MessageMetaData.fromJava(entry._2)).toMap
+    expunged = event.getExpunged.asScala.mapValues(DTOs.MessageMetaData.fromJava).toMap
   )
 
   def toScala(javaEvent: JavaEvent): Event = javaEvent match {
+    case e: JavaAdded => toScala(e)
+    case e: JavaExpunged => toScala(e)
     case e: JavaMailboxACLUpdated => toScala(e)
     case e: JavaMailboxAdded => toScala(e)
     case e: JavaMailboxDeletion => toScala(e)
     case e: JavaMailboxRenamed => toScala(e)
     case e: JavaQuotaUsageUpdatedEvent => toScala(e)
-    case e: JavaAdded => toScala(e)
-    case e: JavaExpunged => toScala(e)
     case _ => throw new RuntimeException("no Scala conversion known")
   }
 }
@@ -170,7 +177,7 @@ private class JsonSerialize(mailboxIdFactory: MailboxId.Factory, messageIdFactor
   implicit val messageIdWrites: Writes[MessageId] = value => JsString(value.serialize())
   implicit val messageUidWrites: Writes[MessageUid] = value => JsNumber(value.asLong())
   implicit val flagsWrites: Writes[JavaMailFlags] = value => JsArray(Flags.fromJavaFlags(value).map(flag => JsString(flag)))
-  implicit val messageMetaDataWrites: Writes[MetaDataDTO.MessageMetaData] = Json.writes[MetaDataDTO.MessageMetaData]
+  implicit val messageMetaDataWrites: Writes[DTOs.MessageMetaData] = Json.writes[DTOs.MessageMetaData]
 
   implicit val aclEntryKeyReads: Reads[JavaMailboxACL.EntryKey] = {
     case JsString(keyAsString) => JsSuccess(JavaMailboxACL.EntryKey.deserialize(keyAsString))
@@ -255,10 +262,10 @@ private class JsonSerialize(mailboxIdFactory: MailboxId.Factory, messageIdFactor
     }
 
   implicit val aclDiffReads: Reads[ACLDiff] = Json.reads[ACLDiff]
-  implicit val mailboxPathReads: Reads[MailboxPath] = Json.reads[MailboxPath]
-  implicit val quotaCReads: Reads[Quota[QuotaCount]] = Json.reads[Quota[QuotaCount]]
-  implicit val quotaSReads: Reads[Quota[QuotaSize]] = Json.reads[Quota[QuotaSize]]
-  implicit val messageMetaDataReads: Reads[MetaDataDTO.MessageMetaData] = Json.reads[MetaDataDTO.MessageMetaData]
+  implicit val quotaCReads: Reads[DTOs.Quota[QuotaCount]] = Json.reads[DTOs.Quota[QuotaCount]]
+  implicit val quotaSReads: Reads[DTOs.Quota[QuotaSize]] = Json.reads[DTOs.Quota[QuotaSize]]
+  implicit val mailboxPathReads: Reads[DTOs.MailboxPath] = Json.reads[DTOs.MailboxPath]
+  implicit val messageMetaDataReads: Reads[DTOs.MessageMetaData] = Json.reads[DTOs.MessageMetaData]
 
   implicit val eventOFormat: OFormat[Event] = derived.oformat()
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/a8095043/mailbox/event/json/src/main/scala/org/apache/james/event/json/MetaDataDTO.scala
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/main/scala/org/apache/james/event/json/MetaDataDTO.scala b/mailbox/event/json/src/main/scala/org/apache/james/event/json/MetaDataDTO.scala
deleted file mode 100644
index ee314f0..0000000
--- a/mailbox/event/json/src/main/scala/org/apache/james/event/json/MetaDataDTO.scala
+++ /dev/null
@@ -1,90 +0,0 @@
-/** **************************************************************
-  * 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.event.json
-
-import java.time.Instant
-import java.util.Date
-
-import javax.mail.{Flags => JavaMailFlags}
-import org.apache.james.mailbox.model.{MessageId, MessageMetaData => JavaMessageMetaData}
-import org.apache.james.mailbox.{FlagsBuilder, MessageUid}
-
-object MetaDataDTO {
-
-  object MessageMetaData {
-    def fromJava(javaMessageMetaData: JavaMessageMetaData): MessageMetaData = MetaDataDTO.MessageMetaData(
-      javaMessageMetaData.getUid,
-      javaMessageMetaData.getModSeq,
-      javaMessageMetaData.getFlags,
-      javaMessageMetaData.getSize,
-      javaMessageMetaData.getInternalDate.toInstant,
-      javaMessageMetaData.getMessageId)
-  }
-
-  object Flags {
-    val ANSWERED = "\\Answered"
-    val DELETED = "\\Deleted"
-    val DRAFT = "\\Draft"
-    val FLAGGED = "\\Flagged"
-    val RECENT = "\\Recent"
-    val SEEN = "\\Seen"
-    val ALL_SYSTEM_FLAGS = List(ANSWERED, DELETED, DRAFT, FLAGGED, RECENT, SEEN)
-
-    def toJavaFlags(serializedFlags: Array[String]): JavaMailFlags = {
-      serializedFlags
-        .map(toFlagBuilder)
-        .reduceOption(_ merge _)
-        .getOrElse(new FlagsBuilder())
-        .build()
-    }
-
-    def toFlagBuilder(flag: String): FlagsBuilder = ALL_SYSTEM_FLAGS.contains(flag) match {
-      case true => new FlagsBuilder().add(stringToSystemFlag(flag))
-      case false => new FlagsBuilder().add(flag)
-    }
-
-    def fromJavaFlags(flags: JavaMailFlags): Array[String] = {
-      flags.getUserFlags ++ flags.getSystemFlags.map(flag => systemFlagToString(flag))
-    }
-
-    private def stringToSystemFlag(serializedFlag: String): JavaMailFlags.Flag = serializedFlag match {
-      case ANSWERED => JavaMailFlags.Flag.ANSWERED
-      case DELETED => JavaMailFlags.Flag.DELETED
-      case DRAFT => JavaMailFlags.Flag.DRAFT
-      case FLAGGED => JavaMailFlags.Flag.FLAGGED
-      case RECENT => JavaMailFlags.Flag.RECENT
-      case SEEN => JavaMailFlags.Flag.SEEN
-      case _ => throw new IllegalArgumentException(serializedFlag + " is not a system flag")
-    }
-
-    private def systemFlagToString(flag: JavaMailFlags.Flag): String = flag match {
-      case JavaMailFlags.Flag.ANSWERED => ANSWERED
-      case JavaMailFlags.Flag.DELETED => DELETED
-      case JavaMailFlags.Flag.DRAFT => DRAFT
-      case JavaMailFlags.Flag.FLAGGED => FLAGGED
-      case JavaMailFlags.Flag.RECENT => RECENT
-      case JavaMailFlags.Flag.SEEN => SEEN
-    }
-  }
-
-  case class MessageMetaData(uid: MessageUid, modSeq: Long, flags: JavaMailFlags, size: Long, internalDate: Instant, messageId: MessageId) {
-    def toJava: JavaMessageMetaData = new JavaMessageMetaData(uid, modSeq, flags, size, Date.from(internalDate), messageId)
-  }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/a8095043/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java b/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
index 3ddc475..cbc481a 100644
--- a/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
+++ b/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
@@ -27,8 +27,8 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.time.Instant;
 import java.util.Date;
-import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.SortedMap;
 
 import javax.mail.Flags;
 
@@ -46,7 +46,7 @@ import org.apache.james.mailbox.model.TestMessageId;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
-import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedMap;
 
 class AddedSerializationTest {
 
@@ -64,7 +64,7 @@ class AddedSerializationTest {
         .add(Flags.Flag.ANSWERED, Flags.Flag.DRAFT)
         .add("User Custom Flag")
         .build();
-    private static final Map<MessageUid, MessageMetaData> ADDED = ImmutableMap.of(
+    private static final SortedMap<MessageUid, MessageMetaData> ADDED = ImmutableSortedMap.of(
         MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(INSTANT), MESSAGE_ID));
 
     private static final MailboxListener.Added DEFAULT_ADDED_EVENT = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, ADDED);
@@ -109,7 +109,7 @@ class AddedSerializationTest {
     @Nested
     class WithEmptyAddedMap {
 
-        private final MailboxListener.Added emptyAddedEvent = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, ImmutableMap.of());
+        private final MailboxListener.Added emptyAddedEvent = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, ImmutableSortedMap.of());
         private final String emptyAddedEventJson =
             "{" +
             "  \"Added\": {" +
@@ -145,7 +145,7 @@ class AddedSerializationTest {
         class WithEmptyFlags {
             private final Flags emptyFlags = new FlagsBuilder().build();
             private final MailboxListener.Added emptyFlagsAddedEvent = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID,
-                ImmutableMap.of(
+                ImmutableSortedMap.of(
                     MESSAGE_UID,
                     new MessageMetaData(MESSAGE_UID, MOD_SEQ, emptyFlags, SIZE, Date.from(INSTANT), MESSAGE_ID)));
 
@@ -192,7 +192,7 @@ class AddedSerializationTest {
                 .add("Custom 1", "Custom 2", "")
                 .build();
             private final MailboxListener.Added onlyUserFlagsAddedEvent = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID,
-                ImmutableMap.of(
+                ImmutableSortedMap.of(
                     MESSAGE_UID,
                     new MessageMetaData(MESSAGE_UID, MOD_SEQ, onlyUserFlags, SIZE, Date.from(INSTANT), MESSAGE_ID)));
 
@@ -240,7 +240,7 @@ class AddedSerializationTest {
                 .add(Flags.Flag.SEEN, Flags.Flag.ANSWERED, Flags.Flag.DELETED)
                 .build();
             private final MailboxListener.Added onlySystemFlagsAddedEvent = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID,
-                ImmutableMap.of(
+                ImmutableSortedMap.of(
                     MESSAGE_UID,
                     new MessageMetaData(MESSAGE_UID, MOD_SEQ, onlySystemFlags, SIZE, Date.from(INSTANT), MESSAGE_ID)));
 
@@ -326,7 +326,7 @@ class AddedSerializationTest {
 
         @Test
         void addedShouldDeserializeWhenInternalDateIsInGoodISOFormat() {
-            Map<MessageUid, MessageMetaData> added = ImmutableMap.of(
+            SortedMap<MessageUid, MessageMetaData> added = ImmutableSortedMap.of(
                 MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(Instant.parse("2018-12-14T09:41:51Z")), MESSAGE_ID));
             MailboxListener.Added eventRoundToMillis = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, added);
 
@@ -358,7 +358,7 @@ class AddedSerializationTest {
 
         @Test
         void addedShouldDeserializeWhenInternalDateIsMissingMilliSeconds() {
-            Map<MessageUid, MessageMetaData> added = ImmutableMap.of(
+            SortedMap<MessageUid, MessageMetaData> added = ImmutableSortedMap.of(
                 MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(Instant.parse("2018-12-14T09:41:51Z")), MESSAGE_ID));
             MailboxListener.Added eventRoundToMillis = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, added);
 
@@ -390,7 +390,7 @@ class AddedSerializationTest {
 
         @Test
         void addedShouldDeserializeWhenInternalDateIsMissingSeconds() {
-            Map<MessageUid, MessageMetaData> added = ImmutableMap.of(
+            SortedMap<MessageUid, MessageMetaData> added = ImmutableSortedMap.of(
                 MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(Instant.parse("2018-12-14T09:41:00Z")), MESSAGE_ID));
             MailboxListener.Added eventRoundToMinute = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, added);
 


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


[04/10] james-project git commit: MAILBOX-359 Expunged Scala Event Serialization

Posted by bt...@apache.org.
MAILBOX-359 Expunged Scala Event Serialization

Added and Expunged have 100% same structure, so nothing different except
class name and Json Object name! Event Serialization and tests for Expunged is
duplicated from Added


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

Branch: refs/heads/master
Commit: 9e5b1bf1516908c0aa57b439bc0cd5b42ebf3617
Parents: 39bc507
Author: tran tien duc <dt...@linagora.com>
Authored: Mon Dec 17 11:17:12 2018 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Wed Dec 19 18:05:58 2018 +0700

----------------------------------------------------------------------
 .../apache/james/mailbox/MailboxListener.java   |   31 +-
 .../james/mailbox/MailboxListenerTest.java      |    5 +
 .../james/event/json/EventSerializer.scala      |   25 +-
 .../event/json/ExpungedSerializationTest.java   | 1465 ++++++++++++++++++
 4 files changed, 1519 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/9e5b1bf1/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
index 2b7fdb9..60d8aa2 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
@@ -376,16 +376,16 @@ public interface MailboxListener {
     }
 
     class Expunged extends MetaDataHoldingEvent {
-        private final Map<MessageUid, MessageMetaData> uids;
+        private final Map<MessageUid, MessageMetaData> expunged;
 
         public Expunged(MailboxSession.SessionId sessionId, User user, MailboxPath path, MailboxId mailboxId, Map<MessageUid, MessageMetaData> uids) {
             super(sessionId, user, path, mailboxId);
-            this.uids = ImmutableMap.copyOf(uids);
+            this.expunged = ImmutableMap.copyOf(uids);
         }
 
         @Override
         public Collection<MessageUid> getUids() {
-            return uids.keySet();
+            return expunged.keySet();
         }
 
         /**
@@ -395,7 +395,30 @@ public interface MailboxListener {
          */
         @Override
         public MessageMetaData getMetaData(MessageUid uid) {
-            return uids.get(uid);
+            return expunged.get(uid);
+        }
+
+        public Map<MessageUid, MessageMetaData> getExpunged() {
+            return expunged;
+        }
+
+        @Override
+        public final boolean equals(Object o) {
+            if (o instanceof Expunged) {
+                Expunged that = (Expunged) o;
+
+                return Objects.equals(this.sessionId, that.sessionId)
+                    && Objects.equals(this.user, that.user)
+                    && Objects.equals(this.path, that.path)
+                    && Objects.equals(this.mailboxId, that.mailboxId)
+                    && Objects.equals(this.expunged, that.expunged);
+            }
+            return false;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(sessionId, user, path, mailboxId, expunged);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/9e5b1bf1/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
index a276c3f..1316753 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
@@ -48,4 +48,9 @@ class MailboxListenerTest {
     void addedShouldMatchBeanContract() {
         EqualsVerifier.forClass(MailboxListener.Added.class).verify();
     }
+
+    @Test
+    void expungedShouldMatchBeanContract() {
+        EqualsVerifier.forClass(MailboxListener.Expunged.class).verify();
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/9e5b1bf1/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
index 549e105..a638165 100644
--- a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
+++ b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
@@ -28,9 +28,9 @@ import org.apache.james.core.quota.{QuotaCount, QuotaSize, QuotaValue}
 import org.apache.james.core.{Domain, User}
 import org.apache.james.event.json.DTOs.{ACLDiff, MailboxPath, Quota}
 import org.apache.james.event.json.MetaDataDTO.Flags
-import org.apache.james.mailbox.MailboxListener.{Added => JavaAdded, MailboxACLUpdated => JavaMailboxACLUpdated,
-  MailboxAdded => JavaMailboxAdded, MailboxDeletion => JavaMailboxDeletion, MailboxRenamed => JavaMailboxRenamed,
-  QuotaUsageUpdatedEvent => JavaQuotaUsageUpdatedEvent}
+import org.apache.james.mailbox.MailboxListener.{Added => JavaAdded, Expunged => JavaExpunged,
+  MailboxACLUpdated => JavaMailboxACLUpdated, MailboxAdded => JavaMailboxAdded, MailboxDeletion => JavaMailboxDeletion,
+  MailboxRenamed => JavaMailboxRenamed, QuotaUsageUpdatedEvent => JavaQuotaUsageUpdatedEvent}
 import org.apache.james.mailbox.MailboxSession.SessionId
 import org.apache.james.mailbox.model.{MailboxId, MessageId, QuotaRoot, MailboxACL => JavaMailboxACL, Quota => JavaQuota}
 import org.apache.james.mailbox.{MessageUid, Event => JavaEvent}
@@ -76,6 +76,16 @@ private object DTO {
       mailboxId,
       added.map(entry => entry._1 -> entry._2.toJava).asJava)
   }
+
+  case class Expunged(sessionId: SessionId, user: User, path: MailboxPath, mailboxId: MailboxId,
+                      expunged: Map[MessageUid, MetaDataDTO.MessageMetaData]) extends Event {
+    override def toJava: JavaEvent = new JavaExpunged(
+      sessionId,
+      user,
+      path.toJava,
+      mailboxId,
+      expunged.map(entry => entry._1 -> entry._2.toJava).asJava)
+  }
 }
 
 private object ScalaConverter {
@@ -124,6 +134,14 @@ private object ScalaConverter {
     added = event.getAdded.asScala.map(entry => entry._1 -> MetaDataDTO.MessageMetaData.fromJava(entry._2)).toMap
   )
 
+  private def toScala(event: JavaExpunged): DTO.Expunged = DTO.Expunged(
+    sessionId = event.getSessionId,
+    user = event.getUser,
+    path = MailboxPath.fromJava(event.getMailboxPath),
+    mailboxId = event.getMailboxId,
+    expunged = event.getExpunged.asScala.map(entry => entry._1 -> MetaDataDTO.MessageMetaData.fromJava(entry._2)).toMap
+  )
+
   def toScala(javaEvent: JavaEvent): Event = javaEvent match {
     case e: JavaMailboxACLUpdated => toScala(e)
     case e: JavaMailboxAdded => toScala(e)
@@ -131,6 +149,7 @@ private object ScalaConverter {
     case e: JavaMailboxRenamed => toScala(e)
     case e: JavaQuotaUsageUpdatedEvent => toScala(e)
     case e: JavaAdded => toScala(e)
+    case e: JavaExpunged => toScala(e)
     case _ => throw new RuntimeException("no Scala conversion known")
   }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/9e5b1bf1/mailbox/event/json/src/test/java/org/apache/james/event/json/ExpungedSerializationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/test/java/org/apache/james/event/json/ExpungedSerializationTest.java b/mailbox/event/json/src/test/java/org/apache/james/event/json/ExpungedSerializationTest.java
new file mode 100644
index 0000000..73521c7
--- /dev/null
+++ b/mailbox/event/json/src/test/java/org/apache/james/event/json/ExpungedSerializationTest.java
@@ -0,0 +1,1465 @@
+/****************************************************************
+ * 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.event.json;
+
+import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
+import static net.javacrumbs.jsonunit.core.Option.IGNORING_ARRAY_ORDER;
+import static org.apache.james.mailbox.model.MailboxConstants.USER_NAMESPACE;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.time.Instant;
+import java.util.Date;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import javax.mail.Flags;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.FlagsBuilder;
+import org.apache.james.mailbox.MailboxListener;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.model.MailboxConstants;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.MessageMetaData;
+import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.TestMessageId;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+class ExpungedSerializationTest {
+
+    private static final User USER = User.fromUsername("user");
+    private static final MailboxSession.SessionId SESSION_ID = MailboxSession.SessionId.of(42);
+    private static final MailboxId MAILBOX_ID = TestId.of(18);
+    private static final String MAILBOX_NAME = "mailboxName";
+    private static final MailboxPath MAILBOX_PATH = new MailboxPath(MailboxConstants.USER_NAMESPACE, "user", MAILBOX_NAME);
+    private static final MessageUid MESSAGE_UID = MessageUid.of(123456);
+    private static final Instant INSTANT = Instant.parse("2018-12-14T09:41:51.541Z");
+    private static final TestMessageId MESSAGE_ID = TestMessageId.of(42);
+    private static final int MOD_SEQ = 35;
+    private static final int SIZE = 45;
+    private static final Flags FLAGS = FlagsBuilder.builder()
+        .add(Flags.Flag.ANSWERED, Flags.Flag.DRAFT)
+        .add("User Custom Flag")
+        .build();
+    private static final Map<MessageUid, MessageMetaData> EXPUNGED = ImmutableMap.of(
+        MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(INSTANT), MESSAGE_ID));
+
+    private static final MailboxListener.Expunged DEFAULT_EXPUNGED_EVENT = new MailboxListener.Expunged(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, EXPUNGED);
+    private static final String DEFAULT_EXPUNGED_EVENT_JSON =
+        "{" +
+        "  \"Expunged\": {" +
+        "    \"path\": {" +
+        "      \"namespace\": \"#private\"," +
+        "      \"user\": \"user\"," +
+        "      \"name\": \"mailboxName\"" +
+        "    }," +
+        "    \"mailboxId\": \"18\"," +
+        "    \"expunged\": {" +
+        "      \"123456\": {" +
+        "        \"uid\": 123456," +
+        "        \"modSeq\": 35," +
+        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+        "        \"size\": 45,  " +
+        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+        "        \"messageId\": \"42\"" +
+        "      }" +
+        "    }," +
+        "    \"sessionId\": 42," +
+        "    \"user\": \"user\"" +
+        "  }" +
+        "}";
+
+    private static final EventSerializer EVENT_SERIALIZER = new EventSerializer(new TestId.Factory(), new TestMessageId.Factory());
+
+    @Test
+    void expungedShouldBeWellSerialized() {
+        assertThatJson(EVENT_SERIALIZER.toJson(DEFAULT_EXPUNGED_EVENT))
+            .isEqualTo(DEFAULT_EXPUNGED_EVENT_JSON);
+    }
+
+    @Test
+    void expungedShouldBeWellDeSerialized() {
+        assertThat(EVENT_SERIALIZER.fromJson(DEFAULT_EXPUNGED_EVENT_JSON).get())
+            .isEqualTo(DEFAULT_EXPUNGED_EVENT);
+    }
+
+    @Nested
+    class WithEmptyExpungedMap {
+
+        private final MailboxListener.Expunged emptyExpungedEvent = new MailboxListener.Expunged(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, ImmutableMap.of());
+        private final String emptyExpungedEventJson =
+            "{" +
+            "  \"Expunged\": {" +
+            "    \"path\": {" +
+            "      \"namespace\": \"#private\"," +
+            "      \"user\": \"user\"," +
+            "      \"name\": \"mailboxName\"" +
+            "    }," +
+            "    \"mailboxId\": \"18\"," +
+            "    \"expunged\": {}," +
+            "    \"sessionId\": 42," +
+            "    \"user\": \"user\"" +
+            "  }" +
+            "}";
+
+        @Test
+        void expungedShouldBeWellSerializedWhenMapKeyIsEmpty() {
+            assertThatJson(EVENT_SERIALIZER.toJson(emptyExpungedEvent))
+                .isEqualTo(emptyExpungedEventJson);
+        }
+
+        @Test
+        void expungedShouldBeWellDeSerializedWhenMapKeyIsEmpty() {
+            assertThat(EVENT_SERIALIZER.fromJson(emptyExpungedEventJson).get())
+                .isEqualTo(emptyExpungedEvent);
+        }
+    }
+
+    @Nested
+    class WithFlags {
+
+        @Nested
+        class WithEmptyFlags {
+            private final Flags emptyFlags = new FlagsBuilder().build();
+            private final MailboxListener.Expunged emptyFlagsExpungedEvent = new MailboxListener.Expunged(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID,
+                ImmutableMap.of(
+                    MESSAGE_UID,
+                    new MessageMetaData(MESSAGE_UID, MOD_SEQ, emptyFlags, SIZE, Date.from(INSTANT), MESSAGE_ID)));
+
+            private final String emptyFlagsExpungedEventJson =
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": []," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+            @Test
+            void expungedShouldBeWellSerializedWhenEmptyFlags() {
+                assertThatJson(EVENT_SERIALIZER.toJson(emptyFlagsExpungedEvent))
+                    .isEqualTo(emptyFlagsExpungedEventJson);
+            }
+
+            @Test
+            void expungedShouldBeWellDeSerializedWhenEmptyFlags() {
+                assertThat(EVENT_SERIALIZER.fromJson(emptyFlagsExpungedEventJson).get())
+                    .isEqualTo(emptyFlagsExpungedEvent);
+            }
+        }
+
+        @Nested
+        class WithOnlyUserFlags {
+            private final Flags onlyUserFlags = new FlagsBuilder()
+                .add("Custom 1", "Custom 2", "")
+                .build();
+            private final MailboxListener.Expunged onlyUserFlagsExpungedEvent = new MailboxListener.Expunged(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID,
+                ImmutableMap.of(
+                    MESSAGE_UID,
+                    new MessageMetaData(MESSAGE_UID, MOD_SEQ, onlyUserFlags, SIZE, Date.from(INSTANT), MESSAGE_ID)));
+
+            private final String userOnlyFlagsExpungedEventJson =
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"Custom 1\", \"Custom 2\", \"\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+            @Test
+            void expungedShouldBeWellSerializedWhenOnlyUserFlags() {
+                assertThatJson(EVENT_SERIALIZER.toJson(onlyUserFlagsExpungedEvent))
+                    .when(IGNORING_ARRAY_ORDER)
+                    .isEqualTo(userOnlyFlagsExpungedEventJson);
+            }
+
+            @Test
+            void expungedShouldBeWellDeSerializedWhenOnlyUserFlags() {
+                assertThat(EVENT_SERIALIZER.fromJson(userOnlyFlagsExpungedEventJson).get())
+                    .isEqualTo(onlyUserFlagsExpungedEvent);
+            }
+        }
+
+        @Nested
+        class WithOnlySystemFlags {
+            private final Flags onlySystemFlags = new FlagsBuilder()
+                .add(Flags.Flag.SEEN, Flags.Flag.ANSWERED, Flags.Flag.DELETED)
+                .build();
+            private final MailboxListener.Expunged onlySystemFlagsExpungedEvent = new MailboxListener.Expunged(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID,
+                ImmutableMap.of(
+                    MESSAGE_UID,
+                    new MessageMetaData(MESSAGE_UID, MOD_SEQ, onlySystemFlags, SIZE, Date.from(INSTANT), MESSAGE_ID)));
+
+            private final String systemOnlyFlagsExpungedEventJson =
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"\\\\Seen\", \"\\\\Answered\", \"\\\\Deleted\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+            @Test
+            void expungedShouldBeWellSerializedWhenOnlySystemFlags() {
+                assertThatJson(EVENT_SERIALIZER.toJson(onlySystemFlagsExpungedEvent))
+                    .when(IGNORING_ARRAY_ORDER)
+                    .isEqualTo(systemOnlyFlagsExpungedEventJson);
+            }
+
+            @Test
+            void expungedShouldBeWellDeSerializedWhenOnlySystemFlags() {
+                assertThat(EVENT_SERIALIZER.fromJson(systemOnlyFlagsExpungedEventJson).get())
+                    .isEqualTo(onlySystemFlagsExpungedEvent);
+            }
+        }
+
+        @Nested
+        class WithFlagCaseSensitive {
+
+            private static final String CASE_SENSITIVE_SYSTEM_FLAGS_EVENT_JSON =
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\answereD\", \"\\\\dRaFt\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+             @Test
+            void expungedShouldCareAboutSystemFlagsCaseSensitive() {
+                 MailboxListener.Expunged deSerializedEvent = (MailboxListener.Expunged) EVENT_SERIALIZER
+                     .fromJson(CASE_SENSITIVE_SYSTEM_FLAGS_EVENT_JSON)
+                     .get();
+
+                 assertThat(deSerializedEvent.getMetaData(MESSAGE_UID).getFlags().getSystemFlags())
+                     .isEmpty();
+            }
+        }
+    }
+
+    @Nested
+    class WithInternalDate {
+
+        @Test
+        void expungedShouldDeserializeWhenInternalDateIsInGoodISOFormat() {
+            Map<MessageUid, MessageMetaData> Expunged = ImmutableMap.of(
+                MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(Instant.parse("2018-12-14T09:41:51Z")), MESSAGE_ID));
+            MailboxListener.Expunged eventRoundToMillis = new MailboxListener.Expunged(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, Expunged);
+
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51+00:00\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(eventRoundToMillis);
+        }
+
+        @Test
+        void expungedShouldDeserializeWhenInternalDateIsMissingMilliSeconds() {
+            Map<MessageUid, MessageMetaData> Expunged = ImmutableMap.of(
+                MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(Instant.parse("2018-12-14T09:41:51Z")), MESSAGE_ID));
+            MailboxListener.Expunged eventRoundToMillis = new MailboxListener.Expunged(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, Expunged);
+
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(eventRoundToMillis);
+        }
+
+        @Test
+        void expungedShouldDeserializeWhenInternalDateIsMissingSeconds() {
+            Map<MessageUid, MessageMetaData> Expunged = ImmutableMap.of(
+                MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(Instant.parse("2018-12-14T09:41:00Z")), MESSAGE_ID));
+            MailboxListener.Expunged eventRoundToMinute = new MailboxListener.Expunged(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, Expunged);
+
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(eventRoundToMinute);
+        }
+    }
+    @Nested
+    class NullOrEmptyNameSpaceInMailboxPath {
+
+        @Test
+        void expungedShouldBeWellDeSerializedWhenNullNameSpace() {
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(DEFAULT_EXPUNGED_EVENT);
+        }
+
+        @Test
+        void expungedShouldBeWellDeSerializedWhenEmptyNameSpace() {
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(DEFAULT_EXPUNGED_EVENT);
+        }
+    }
+
+    @Nested
+    class NullUserInMailboxPath {
+        private final String nullUser = null;
+        private final MailboxListener.Expunged eventWithNullUserInPath = new MailboxListener.Expunged(
+            SESSION_ID,
+            USER,
+            new MailboxPath(USER_NAMESPACE, nullUser, MAILBOX_NAME),
+            MAILBOX_ID,
+            EXPUNGED);
+
+        private static final String EVENT_JSON_WITH_NULL_USER_IN_PATH =
+            "{" +
+            "  \"Expunged\": {" +
+            "    \"path\": {" +
+            "      \"namespace\": \"#private\"," +
+            "      \"name\": \"mailboxName\"" +
+            "    }," +
+            "    \"mailboxId\": \"18\"," +
+            "    \"expunged\": {" +
+            "      \"123456\": {" +
+            "        \"uid\": 123456," +
+            "        \"modSeq\": 35," +
+            "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+            "        \"size\": 45,  " +
+            "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+            "        \"messageId\": \"42\"" +
+            "      }" +
+            "    }," +
+            "    \"sessionId\": 42," +
+            "    \"user\": \"user\"" +
+            "  }" +
+            "}";
+
+        @Test
+        void expungedShouldBeWellSerialized() {
+            assertThatJson(EVENT_SERIALIZER.toJson(eventWithNullUserInPath))
+                .isEqualTo(EVENT_JSON_WITH_NULL_USER_IN_PATH);
+        }
+
+        @Test
+        void expungedShouldBeWellDeSerialized() {
+            assertThat(EVENT_SERIALIZER.fromJson(EVENT_JSON_WITH_NULL_USER_IN_PATH).get())
+                .isEqualTo(eventWithNullUserInPath);
+        }
+    }
+
+    @Nested
+    class DeserializationErrors {
+
+        @Nested
+        class DeserializationErrorOnSessionId {
+            @Test
+            void expungedShouldThrowWhenMissingSessionId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void expungedShouldThrowWhenNullSessionId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": null," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void expungedShouldThrowWhenStringSessionId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": \"42\"," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+        }
+
+        @Nested
+        class DeserializationErrorOnUser {
+            @Test
+            void expungedShouldThrowWhenMissingUser() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void expungedShouldThrowWhenUserIsNotAString() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": 596" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void expungedShouldThrowWhenUserIsNotWellFormatted() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": \"user@user@anotherUser\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(IllegalArgumentException.class);
+            }
+        }
+
+        @Nested
+        class DeserializationErrorOnMailboxId {
+            @Test
+            void expungedShouldThrowWhenMissingMailboxId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void expungedShouldThrowWhenNullMailboxId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": null," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void expungedShouldThrowWhenMailboxIdIsANumber() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": 18," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+        }
+
+        @Nested
+        class DeserializationErrorOnMailboxPath {
+
+            @Nested
+            class DeserializationErrorOnNameSpace {
+                @Test
+                void expungedShouldThrowWhenNameSpaceIsNotAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 48246," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnUser {
+                @Test
+                void expungedShouldThrowWhenUserIsNotAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": 265412.64," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnMailboxName {
+
+                @Test
+                void expungedShouldThrowWhenNullMailboxName() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenMailboxNameIdIsANumber() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": 11861" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+        }
+
+        @Nested
+        class DeserializationErrorOnExpungedMap {
+            @Test
+            void expungedShouldThrowWhenMapKeyIsNull() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"Expunged\": null," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Nested
+            class DeserializationErrorOnMessageUid {
+
+                @Test
+                void expungedShouldThrowWhenMessageUidIsAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": \"123456\"," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenMessageUidIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": null," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnModSeq {
+
+                @Test
+                void expungedShouldThrowWhenModSeqIsAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": \"35\"," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenModSeqIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": null," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+
+            @Nested
+            class DeserializationErrorOnSize {
+
+                @Test
+                void expungedShouldThrowWhenSizeIsAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": \"45\",  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenSizeIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": null,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnMessageId {
+
+                @Test
+                void expungedShouldThrowWhenMessageIdIsANumber() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": 42" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenMessageIdIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": null" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnInternalDate {
+                @Test
+                void expungedShouldThrowWhenInternalDateIsNotInISOFormatBecauseOfMissingTWord() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14 12:52:36+07:00\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenInternalDateContainsOnlyDate() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenInternalDateIsMissingHourPart() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14TZ\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenInternalDateIsMissingTimeZone() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenInternalDateIsMissingHours() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenInternalDateIsEmpty() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenInternalDateIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": null," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnFlags {
+
+                @Test
+                void expungedShouldThrowWhenFlagsIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": null," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenFlagsContainsNullElements() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"flag 1\", null, \"flags 2\", null]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenFlagsContainsNumberElements() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"flag 1\", 1254, \"flags 2\", 125.36]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+        }
+    }
+}


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


[07/10] james-project git commit: JAMES-2633 FlagsUpdated event constructor simplification

Posted by bt...@apache.org.
JAMES-2633 FlagsUpdated event constructor simplification


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

Branch: refs/heads/master
Commit: 544924a9cb181704bbbc42101f0fb8d2d614fad0
Parents: e5a6765
Author: tran tien duc <dt...@linagora.com>
Authored: Tue Dec 18 15:20:38 2018 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Wed Dec 19 18:24:21 2018 +0700

----------------------------------------------------------------------
 .../apache/james/mailbox/MailboxListener.java   |  7 ++++--
 .../james/event/json/EventSerializer.scala      |  1 -
 .../json/FlagsUpdatedSerializationTest.java     | 15 +++++-------
 .../mailbox/store/StoreMessageIdManager.java    |  2 +-
 .../mailbox/store/StoreMessageManager.java      |  2 +-
 .../james/mailbox/store/event/EventFactory.java |  8 ++-----
 .../store/event/MailboxEventDispatcher.java     | 10 ++++----
 .../AbstractMessageIdManagerSideEffectTest.java |  4 ++--
 .../store/MailboxEventDispatcherTest.java       | 24 +-------------------
 .../base/MailboxEventAnalyserTest.java          |  6 -----
 10 files changed, 23 insertions(+), 56 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/544924a9/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
index edce191..3e812c2 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
@@ -37,6 +37,7 @@ import org.apache.james.mailbox.model.Quota;
 import org.apache.james.mailbox.model.QuotaRoot;
 import org.apache.james.mailbox.model.UpdatedFlags;
 
+import com.github.steveash.guavate.Guavate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
@@ -430,10 +431,12 @@ public interface MailboxListener {
         private final List<MessageUid> uids;
         private final List<UpdatedFlags> updatedFlags;
 
-        public FlagsUpdated(MailboxSession.SessionId sessionId, User user, MailboxPath path, MailboxId mailboxId, List<MessageUid> uids, List<UpdatedFlags> updatedFlags) {
+        public FlagsUpdated(MailboxSession.SessionId sessionId, User user, MailboxPath path, MailboxId mailboxId, List<UpdatedFlags> updatedFlags) {
             super(sessionId, user, path, mailboxId);
-            this.uids = ImmutableList.copyOf(uids);
             this.updatedFlags = ImmutableList.copyOf(updatedFlags);
+            this.uids = updatedFlags.stream()
+                .map(UpdatedFlags::getUid)
+                .collect(Guavate.toImmutableList());
         }
 
         @Override

http://git-wip-us.apache.org/repos/asf/james-project/blob/544924a9/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
index c6732e0..b434164 100644
--- a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
+++ b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
@@ -103,7 +103,6 @@ private object DTO {
       user,
       path.toJava,
       mailboxId,
-      updatedFlags.map(_.uid).asJava,
       updatedFlags.map(_.toJava).asJava)
   }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/544924a9/mailbox/event/json/src/test/java/org/apache/james/event/json/FlagsUpdatedSerializationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/test/java/org/apache/james/event/json/FlagsUpdatedSerializationTest.java b/mailbox/event/json/src/test/java/org/apache/james/event/json/FlagsUpdatedSerializationTest.java
index 5b478ad..63316e1 100644
--- a/mailbox/event/json/src/test/java/org/apache/james/event/json/FlagsUpdatedSerializationTest.java
+++ b/mailbox/event/json/src/test/java/org/apache/james/event/json/FlagsUpdatedSerializationTest.java
@@ -55,8 +55,6 @@ class FlagsUpdatedSerializationTest {
     private static final MailboxPath MAILBOX_PATH = new MailboxPath(MailboxConstants.USER_NAMESPACE, "user", MAILBOX_NAME);
     private static final MessageUid MESSAGE_UID_1 = MessageUid.of(123456);
     private static final MessageUid MESSAGE_UID_2 = MessageUid.of(654321);
-    private static final List<MessageUid> MESSAGE_UID_1_IN_LIST = ImmutableList.of(MESSAGE_UID_1);
-    private static final List<MessageUid> MESSAGE_UID_LIST = ImmutableList.of(MESSAGE_UID_1, MESSAGE_UID_2);
 
     private static final int MOD_SEQ_1 = 35;
     private static final Flags OLD_FLAGS_1 = FlagsBuilder.builder()
@@ -93,7 +91,7 @@ class FlagsUpdatedSerializationTest {
     private static List<UpdatedFlags> UPDATED_FLAGS_LIST = ImmutableList.of(UPDATED_FLAG_1, UPDATED_FLAG_2);
 
     private static final MailboxListener.FlagsUpdated DEFAULT_EVENT = new MailboxListener.FlagsUpdated(SESSION_ID, USER,
-        MAILBOX_PATH, MAILBOX_ID, MESSAGE_UID_LIST, UPDATED_FLAGS_LIST);
+        MAILBOX_PATH, MAILBOX_ID, UPDATED_FLAGS_LIST);
     private static final String DEFAULT_EVENT_JSON =
         "{" +
         "  \"FlagsUpdated\": {" +
@@ -210,7 +208,7 @@ class FlagsUpdatedSerializationTest {
         private final String nullUser = null;
         private final MailboxListener.FlagsUpdated nullUserEvent = new MailboxListener.FlagsUpdated(SESSION_ID, USER,
             new MailboxPath(MailboxConstants.USER_NAMESPACE, nullUser, MAILBOX_NAME),
-            MAILBOX_ID, MESSAGE_UID_LIST, UPDATED_FLAGS_LIST);
+            MAILBOX_ID, UPDATED_FLAGS_LIST);
 
         private static final String EVENT_JSON_WITH_NULL_USER_IN_PATH =
             "{" +
@@ -258,10 +256,9 @@ class FlagsUpdatedSerializationTest {
 
         @Nested
         class EmptyUpdatedFlags {
-            private final List<MessageUid> emptyUids = ImmutableList.of();
             private final List<UpdatedFlags> emptyUpdatedFlags = ImmutableList.of();
             private final MailboxListener.FlagsUpdated emptyUpdatedFlagsEvent = new MailboxListener.FlagsUpdated(SESSION_ID, USER, MAILBOX_PATH,
-                MAILBOX_ID, emptyUids, emptyUpdatedFlags);
+                MAILBOX_ID, emptyUpdatedFlags);
 
             private static final String EVENT_JSON_WITH_EMPTY_UPDATED_FLAGS =
                 "{" +
@@ -301,7 +298,7 @@ class FlagsUpdatedSerializationTest {
                 .newFlags(NEW_FLAGS_1)
                 .build();
             private final MailboxListener.FlagsUpdated emptyOldFlagsUpdatedFlagsEvent = new MailboxListener.FlagsUpdated(SESSION_ID, USER, MAILBOX_PATH,
-                MAILBOX_ID, MESSAGE_UID_1_IN_LIST, ImmutableList.of(emptyOldFlags));
+                MAILBOX_ID, ImmutableList.of(emptyOldFlags));
 
             private static final String EVENT_JSON_WITH_EMPTY_OLD_FLAGS =
                 "{" +
@@ -348,7 +345,7 @@ class FlagsUpdatedSerializationTest {
                 .newFlags(FlagsBuilder.builder().build())
                 .build();
             private final MailboxListener.FlagsUpdated emptyNewFlagsUpdatedFlagsEvent = new MailboxListener.FlagsUpdated(SESSION_ID, USER, MAILBOX_PATH,
-                MAILBOX_ID, MESSAGE_UID_1_IN_LIST, ImmutableList.of(emptyNewFlags));
+                MAILBOX_ID, ImmutableList.of(emptyNewFlags));
 
             private static final String EVENT_JSON_WITH_EMPTY_NEW_FLAGS =
                 "{" +
@@ -395,7 +392,7 @@ class FlagsUpdatedSerializationTest {
                 .newFlags(FlagsBuilder.builder().build())
                 .build();
             private final MailboxListener.FlagsUpdated emptyFlagsUpdatedFlagsEvent = new MailboxListener.FlagsUpdated(SESSION_ID, USER, MAILBOX_PATH,
-                MAILBOX_ID, MESSAGE_UID_1_IN_LIST, ImmutableList.of(emptyFlags));
+                MAILBOX_ID, ImmutableList.of(emptyFlags));
 
             private static final String EVENT_JSON_WITH_EMPTY_OLD_AND_NEW_FLAGS =
                 "{" +

http://git-wip-us.apache.org/repos/asf/james-project/blob/544924a9/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java
index 907d675..ff93e53 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java
@@ -301,7 +301,7 @@ public class StoreMessageIdManager implements MessageIdManager {
     private void dispatchFlagsChange(MailboxSession mailboxSession, MailboxId mailboxId, UpdatedFlags updatedFlags) throws MailboxException {
         if (updatedFlags.flagsChanged()) {
             Mailbox mailbox = mailboxSessionMapperFactory.getMailboxMapper(mailboxSession).findMailboxById(mailboxId);
-            dispatcher.flagsUpdated(mailboxSession, updatedFlags.getUid(), mailbox, updatedFlags);
+            dispatcher.flagsUpdated(mailboxSession, mailbox, updatedFlags);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/544924a9/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java
index 76595af..217940d 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java
@@ -582,7 +582,7 @@ public class StoreMessageManager implements org.apache.james.mailbox.MessageMana
             uFlags.put(flag.getUid(), flag);
         }
 
-        dispatcher.flagsUpdated(mailboxSession, new ArrayList<>(uFlags.keySet()), getMailboxEntity(), new ArrayList<>(uFlags.values()));
+        dispatcher.flagsUpdated(mailboxSession, getMailboxEntity(), new ArrayList<>(uFlags.values()));
 
         return newFlagsByUid;
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/544924a9/mailbox/store/src/main/java/org/apache/james/mailbox/store/event/EventFactory.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/event/EventFactory.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/event/EventFactory.java
index 433bf8f..4dfd76c 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/event/EventFactory.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/event/EventFactory.java
@@ -59,12 +59,8 @@ public class EventFactory {
         return new MailboxListener.Expunged(sessionId, user, mailbox.generateAssociatedPath(), mailbox.getMailboxId(), uids);
     }
 
-    public MailboxListener.FlagsUpdated flagsUpdated(MailboxSession session, List<MessageUid> uids, Mailbox mailbox, List<UpdatedFlags> uflags) {
-        return flagsUpdated(session.getSessionId(), session.getUser(), uids, mailbox, uflags);
-    }
-
-    public MailboxListener.FlagsUpdated flagsUpdated(MailboxSession.SessionId sessionId, User user, List<MessageUid> uids, Mailbox mailbox, List<UpdatedFlags> uflags) {
-        return new MailboxListener.FlagsUpdated(sessionId, user, mailbox.generateAssociatedPath(), mailbox.getMailboxId(), uids, uflags);
+    public MailboxListener.FlagsUpdated flagsUpdated(MailboxSession session, Mailbox mailbox, List<UpdatedFlags> uflags) {
+        return new MailboxListener.FlagsUpdated(session.getSessionId(), session.getUser(), mailbox.generateAssociatedPath(), mailbox.getMailboxId(), uflags);
     }
 
     public MailboxListener.MailboxRenamed mailboxRenamed(MailboxSession session, MailboxPath from, Mailbox to) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/544924a9/mailbox/store/src/main/java/org/apache/james/mailbox/store/event/MailboxEventDispatcher.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/event/MailboxEventDispatcher.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/event/MailboxEventDispatcher.java
index 139c807..812472c 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/event/MailboxEventDispatcher.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/event/MailboxEventDispatcher.java
@@ -127,14 +127,14 @@ public class MailboxEventDispatcher {
      * Should get called when the message flags were update in a Mailbox. All
      * registered MailboxListener will get triggered then
      */
-    public void flagsUpdated(MailboxSession session, List<MessageUid> uids, Mailbox mailbox, List<UpdatedFlags> uflags) {
-        if (!uids.isEmpty()) {
-            listener.event(eventFactory.flagsUpdated(session, uids, mailbox, uflags));
+    public void flagsUpdated(MailboxSession session, Mailbox mailbox, List<UpdatedFlags> uflags) {
+        if (!uflags.isEmpty()) {
+            listener.event(eventFactory.flagsUpdated(session, mailbox, uflags));
         }
     }
 
-    public void flagsUpdated(MailboxSession session, MessageUid uid, Mailbox mailbox, UpdatedFlags uflags) {
-        flagsUpdated(session, ImmutableList.of(uid), mailbox, ImmutableList.of(uflags));
+    public void flagsUpdated(MailboxSession session, Mailbox mailbox, UpdatedFlags uflags) {
+        flagsUpdated(session, mailbox, ImmutableList.of(uflags));
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/james-project/blob/544924a9/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java
index 0a02502..e98bd59 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java
@@ -275,7 +275,7 @@ public abstract class AbstractMessageIdManagerSideEffectTest {
 
         messageIdManager.setFlags(newFlags, MessageManager.FlagsUpdateMode.ADD, messageId, ImmutableList.of(mailbox1.getMailboxId(), mailbox2.getMailboxId()), session);
 
-        verify(dispatcher, times(2)).flagsUpdated(eq(session), any(MessageUid.class), any(Mailbox.class), any(UpdatedFlags.class));
+        verify(dispatcher, times(2)).flagsUpdated(eq(session), any(Mailbox.class), any(UpdatedFlags.class));
         verifyNoMoreInteractions(dispatcher);
     }
 
@@ -300,7 +300,7 @@ public abstract class AbstractMessageIdManagerSideEffectTest {
             .newFlags(newFlags)
             .build();
 
-        verify(dispatcher, times(1)).flagsUpdated(session, messageUid, mailbox2, updatedFlags);
+        verify(dispatcher, times(1)).flagsUpdated(session, mailbox2, updatedFlags);
         verifyNoMoreInteractions(dispatcher);
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/544924a9/mailbox/store/src/test/java/org/apache/james/mailbox/store/MailboxEventDispatcherTest.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/MailboxEventDispatcherTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/MailboxEventDispatcherTest.java
index d681c9a..8b1e92b 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/MailboxEventDispatcherTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/MailboxEventDispatcherTest.java
@@ -24,7 +24,6 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import javax.mail.Flags;
-import javax.mail.Flags.Flag;
 
 import org.apache.james.mailbox.Event;
 import org.apache.james.mailbox.FlagsBuilder;
@@ -81,7 +80,6 @@ public class MailboxEventDispatcherTest {
     @Test
     public void testShouldReturnNoChangesWhenSystemFlagsUnchanged() {
         dispatcher.flagsUpdated(session,
-            ImmutableList.of(result.getUid()),
             mailbox,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(result.getUid())
@@ -100,7 +98,6 @@ public class MailboxEventDispatcherTest {
     @Test
     public void testShouldShowAnsweredAdded() {
         dispatcher.flagsUpdated(session,
-            ImmutableList.of(result.getUid()),
             mailbox,
             ImmutableList.of(
                 UpdatedFlags.builder()
@@ -121,7 +118,6 @@ public class MailboxEventDispatcherTest {
     @Test
     public void testShouldShowAnsweredRemoved() {
         dispatcher.flagsUpdated(session,
-            ImmutableList.of(result.getUid()),
             mailbox,
             ImmutableList.of(
                 UpdatedFlags.builder()
@@ -142,7 +138,6 @@ public class MailboxEventDispatcherTest {
     @Test
     public void testShouldShowDeletedAdded() {
         dispatcher.flagsUpdated(session,
-            ImmutableList.of(result.getUid()),
             mailbox,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(result.getUid())
@@ -162,7 +157,6 @@ public class MailboxEventDispatcherTest {
     @Test
     public void testShouldShowDeletedRemoved() {
         dispatcher.flagsUpdated(session,
-            ImmutableList.of(result.getUid()),
             mailbox,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(result.getUid())
@@ -182,7 +176,6 @@ public class MailboxEventDispatcherTest {
     @Test
     public void testShouldShowDraftAdded() {
         dispatcher.flagsUpdated(session,
-            ImmutableList.of(result.getUid()),
             mailbox,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(result.getUid())
@@ -202,7 +195,6 @@ public class MailboxEventDispatcherTest {
     @Test
     public void testShouldShowDraftRemoved() {
         dispatcher.flagsUpdated(session,
-            ImmutableList.of(result.getUid()),
             mailbox,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(result.getUid())
@@ -222,7 +214,6 @@ public class MailboxEventDispatcherTest {
     @Test
     public void testShouldShowFlaggedAdded() {
         dispatcher.flagsUpdated(session,
-            ImmutableList.of(result.getUid()),
             mailbox,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(result.getUid())
@@ -242,7 +233,6 @@ public class MailboxEventDispatcherTest {
     @Test
     public void testShouldShowFlaggedRemoved() {
         dispatcher.flagsUpdated(session,
-            ImmutableList.of(result.getUid()),
             mailbox,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(result.getUid())
@@ -262,7 +252,6 @@ public class MailboxEventDispatcherTest {
     @Test
     public void testShouldShowRecentAdded() {
         dispatcher.flagsUpdated(session,
-            ImmutableList.of(result.getUid()),
             mailbox,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(result.getUid())
@@ -282,7 +271,6 @@ public class MailboxEventDispatcherTest {
     @Test
     public void testShouldShowRecentRemoved() {
         dispatcher.flagsUpdated(session,
-            ImmutableList.of(result.getUid()),
             mailbox,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(result.getUid())
@@ -302,7 +290,6 @@ public class MailboxEventDispatcherTest {
     @Test
     public void testShouldShowSeenAdded() {
         dispatcher.flagsUpdated(session,
-            ImmutableList.of(result.getUid()),
             mailbox,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(result.getUid())
@@ -322,7 +309,6 @@ public class MailboxEventDispatcherTest {
     @Test
     public void testShouldShowSeenRemoved() {
         dispatcher.flagsUpdated(session,
-            ImmutableList.of(result.getUid()),
             mailbox,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(result.getUid())
@@ -342,7 +328,6 @@ public class MailboxEventDispatcherTest {
     @Test
     public void testShouldShowMixedChanges() {
         dispatcher.flagsUpdated(session,
-            ImmutableList.of(result.getUid()),
             mailbox,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(result.getUid())
@@ -371,14 +356,7 @@ public class MailboxEventDispatcherTest {
 
     @Test
     public void flagsUpdatedShouldNotFireEventWhenEmptyIdList() {
-        UpdatedFlags updatedFlags = UpdatedFlags.builder()
-                .uid(MessageUid.of(1))
-                .modSeq(2)
-                .oldFlags(new Flags(Flag.RECENT))
-                .newFlags(new Flags(Flag.ANSWERED))
-                .build();
-        
-        dispatcher.flagsUpdated(session, ImmutableList.of(), mailbox, ImmutableList.of(updatedFlags));
+        dispatcher.flagsUpdated(session, mailbox, ImmutableList.of());
         assertThat(collector.getEvents()).isEmpty();
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/544924a9/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java
----------------------------------------------------------------------
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java
index cc12128..14bab7a 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java
@@ -171,7 +171,6 @@ public class MailboxEventAnalyserTest {
     public void testShouldNotSetUidWhenNoSystemFlagChange() {
         MailboxListener.FlagsUpdated update = eventFactory.flagsUpdated(
             MAILBOX_SESSION,
-            ImmutableList.of(MessageUid.of(90L)),
             DEFAULT_MAILBOX,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(MessageUid.of(90))
@@ -190,7 +189,6 @@ public class MailboxEventAnalyserTest {
         
         MailboxListener.FlagsUpdated update = eventFactory.flagsUpdated(
             OTHER_MAILBOX_SESSION,
-            ImmutableList.of(uid),
             DEFAULT_MAILBOX,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(uid)
@@ -210,7 +208,6 @@ public class MailboxEventAnalyserTest {
         
         MailboxListener.FlagsUpdated update = eventFactory.flagsUpdated(
             MAILBOX_SESSION,
-            ImmutableList.of(uid),
             DEFAULT_MAILBOX,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(uid)
@@ -231,7 +228,6 @@ public class MailboxEventAnalyserTest {
 
         MailboxListener.FlagsUpdated update = eventFactory.flagsUpdated(
             OTHER_MAILBOX_SESSION,
-            ImmutableList.of(uid),
             DEFAULT_MAILBOX,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(uid)
@@ -250,7 +246,6 @@ public class MailboxEventAnalyserTest {
     public void testShouldNotSetUidWhenSystemFlagChangeSameSessionInSilentMode() {
         MailboxListener.FlagsUpdated update = eventFactory.flagsUpdated(
             MAILBOX_SESSION,
-            ImmutableList.of(MessageUid.of(345)),
             DEFAULT_MAILBOX,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(MessageUid.of(345))
@@ -269,7 +264,6 @@ public class MailboxEventAnalyserTest {
     public void testShouldNotSetUidWhenOnlyRecentFlagUpdated() {
         MailboxListener.FlagsUpdated update = eventFactory.flagsUpdated(
             MAILBOX_SESSION,
-            ImmutableList.of(MessageUid.of(886)),
             DEFAULT_MAILBOX,
             ImmutableList.of(UpdatedFlags.builder()
                 .uid(MessageUid.of(886))


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


[06/10] james-project git commit: MAILBOX-359 FlagsUpdated Scala Event Serialization

Posted by bt...@apache.org.
MAILBOX-359 FlagsUpdated Scala Event Serialization


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

Branch: refs/heads/master
Commit: a318d271b4b1b73021813df59021d355826e1e67
Parents: e151594
Author: tran tien duc <dt...@linagora.com>
Authored: Mon Dec 17 14:14:46 2018 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Wed Dec 19 18:24:21 2018 +0700

----------------------------------------------------------------------
 .../apache/james/mailbox/MailboxListener.java   |   20 +
 .../james/mailbox/MailboxListenerTest.java      |    5 +
 .../org/apache/james/event/json/DTOs.scala      |   20 +-
 .../james/event/json/EventSerializer.scala      |   26 +-
 .../json/FlagsUpdatedSerializationTest.java     | 1260 ++++++++++++++++++
 5 files changed, 1326 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/a318d271/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
index 83797ca..edce191 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
@@ -444,6 +444,26 @@ public interface MailboxListener {
         public List<UpdatedFlags> getUpdatedFlags() {
             return updatedFlags;
         }
+
+        @Override
+        public final boolean equals(Object o) {
+            if (o instanceof FlagsUpdated) {
+                FlagsUpdated that = (FlagsUpdated) o;
+
+                return Objects.equals(this.sessionId, that.sessionId)
+                    && Objects.equals(this.user, that.user)
+                    && Objects.equals(this.path, that.path)
+                    && Objects.equals(this.mailboxId, that.mailboxId)
+                    && Objects.equals(this.uids, that.uids)
+                    && Objects.equals(this.updatedFlags, that.updatedFlags);
+            }
+            return false;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(sessionId, user, path, mailboxId, uids, updatedFlags);
+        }
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/james-project/blob/a318d271/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
index 1316753..24db57b 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
@@ -53,4 +53,9 @@ class MailboxListenerTest {
     void expungedShouldMatchBeanContract() {
         EqualsVerifier.forClass(MailboxListener.Expunged.class).verify();
     }
+
+    @Test
+    void flagUpdatedShouldMatchBeanContract() {
+        EqualsVerifier.forClass(MailboxListener.FlagsUpdated.class).verify();
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/a318d271/mailbox/event/json/src/main/scala/org/apache/james/event/json/DTOs.scala
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/main/scala/org/apache/james/event/json/DTOs.scala b/mailbox/event/json/src/main/scala/org/apache/james/event/json/DTOs.scala
index ee4529e..c708f92 100644
--- a/mailbox/event/json/src/main/scala/org/apache/james/event/json/DTOs.scala
+++ b/mailbox/event/json/src/main/scala/org/apache/james/event/json/DTOs.scala
@@ -25,8 +25,7 @@ import java.util.Date
 import javax.mail.{Flags => JavaMailFlags}
 import org.apache.james.core.quota.QuotaValue
 import org.apache.james.mailbox.acl.{ACLDiff => JavaACLDiff}
-import org.apache.james.mailbox.model.{MailboxACL, MessageId, MailboxPath => JavaMailboxPath, MessageMetaData => JavaMessageMetaData,
-  Quota => JavaQuota}
+import org.apache.james.mailbox.model.{MailboxACL, MessageId, MailboxPath => JavaMailboxPath, MessageMetaData => JavaMessageMetaData, Quota => JavaQuota, UpdatedFlags => JavaUpdatedFlags}
 import org.apache.james.mailbox.{FlagsBuilder, MessageUid}
 
 import scala.collection.JavaConverters._
@@ -129,4 +128,21 @@ object DTOs {
       case JavaMailFlags.Flag.SEEN => SEEN
     }
   }
+
+  object UpdatedFlags {
+    def toUpdatedFlags(javaUpdatedFlags: JavaUpdatedFlags): UpdatedFlags = UpdatedFlags(
+      javaUpdatedFlags.getUid,
+      javaUpdatedFlags.getModSeq,
+      Flags.fromJavaFlags(javaUpdatedFlags.getOldFlags).toList,
+      Flags.fromJavaFlags(javaUpdatedFlags.getNewFlags).toList)
+  }
+
+  case class UpdatedFlags(uid: MessageUid, modSeq: Long, oldFlags: List[String], newFlags: List[String]) {
+    def toJava: JavaUpdatedFlags = JavaUpdatedFlags.builder()
+      .uid(uid)
+      .modSeq(modSeq)
+      .oldFlags(Flags.toJavaFlags(oldFlags.toArray))
+      .newFlags(Flags.toJavaFlags(newFlags.toArray))
+      .build()
+  }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/a318d271/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
index 3a64653..7875fe1 100644
--- a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
+++ b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
@@ -27,7 +27,7 @@ import julienrf.json.derived
 import org.apache.james.core.quota.{QuotaCount, QuotaSize, QuotaValue}
 import org.apache.james.core.{Domain, User}
 import org.apache.james.event.json.DTOs.{ACLDiff, Flags, MailboxPath, Quota}
-import org.apache.james.mailbox.MailboxListener.{Added => JavaAdded, Expunged => JavaExpunged, MailboxACLUpdated => JavaMailboxACLUpdated, MailboxAdded => JavaMailboxAdded, MailboxDeletion => JavaMailboxDeletion, MailboxRenamed => JavaMailboxRenamed, QuotaUsageUpdatedEvent => JavaQuotaUsageUpdatedEvent}
+import org.apache.james.mailbox.MailboxListener.{Added => JavaAdded, Expunged => JavaExpunged, FlagsUpdated => JavaFlagsUpdated, MailboxACLUpdated => JavaMailboxACLUpdated, MailboxAdded => JavaMailboxAdded, MailboxDeletion => JavaMailboxDeletion, MailboxRenamed => JavaMailboxRenamed, QuotaUsageUpdatedEvent => JavaQuotaUsageUpdatedEvent}
 import org.apache.james.mailbox.MailboxSession.SessionId
 import org.apache.james.mailbox.model.{MailboxId, MessageId, MessageMoves, QuotaRoot, MailboxACL => JavaMailboxACL, MessageMetaData => JavaMessageMetaData, Quota => JavaQuota}
 import org.apache.james.mailbox.{MessageUid, Event => JavaEvent, MessageMoveEvent => JavaMessageMoveEvent}
@@ -90,11 +90,22 @@ private object DTO {
       .user(user)
       .messageId(messageIds.asJava)
       .messageMoves(MessageMoves.builder()
-          .previousMailboxIds(previousMailboxIds.asJava)
-          .targetMailboxIds(targetMailboxIds.asJava)
+        .previousMailboxIds(previousMailboxIds.asJava)
+        .targetMailboxIds(targetMailboxIds.asJava)
         .build())
       .build()
   }
+
+  case class FlagsUpdated(sessionId: SessionId, user: User, path: MailboxPath, mailboxId: MailboxId,
+                          updatedFlags: List[DTOs.UpdatedFlags]) extends Event {
+    override def toJava: JavaEvent = new JavaFlagsUpdated(
+      sessionId,
+      user,
+      path.toJava,
+      mailboxId,
+      updatedFlags.map(_.uid).asJava,
+      updatedFlags.map(_.toJava).asJava)
+  }
 }
 
 private object ScalaConverter {
@@ -161,10 +172,17 @@ private object ScalaConverter {
     targetMailboxIds = event.getMessageMoves.getTargetMailboxIds.asScala,
     messageIds = event.getMessageIds.asScala)
 
+  private def toScala(event: JavaFlagsUpdated): DTO.FlagsUpdated = DTO.FlagsUpdated(
+    sessionId = event.getSessionId,
+    user = event.getUser,
+    path = DTOs.MailboxPath.fromJava(event.getMailboxPath),
+    mailboxId = event.getMailboxId,
+    updatedFlags = event.getUpdatedFlags.asScala.map(DTOs.UpdatedFlags.toUpdatedFlags).toList)
 
   def toScala(javaEvent: JavaEvent): Event = javaEvent match {
     case e: JavaAdded => toScala(e)
     case e: JavaExpunged => toScala(e)
+    case e: JavaFlagsUpdated => toScala(e)
     case e: JavaMailboxACLUpdated => toScala(e)
     case e: JavaMailboxAdded => toScala(e)
     case e: JavaMailboxDeletion => toScala(e)
@@ -192,6 +210,7 @@ private class JsonSerialize(mailboxIdFactory: MailboxId.Factory, messageIdFactor
   implicit val messageUidWrites: Writes[MessageUid] = value => JsNumber(value.asLong())
   implicit val flagsWrites: Writes[JavaMailFlags] = value => JsArray(Flags.fromJavaFlags(value).map(flag => JsString(flag)))
   implicit val messageMetaDataWrites: Writes[DTOs.MessageMetaData] = Json.writes[DTOs.MessageMetaData]
+  implicit val updatedFlagsWrites: Writes[DTOs.UpdatedFlags] = Json.writes[DTOs.UpdatedFlags]
 
   implicit val aclEntryKeyReads: Reads[JavaMailboxACL.EntryKey] = {
     case JsString(keyAsString) => JsSuccess(JavaMailboxACL.EntryKey.deserialize(keyAsString))
@@ -280,6 +299,7 @@ private class JsonSerialize(mailboxIdFactory: MailboxId.Factory, messageIdFactor
   implicit val quotaSReads: Reads[DTOs.Quota[QuotaSize]] = Json.reads[DTOs.Quota[QuotaSize]]
   implicit val mailboxPathReads: Reads[DTOs.MailboxPath] = Json.reads[DTOs.MailboxPath]
   implicit val messageMetaDataReads: Reads[DTOs.MessageMetaData] = Json.reads[DTOs.MessageMetaData]
+  implicit val updatedFlagsReads: Reads[DTOs.UpdatedFlags] = Json.reads[DTOs.UpdatedFlags]
 
   implicit val eventOFormat: OFormat[Event] = derived.oformat()
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/a318d271/mailbox/event/json/src/test/java/org/apache/james/event/json/FlagsUpdatedSerializationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/test/java/org/apache/james/event/json/FlagsUpdatedSerializationTest.java b/mailbox/event/json/src/test/java/org/apache/james/event/json/FlagsUpdatedSerializationTest.java
new file mode 100644
index 0000000..5b478ad
--- /dev/null
+++ b/mailbox/event/json/src/test/java/org/apache/james/event/json/FlagsUpdatedSerializationTest.java
@@ -0,0 +1,1260 @@
+/****************************************************************
+ * 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.event.json;
+
+import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import javax.mail.Flags;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.FlagsBuilder;
+import org.apache.james.mailbox.MailboxListener;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.model.MailboxConstants;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.TestMessageId;
+import org.apache.james.mailbox.model.UpdatedFlags;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.ImmutableList;
+
+import net.javacrumbs.jsonunit.core.Option;
+
+class FlagsUpdatedSerializationTest {
+
+    private static final User USER = User.fromUsername("user");
+    private static final MailboxSession.SessionId SESSION_ID = MailboxSession.SessionId.of(42);
+    private static final MailboxId MAILBOX_ID = TestId.of(18);
+    private static final String MAILBOX_NAME = "mailboxName";
+    private static final MailboxPath MAILBOX_PATH = new MailboxPath(MailboxConstants.USER_NAMESPACE, "user", MAILBOX_NAME);
+    private static final MessageUid MESSAGE_UID_1 = MessageUid.of(123456);
+    private static final MessageUid MESSAGE_UID_2 = MessageUid.of(654321);
+    private static final List<MessageUid> MESSAGE_UID_1_IN_LIST = ImmutableList.of(MESSAGE_UID_1);
+    private static final List<MessageUid> MESSAGE_UID_LIST = ImmutableList.of(MESSAGE_UID_1, MESSAGE_UID_2);
+
+    private static final int MOD_SEQ_1 = 35;
+    private static final Flags OLD_FLAGS_1 = FlagsBuilder.builder()
+        .add(Flags.Flag.SEEN, Flags.Flag.DELETED)
+        .add("Old Flag 1")
+        .build();
+    private static final Flags NEW_FLAGS_1 = FlagsBuilder.builder()
+        .add(Flags.Flag.ANSWERED, Flags.Flag.DRAFT)
+        .add("New Flag 1")
+        .build();
+    private static UpdatedFlags UPDATED_FLAG_1 = UpdatedFlags.builder()
+        .uid(MESSAGE_UID_1)
+        .modSeq(MOD_SEQ_1)
+        .oldFlags(OLD_FLAGS_1)
+        .newFlags(NEW_FLAGS_1)
+        .build();
+
+    private static final int MOD_SEQ_2 = 36;
+    private static final Flags OLD_FLAGS_2 = FlagsBuilder.builder()
+        .add(Flags.Flag.RECENT, Flags.Flag.FLAGGED)
+        .add("Old Flag 2")
+        .build();
+    private static final Flags NEW_FLAGS_2 = FlagsBuilder.builder()
+        .add(Flags.Flag.SEEN, Flags.Flag.ANSWERED)
+        .add("New Flag 2")
+        .build();
+    private static UpdatedFlags UPDATED_FLAG_2 = UpdatedFlags.builder()
+        .uid(MESSAGE_UID_2)
+        .modSeq(MOD_SEQ_2)
+        .oldFlags(OLD_FLAGS_2)
+        .newFlags(NEW_FLAGS_2)
+        .build();
+
+    private static List<UpdatedFlags> UPDATED_FLAGS_LIST = ImmutableList.of(UPDATED_FLAG_1, UPDATED_FLAG_2);
+
+    private static final MailboxListener.FlagsUpdated DEFAULT_EVENT = new MailboxListener.FlagsUpdated(SESSION_ID, USER,
+        MAILBOX_PATH, MAILBOX_ID, MESSAGE_UID_LIST, UPDATED_FLAGS_LIST);
+    private static final String DEFAULT_EVENT_JSON =
+        "{" +
+        "  \"FlagsUpdated\": {" +
+        "    \"path\": {" +
+        "      \"namespace\": \"#private\"," +
+        "      \"user\": \"user\"," +
+        "      \"name\": \"mailboxName\"" +
+        "    }," +
+        "    \"mailboxId\": \"18\"," +
+        "    \"sessionId\": 42," +
+        "    \"updatedFlags\": [" +
+        "      {" +
+        "        \"uid\": 123456," +
+        "        \"modSeq\": 35," +
+        "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+        "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+        "      }," +
+        "      {" +
+        "        \"uid\": 654321," +
+        "        \"modSeq\": 36," +
+        "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+        "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+        "      }" +
+        "    ]," +
+        "    \"user\": \"user\"" +
+        "  }" +
+        "}";
+
+    private static final EventSerializer EVENT_SERIALIZER = new EventSerializer(new TestId.Factory(), new TestMessageId.Factory());
+
+    @Test
+    void flagsUpdatedShouldBeWellSerialized() {
+        assertThatJson(EVENT_SERIALIZER.toJson(DEFAULT_EVENT))
+            .when(Option.IGNORING_ARRAY_ORDER)
+            .isEqualTo(DEFAULT_EVENT_JSON);
+    }
+
+    @Test
+    void flagsUpdatedShouldBeWellDeSerialized() {
+        assertThat(EVENT_SERIALIZER.fromJson(DEFAULT_EVENT_JSON).get())
+            .isEqualTo(DEFAULT_EVENT);
+    }
+
+    @Nested
+    class NullOrEmptyNameSpaceInMailboxPath {
+
+        @Test
+        void flagsUpdatedShouldBeWellDeSerializedWhenNullNameSpace() {
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"FlagsUpdated\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": null," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"sessionId\": 42," +
+                "    \"updatedFlags\": [" +
+                "      {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                "      }," +
+                "      {" +
+                "        \"uid\": 654321," +
+                "        \"modSeq\": 36," +
+                "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+                "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+                "      }" +
+                "    ]," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(DEFAULT_EVENT);
+        }
+
+        @Test
+        void flagsUpdatedShouldBeWellDeSerializedWhenEmptyNameSpace() {
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"FlagsUpdated\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"sessionId\": 42," +
+                "    \"updatedFlags\": [" +
+                "      {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                "      }," +
+                "      {" +
+                "        \"uid\": 654321," +
+                "        \"modSeq\": 36," +
+                "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+                "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+                "      }" +
+                "    ]," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(DEFAULT_EVENT);
+        }
+    }
+
+    @Nested
+    class NullUserInMailboxPath {
+        private final String nullUser = null;
+        private final MailboxListener.FlagsUpdated nullUserEvent = new MailboxListener.FlagsUpdated(SESSION_ID, USER,
+            new MailboxPath(MailboxConstants.USER_NAMESPACE, nullUser, MAILBOX_NAME),
+            MAILBOX_ID, MESSAGE_UID_LIST, UPDATED_FLAGS_LIST);
+
+        private static final String EVENT_JSON_WITH_NULL_USER_IN_PATH =
+            "{" +
+            "  \"FlagsUpdated\": {" +
+            "    \"path\": {" +
+            "      \"namespace\": \"#private\"," +
+            "      \"name\": \"mailboxName\"" +
+            "    }," +
+            "    \"mailboxId\": \"18\"," +
+            "    \"sessionId\": 42," +
+            "    \"updatedFlags\": [" +
+            "      {" +
+            "        \"uid\": 123456," +
+            "        \"modSeq\": 35," +
+            "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+            "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+            "      }," +
+            "      {" +
+            "        \"uid\": 654321," +
+            "        \"modSeq\": 36," +
+            "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+            "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+            "      }" +
+            "    ]," +
+            "    \"user\": \"user\"" +
+            "  }" +
+            "}";
+
+        @Test
+        void flagsUpdatedShouldBeWellSerialized() {
+            assertThatJson(EVENT_SERIALIZER.toJson(nullUserEvent))
+                .when(Option.IGNORING_ARRAY_ORDER)
+                .isEqualTo(EVENT_JSON_WITH_NULL_USER_IN_PATH);
+        }
+
+        @Test
+        void flagsUpdatedShouldBeWellDeSerialized() {
+            assertThat(EVENT_SERIALIZER.fromJson(EVENT_JSON_WITH_NULL_USER_IN_PATH).get())
+                .isEqualTo(nullUserEvent);
+        }
+    }
+
+    @Nested
+    class WithUpdatedFlags {
+
+        @Nested
+        class EmptyUpdatedFlags {
+            private final List<MessageUid> emptyUids = ImmutableList.of();
+            private final List<UpdatedFlags> emptyUpdatedFlags = ImmutableList.of();
+            private final MailboxListener.FlagsUpdated emptyUpdatedFlagsEvent = new MailboxListener.FlagsUpdated(SESSION_ID, USER, MAILBOX_PATH,
+                MAILBOX_ID, emptyUids, emptyUpdatedFlags);
+
+            private static final String EVENT_JSON_WITH_EMPTY_UPDATED_FLAGS =
+                "{" +
+                "  \"FlagsUpdated\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"sessionId\": 42," +
+                "    \"updatedFlags\": []," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+            @Test
+            void flagsUpdatedShouldBeWellSerialized() {
+                assertThatJson(EVENT_SERIALIZER.toJson(emptyUpdatedFlagsEvent))
+                    .when(Option.IGNORING_ARRAY_ORDER)
+                    .isEqualTo(EVENT_JSON_WITH_EMPTY_UPDATED_FLAGS);
+            }
+
+            @Test
+            void flagsUpdatedShouldBeWellDeSerialized() {
+                assertThat(EVENT_SERIALIZER.fromJson(EVENT_JSON_WITH_EMPTY_UPDATED_FLAGS).get())
+                    .isEqualTo(emptyUpdatedFlagsEvent);
+            }
+        }
+
+        @Nested
+        class EmptyOldFlags {
+            private final UpdatedFlags emptyOldFlags = UpdatedFlags.builder()
+                .uid(MESSAGE_UID_1)
+                .modSeq(MOD_SEQ_1)
+                .oldFlags(FlagsBuilder.builder().build())
+                .newFlags(NEW_FLAGS_1)
+                .build();
+            private final MailboxListener.FlagsUpdated emptyOldFlagsUpdatedFlagsEvent = new MailboxListener.FlagsUpdated(SESSION_ID, USER, MAILBOX_PATH,
+                MAILBOX_ID, MESSAGE_UID_1_IN_LIST, ImmutableList.of(emptyOldFlags));
+
+            private static final String EVENT_JSON_WITH_EMPTY_OLD_FLAGS =
+                "{" +
+                "  \"FlagsUpdated\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"sessionId\": 42," +
+                "    \"updatedFlags\": [" +
+                "      {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"oldFlags\": []," +
+                "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                "      }" +
+                "    ]," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+            @Test
+            void flagsUpdatedShouldBeWellSerialized() {
+                assertThatJson(EVENT_SERIALIZER.toJson(emptyOldFlagsUpdatedFlagsEvent))
+                    .when(Option.IGNORING_ARRAY_ORDER)
+                    .isEqualTo(EVENT_JSON_WITH_EMPTY_OLD_FLAGS);
+            }
+
+            @Test
+            void flagsUpdatedShouldBeWellDeSerialized() {
+                assertThat(EVENT_SERIALIZER.fromJson(EVENT_JSON_WITH_EMPTY_OLD_FLAGS).get())
+                    .isEqualTo(emptyOldFlagsUpdatedFlagsEvent);
+            }
+        }
+
+        @Nested
+        class EmptyNewFlags {
+            private final UpdatedFlags emptyNewFlags = UpdatedFlags.builder()
+                .uid(MESSAGE_UID_1)
+                .modSeq(MOD_SEQ_1)
+                .oldFlags(OLD_FLAGS_1)
+                .newFlags(FlagsBuilder.builder().build())
+                .build();
+            private final MailboxListener.FlagsUpdated emptyNewFlagsUpdatedFlagsEvent = new MailboxListener.FlagsUpdated(SESSION_ID, USER, MAILBOX_PATH,
+                MAILBOX_ID, MESSAGE_UID_1_IN_LIST, ImmutableList.of(emptyNewFlags));
+
+            private static final String EVENT_JSON_WITH_EMPTY_NEW_FLAGS =
+                "{" +
+                "  \"FlagsUpdated\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"sessionId\": 42," +
+                "    \"updatedFlags\": [" +
+                "      {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                "        \"newFlags\": []" +
+                "      }" +
+                "    ]," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+            @Test
+            void flagsUpdatedShouldBeWellSerialized() {
+                assertThatJson(EVENT_SERIALIZER.toJson(emptyNewFlagsUpdatedFlagsEvent))
+                    .when(Option.IGNORING_ARRAY_ORDER)
+                    .isEqualTo(EVENT_JSON_WITH_EMPTY_NEW_FLAGS);
+            }
+
+            @Test
+            void flagsUpdatedShouldBeWellDeSerialized() {
+                assertThat(EVENT_SERIALIZER.fromJson(EVENT_JSON_WITH_EMPTY_NEW_FLAGS).get())
+                    .isEqualTo(emptyNewFlagsUpdatedFlagsEvent);
+            }
+        }
+
+        @Nested
+        class EmptyOldFlagsAndNewFlags {
+            private final UpdatedFlags emptyFlags = UpdatedFlags.builder()
+                .uid(MESSAGE_UID_1)
+                .modSeq(MOD_SEQ_1)
+                .oldFlags(FlagsBuilder.builder().build())
+                .newFlags(FlagsBuilder.builder().build())
+                .build();
+            private final MailboxListener.FlagsUpdated emptyFlagsUpdatedFlagsEvent = new MailboxListener.FlagsUpdated(SESSION_ID, USER, MAILBOX_PATH,
+                MAILBOX_ID, MESSAGE_UID_1_IN_LIST, ImmutableList.of(emptyFlags));
+
+            private static final String EVENT_JSON_WITH_EMPTY_OLD_AND_NEW_FLAGS =
+                "{" +
+                "  \"FlagsUpdated\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"sessionId\": 42," +
+                "    \"updatedFlags\": [" +
+                "      {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"oldFlags\": []," +
+                "        \"newFlags\": []" +
+                "      }" +
+                "    ]," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+            @Test
+            void flagsUpdatedShouldBeWellSerialized() {
+                assertThatJson(EVENT_SERIALIZER.toJson(emptyFlagsUpdatedFlagsEvent))
+                    .when(Option.IGNORING_ARRAY_ORDER)
+                    .isEqualTo(EVENT_JSON_WITH_EMPTY_OLD_AND_NEW_FLAGS);
+            }
+
+            @Test
+            void flagsUpdatedShouldBeWellDeSerialized() {
+                assertThat(EVENT_SERIALIZER.fromJson(EVENT_JSON_WITH_EMPTY_OLD_AND_NEW_FLAGS).get())
+                    .isEqualTo(emptyFlagsUpdatedFlagsEvent);
+            }
+        }
+    }
+
+    @Nested
+    class DeserializationError {
+
+        @Nested
+        class DeserializationErrorOnSessionId {
+            @Test
+            void flagsUpdatedShouldThrowWhenMissingSessionId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"FlagsUpdated\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"updatedFlags\": [" +
+                    "      {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                    "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                    "      }," +
+                    "      {" +
+                    "        \"uid\": 654321," +
+                    "        \"modSeq\": 36," +
+                    "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+                    "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+                    "      }" +
+                    "    ]," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void flagsUpdatedShouldThrowWhenNullSessionId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"FlagsUpdated\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"sessionId\": null," +
+                    "    \"updatedFlags\": [" +
+                    "      {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                    "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                    "      }," +
+                    "      {" +
+                    "        \"uid\": 654321," +
+                    "        \"modSeq\": 36," +
+                    "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+                    "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+                    "      }" +
+                    "    ]," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void flagsUpdatedShouldThrowWhenStringSessionId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"FlagsUpdated\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"sessionId\": \"42\"," +
+                    "    \"updatedFlags\": [" +
+                    "      {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                    "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                    "      }," +
+                    "      {" +
+                    "        \"uid\": 654321," +
+                    "        \"modSeq\": 36," +
+                    "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+                    "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+                    "      }" +
+                    "    ]," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+        }
+
+        @Nested
+        class DeserializationErrorOnUser {
+            @Test
+            void flagsUpdatedShouldThrowWhenMissingUser() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"FlagsUpdated\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"sessionId\": 42," +
+                    "    \"updatedFlags\": [" +
+                    "      {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                    "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                    "      }," +
+                    "      {" +
+                    "        \"uid\": 654321," +
+                    "        \"modSeq\": 36," +
+                    "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+                    "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+                    "      }" +
+                    "    ]" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void flagsUpdatedShouldThrowWhenUserIsNotAString() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"FlagsUpdated\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"sessionId\": 42," +
+                    "    \"updatedFlags\": [" +
+                    "      {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                    "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                    "      }," +
+                    "      {" +
+                    "        \"uid\": 654321," +
+                    "        \"modSeq\": 36," +
+                    "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+                    "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+                    "      }" +
+                    "    ]," +
+                    "    \"user\": 4569" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void flagsUpdatedShouldThrowWhenUserIsNotWellFormatted() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"FlagsUpdated\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"sessionId\": 42," +
+                    "    \"updatedFlags\": [" +
+                    "      {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                    "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                    "      }," +
+                    "      {" +
+                    "        \"uid\": 654321," +
+                    "        \"modSeq\": 36," +
+                    "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+                    "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+                    "      }" +
+                    "    ]," +
+                    "    \"user\": \"user@user@anotherUser\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(IllegalArgumentException.class);
+            }
+        }
+
+        @Nested
+        class DeserializationErrorOnMailboxId {
+            @Test
+            void flagsUpdatedShouldThrowWhenMissingMailboxId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"FlagsUpdated\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"sessionId\": 42," +
+                    "    \"updatedFlags\": [" +
+                    "      {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                    "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                    "      }," +
+                    "      {" +
+                    "        \"uid\": 654321," +
+                    "        \"modSeq\": 36," +
+                    "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+                    "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+                    "      }" +
+                    "    ]," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void flagsUpdatedShouldThrowWhenNullMailboxId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"FlagsUpdated\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": null," +
+                    "    \"sessionId\": 42," +
+                    "    \"updatedFlags\": [" +
+                    "      {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                    "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                    "      }," +
+                    "      {" +
+                    "        \"uid\": 654321," +
+                    "        \"modSeq\": 36," +
+                    "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+                    "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+                    "      }" +
+                    "    ]," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void flagsUpdatedShouldThrowWhenMailboxIdIsANumber() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"FlagsUpdated\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": 18," +
+                    "    \"sessionId\": 42," +
+                    "    \"updatedFlags\": [" +
+                    "      {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                    "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                    "      }," +
+                    "      {" +
+                    "        \"uid\": 654321," +
+                    "        \"modSeq\": 36," +
+                    "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+                    "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+                    "      }" +
+                    "    ]," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+        }
+
+        @Nested
+        class DeserializationErrorOnMailboxPath {
+
+            @Nested
+            class DeserializationErrorOnNameSpace {
+                @Test
+                void flagsUpdatedShouldThrowWhenNameSpaceIsNotAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 482," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                        "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                        "      }," +
+                        "      {" +
+                        "        \"uid\": 654321," +
+                        "        \"modSeq\": 36," +
+                        "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+                        "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnUser {
+                @Test
+                void flagsUpdatedShouldThrowWhenUserIsNotAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": 682695," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                        "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                        "      }," +
+                        "      {" +
+                        "        \"uid\": 654321," +
+                        "        \"modSeq\": 36," +
+                        "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+                        "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnMailboxName {
+
+                @Test
+                void flagsUpdatedShouldThrowWhenNullMailboxName() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                        "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                        "      }," +
+                        "      {" +
+                        "        \"uid\": 654321," +
+                        "        \"modSeq\": 36," +
+                        "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+                        "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void flagsUpdatedShouldThrowWhenMailboxNameIsANumber() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": 156.88541" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                        "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                        "      }," +
+                        "      {" +
+                        "        \"uid\": 654321," +
+                        "        \"modSeq\": 36," +
+                        "        \"oldFlags\": [\"Old Flag 2\", \"\\\\Flagged\", \"\\\\Recent\"]," +
+                        "        \"newFlags\": [\"New Flag 2\", \"\\\\Answered\", \"\\\\Seen\" ]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+        }
+
+        @Nested
+        class DeserializationErrorOnUpdatedFlags {
+
+            @Nested
+            class DeserializationErrorOnUid {
+
+                @Test
+                void flagsUpdatedShouldThrowWhenUidIsAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 482," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": \"123456\"," +
+                        "        \"modSeq\": 35," +
+                        "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                        "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void flagsUpdatedShouldThrowWhenUidIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 482," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": null," +
+                        "        \"modSeq\": 35," +
+                        "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                        "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void flagsUpdatedShouldThrowWhenUidIsNotALongNumber() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 482," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": 12563.3564," +
+                        "        \"modSeq\": 35," +
+                        "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                        "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnMoqSeq {
+
+                @Test
+                void flagsUpdatedShouldThrowWhenMoqSeqIsAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 482," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": \"35\"," +
+                        "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                        "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void flagsUpdatedShouldThrowWhenMoqSeqIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 482," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": null," +
+                        "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                        "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void flagsUpdatedShouldThrowWhenMoqSeqIsNotALongNumber() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 482," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35.2567454," +
+                        "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                        "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnOldFlags {
+
+                @Test
+                void flagsUpdatedShouldThrowWhenOldFlagsIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 482," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": \"35\"," +
+                        "        \"oldFlags\": null," +
+                        "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void flagsUpdatedShouldThrowWhenOldFlagsContainsNullElements() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 482," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": \"35\"," +
+                        "        \"oldFlags\": [null, null, \"\\\\Deleted\", \"\\\\Seen\"]," +
+                        "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void flagsUpdatedShouldThrowWhenOldFlagsContainsNotStringElements() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 482," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": \"35\"," +
+                        "        \"oldFlags\": [\"Old Flag 1\", 1256, 5894.9523]," +
+                        "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void flagsUpdatedShouldThrowWhenOldFlagsContainsNestedArray() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 482," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": \"35\"," +
+                        "        \"oldFlags\": [\"Old Flag 1\", [\"\\\\Answered\", \"\\\\Draft\"]]," +
+                        "        \"newFlags\": [\"New Flag 1\", \"\\\\Answered\", \"\\\\Draft\"]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnNewFlags {
+                @Test
+                void flagsUpdatedShouldThrowWhenNewFlagsIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 482," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": \"35\"," +
+                        "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                        "        \"newFlags\": null" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void flagsUpdatedShouldThrowWhenNewFlagsContainsNullElements() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 482," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": \"35\"," +
+                        "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                        "        \"newFlags\": [null, \"\\\\Answered\", \"\\\\Draft\", null]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void flagsUpdatedShouldThrowWhenNewFlagsContainsNotStringElements() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 482," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": \"35\"," +
+                        "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                        "        \"newFlags\": [\"New Flag 1\", 1666.3984, 2152, \"\\\\Answered\", \"\\\\Draft\"]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void flagsUpdatedShouldThrowWhenNewFlagsContainsNestedArray() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"FlagsUpdated\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 482," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"sessionId\": 42," +
+                        "    \"updatedFlags\": [" +
+                        "      {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": \"35\"," +
+                        "        \"oldFlags\": [\"Old Flag 1\", \"\\\\Deleted\", \"\\\\Seen\"]," +
+                        "        \"newFlags\": [\"New Flag 1\", [\"\\\\Answered\", \"\\\\Draft\"]]" +
+                        "      }" +
+                        "    ]," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+        }
+    }
+
+}


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


[09/10] james-project git commit: MAILBOX-363 MessageMoveEvent JSON serialization

Posted by bt...@apache.org.
MAILBOX-363 MessageMoveEvent JSON serialization


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

Branch: refs/heads/master
Commit: 26af5b2bd46dedc2e7734f508930443566639891
Parents: 8a91427
Author: Benoit Tellier <bt...@linagora.com>
Authored: Mon Dec 17 10:46:17 2018 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Wed Dec 19 18:24:21 2018 +0700

----------------------------------------------------------------------
 .../james/event/json/EventSerializer.scala      |  34 +-
 .../json/MessageMoveEventSerializationTest.java | 412 +++++++++++++++++++
 2 files changed, 436 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/26af5b2b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
index 01cb1b8..31ebf49 100644
--- a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
+++ b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
@@ -26,17 +26,11 @@ import javax.mail.{Flags => JavaMailFlags}
 import julienrf.json.derived
 import org.apache.james.core.quota.{QuotaCount, QuotaSize, QuotaValue}
 import org.apache.james.core.{Domain, User}
-import org.apache.james.event.json.DTOs.{ACLDiff, MailboxPath, Quota}
-import org.apache.james.mailbox.MailboxListener.{Added => JavaAdded, Expunged => JavaExpunged,
-  MailboxACLUpdated => JavaMailboxACLUpdated, MailboxAdded => JavaMailboxAdded, MailboxDeletion => JavaMailboxDeletion,
-  MailboxRenamed => JavaMailboxRenamed, QuotaUsageUpdatedEvent => JavaQuotaUsageUpdatedEvent}
+import org.apache.james.event.json.DTOs.{ACLDiff, Flags, MailboxPath, Quota}
+import org.apache.james.mailbox.MailboxListener.{Added => JavaAdded, Expunged => JavaExpunged, MailboxACLUpdated => JavaMailboxACLUpdated, MailboxAdded => JavaMailboxAdded, MailboxDeletion => JavaMailboxDeletion, MailboxRenamed => JavaMailboxRenamed, QuotaUsageUpdatedEvent => JavaQuotaUsageUpdatedEvent}
 import org.apache.james.mailbox.MailboxSession.SessionId
-import org.apache.james.mailbox.model.{MailboxId, MessageId, QuotaRoot, MailboxACL => JavaMailboxACL, Quota => JavaQuota}
-import org.apache.james.event.json.DTOs.{Flags, MailboxPath, Quota}
-import org.apache.james.mailbox.MailboxListener.{Added => JavaAdded, Expunged => JavaExpunged, MailboxAdded => JavaMailboxAdded, MailboxDeletion => JavaMailboxDeletion, MailboxRenamed => JavaMailboxRenamed, QuotaUsageUpdatedEvent => JavaQuotaUsageUpdatedEvent}
-import org.apache.james.mailbox.MailboxSession.SessionId
-import org.apache.james.mailbox.model.{MailboxId, MessageId, QuotaRoot, MessageMetaData => JavaMessageMetaData, Quota => JavaQuota}
-import org.apache.james.mailbox.{MessageUid, Event => JavaEvent}
+import org.apache.james.mailbox.model.{MailboxId, MessageId, MessageMoves, QuotaRoot, MailboxACL => JavaMailboxACL, MessageMetaData => JavaMessageMetaData, Quota => JavaQuota}
+import org.apache.james.mailbox.{MessageUid, Event => JavaEvent, MessageMoveEvent => JavaMessageMoveEvent}
 import play.api.libs.json.{JsArray, JsError, JsNull, JsNumber, JsObject, JsResult, JsString, JsSuccess, Json, OFormat, Reads, Writes}
 
 import scala.collection.JavaConverters._
@@ -89,6 +83,18 @@ private object DTO {
       mailboxId,
       expunged.mapValues(_.toJava).asJava)
   }
+
+  case class MessageMoveEvent(user: User, previousMailboxIds: Seq[MailboxId], targetMailboxIds: Seq[MailboxId],
+                              messageIds: Seq[MessageId]) extends Event {
+    override def toJava: JavaEvent = JavaMessageMoveEvent.builder()
+      .user(user)
+      .messageId(messageIds.asJava)
+      .messageMoves(MessageMoves.builder()
+          .previousMailboxIds(previousMailboxIds.asJava)
+          .targetMailboxIds(targetMailboxIds.asJava)
+        .build())
+      .build()
+  }
 }
 
 private object ScalaConverter {
@@ -149,6 +155,13 @@ private object ScalaConverter {
     expunged = event.getExpunged.asScala.mapValues(DTOs.MessageMetaData.fromJava).toMap
   )
 
+  private def toScala(event: JavaMessageMoveEvent): DTO.MessageMoveEvent = DTO.MessageMoveEvent(
+    user = event.getUser,
+    previousMailboxIds = event.getMessageMoves.getPreviousMailboxIds.asScala.toList,
+    targetMailboxIds = event.getMessageMoves.getTargetMailboxIds.asScala.toList,
+    messageIds = event.getMessageIds.asScala.toList)
+
+
   def toScala(javaEvent: JavaEvent): Event = javaEvent match {
     case e: JavaAdded => toScala(e)
     case e: JavaExpunged => toScala(e)
@@ -156,6 +169,7 @@ private object ScalaConverter {
     case e: JavaMailboxAdded => toScala(e)
     case e: JavaMailboxDeletion => toScala(e)
     case e: JavaMailboxRenamed => toScala(e)
+    case e: JavaMessageMoveEvent => toScala(e)
     case e: JavaQuotaUsageUpdatedEvent => toScala(e)
     case _ => throw new RuntimeException("no Scala conversion known")
   }

http://git-wip-us.apache.org/repos/asf/james-project/blob/26af5b2b/mailbox/event/json/src/test/java/org/apache/james/event/json/MessageMoveEventSerializationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/test/java/org/apache/james/event/json/MessageMoveEventSerializationTest.java b/mailbox/event/json/src/test/java/org/apache/james/event/json/MessageMoveEventSerializationTest.java
new file mode 100644
index 0000000..f3008dc
--- /dev/null
+++ b/mailbox/event/json/src/test/java/org/apache/james/event/json/MessageMoveEventSerializationTest.java
@@ -0,0 +1,412 @@
+/****************************************************************
+ * 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.event.json;
+
+import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.util.NoSuchElementException;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.Event;
+import org.apache.james.mailbox.MessageMoveEvent;
+import org.apache.james.mailbox.model.MessageMoves;
+import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.TestMessageId;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+class MessageMoveEventSerializationTest {
+    private static final EventSerializer EVENT_SERIALIZER = new EventSerializer(new TestId.Factory(), new TestMessageId.Factory());
+
+    private static final Event EVENT = MessageMoveEvent.builder()
+        .user(User.fromUsername("bob@domain.tld"))
+        .messageId(TestMessageId.of(42))
+        .messageMoves(
+            MessageMoves.builder()
+                .previousMailboxIds(TestId.of(18), TestId.of(24))
+                .targetMailboxIds(TestId.of(36))
+                .build())
+        .build();
+    private static final String JSON = "{" +
+        "  \"MessageMoveEvent\": {" +
+        "    \"user\": \"bob@domain.tld\"," +
+        "    \"previousMailboxIds\": [\"18\", \"24\"]," +
+        "    \"targetMailboxIds\": [\"36\"]," +
+        "    \"messageIds\": [\"42\"]" +
+        "  }" +
+        "}";
+
+    @Test
+    void messageMoveEventShouldBeWellSerialized() {
+        assertThatJson(EVENT_SERIALIZER.toJson(EVENT))
+            .isEqualTo(JSON);
+    }
+
+    @Test
+    void messageMoveEventShouldBeWellDeSerialized() {
+        assertThat(EVENT_SERIALIZER.fromJson(JSON).get())
+            .isEqualTo(EVENT);
+    }
+
+    @Nested
+    class ValidPayloads {
+        @Nested
+        class NoVirtualHosting {
+            private final Event event = MessageMoveEvent.builder()
+                .user(User.fromUsername("bob"))
+                .messageId(TestMessageId.of(42))
+                .messageMoves(
+                    MessageMoves.builder()
+                        .previousMailboxIds(TestId.of(18), TestId.of(24))
+                        .targetMailboxIds(TestId.of(36))
+                        .build())
+                .build();
+            private final String json = "{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob\"," +
+                "    \"previousMailboxIds\": [\"18\", \"24\"]," +
+                "    \"targetMailboxIds\": [\"36\"]," +
+                "    \"messageIds\": [\"42\"]" +
+                "  }" +
+                "}";
+
+            @Test
+            void messageMoveEventShouldBeWellSerialized() {
+                assertThatJson(EVENT_SERIALIZER.toJson(event))
+                    .isEqualTo(json);
+            }
+
+            @Test
+            void messageMoveEventShouldBeWellDeSerialized() {
+                assertThat(EVENT_SERIALIZER.fromJson(json).get())
+                    .isEqualTo(event);
+            }
+        }
+
+        @Nested
+        class EmptyTargetMailboxIds {
+            private final Event event = MessageMoveEvent.builder()
+                .user(User.fromUsername("bob"))
+                .messageId(TestMessageId.of(42))
+                .messageMoves(
+                    MessageMoves.builder()
+                        .previousMailboxIds(TestId.of(18), TestId.of(24))
+                        .build())
+                .build();
+            private final String json = "{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob\"," +
+                "    \"previousMailboxIds\": [\"18\", \"24\"]," +
+                "    \"targetMailboxIds\": []," +
+                "    \"messageIds\": [\"42\"]" +
+                "  }" +
+                "}";
+
+            @Test
+            void messageMoveEventShouldBeWellSerialized() {
+                assertThatJson(EVENT_SERIALIZER.toJson(event))
+                    .isEqualTo(json);
+            }
+
+            @Test
+            void messageMoveEventShouldBeWellDeSerialized() {
+                assertThat(EVENT_SERIALIZER.fromJson(json).get())
+                    .isEqualTo(event);
+            }
+        }
+
+        @Nested
+        class EmptyPreviousMailboxIds {
+            private final Event event = MessageMoveEvent.builder()
+                .user(User.fromUsername("bob"))
+                .messageId(TestMessageId.of(42))
+                .messageMoves(
+                    MessageMoves.builder()
+                        .targetMailboxIds(TestId.of(36))
+                        .build())
+                .build();
+            private final String json = "{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob\"," +
+                "    \"previousMailboxIds\": []," +
+                "    \"targetMailboxIds\": [\"36\"]," +
+                "    \"messageIds\": [\"42\"]" +
+                "  }" +
+                "}";
+
+            @Test
+            void messageMoveEventShouldBeWellSerialized() {
+                assertThatJson(EVENT_SERIALIZER.toJson(event))
+                    .isEqualTo(json);
+            }
+
+            @Test
+            void messageMoveEventShouldBeWellDeSerialized() {
+                assertThat(EVENT_SERIALIZER.fromJson(json).get())
+                    .isEqualTo(event);
+            }
+        }
+
+        @Nested
+        class EmptyMessagesIds {
+            private final Event event = MessageMoveEvent.builder()
+                .user(User.fromUsername("bob"))
+                .messageMoves(
+                    MessageMoves.builder()
+                        .previousMailboxIds(TestId.of(18), TestId.of(24))
+                        .targetMailboxIds(TestId.of(36))
+                        .build())
+                .build();
+            private final String json = "{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob\"," +
+                "    \"previousMailboxIds\": [\"18\", \"24\"]," +
+                "    \"targetMailboxIds\": [\"36\"]," +
+                "    \"messageIds\": []" +
+                "  }" +
+                "}";
+
+            @Test
+            void messageMoveEventShouldBeWellSerialized() {
+                assertThatJson(EVENT_SERIALIZER.toJson(event))
+                    .isEqualTo(json);
+            }
+
+            @Test
+            void messageMoveEventShouldBeWellDeSerialized() {
+                assertThat(EVENT_SERIALIZER.fromJson(json).get())
+                    .isEqualTo(event);
+            }
+        }
+    }
+
+    @Nested
+    class InvalidPayloads {
+        @Test
+        void emptyUserShouldBeRejected() {
+            assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson("{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"\"," +
+                "    \"previousMailboxIds\": [\"18\", \"24\"]," +
+                "    \"targetMailboxIds\": [\"36\"]," +
+                "    \"messageIds\": [\"42\"]" +
+                "  }" +
+                "}").get())
+                .isInstanceOf(IllegalArgumentException.class);
+        }
+
+        @Test
+        void basUsersShouldBeRejected() {
+            assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson("{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob@domain.tld@bad\"," +
+                "    \"previousMailboxIds\": [\"18\", \"24\"]," +
+                "    \"targetMailboxIds\": [\"36\"]," +
+                "    \"messageIds\": [\"42\"]" +
+                "  }" +
+                "}").get())
+                .isInstanceOf(IllegalArgumentException.class);
+        }
+
+        @Test
+        void nonStringUserShouldBeRejected() {
+            assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson("{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": 42," +
+                "    \"previousMailboxIds\": [\"18\", \"24\"]," +
+                "    \"targetMailboxIds\": [\"36\"]," +
+                "    \"messageIds\": [\"42\"]" +
+                "  }" +
+                "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+        }
+
+        @Test
+        void nullUserShouldBeRejected() {
+            assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson("{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": null," +
+                "    \"previousMailboxIds\": [\"18\", \"24\"]," +
+                "    \"targetMailboxIds\": [\"36\"]," +
+                "    \"messageIds\": [\"42\"]" +
+                "  }" +
+                "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+        }
+
+        @Test
+        void nullPreviousMailboxIdsShouldBeRejected() {
+            assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson("{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob@domain.tld\"," +
+                "    \"previousMailboxIds\": null," +
+                "    \"targetMailboxIds\": [\"36\"]," +
+                "    \"messageIds\": [\"42\"]" +
+                "  }" +
+                "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+        }
+
+        @Test
+        void nonCollectionPreviousMailboxIdsShouldBeRejected() {
+            assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson("{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob@domain.tld\"," +
+                "    \"previousMailboxIds\": 42," +
+                "    \"targetMailboxIds\": [\"36\"]," +
+                "    \"messageIds\": [\"42\"]" +
+                "  }" +
+                "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+        }
+
+        @Test
+        void nonStringElementInPreviousMailboxIdsShouldBeRejected() {
+            assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson("{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob@domain.tld\"," +
+                "    \"previousMailboxIds\": [42]," +
+                "    \"targetMailboxIds\": [\"36\"]," +
+                "    \"messageIds\": [\"42\"]" +
+                "  }" +
+                "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+        }
+
+        @Test
+        void nullElementInPreviousMailboxIdsShouldBeRejected() {
+            assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson("{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob@domain.tld\"," +
+                "    \"previousMailboxIds\": [null]," +
+                "    \"targetMailboxIds\": [\"36\"]," +
+                "    \"messageIds\": [\"42\"]" +
+                "  }" +
+                "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+        }
+
+        @Test
+        void nullTargetMailboxIdsShouldBeRejected() {
+            assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson("{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob@domain.tld\"," +
+                "    \"previousMailboxIds\": [\"36\"]," +
+                "    \"targetMailboxIds\": null," +
+                "    \"messageIds\": [\"42\"]" +
+                "  }" +
+                "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+        }
+
+        @Test
+        void nonCollectionTargetMailboxIdsShouldBeRejected() {
+            assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson("{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob@domain.tld\"," +
+                "    \"previousMailboxIds\": [\"36\"]," +
+                "    \"targetMailboxIds\": 42," +
+                "    \"messageIds\": [\"42\"]" +
+                "  }" +
+                "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+        }
+
+        @Test
+        void nonStringElementInTargetMailboxIdsShouldBeRejected() {
+            assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson("{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob@domain.tld\"," +
+                "    \"previousMailboxIds\": [\"36\"]," +
+                "    \"targetMailboxIds\": [42]," +
+                "    \"messageIds\": [\"42\"]" +
+                "  }" +
+                "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+        }
+
+        @Test
+        void nullElementInTargetMailboxIdsShouldBeRejected() {
+            assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson("{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob@domain.tld\"," +
+                "    \"previousMailboxIds\": [\"36\"]," +
+                "    \"targetMailboxIds\": [null]," +
+                "    \"messageIds\": [\"42\"]" +
+                "  }" +
+                "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+        }
+
+        @Test
+        void nullMessageIdsShouldBeRejected() {
+            assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson("{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob@domain.tld\"," +
+                "    \"previousMailboxIds\": [\"36\"]," +
+                "    \"targetMailboxIds\": [\"42\"]," +
+                "    \"messageIds\": null" +
+                "  }" +
+                "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+        }
+
+        @Test
+        void nonCollectionMessageIdsShouldBeRejected() {
+            assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson("{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob@domain.tld\"," +
+                "    \"previousMailboxIds\": [\"36\"]," +
+                "    \"targetMailboxIds\": [\"42\"]," +
+                "    \"messageIds\": 42" +
+                "  }" +
+                "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+        }
+
+        @Test
+        void nonStringElementInMessageIdsShouldBeRejected() {
+            assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson("{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob@domain.tld\"," +
+                "    \"previousMailboxIds\": [\"36\"]," +
+                "    \"targetMailboxIds\": [\"42\"]," +
+                "    \"messageIds\": [42]" +
+                "  }" +
+                "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+        }
+
+        @Test
+        void nullElementInMessageIdsShouldBeRejected() {
+            assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson("{" +
+                "  \"MessageMoveEvent\": {" +
+                "    \"user\": \"bob@domain.tld\"," +
+                "    \"previousMailboxIds\": [\"36\"]," +
+                "    \"targetMailboxIds\": [\"42\"]," +
+                "    \"messageIds\": [null]" +
+                "  }" +
+                "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+        }
+    }
+}


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


[08/10] james-project git commit: MAILBOX-359 FlagsUpdated Scala Event Serialization

Posted by bt...@apache.org.
MAILBOX-359 FlagsUpdated Scala Event Serialization


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

Branch: refs/heads/master
Commit: e5a6765f75cfb581bd04af99fa847f1903027f23
Parents: a318d27
Author: tran tien duc <dt...@linagora.com>
Authored: Mon Dec 17 14:14:46 2018 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Wed Dec 19 18:24:21 2018 +0700

----------------------------------------------------------------------
 .../scala/org/apache/james/event/json/EventSerializer.scala    | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/e5a6765f/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
index 7875fe1..c6732e0 100644
--- a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
+++ b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
@@ -155,16 +155,14 @@ private object ScalaConverter {
     user = event.getUser,
     path = MailboxPath.fromJava(event.getMailboxPath),
     mailboxId = event.getMailboxId,
-    added = event.getAdded.asScala.mapValues(DTOs.MessageMetaData.fromJava).toMap
-  )
+    added = event.getAdded.asScala.mapValues(DTOs.MessageMetaData.fromJava).toMap)
 
   private def toScala(event: JavaExpunged): DTO.Expunged = DTO.Expunged(
     sessionId = event.getSessionId,
     user = event.getUser,
     path = MailboxPath.fromJava(event.getMailboxPath),
     mailboxId = event.getMailboxId,
-    expunged = event.getExpunged.asScala.mapValues(DTOs.MessageMetaData.fromJava).toMap
-  )
+    expunged = event.getExpunged.asScala.mapValues(DTOs.MessageMetaData.fromJava).toMap)
 
   private def toScala(event: JavaMessageMoveEvent): DTO.MessageMoveEvent = DTO.MessageMoveEvent(
     user = event.getUser,


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


[02/10] james-project git commit: MAILBOX-359 Added Scala Event Serialization

Posted by bt...@apache.org.
MAILBOX-359 Added Scala Event Serialization


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

Branch: refs/heads/master
Commit: 39bc507acae024f4686a78c750f09a494cde1282
Parents: 0f9024f
Author: tran tien duc <dt...@linagora.com>
Authored: Fri Dec 14 17:49:09 2018 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Wed Dec 19 18:05:57 2018 +0700

----------------------------------------------------------------------
 .../org/apache/james/mailbox/FlagsBuilder.java  |    5 +
 .../apache/james/mailbox/MailboxListener.java   |   26 +-
 .../james/mailbox/MailboxListenerTest.java      |    5 +
 .../james/event/json/EventSerializer.scala      |   73 +-
 .../apache/james/event/json/MetaDataDTO.scala   |   90 ++
 .../event/json/AddedSerializationTest.java      | 1465 ++++++++++++++++++
 ...MailboxACLUpdatedEventSerializationTest.java |    3 +-
 .../json/MailboxAddedSerializationTest.java     |    3 +-
 .../json/MailboxDeletionSerializationTest.java  |    3 +-
 .../json/MailboxRenamedSerializationTest.java   |    3 +-
 ...QuotaUsageUpdatedEventSerializationTest.java |    3 +-
 11 files changed, 1663 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java b/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java
index 7a791ec..f36189b 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java
@@ -61,6 +61,11 @@ public class FlagsBuilder {
         return this;
     }
 
+    public FlagsBuilder merge(FlagsBuilder flagsBuilder) {
+        internalFlags.add(flagsBuilder.internalFlags);
+        return this;
+    }
+
     public Flags build() {
         return new Flags(internalFlags);
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
index b3d4785..2b7fdb9 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
@@ -24,7 +24,6 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.SortedMap;
 
 import org.apache.james.core.User;
 import org.apache.james.core.quota.QuotaCount;
@@ -429,7 +428,7 @@ public interface MailboxListener {
     class Added extends MetaDataHoldingEvent {
         private final Map<MessageUid, MessageMetaData> added;
 
-        public Added(MailboxSession.SessionId sessionId, User user, MailboxPath path, MailboxId mailboxId, SortedMap<MessageUid, MessageMetaData> uids) {
+        public Added(MailboxSession.SessionId sessionId, User user, MailboxPath path, MailboxId mailboxId, Map<MessageUid, MessageMetaData> uids) {
             super(sessionId, user, path, mailboxId);
             this.added = ImmutableMap.copyOf(uids);
         }
@@ -447,6 +446,29 @@ public interface MailboxListener {
         public Collection<MessageUid> getUids() {
             return added.keySet();
         }
+
+        public Map<MessageUid, MessageMetaData> getAdded() {
+            return added;
+        }
+
+        @Override
+        public final boolean equals(Object o) {
+            if (o instanceof Added) {
+                Added that = (Added) o;
+
+                return Objects.equals(this.sessionId, that.sessionId)
+                    && Objects.equals(this.user, that.user)
+                    && Objects.equals(this.path, that.path)
+                    && Objects.equals(this.mailboxId, that.mailboxId)
+                    && Objects.equals(this.added, that.added);
+            }
+            return false;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(sessionId, user, path, mailboxId, added);
+        }
     }
 
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
index ac9c364..a276c3f 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
@@ -43,4 +43,9 @@ class MailboxListenerTest {
     void mailboxACLUpdatedShouldMatchBeanContract() {
         EqualsVerifier.forClass(MailboxListener.MailboxACLUpdated.class).verify();
     }
+
+    @Test
+    void addedShouldMatchBeanContract() {
+        EqualsVerifier.forClass(MailboxListener.Added.class).verify();
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
index 5a14668..549e105 100644
--- a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
+++ b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
@@ -22,15 +22,21 @@ package org.apache.james.event.json
 import java.time.Instant
 import java.util.Optional
 
+import javax.mail.{Flags => JavaMailFlags}
 import julienrf.json.derived
 import org.apache.james.core.quota.{QuotaCount, QuotaSize, QuotaValue}
 import org.apache.james.core.{Domain, User}
 import org.apache.james.event.json.DTOs.{ACLDiff, MailboxPath, Quota}
-import org.apache.james.mailbox.MailboxListener.{MailboxACLUpdated => JavaMailboxACLUpdated, MailboxAdded => JavaMailboxAdded, MailboxDeletion => JavaMailboxDeletion, MailboxRenamed => JavaMailboxRenamed, QuotaUsageUpdatedEvent => JavaQuotaUsageUpdatedEvent}
+import org.apache.james.event.json.MetaDataDTO.Flags
+import org.apache.james.mailbox.MailboxListener.{Added => JavaAdded, MailboxACLUpdated => JavaMailboxACLUpdated,
+  MailboxAdded => JavaMailboxAdded, MailboxDeletion => JavaMailboxDeletion, MailboxRenamed => JavaMailboxRenamed,
+  QuotaUsageUpdatedEvent => JavaQuotaUsageUpdatedEvent}
 import org.apache.james.mailbox.MailboxSession.SessionId
-import org.apache.james.mailbox.model.{MailboxId, QuotaRoot, MailboxACL => JavaMailboxACL, Quota => JavaQuota}
-import org.apache.james.mailbox.{Event => JavaEvent}
-import play.api.libs.json.{JsError, JsNull, JsNumber, JsObject, JsResult, JsString, JsSuccess, Json, OFormat, Reads, Writes}
+import org.apache.james.mailbox.model.{MailboxId, MessageId, QuotaRoot, MailboxACL => JavaMailboxACL, Quota => JavaQuota}
+import org.apache.james.mailbox.{MessageUid, Event => JavaEvent}
+import play.api.libs.json.{JsArray, JsError, JsNull, JsNumber, JsObject, JsResult, JsString, JsSuccess, Json, OFormat, Reads, Writes}
+
+import scala.collection.JavaConverters._
 
 private sealed trait Event {
   def toJava: JavaEvent
@@ -60,6 +66,16 @@ private object DTO {
                                     sizeQuota: Quota[QuotaSize], time: Instant) extends Event {
     override def toJava: JavaEvent = new JavaQuotaUsageUpdatedEvent(user, quotaRoot, countQuota.toJava, sizeQuota.toJava, time)
   }
+
+  case class Added(sessionId: SessionId, user: User, path: MailboxPath, mailboxId: MailboxId,
+                   added: Map[MessageUid, MetaDataDTO.MessageMetaData]) extends Event {
+    override def toJava: JavaEvent = new JavaAdded(
+      sessionId,
+      user,
+      path.toJava,
+      mailboxId,
+      added.map(entry => entry._1 -> entry._2.toJava).asJava)
+  }
 }
 
 private object ScalaConverter {
@@ -85,6 +101,7 @@ private object ScalaConverter {
     totalDeletedSize = event.getTotalDeletedSize,
     mailboxId = event.getMailboxId)
 
+
   private def toScala(event: JavaMailboxRenamed): DTO.MailboxRenamed = DTO.MailboxRenamed(
     sessionId = event.getSessionId,
     user = event.getUser,
@@ -99,17 +116,26 @@ private object ScalaConverter {
     sizeQuota = Quota.toScala(event.getSizeQuota),
     time = event.getInstant)
 
+  private def toScala(event: JavaAdded): DTO.Added = DTO.Added(
+    sessionId = event.getSessionId,
+    user = event.getUser,
+    path = MailboxPath.fromJava(event.getMailboxPath),
+    mailboxId = event.getMailboxId,
+    added = event.getAdded.asScala.map(entry => entry._1 -> MetaDataDTO.MessageMetaData.fromJava(entry._2)).toMap
+  )
+
   def toScala(javaEvent: JavaEvent): Event = javaEvent match {
     case e: JavaMailboxACLUpdated => toScala(e)
     case e: JavaMailboxAdded => toScala(e)
     case e: JavaMailboxDeletion => toScala(e)
     case e: JavaMailboxRenamed => toScala(e)
     case e: JavaQuotaUsageUpdatedEvent => toScala(e)
-    case _ => throw new RuntimeException("no Scala convertion known")
+    case e: JavaAdded => toScala(e)
+    case _ => throw new RuntimeException("no Scala conversion known")
   }
 }
 
-private class JsonSerialize(mailboxIdFactory: MailboxId.Factory) {
+private class JsonSerialize(mailboxIdFactory: MailboxId.Factory, messageIdFactory: MessageId.Factory) {
   implicit val userWriters: Writes[User] = (user: User) => JsString(user.asString)
   implicit val quotaRootWrites: Writes[QuotaRoot] = quotaRoot => JsString(quotaRoot.getValue)
   implicit val quotaValueWrites: Writes[QuotaValue[_]] = value => if (value.isUnlimited) JsNull else JsNumber(value.asLong())
@@ -122,6 +148,10 @@ private class JsonSerialize(mailboxIdFactory: MailboxId.Factory) {
   implicit val aclEntryKeyWrites: Writes[JavaMailboxACL.EntryKey] = value => JsString(value.serialize())
   implicit val aclRightsWrites: Writes[JavaMailboxACL.Rfc4314Rights] = value => JsString(value.serialize())
   implicit val aclDiffWrites: Writes[ACLDiff] = Json.writes[ACLDiff]
+  implicit val messageIdWrites: Writes[MessageId] = value => JsString(value.serialize())
+  implicit val messageUidWrites: Writes[MessageUid] = value => JsNumber(value.asLong())
+  implicit val flagsWrites: Writes[JavaMailFlags] = value => JsArray(Flags.fromJavaFlags(value).map(flag => JsString(flag)))
+  implicit val messageMetaDataWrites: Writes[MetaDataDTO.MessageMetaData] = Json.writes[MetaDataDTO.MessageMetaData]
 
   implicit val aclEntryKeyReads: Reads[JavaMailboxACL.EntryKey] = {
     case JsString(keyAsString) => JsSuccess(JavaMailboxACL.EntryKey.deserialize(keyAsString))
@@ -161,6 +191,18 @@ private class JsonSerialize(mailboxIdFactory: MailboxId.Factory) {
     case JsString(userAsString) => JsSuccess(User.fromUsername(userAsString))
     case _ => JsError()
   }
+  implicit val messageIdReads: Reads[MessageId] = {
+    case JsString(value) => JsSuccess(messageIdFactory.fromString(value))
+    case _ => JsError()
+  }
+  implicit val messageUidReads: Reads[MessageUid] = {
+    case JsNumber(value) => JsSuccess(MessageUid.of(value.toLong))
+    case _ => JsError()
+  }
+  implicit val flagsReads: Reads[JavaMailFlags] = {
+    case JsArray(seqOfJsValues) => JsSuccess(Flags.toJavaFlags(seqOfJsValues.toArray.map(jsValue => jsValue.toString())))
+    case _ => JsError()
+  }
 
   implicit def scopeMapReads[V](implicit vr: Reads[V]): Reads[Map[JavaQuota.Scope, V]] =
     Reads.mapReads[JavaQuota.Scope, V] { str =>
@@ -172,6 +214,7 @@ private class JsonSerialize(mailboxIdFactory: MailboxId.Factory) {
       JsObject(m.map { case (k, v) => (k.toString, vr.writes(v)) }.toSeq)
     }
 
+
   implicit def scopeMapReadsACL[V](implicit vr: Reads[V]): Reads[Map[JavaMailboxACL.EntryKey, V]] =
     Reads.mapReads[JavaMailboxACL.EntryKey, V] { str =>
       Json.fromJson[JavaMailboxACL.EntryKey](JsString(str))
@@ -182,10 +225,21 @@ private class JsonSerialize(mailboxIdFactory: MailboxId.Factory) {
       JsObject(m.map { case (k, v) => (k.toString, vr.writes(v)) }.toSeq)
     }
 
+  implicit def scopeMessageUidMapReads[V](implicit vr: Reads[V]): Reads[Map[MessageUid, V]] =
+    Reads.mapReads[MessageUid, V] { str =>
+      JsSuccess(MessageUid.of(str.toLong))
+    }
+
+  implicit def scopeMessageUidMapWrite[V](implicit vr: Writes[V]): Writes[Map[MessageUid, V]] =
+    (m: Map[MessageUid, V]) => {
+      JsObject(m.map { case (k, v) => (String.valueOf(k.asLong()), vr.writes(v)) }.toSeq)
+    }
+
   implicit val aclDiffReads: Reads[ACLDiff] = Json.reads[ACLDiff]
   implicit val mailboxPathReads: Reads[MailboxPath] = Json.reads[MailboxPath]
   implicit val quotaCReads: Reads[Quota[QuotaCount]] = Json.reads[Quota[QuotaCount]]
   implicit val quotaSReads: Reads[Quota[QuotaSize]] = Json.reads[Quota[QuotaSize]]
+  implicit val messageMetaDataReads: Reads[MetaDataDTO.MessageMetaData] = Json.reads[MetaDataDTO.MessageMetaData]
 
   implicit val eventOFormat: OFormat[Event] = derived.oformat()
 
@@ -194,12 +248,13 @@ private class JsonSerialize(mailboxIdFactory: MailboxId.Factory) {
   def fromJson(json: String): JsResult[Event] = Json.fromJson[Event](Json.parse(json))
 }
 
-class EventSerializer(mailboxIdFactory: MailboxId.Factory) {
-  def toJson(event: JavaEvent): String = new JsonSerialize(mailboxIdFactory).toJson(ScalaConverter.toScala(event))
+class EventSerializer(mailboxIdFactory: MailboxId.Factory, messageIdFactory: MessageId.Factory) {
+  def toJson(event: JavaEvent): String = new JsonSerialize(mailboxIdFactory, messageIdFactory).toJson(ScalaConverter.toScala(event))
 
   def fromJson(json: String): JsResult[JavaEvent] = {
-    new JsonSerialize(mailboxIdFactory)
+    new JsonSerialize(mailboxIdFactory, messageIdFactory)
       .fromJson(json)
       .map(event => event.toJava)
   }
 }
+

http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/mailbox/event/json/src/main/scala/org/apache/james/event/json/MetaDataDTO.scala
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/main/scala/org/apache/james/event/json/MetaDataDTO.scala b/mailbox/event/json/src/main/scala/org/apache/james/event/json/MetaDataDTO.scala
new file mode 100644
index 0000000..ee314f0
--- /dev/null
+++ b/mailbox/event/json/src/main/scala/org/apache/james/event/json/MetaDataDTO.scala
@@ -0,0 +1,90 @@
+/** **************************************************************
+  * 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.event.json
+
+import java.time.Instant
+import java.util.Date
+
+import javax.mail.{Flags => JavaMailFlags}
+import org.apache.james.mailbox.model.{MessageId, MessageMetaData => JavaMessageMetaData}
+import org.apache.james.mailbox.{FlagsBuilder, MessageUid}
+
+object MetaDataDTO {
+
+  object MessageMetaData {
+    def fromJava(javaMessageMetaData: JavaMessageMetaData): MessageMetaData = MetaDataDTO.MessageMetaData(
+      javaMessageMetaData.getUid,
+      javaMessageMetaData.getModSeq,
+      javaMessageMetaData.getFlags,
+      javaMessageMetaData.getSize,
+      javaMessageMetaData.getInternalDate.toInstant,
+      javaMessageMetaData.getMessageId)
+  }
+
+  object Flags {
+    val ANSWERED = "\\Answered"
+    val DELETED = "\\Deleted"
+    val DRAFT = "\\Draft"
+    val FLAGGED = "\\Flagged"
+    val RECENT = "\\Recent"
+    val SEEN = "\\Seen"
+    val ALL_SYSTEM_FLAGS = List(ANSWERED, DELETED, DRAFT, FLAGGED, RECENT, SEEN)
+
+    def toJavaFlags(serializedFlags: Array[String]): JavaMailFlags = {
+      serializedFlags
+        .map(toFlagBuilder)
+        .reduceOption(_ merge _)
+        .getOrElse(new FlagsBuilder())
+        .build()
+    }
+
+    def toFlagBuilder(flag: String): FlagsBuilder = ALL_SYSTEM_FLAGS.contains(flag) match {
+      case true => new FlagsBuilder().add(stringToSystemFlag(flag))
+      case false => new FlagsBuilder().add(flag)
+    }
+
+    def fromJavaFlags(flags: JavaMailFlags): Array[String] = {
+      flags.getUserFlags ++ flags.getSystemFlags.map(flag => systemFlagToString(flag))
+    }
+
+    private def stringToSystemFlag(serializedFlag: String): JavaMailFlags.Flag = serializedFlag match {
+      case ANSWERED => JavaMailFlags.Flag.ANSWERED
+      case DELETED => JavaMailFlags.Flag.DELETED
+      case DRAFT => JavaMailFlags.Flag.DRAFT
+      case FLAGGED => JavaMailFlags.Flag.FLAGGED
+      case RECENT => JavaMailFlags.Flag.RECENT
+      case SEEN => JavaMailFlags.Flag.SEEN
+      case _ => throw new IllegalArgumentException(serializedFlag + " is not a system flag")
+    }
+
+    private def systemFlagToString(flag: JavaMailFlags.Flag): String = flag match {
+      case JavaMailFlags.Flag.ANSWERED => ANSWERED
+      case JavaMailFlags.Flag.DELETED => DELETED
+      case JavaMailFlags.Flag.DRAFT => DRAFT
+      case JavaMailFlags.Flag.FLAGGED => FLAGGED
+      case JavaMailFlags.Flag.RECENT => RECENT
+      case JavaMailFlags.Flag.SEEN => SEEN
+    }
+  }
+
+  case class MessageMetaData(uid: MessageUid, modSeq: Long, flags: JavaMailFlags, size: Long, internalDate: Instant, messageId: MessageId) {
+    def toJava: JavaMessageMetaData = new JavaMessageMetaData(uid, modSeq, flags, size, Date.from(internalDate), messageId)
+  }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java b/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
new file mode 100644
index 0000000..3ddc475
--- /dev/null
+++ b/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
@@ -0,0 +1,1465 @@
+/****************************************************************
+ * 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.event.json;
+
+import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
+import static net.javacrumbs.jsonunit.core.Option.IGNORING_ARRAY_ORDER;
+import static org.apache.james.mailbox.model.MailboxConstants.USER_NAMESPACE;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.time.Instant;
+import java.util.Date;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import javax.mail.Flags;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.FlagsBuilder;
+import org.apache.james.mailbox.MailboxListener;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.model.MailboxConstants;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.MessageMetaData;
+import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.TestMessageId;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+class AddedSerializationTest {
+
+    private static final User USER = User.fromUsername("user");
+    private static final MailboxSession.SessionId SESSION_ID = MailboxSession.SessionId.of(42);
+    private static final MailboxId MAILBOX_ID = TestId.of(18);
+    private static final String MAILBOX_NAME = "mailboxName";
+    private static final MailboxPath MAILBOX_PATH = new MailboxPath(MailboxConstants.USER_NAMESPACE, "user", MAILBOX_NAME);
+    private static final MessageUid MESSAGE_UID = MessageUid.of(123456);
+    private static final Instant INSTANT = Instant.parse("2018-12-14T09:41:51.541Z");
+    private static final TestMessageId MESSAGE_ID = TestMessageId.of(42);
+    private static final int MOD_SEQ = 35;
+    private static final int SIZE = 45;
+    private static final Flags FLAGS = FlagsBuilder.builder()
+        .add(Flags.Flag.ANSWERED, Flags.Flag.DRAFT)
+        .add("User Custom Flag")
+        .build();
+    private static final Map<MessageUid, MessageMetaData> ADDED = ImmutableMap.of(
+        MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(INSTANT), MESSAGE_ID));
+
+    private static final MailboxListener.Added DEFAULT_ADDED_EVENT = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, ADDED);
+    private static final String DEFAULT_ADDED_EVENT_JSON = 
+        "{" +
+        "  \"Added\": {" +
+        "    \"path\": {" +
+        "      \"namespace\": \"#private\"," +
+        "      \"user\": \"user\"," +
+        "      \"name\": \"mailboxName\"" +
+        "    }," +
+        "    \"mailboxId\": \"18\"," +
+        "    \"added\": {" +
+        "      \"123456\": {" +
+        "        \"uid\": 123456," +
+        "        \"modSeq\": 35," +
+        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+        "        \"size\": 45,  " +
+        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+        "        \"messageId\": \"42\"" +
+        "      }" +
+        "    }," +
+        "    \"sessionId\": 42," +
+        "    \"user\": \"user\"" +
+        "  }" +
+        "}";
+
+    private static final EventSerializer EVENT_SERIALIZER = new EventSerializer(new TestId.Factory(), new TestMessageId.Factory());
+
+    @Test
+    void addedShouldBeWellSerialized() {
+        assertThatJson(EVENT_SERIALIZER.toJson(DEFAULT_ADDED_EVENT))
+            .isEqualTo(DEFAULT_ADDED_EVENT_JSON);
+    }
+
+    @Test
+    void addedShouldBeWellDeSerialized() {
+        assertThat(EVENT_SERIALIZER.fromJson(DEFAULT_ADDED_EVENT_JSON).get())
+            .isEqualTo(DEFAULT_ADDED_EVENT);
+    }
+
+    @Nested
+    class WithEmptyAddedMap {
+
+        private final MailboxListener.Added emptyAddedEvent = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, ImmutableMap.of());
+        private final String emptyAddedEventJson =
+            "{" +
+            "  \"Added\": {" +
+            "    \"path\": {" +
+            "      \"namespace\": \"#private\"," +
+            "      \"user\": \"user\"," +
+            "      \"name\": \"mailboxName\"" +
+            "    }," +
+            "    \"mailboxId\": \"18\"," +
+            "    \"added\": {}," +
+            "    \"sessionId\": 42," +
+            "    \"user\": \"user\"" +
+            "  }" +
+            "}";
+
+        @Test
+        void addedShouldBeWellSerializedWhenMapKeyIsEmpty() {
+            assertThatJson(EVENT_SERIALIZER.toJson(emptyAddedEvent))
+                .isEqualTo(emptyAddedEventJson);
+        }
+
+        @Test
+        void addedShouldBeWellDeSerializedWhenMapKeyIsEmpty() {
+            assertThat(EVENT_SERIALIZER.fromJson(emptyAddedEventJson).get())
+                .isEqualTo(emptyAddedEvent);
+        }
+    }
+
+    @Nested
+    class WithFlags {
+
+        @Nested
+        class WithEmptyFlags {
+            private final Flags emptyFlags = new FlagsBuilder().build();
+            private final MailboxListener.Added emptyFlagsAddedEvent = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID,
+                ImmutableMap.of(
+                    MESSAGE_UID,
+                    new MessageMetaData(MESSAGE_UID, MOD_SEQ, emptyFlags, SIZE, Date.from(INSTANT), MESSAGE_ID)));
+
+            private final String emptyFlagsAddedEventJson =
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": []," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+            @Test
+            void addedShouldBeWellSerializedWhenEmptyFlags() {
+                assertThatJson(EVENT_SERIALIZER.toJson(emptyFlagsAddedEvent))
+                    .isEqualTo(emptyFlagsAddedEventJson);
+            }
+
+            @Test
+            void addedShouldBeWellDeSerializedWhenEmptyFlags() {
+                assertThat(EVENT_SERIALIZER.fromJson(emptyFlagsAddedEventJson).get())
+                    .isEqualTo(emptyFlagsAddedEvent);
+            }
+        }
+
+        @Nested
+        class WithOnlyUserFlags {
+            private final Flags onlyUserFlags = new FlagsBuilder()
+                .add("Custom 1", "Custom 2", "")
+                .build();
+            private final MailboxListener.Added onlyUserFlagsAddedEvent = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID,
+                ImmutableMap.of(
+                    MESSAGE_UID,
+                    new MessageMetaData(MESSAGE_UID, MOD_SEQ, onlyUserFlags, SIZE, Date.from(INSTANT), MESSAGE_ID)));
+
+            private final String userOnlyFlagsAddedEventJson =
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"Custom 1\", \"Custom 2\", \"\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+            @Test
+            void addedShouldBeWellSerializedWhenOnlyUserFlags() {
+                assertThatJson(EVENT_SERIALIZER.toJson(onlyUserFlagsAddedEvent))
+                    .when(IGNORING_ARRAY_ORDER)
+                    .isEqualTo(userOnlyFlagsAddedEventJson);
+            }
+
+            @Test
+            void addedShouldBeWellDeSerializedWhenOnlyUserFlags() {
+                assertThat(EVENT_SERIALIZER.fromJson(userOnlyFlagsAddedEventJson).get())
+                    .isEqualTo(onlyUserFlagsAddedEvent);
+            }
+        }
+
+        @Nested
+        class WithOnlySystemFlags {
+            private final Flags onlySystemFlags = new FlagsBuilder()
+                .add(Flags.Flag.SEEN, Flags.Flag.ANSWERED, Flags.Flag.DELETED)
+                .build();
+            private final MailboxListener.Added onlySystemFlagsAddedEvent = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID,
+                ImmutableMap.of(
+                    MESSAGE_UID,
+                    new MessageMetaData(MESSAGE_UID, MOD_SEQ, onlySystemFlags, SIZE, Date.from(INSTANT), MESSAGE_ID)));
+
+            private final String systemOnlyFlagsAddedEventJson =
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"\\\\Seen\", \"\\\\Answered\", \"\\\\Deleted\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+            @Test
+            void addedShouldBeWellSerializedWhenOnlySystemFlags() {
+                assertThatJson(EVENT_SERIALIZER.toJson(onlySystemFlagsAddedEvent))
+                    .when(IGNORING_ARRAY_ORDER)
+                    .isEqualTo(systemOnlyFlagsAddedEventJson);
+            }
+
+            @Test
+            void addedShouldBeWellDeSerializedWhenOnlySystemFlags() {
+                assertThat(EVENT_SERIALIZER.fromJson(systemOnlyFlagsAddedEventJson).get())
+                    .isEqualTo(onlySystemFlagsAddedEvent);
+            }
+        }
+
+        @Nested
+        class WithFlagCaseSensitive {
+
+            private static final String CASE_SENSITIVE_SYSTEM_FLAGS_EVENT_JSON =
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\answereD\", \"\\\\dRaFt\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+             @Test
+            void addedShouldCareAboutSystemFlagsCaseSensitive() {
+                 MailboxListener.Added deSerializedEvent = (MailboxListener.Added) EVENT_SERIALIZER
+                     .fromJson(CASE_SENSITIVE_SYSTEM_FLAGS_EVENT_JSON)
+                     .get();
+
+                 assertThat(deSerializedEvent.getMetaData(MESSAGE_UID).getFlags().getSystemFlags())
+                     .isEmpty();
+            }
+        }
+    }
+
+    @Nested
+    class WithInternalDate {
+
+        @Test
+        void addedShouldDeserializeWhenInternalDateIsInGoodISOFormat() {
+            Map<MessageUid, MessageMetaData> added = ImmutableMap.of(
+                MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(Instant.parse("2018-12-14T09:41:51Z")), MESSAGE_ID));
+            MailboxListener.Added eventRoundToMillis = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, added);
+
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51+00:00\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(eventRoundToMillis);
+        }
+
+        @Test
+        void addedShouldDeserializeWhenInternalDateIsMissingMilliSeconds() {
+            Map<MessageUid, MessageMetaData> added = ImmutableMap.of(
+                MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(Instant.parse("2018-12-14T09:41:51Z")), MESSAGE_ID));
+            MailboxListener.Added eventRoundToMillis = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, added);
+
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(eventRoundToMillis);
+        }
+
+        @Test
+        void addedShouldDeserializeWhenInternalDateIsMissingSeconds() {
+            Map<MessageUid, MessageMetaData> added = ImmutableMap.of(
+                MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(Instant.parse("2018-12-14T09:41:00Z")), MESSAGE_ID));
+            MailboxListener.Added eventRoundToMinute = new MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, added);
+
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(eventRoundToMinute);
+        }
+    }
+    @Nested
+    class NullOrEmptyNameSpaceInMailboxPath {
+
+        @Test
+        void addedShouldBeWellDeSerializedWhenNullNameSpace() {
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(DEFAULT_ADDED_EVENT);
+        }
+
+        @Test
+        void addedShouldBeWellDeSerializedWhenEmptyNameSpace() {
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(DEFAULT_ADDED_EVENT);
+        }
+    }
+
+    @Nested
+    class NullUserInMailboxPath {
+        private final String nullUser = null;
+        private final MailboxListener.Added eventWithNullUserInPath = new MailboxListener.Added(
+            SESSION_ID,
+            USER,
+            new MailboxPath(USER_NAMESPACE, nullUser, MAILBOX_NAME),
+            MAILBOX_ID,
+            ADDED);
+
+        private static final String EVENT_JSON_WITH_NULL_USER_IN_PATH =
+            "{" +
+            "  \"Added\": {" +
+            "    \"path\": {" +
+            "      \"namespace\": \"#private\"," +
+            "      \"name\": \"mailboxName\"" +
+            "    }," +
+            "    \"mailboxId\": \"18\"," +
+            "    \"added\": {" +
+            "      \"123456\": {" +
+            "        \"uid\": 123456," +
+            "        \"modSeq\": 35," +
+            "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+            "        \"size\": 45,  " +
+            "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+            "        \"messageId\": \"42\"" +
+            "      }" +
+            "    }," +
+            "    \"sessionId\": 42," +
+            "    \"user\": \"user\"" +
+            "  }" +
+            "}";
+
+        @Test
+        void addedShouldBeWellSerialized() {
+            assertThatJson(EVENT_SERIALIZER.toJson(eventWithNullUserInPath))
+                .isEqualTo(EVENT_JSON_WITH_NULL_USER_IN_PATH);
+        }
+
+        @Test
+        void addedShouldBeWellDeSerialized() {
+            assertThat(EVENT_SERIALIZER.fromJson(EVENT_JSON_WITH_NULL_USER_IN_PATH).get())
+                .isEqualTo(eventWithNullUserInPath);
+        }
+    }
+
+    @Nested
+    class DeserializationErrors {
+
+        @Nested
+        class DeserializationErrorOnSessionId {
+            @Test
+            void addedShouldThrowWhenMissingSessionId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"added\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void addedShouldThrowWhenNullSessionId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"added\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": null," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void addedShouldThrowWhenStringSessionId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"added\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": \"42\"," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+        }
+
+        @Nested
+        class DeserializationErrorOnUser {
+            @Test
+            void addedShouldThrowWhenMissingUser() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"added\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void addedShouldThrowWhenUserIsNotAString() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"added\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": 596" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void addedShouldThrowWhenUserIsNotWellFormatted() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"added\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": \"user@user@anotherUser\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(IllegalArgumentException.class);
+            }
+        }
+
+        @Nested
+        class DeserializationErrorOnMailboxId {
+            @Test
+            void addedShouldThrowWhenMissingMailboxId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"added\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void addedShouldThrowWhenNullMailboxId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": null," +
+                    "    \"added\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void addedShouldThrowWhenMailboxIdIsANumber() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": 18," +
+                    "    \"added\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+        }
+
+        @Nested
+        class DeserializationErrorOnMailboxPath {
+
+            @Nested
+            class DeserializationErrorOnNameSpace {
+                @Test
+                void addedShouldThrowWhenNameSpaceIsNotAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 48246," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnUser {
+                @Test
+                void addedShouldThrowWhenUserIsNotAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": 265412.64," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnMailboxName {
+
+                @Test
+                void addedShouldThrowWhenNullMailboxName() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void addedShouldThrowWhenMailboxNameIdIsANumber() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": 11861" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+        }
+
+        @Nested
+        class DeserializationErrorOnAddedMap {
+            @Test
+            void addedShouldThrowWhenMapKeyIsNull() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"added\": null," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Nested
+            class DeserializationErrorOnMessageUid {
+
+                @Test
+                void addedShouldThrowWhenMessageUidIsAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": \"123456\"," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void addedShouldThrowWhenMessageUidIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": null," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnModSeq {
+
+                @Test
+                void addedShouldThrowWhenModSeqIsAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": \"35\"," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void addedShouldThrowWhenModSeqIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": null," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+
+            @Nested
+            class DeserializationErrorOnSize {
+
+                @Test
+                void addedShouldThrowWhenSizeIsAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": \"45\",  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void addedShouldThrowWhenSizeIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": null,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnMessageId {
+
+                @Test
+                void addedShouldThrowWhenMessageIdIsANumber() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": 42" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void addedShouldThrowWhenMessageIdIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": null" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnInternalDate {
+                @Test
+                void addedShouldThrowWhenInternalDateIsNotInISOFormatBecauseOfMissingTWord() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14 12:52:36+07:00\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void addedShouldThrowWhenInternalDateContainsOnlyDate() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void addedShouldThrowWhenInternalDateIsMissingHourPart() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14TZ\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void addedShouldThrowWhenInternalDateIsMissingTimeZone() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void addedShouldThrowWhenInternalDateIsMissingHours() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void addedShouldThrowWhenInternalDateIsEmpty() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void addedShouldThrowWhenInternalDateIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": null," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnFlags {
+
+                @Test
+                void addedShouldThrowWhenFlagsIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": null," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void addedShouldThrowWhenFlagsContainsNullElements() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"flag 1\", null, \"flags 2\", null]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void addedShouldThrowWhenFlagsContainsNumberElements() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"flag 1\", 1254, \"flags 2\", 125.36]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxACLUpdatedEventSerializationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxACLUpdatedEventSerializationTest.java b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxACLUpdatedEventSerializationTest.java
index ceed360..14d3bf1 100644
--- a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxACLUpdatedEventSerializationTest.java
+++ b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxACLUpdatedEventSerializationTest.java
@@ -34,6 +34,7 @@ import org.apache.james.mailbox.model.MailboxACL;
 import org.apache.james.mailbox.model.MailboxConstants;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.TestMessageId;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
@@ -44,7 +45,7 @@ class MailboxACLUpdatedEventSerializationTest {
     private static final User USER = User.fromUsername("user");
     private static final MailboxACL.EntryKey ENTRY_KEY = org.apache.james.mailbox.model.MailboxACL.EntryKey.createGroupEntryKey("any", false);
     private static final MailboxACL.Rfc4314Rights RIGHTS = new MailboxACL.Rfc4314Rights(MailboxACL.Right.Administer, MailboxACL.Right.Read);
-    private static final EventSerializer EVENT_SERIALIZER = new EventSerializer(new TestId.Factory());
+    private static final EventSerializer EVENT_SERIALIZER = new EventSerializer(new TestId.Factory(), new TestMessageId.Factory());
     private static final MailboxACL MAILBOX_ACL = new MailboxACL(
         new MailboxACL.Entry(ENTRY_KEY, RIGHTS),
         new MailboxACL.Entry(MailboxACL.EntryKey.createUserEntryKey("alice", true),

http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxAddedSerializationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxAddedSerializationTest.java b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxAddedSerializationTest.java
index 0c7b3ea..339e4d6 100644
--- a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxAddedSerializationTest.java
+++ b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxAddedSerializationTest.java
@@ -31,6 +31,7 @@ import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.model.MailboxConstants;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.TestMessageId;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
@@ -38,7 +39,7 @@ class MailboxAddedSerializationTest {
 
     private static final User USER = User.fromUsername("user");
 
-    private static final EventSerializer EVENT_SERIALIZER = new EventSerializer(new TestId.Factory());
+    private static final EventSerializer EVENT_SERIALIZER = new EventSerializer(new TestId.Factory(), new TestMessageId.Factory());
 
     private static final MailboxListener.MailboxAdded EVENT_1 = new MailboxListener.MailboxAdded(
         MailboxSession.SessionId.of(42),


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


[10/10] james-project git commit: MAILBOX-363 Rely on iterable in Scala MessageMoveEvent

Posted by bt...@apache.org.
MAILBOX-363 Rely on iterable in Scala MessageMoveEvent

Only this is required for Java <-> scala transformation. Furthermore,
JavaSet.asScala returns an Iterable and not a Set....


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

Branch: refs/heads/master
Commit: e151594fda77db2dae2aa5f524e26f6afd367a3e
Parents: 26af5b2
Author: Benoit Tellier <bt...@linagora.com>
Authored: Tue Dec 18 09:19:41 2018 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Wed Dec 19 18:24:21 2018 +0700

----------------------------------------------------------------------
 .../java/org/apache/james/mailbox/MessageMoveEvent.java   |  2 +-
 .../java/org/apache/james/mailbox/model/MessageMoves.java |  4 ++--
 .../org/apache/james/event/json/EventSerializer.scala     | 10 +++++-----
 3 files changed, 8 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/e151594f/mailbox/api/src/main/java/org/apache/james/mailbox/MessageMoveEvent.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MessageMoveEvent.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MessageMoveEvent.java
index 5122766..d10058b 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MessageMoveEvent.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MessageMoveEvent.java
@@ -65,7 +65,7 @@ public class MessageMoveEvent implements Event {
             return this;
         }
 
-        public Builder messageId(Collection<MessageId> messageIds) {
+        public Builder messageId(Iterable<MessageId> messageIds) {
             this.messageIds.addAll(messageIds);
             return this;
         }

http://git-wip-us.apache.org/repos/asf/james-project/blob/e151594f/mailbox/api/src/main/java/org/apache/james/mailbox/model/MessageMoves.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MessageMoves.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MessageMoves.java
index a041a25..b4ef7ea 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MessageMoves.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MessageMoves.java
@@ -37,7 +37,7 @@ public class MessageMoves {
             targetMailboxIds = ImmutableSet.builder();
         }
 
-        public Builder previousMailboxIds(Collection<MailboxId> mailboxIds) {
+        public Builder previousMailboxIds(Iterable<MailboxId> mailboxIds) {
             previousMailboxIds.addAll(mailboxIds);
             return this;
         }
@@ -47,7 +47,7 @@ public class MessageMoves {
             return this;
         }
 
-        public Builder targetMailboxIds(Collection<MailboxId> mailboxIds) {
+        public Builder targetMailboxIds(Iterable<MailboxId> mailboxIds) {
             targetMailboxIds.addAll(mailboxIds);
             return this;
         }

http://git-wip-us.apache.org/repos/asf/james-project/blob/e151594f/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
index 31ebf49..3a64653 100644
--- a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
+++ b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
@@ -84,8 +84,8 @@ private object DTO {
       expunged.mapValues(_.toJava).asJava)
   }
 
-  case class MessageMoveEvent(user: User, previousMailboxIds: Seq[MailboxId], targetMailboxIds: Seq[MailboxId],
-                              messageIds: Seq[MessageId]) extends Event {
+  case class MessageMoveEvent(user: User, previousMailboxIds: Iterable[MailboxId], targetMailboxIds: Iterable[MailboxId],
+                              messageIds: Iterable[MessageId]) extends Event {
     override def toJava: JavaEvent = JavaMessageMoveEvent.builder()
       .user(user)
       .messageId(messageIds.asJava)
@@ -157,9 +157,9 @@ private object ScalaConverter {
 
   private def toScala(event: JavaMessageMoveEvent): DTO.MessageMoveEvent = DTO.MessageMoveEvent(
     user = event.getUser,
-    previousMailboxIds = event.getMessageMoves.getPreviousMailboxIds.asScala.toList,
-    targetMailboxIds = event.getMessageMoves.getTargetMailboxIds.asScala.toList,
-    messageIds = event.getMessageIds.asScala.toList)
+    previousMailboxIds = event.getMessageMoves.getPreviousMailboxIds.asScala,
+    targetMailboxIds = event.getMessageMoves.getTargetMailboxIds.asScala,
+    messageIds = event.getMessageIds.asScala)
 
 
   def toScala(javaEvent: JavaEvent): Event = javaEvent match {


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