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 rc...@apache.org on 2020/06/08 03:12:21 UTC

[james-project] branch master updated (9239b2f -> 0d1d98d)

This is an automated email from the ASF dual-hosted git repository.

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


    from 9239b2f  JAMES-3093 Organize AuthenticationContract and implementations
     new a984dbf  JAMES-3196 Add toString() for IMAP commands
     new 5dc7c56  JAMES-3196 IMAP Debug logs should allow reviewing issued commands
     new 0810b62  JAMES-3196 Add an IMAP SessionId to correlate logs
     new fafbcd9  JAMES-3196 Add sender/rcpt/helo to SMTP logs context
     new deef423  JAMES-3196 Carry other SMTP context on DATA hooks
     new 07a3f48  JAMES-3196 MailetContainer: Log correlation for sender aliases
     new 24fbe68  JAMES-3196 CanSendFromImpl: log unexpected exception
     new e70b869  JAMES-3196 CanSendFromImpl: enable sender correlation for SMTP and JMAP
     new 8ba14ba  JAMES-3171 Port QuotaLoader to jmap-rfc8621
     new 24482cc  JAMES-3171 Port MailboxFactory to jmap-rfc8621
     new 10e46aa  JAMES-3171 Add MailboxSession in Method and do a better handling of method processing in JMAPApiRoutes
     new d307d39  JAMES-3171 Mailbox/get all implementation
     new ac98902  JAMES-3171 Mailbox/get all integration tests
     new 613a87b  JAMES-3171 Add metrics to MailboxGetMethod process
     new eef1f7e  JAMES-3171 JsError serialization
     new 0d1d98d  JAMES-3171 MailboxFactory should take care of mailbox validation, not resolution

The 16 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../imap/api/message/request/SearchOperation.java  |   1 +
 .../apache/james/imap/api/process/ImapSession.java |  50 +++
 .../apache/james/imap/encode/FakeImapSession.java  |   7 +
 .../james/imap/message/request/AppendRequest.java  |  11 +
 .../imap/message/request/AuthenticateRequest.java  |   9 +
 .../james/imap/message/request/CopyRequest.java    |  11 +
 .../james/imap/message/request/CreateRequest.java  |   9 +
 .../imap/message/request/DeleteACLRequest.java     |   9 +
 .../james/imap/message/request/DeleteRequest.java  |   9 +
 .../james/imap/message/request/EnableRequest.java  |   9 +
 .../james/imap/message/request/ExamineRequest.java |  14 +
 .../james/imap/message/request/ExpungeRequest.java |   8 +
 .../james/imap/message/request/FetchRequest.java   |  10 +
 .../james/imap/message/request/GetACLRequest.java  |   8 +
 .../imap/message/request/GetAnnotationRequest.java |  11 +
 .../imap/message/request/GetQuotaRequest.java      |   8 +
 .../imap/message/request/GetQuotaRootRequest.java  |   8 +
 .../james/imap/message/request/ListRequest.java    |  10 +
 .../imap/message/request/ListRightsRequest.java    |   9 +
 .../james/imap/message/request/LoginRequest.java   |   9 +
 .../james/imap/message/request/LsubRequest.java    |  10 +
 .../james/imap/message/request/MoveRequest.java    |  10 +
 .../imap/message/request/MyRightsRequest.java      |   9 +
 .../james/imap/message/request/RenameRequest.java  |  10 +
 .../james/imap/message/request/SearchRequest.java  |  10 +
 .../james/imap/message/request/SelectRequest.java  |  15 +
 .../james/imap/message/request/SetACLRequest.java  |  10 +
 .../imap/message/request/SetAnnotationRequest.java |   8 +
 .../imap/message/request/SetQuotaRequest.java      |   8 +
 .../james/imap/message/request/StatusRequest.java  |  10 +
 .../imap/message/request/SubscribeRequest.java     |   9 +
 .../imap/message/request/UnselectRequest.java      |   8 +
 .../imap/message/request/UnsubscribeRequest.java   |   9 +
 .../processor/base/AbstractChainedProcessor.java   |   1 +
 .../netty/BasicChannelUpstreamHandler.java         |  21 +-
 .../apache/james/protocols/netty/NettyServer.java  |   2 +-
 ...Context.java => ProtocolMDCContextFactory.java} |  28 +-
 .../smtp/core/SMTPMDCContextFactory.java}          |  52 +--
 .../james/jmap/rfc8621/RFC8621MethodsModule.java   |   6 +-
 .../org/apache/james/rrt/lib/CanSendFromImpl.java  |   9 +-
 .../james/mailetcontainer/impl/LocalResources.java |   8 +-
 .../methods/integration/SetMessagesMethodTest.java |   4 +-
 .../methods/MailboxSendingNotAllowedException.java |  21 +-
 .../methods/SetMessagesCreationProcessor.java      |  24 +-
 .../draft/methods/SetMessagesUpdateProcessor.java  |   5 +-
 ...t.java => DistributedMailboxGetMethodTest.java} |   6 +-
 .../james/jmap/rfc8621/contract/Fixture.scala      |   2 +
 .../contract/MailboxGetMethodContract.scala        | 476 +++++++++++++++++++++
 ...odTest.java => MemoryMailboxGetMethodTest.java} |   4 +-
 .../org/apache/james/jmap/json/Serializer.scala    |  62 +--
 .../scala/org/apache/james/jmap/mail/Mailbox.scala |  48 ++-
 .../scala/org/apache/james/jmap/mail/Quotas.scala  |  10 +-
 .../{CoreEcho.scala => CoreEchoMethod.scala}       |   5 +-
 .../james/jmap/method/MailboxGetMethod.scala       | 104 +++++
 .../org/apache/james/jmap/method/Method.scala      |   5 +-
 .../apache/james/jmap/model/MailboxFactory.scala   | 158 +++++++
 .../org/apache/james/jmap/model/Session.scala      |   2 +-
 .../org/apache/james/jmap/model/UnsignedInt.scala  |  18 +-
 .../apache/james/jmap/routes/JMAPApiRoutes.scala   |  46 +-
 .../james/jmap/utils/quotas/QuotaLoader.scala      |  15 +-
 .../quotas/QuotaLoaderWithPreloadedDefault.scala   |  60 +++
 .../james/jmap/utils/quotas/QuotaReader.scala      |  76 ++--
 .../james/jmap/json/JsErrorSerializationTest.scala |  66 +++
 .../james/jmap/json/MailboxSerializationTest.scala |   2 +-
 ...CoreEchoTest.scala => CoreEchoMethodTest.scala} |  11 +-
 .../james/jmap/model/MailboxValidationTest.scala   | 138 ++++++
 .../james/jmap/routes/JMAPApiRoutesTest.scala      |   6 +-
 .../james/imapserver/netty/IMAPMDCContext.java     |   1 +
 .../netty/ImapChannelUpstreamHandler.java          |   6 +-
 .../james/imapserver/netty/NettyImapSession.java   |   9 +-
 .../apache/james/pop3server/netty/POP3Server.java  |   3 +-
 .../DataLineJamesMessageHookHandler.java           |   4 +-
 .../SenderAuthIdentifyVerificationRcptHook.java    |  12 +-
 .../netty/SMTPChannelUpstreamHandler.java          |   5 +-
 74 files changed, 1682 insertions(+), 205 deletions(-)
 copy protocols/netty/src/main/java/org/apache/james/protocols/netty/{ProtocolMDCContext.java => ProtocolMDCContextFactory.java} (76%)
 rename protocols/{netty/src/main/java/org/apache/james/protocols/netty/ProtocolMDCContext.java => smtp/src/main/java/org/apache/james/protocols/smtp/core/SMTPMDCContextFactory.java} (50%)
 copy server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/{DistributedEchoMethodTest.java => DistributedMailboxGetMethodTest.java} (94%)
 create mode 100644 server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
 copy server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/{MemoryEchoMethodTest.java => MemoryMailboxGetMethodTest.java} (92%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/{CoreEcho.scala => CoreEchoMethod.scala} (86%)
 create mode 100644 server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
 create mode 100644 server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/MailboxFactory.scala
 copy backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/versions/table/CassandraSchemaVersionTable.java => server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/quotas/QuotaLoader.scala (77%)
 create mode 100644 server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/quotas/QuotaLoaderWithPreloadedDefault.scala
 copy mailbox/api/src/main/java/org/apache/james/mailbox/extractor/ParsedContent.java => server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/quotas/QuotaReader.scala (50%)
 create mode 100644 server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/JsErrorSerializationTest.scala
 rename server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/{CoreEchoTest.scala => CoreEchoMethodTest.scala} (84%)
 create mode 100644 server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/MailboxValidationTest.scala


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


[james-project] 09/16: JAMES-3171 Port QuotaLoader to jmap-rfc8621

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 8ba14ba0184a7b8b9a716e0ffcd241894b68c8e2
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Wed May 20 11:06:27 2020 +0700

    JAMES-3171 Port QuotaLoader to jmap-rfc8621
---
 .../scala/org/apache/james/jmap/mail/Quotas.scala  | 10 ++--
 .../org/apache/james/jmap/model/UnsignedInt.scala  | 12 ++++-
 .../quotas/QuotaLoader.scala}                      | 25 ++++-----
 .../quotas/QuotaLoaderWithPreloadedDefault.scala   | 60 ++++++++++++++++++++++
 .../james/jmap/utils/quotas/QuotaReader.scala      | 52 +++++++++++++++++++
 5 files changed, 142 insertions(+), 17 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Quotas.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Quotas.scala
index c9d4212..e107efe 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Quotas.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Quotas.scala
@@ -21,16 +21,16 @@ package org.apache.james.jmap.mail
 
 import org.apache.james.core.Domain
 import org.apache.james.jmap.model.UnsignedInt.UnsignedInt
-import org.apache.james.mailbox.model.{QuotaRoot => JavaQuotaRoot}
+import org.apache.james.mailbox.model.{QuotaRoot => ModelQuotaRoot}
 
 import scala.compat.java8.OptionConverters._
 
 object QuotaRoot{
-  def fromJava(quotaRoot: JavaQuotaRoot) = QuotaRoot(quotaRoot.getValue, quotaRoot.getDomain.asScala)
+  def toJmap(quotaRoot: ModelQuotaRoot) = QuotaRoot(quotaRoot.getValue, quotaRoot.getDomain.asScala)
 }
 
 case class QuotaRoot(value: String, domain: Option[Domain]) {
-  def asJava: JavaQuotaRoot = JavaQuotaRoot.quotaRoot(value, domain.asJava)
+  def toModel: ModelQuotaRoot = ModelQuotaRoot.quotaRoot(value, domain.asJava)
 }
 
 object Quotas {
@@ -51,6 +51,10 @@ case class QuotaId(quotaRoot: QuotaRoot) extends AnyVal {
   def getName: String = quotaRoot.value
 }
 
+object Quota {
+  def from(quota: Map[Quotas.Type, Value]) = new Quota(quota)
+}
+
 case class Quota(quota: Map[Quotas.Type, Value]) extends AnyVal
 
 case class Value(used: UnsignedInt, max: Option[UnsignedInt])
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/UnsignedInt.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/UnsignedInt.scala
index 3b51240..2fae439 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/UnsignedInt.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/UnsignedInt.scala
@@ -19,11 +19,19 @@
 
 package org.apache.james.jmap.model
 
+import eu.timepit.refined
 import eu.timepit.refined.api.Refined
 import eu.timepit.refined.numeric.Interval.Closed
 
 object UnsignedInt {
-//Unsigned int between [0, 2^53]
-type UnsignedInt = Long Refined Closed[0L, 9007199254740992L]
+  //Unsigned int between [0, 2^53]
+  type UnsignedIntConstraint = Closed[0L, 9007199254740992L]
+  type UnsignedInt = Long Refined UnsignedIntConstraint
+
+  def liftOrThrow(value: Long): UnsignedInt =
+    refined.refineV[UnsignedIntConstraint](value) match {
+      case Right(value) => value
+      case Left(error) => throw new IllegalArgumentException(error)
+    }
 
 }
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/UnsignedInt.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/quotas/QuotaLoader.scala
similarity index 56%
copy from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/UnsignedInt.scala
copy to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/quotas/QuotaLoader.scala
index 3b51240..0c770c7 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/UnsignedInt.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/quotas/QuotaLoader.scala
@@ -1,4 +1,4 @@
-/** **************************************************************
+/****************************************************************
  * 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        *
@@ -6,24 +6,25 @@
  * 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                 *
- * *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
  * Unless required by applicable law or agreed to in writing,   *
  * software distributed under the License is distributed on an  *
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
  * KIND, either express or implied.  See the License for the    *
  * specific language governing permissions and limitations      *
  * under the License.                                           *
- * ***************************************************************/
+ ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.utils.quotas
 
-import eu.timepit.refined.api.Refined
-import eu.timepit.refined.numeric.Interval.Closed
-
-object UnsignedInt {
-//Unsigned int between [0, 2^53]
-type UnsignedInt = Long Refined Closed[0L, 9007199254740992L]
+import org.apache.james.jmap.mail.Quotas
+import org.apache.james.mailbox.exception.MailboxException
+import org.apache.james.mailbox.model.MailboxPath
+import reactor.core.scala.publisher.SMono
 
+trait QuotaLoader {
+  @throws[MailboxException]
+  def getQuotas(mailboxPath: MailboxPath): SMono[Quotas]
 }
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/quotas/QuotaLoaderWithPreloadedDefault.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/quotas/QuotaLoaderWithPreloadedDefault.scala
new file mode 100644
index 0000000..8a4c458
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/quotas/QuotaLoaderWithPreloadedDefault.scala
@@ -0,0 +1,60 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.utils.quotas
+
+import javax.inject.Inject
+import org.apache.james.jmap.mail.{QuotaRoot, Quotas}
+import org.apache.james.mailbox.MailboxSession
+import org.apache.james.mailbox.exception.MailboxException
+import org.apache.james.mailbox.model.{MailboxPath, QuotaRoot => ModelQuotaRoot}
+import org.apache.james.mailbox.quota.UserQuotaRootResolver
+import reactor.core.scala.publisher.SMono
+
+
+class QuotaLoaderWithPreloadedDefaultFactory @Inject()(quotaRootResolver: UserQuotaRootResolver, quotaReader: QuotaReader) {
+
+  def loadFor(session: MailboxSession): SMono[QuotaLoaderWithPreloadedDefault] =
+    SMono.fromCallable(() => new QuotaLoaderWithPreloadedDefault(
+        quotaRootResolver,
+        quotaReader,
+        session,
+        getUserDefaultQuotas(session)))
+
+
+  @throws[MailboxException]
+  private def getUserDefaultQuotas(session:MailboxSession): SMono[Quotas] = {
+    val quotaRoot: ModelQuotaRoot = quotaRootResolver.forUser(session.getUser)
+    quotaReader.retrieveQuotas(QuotaRoot.toJmap(quotaRoot))
+  }
+}
+
+class QuotaLoaderWithPreloadedDefault(quotaRootResolver: UserQuotaRootResolver,
+                                      quotaReader: QuotaReader,
+                                      session: MailboxSession,
+                                      preloadedUserDefaultQuotas: SMono[Quotas]) extends QuotaLoader {
+  @throws[MailboxException]
+  override def getQuotas(mailboxPath: MailboxPath): SMono[Quotas] =
+    if (mailboxPath.belongsTo(session)) {
+      preloadedUserDefaultQuotas
+    } else {
+      val quotaRoot: ModelQuotaRoot = quotaRootResolver.getQuotaRoot(mailboxPath)
+      quotaReader.retrieveQuotas(QuotaRoot.toJmap(quotaRoot))
+    }
+}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/quotas/QuotaReader.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/quotas/QuotaReader.scala
new file mode 100644
index 0000000..2545c83
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/quotas/QuotaReader.scala
@@ -0,0 +1,52 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.utils.quotas
+
+import javax.inject.Inject
+import org.apache.james.core.quota.{QuotaLimitValue, QuotaUsageValue}
+import org.apache.james.jmap.mail._
+import org.apache.james.jmap.model.UnsignedInt
+import org.apache.james.jmap.model.UnsignedInt.UnsignedInt
+import org.apache.james.mailbox.exception.MailboxException
+import org.apache.james.mailbox.model.{Quota => ModelQuota}
+import org.apache.james.mailbox.quota.QuotaManager
+import reactor.core.scala.publisher.SMono
+
+class QuotaReader @Inject() (quotaManager: QuotaManager) {
+  @throws[MailboxException]
+  def retrieveQuotas(quotaRoot: QuotaRoot): SMono[Quotas] =
+    SMono.just(Quotas.from(
+      QuotaId.fromQuotaRoot(quotaRoot),
+      Quota.from(Map(
+        Quotas.Storage -> quotaToValue(quotaManager.getStorageQuota(quotaRoot.toModel)),
+        Quotas.Message -> quotaToValue(quotaManager.getMessageQuota(quotaRoot.toModel))))))
+
+  private def quotaToValue[T <: QuotaLimitValue[T], U <: QuotaUsageValue[U, T]](quota: ModelQuota[T, U]): Value =
+    Value(
+      UnsignedInt.liftOrThrow(quota.getUsed.asLong),
+      asNumber(quota.getLimit))
+
+  private def asNumber(value: QuotaLimitValue[_]): Option[UnsignedInt] =
+    if (value.isUnlimited) {
+      None
+    } else {
+      Some(UnsignedInt.liftOrThrow(value.asLong))
+    }
+}


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


[james-project] 16/16: JAMES-3171 MailboxFactory should take care of mailbox validation, not resolution

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 0d1d98d42f3f45eac8f8b1835bed28f4e5a72322
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Tue Jun 2 16:00:11 2020 +0700

    JAMES-3171 MailboxFactory should take care of mailbox validation, not resolution
---
 .../scala/org/apache/james/jmap/mail/Mailbox.scala |   6 +-
 .../james/jmap/method/MailboxGetMethod.scala       |  18 ++-
 .../apache/james/jmap/model/MailboxFactory.scala   |  87 +++++++++----
 .../org/apache/james/jmap/model/UnsignedInt.scala  |  10 +-
 .../james/jmap/model/MailboxValidationTest.scala   | 138 +++++++++++++++++++++
 5 files changed, 224 insertions(+), 35 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Mailbox.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Mailbox.scala
index fc03a7b..9f3cd6a 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Mailbox.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Mailbox.scala
@@ -118,10 +118,10 @@ object MailboxName {
   type MailboxNameConstraint = NonEmpty
   type MailboxName = String Refined MailboxNameConstraint
 
-  def liftOrThrow(value: String): MailboxName =
+  def validate(value: String): Either[IllegalArgumentException, MailboxName] =
     refined.refineV[MailboxNameConstraint](value) match {
-      case scala.util.Right(value) => value
-      case Left(error) => throw new IllegalArgumentException(error)
+      case Left(error) => Left(new IllegalArgumentException(error))
+      case scala.Right(value) => scala.Right(value)
     }
 }
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
index 22c38c6..5fb7d15 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
@@ -26,7 +26,7 @@ import org.apache.james.jmap.mail._
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model.State.INSTANCE
 import org.apache.james.jmap.model.{Invocation, MailboxFactory}
-import org.apache.james.jmap.utils.quotas.QuotaLoaderWithPreloadedDefaultFactory
+import org.apache.james.jmap.utils.quotas.{QuotaLoader, QuotaLoaderWithPreloadedDefaultFactory}
 import org.apache.james.mailbox.model.MailboxMetaData
 import org.apache.james.mailbox.model.search.MailboxQuery
 import org.apache.james.mailbox.{MailboxManager, MailboxSession}
@@ -79,8 +79,7 @@ class MailboxGetMethod @Inject() (serializer: Serializer,
         getAllMailboxesMetaData(mailboxSession).flatMapMany(mailboxesMetaData =>
           SFlux.fromIterable(mailboxesMetaData)
             .flatMap(mailboxMetaData =>
-              mailboxFactory.create(
-                mailboxMetaData = mailboxMetaData,
+              getMailboxOrThrow(mailboxMetaData = mailboxMetaData,
                 mailboxSession = mailboxSession,
                 allMailboxesMetadata = mailboxesMetaData,
                 quotaLoader = quotaLoader))))
@@ -89,4 +88,17 @@ class MailboxGetMethod @Inject() (serializer: Serializer,
   private def getAllMailboxesMetaData(mailboxSession: MailboxSession): SMono[Seq[MailboxMetaData]] =
     SFlux.fromPublisher(mailboxManager.searchReactive(MailboxQuery.builder.matchesAllMailboxNames.build, mailboxSession))
       .collectSeq()
+
+  private def getMailboxOrThrow(mailboxSession: MailboxSession,
+                                allMailboxesMetadata: Seq[MailboxMetaData],
+                                mailboxMetaData: MailboxMetaData,
+                                quotaLoader: QuotaLoader): SMono[Mailbox] =
+    mailboxFactory.create(mailboxMetaData = mailboxMetaData,
+      mailboxSession = mailboxSession,
+      allMailboxesMetadata = allMailboxesMetadata,
+      quotaLoader = quotaLoader)
+      .flatMap {
+        case Left(error) => SMono.raiseError(error)
+        case scala.Right(mailbox) => SMono.just(mailbox)
+      }
 }
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/MailboxFactory.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/MailboxFactory.scala
index caf9046..98c3eb7 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/MailboxFactory.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/MailboxFactory.scala
@@ -22,6 +22,7 @@ package org.apache.james.jmap.model
 import javax.inject.Inject
 import org.apache.james.jmap.mail.MailboxName.MailboxName
 import org.apache.james.jmap.mail._
+import org.apache.james.jmap.model.UnsignedInt.UnsignedInt
 import org.apache.james.jmap.utils.quotas.QuotaLoader
 import org.apache.james.mailbox.model.MailboxACL.EntryKey
 import org.apache.james.mailbox.model.{MailboxCounters, MailboxId, MailboxMetaData, MailboxPath, MailboxACL => JavaMailboxACL}
@@ -31,23 +32,51 @@ import reactor.core.scala.publisher.SMono
 import scala.jdk.CollectionConverters._
 import scala.jdk.OptionConverters._
 
-sealed trait MailboxConstructionOrder
+object MailboxValidation {
+  def validate(mailboxName: Either[IllegalArgumentException, MailboxName],
+               unreadEmails: Either[NumberFormatException, UnsignedInt],
+               unreadThreads: Either[NumberFormatException, UnsignedInt],
+               totalEmails: Either[NumberFormatException, UnsignedInt],
+               totalThreads: Either[NumberFormatException, UnsignedInt]): Either[Exception, MailboxValidation] = {
+    for {
+      validatedName <- mailboxName
+      validatedUnreadEmails <- unreadEmails.map(UnreadEmails)
+      validatedUnreadThreads <- unreadThreads.map(UnreadThreads)
+      validatedTotalEmails <- totalEmails.map(TotalEmails)
+      validatedTotalThreads <- totalThreads.map(TotalThreads)
+    } yield MailboxValidation(
+      mailboxName = validatedName,
+      unreadEmails = validatedUnreadEmails,
+      unreadThreads = validatedUnreadThreads,
+      totalEmails = validatedTotalEmails,
+      totalThreads = validatedTotalThreads)
+  }
+}
 
-class Factory
+case class MailboxValidation(mailboxName: MailboxName,
+                             unreadEmails: UnreadEmails,
+                             unreadThreads: UnreadThreads,
+                             totalEmails: TotalEmails,
+                             totalThreads: TotalThreads)
 
 class MailboxFactory @Inject() (subscriptionManager: SubscriptionManager) {
 
+  private def retrieveMailboxName(mailboxPath: MailboxPath, mailboxSession: MailboxSession): Either[IllegalArgumentException, MailboxName] =
+    mailboxPath.getName
+      .split(mailboxSession.getPathDelimiter)
+      .lastOption match {
+        case Some(name) => MailboxName.validate(name)
+        case None => Left(new IllegalArgumentException("No name for the mailbox found"))
+      }
+
   def create(mailboxMetaData: MailboxMetaData,
              mailboxSession: MailboxSession,
              allMailboxesMetadata: Seq[MailboxMetaData],
-             quotaLoader: QuotaLoader): SMono[Mailbox] = {
+             quotaLoader: QuotaLoader): SMono[Either[Exception, Mailbox]] = {
 
     val id: MailboxId = mailboxMetaData.getId
 
-    val name: MailboxName = MailboxName.liftOrThrow(mailboxMetaData.getPath
-      .getName
-      .split(mailboxSession.getPathDelimiter)
-      .last)
+    val name: Either[IllegalArgumentException, MailboxName] = retrieveMailboxName(mailboxMetaData.getPath, mailboxSession)
 
     val role: Option[Role] = Role.from(mailboxMetaData.getPath.getName)
       .filter(_ => mailboxMetaData.getPath.belongsTo(mailboxSession)).toScala
@@ -56,10 +85,10 @@ class MailboxFactory @Inject() (subscriptionManager: SubscriptionManager) {
     val rights: Rights = Rights.fromACL(MailboxACL.fromJava(mailboxMetaData.getResolvedAcls))
 
     val sanitizedCounters: MailboxCounters = mailboxMetaData.getCounters.sanitize()
-    val unreadEmails: UnreadEmails = UnreadEmails(UnsignedInt.liftOrThrow(sanitizedCounters.getUnseen))
-    val unreadThreads: UnreadThreads = UnreadThreads(UnsignedInt.liftOrThrow(sanitizedCounters.getUnseen))
-    val totalEmails: TotalEmails = TotalEmails(UnsignedInt.liftOrThrow(sanitizedCounters.getCount))
-    val totalThreads: TotalThreads = TotalThreads(UnsignedInt.liftOrThrow(sanitizedCounters.getCount))
+    val unreadEmails: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(sanitizedCounters.getUnseen)
+    val unreadThreads: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(sanitizedCounters.getUnseen)
+    val totalEmails: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(sanitizedCounters.getCount)
+    val totalThreads: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(sanitizedCounters.getCount)
 
     val isOwner = mailboxMetaData.getPath.belongsTo(mailboxSession)
     val aclEntryKey: EntryKey = EntryKey.createUserEntryKey(mailboxSession.getUser)
@@ -105,21 +134,25 @@ class MailboxFactory @Inject() (subscriptionManager: SubscriptionManager) {
       .subscriptions(mailboxSession)
       .contains(mailboxMetaData.getPath.getName))
 
-    SMono.fromPublisher(quotas)
-      .map(quotas => Mailbox(
-        id = id,
-        name = name,
-        parentId = parentId,
-        role = role,
-        sortOrder = sortOrder,
-        unreadEmails = unreadEmails,
-        totalEmails = totalEmails,
-        unreadThreads = unreadThreads,
-        totalThreads = totalThreads,
-        myRights = myRights,
-        namespace = namespace,
-        rights = rights,
-        quotas = quotas,
-        isSubscribed = retrieveIsSubscribed))
+    MailboxValidation.validate(name, unreadEmails, unreadThreads, totalEmails, totalThreads) match {
+      case Left(error) => SMono.just(Left(error))
+      case scala.Right(mailboxValidation) => SMono.fromPublisher(quotas)
+        .map(quotas => scala.Right(
+          Mailbox(
+            id = id,
+            name = mailboxValidation.mailboxName,
+            parentId = parentId,
+            role = role,
+            sortOrder = sortOrder,
+            unreadEmails = mailboxValidation.unreadEmails,
+            totalEmails = mailboxValidation.totalEmails,
+            unreadThreads = mailboxValidation.unreadThreads,
+            totalThreads = mailboxValidation.totalThreads,
+            myRights = myRights,
+            namespace = namespace,
+            rights = rights,
+            quotas = quotas,
+            isSubscribed = retrieveIsSubscribed)))
+    }
   }
 }
\ No newline at end of file
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/UnsignedInt.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/UnsignedInt.scala
index 2fae439..0cda8fa 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/UnsignedInt.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/UnsignedInt.scala
@@ -28,10 +28,16 @@ object UnsignedInt {
   type UnsignedIntConstraint = Closed[0L, 9007199254740992L]
   type UnsignedInt = Long Refined UnsignedIntConstraint
 
-  def liftOrThrow(value: Long): UnsignedInt =
+  def validate(value: Long): Either[NumberFormatException, UnsignedInt] =
     refined.refineV[UnsignedIntConstraint](value) match {
+      case Right(value) => Right(value)
+      case Left(error) => Left(new NumberFormatException(error))
+    }
+
+  def liftOrThrow(value: Long): UnsignedInt =
+    validate(value) match {
       case Right(value) => value
-      case Left(error) => throw new IllegalArgumentException(error)
+      case Left(error) => throw error
     }
 
 }
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/MailboxValidationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/MailboxValidationTest.scala
new file mode 100644
index 0000000..bbc5e06
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/MailboxValidationTest.scala
@@ -0,0 +1,138 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.model
+
+import eu.timepit.refined.auto._
+import org.apache.james.jmap.mail.MailboxName.MailboxName
+import org.apache.james.jmap.mail._
+import org.apache.james.jmap.model.UnsignedInt.UnsignedInt
+import org.scalatest.matchers.must.Matchers
+import org.scalatest.wordspec.AnyWordSpec
+
+object MailboxValidationTest {
+  private val mailboxName: MailboxName = "name"
+  private val unreadEmails: UnsignedInt = 1L
+  private val unreadThreads: UnsignedInt = 2L
+  private val totalEmails: UnsignedInt = 3L
+  private val totalThreads: UnsignedInt = 4L
+}
+
+class MailboxValidationTest extends AnyWordSpec with Matchers {
+  import MailboxValidationTest._
+
+  "MailboxValidation" should {
+    "succeed" in {
+      val validMailboxname: Either[IllegalArgumentException, MailboxName] = MailboxName.validate(mailboxName)
+      val validUnreadEmails: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(unreadEmails)
+      val validUnreadThreads: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(unreadThreads)
+      val validTotalEmails: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(totalEmails)
+      val validTotalThreads: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(totalThreads)
+
+      val expectedResult: Either[Exception, MailboxValidation] = scala.Right(MailboxValidation(
+        mailboxName = mailboxName,
+        unreadEmails = UnreadEmails(unreadEmails),
+        unreadThreads = UnreadThreads(unreadThreads),
+        totalEmails = TotalEmails(totalEmails),
+        totalThreads = TotalThreads(totalThreads)))
+
+      MailboxValidation.validate(
+        validMailboxname,
+        validUnreadEmails,
+        validUnreadThreads,
+        validTotalEmails,
+        validTotalThreads) must be(expectedResult)
+    }
+
+    "fail when mailboxName is invalid" in {
+      val invalidMailboxname: Either[IllegalArgumentException, MailboxName] = MailboxName.validate("")
+      val validUnreadEmails: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(unreadEmails)
+      val validUnreadThreads: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(unreadThreads)
+      val validTotalEmails: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(totalEmails)
+      val validTotalThreads: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(totalThreads)
+
+      MailboxValidation.validate(
+        invalidMailboxname,
+        validUnreadEmails,
+        validUnreadThreads,
+        validTotalEmails,
+        validTotalThreads) mustBe a[Left[_, _]]
+    }
+
+    "fail when unreadEmails is invalid" in {
+      val validMailboxname: Either[IllegalArgumentException, MailboxName] = MailboxName.validate(mailboxName)
+      val invalidUnreadEmails: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(-1L)
+      val validUnreadThreads: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(unreadThreads)
+      val validTotalEmails: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(totalEmails)
+      val validTotalThreads: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(totalThreads)
+
+      MailboxValidation.validate(
+        validMailboxname,
+        invalidUnreadEmails,
+        validUnreadThreads,
+        validTotalEmails,
+        validTotalThreads) mustBe a[Left[_, _]]
+    }
+
+    "fail when unreadThreads is invalid" in {
+      val validMailboxname: Either[IllegalArgumentException, MailboxName] = MailboxName.validate(mailboxName)
+      val validUnreadEmails: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(unreadEmails)
+      val invalidUnreadThreads: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(-1L)
+      val validTotalEmails: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(totalEmails)
+      val validTotalThreads: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(totalThreads)
+
+      MailboxValidation.validate(
+        validMailboxname,
+        validUnreadEmails,
+        invalidUnreadThreads,
+        validTotalEmails,
+        validTotalThreads) mustBe a[Left[_, _]]
+    }
+
+    "fail when totalEmails is invalid" in {
+      val validMailboxname: Either[IllegalArgumentException, MailboxName] = MailboxName.validate(mailboxName)
+      val validUnreadEmails: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(unreadEmails)
+      val validUnreadThreads: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(unreadThreads)
+      val invalidTotalEmails: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(-1L)
+      val validTotalThreads: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(totalThreads)
+
+      MailboxValidation.validate(
+        validMailboxname,
+        validUnreadEmails,
+        validUnreadThreads,
+        invalidTotalEmails,
+        validTotalThreads) mustBe a[Left[_, _]]
+    }
+
+    "fail when totalThreads is invalid" in {
+      val validMailboxname: Either[IllegalArgumentException, MailboxName] = MailboxName.validate(mailboxName)
+      val validUnreadEmails: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(unreadEmails)
+      val validUnreadThreads: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(unreadThreads)
+      val validTotalEmails: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(totalEmails)
+      val invalidTotalThreads: Either[NumberFormatException, UnsignedInt] = UnsignedInt.validate(-1L)
+
+      MailboxValidation.validate(
+        validMailboxname,
+        validUnreadEmails,
+        validUnreadThreads,
+        validTotalEmails,
+        invalidTotalThreads) mustBe a[Left[_, _]]
+    }
+  }
+}


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


[james-project] 11/16: JAMES-3171 Add MailboxSession in Method and do a better handling of method processing in JMAPApiRoutes

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 10e46aa3e8a43eef367360dac0bc313e8e1978fc
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Fri May 22 16:37:26 2020 +0700

    JAMES-3171 Add MailboxSession in Method and do a better handling of method processing in JMAPApiRoutes
---
 .../james/jmap/rfc8621/RFC8621MethodsModule.java   |  4 +-
 .../{CoreEcho.scala => CoreEchoMethod.scala}       |  5 ++-
 .../org/apache/james/jmap/method/Method.scala      |  3 +-
 .../apache/james/jmap/routes/JMAPApiRoutes.scala   | 45 ++++++++++++++--------
 ...CoreEchoTest.scala => CoreEchoMethodTest.scala} | 11 ++++--
 .../james/jmap/routes/JMAPApiRoutesTest.scala      |  6 ++-
 6 files changed, 49 insertions(+), 25 deletions(-)

diff --git a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java
index f085137..8d855de 100644
--- a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java
+++ b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java
@@ -27,7 +27,7 @@ import org.apache.james.jmap.http.BasicAuthenticationStrategy;
 import org.apache.james.jmap.http.rfc8621.InjectionKeys;
 import org.apache.james.jmap.json.Serializer;
 import org.apache.james.jmap.jwt.JWTAuthenticationStrategy;
-import org.apache.james.jmap.method.CoreEcho;
+import org.apache.james.jmap.method.CoreEchoMethod;
 import org.apache.james.jmap.method.Method;
 import org.apache.james.jmap.routes.JMAPApiRoutes;
 import org.apache.james.metrics.api.MetricFactory;
@@ -47,7 +47,7 @@ public class RFC8621MethodsModule extends AbstractModule {
         bind(Serializer.class).in(Scopes.SINGLETON);
 
         Multibinder<Method> methods = Multibinder.newSetBinder(binder(), Method.class);
-        methods.addBinding().to(CoreEcho.class);
+        methods.addBinding().to(CoreEchoMethod.class);
     }
 
     @ProvidesIntoSet
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEcho.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEchoMethod.scala
similarity index 86%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEcho.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEchoMethod.scala
index efeb0e7..fad57d8 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEcho.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEchoMethod.scala
@@ -22,11 +22,12 @@ package org.apache.james.jmap.method
 import eu.timepit.refined.auto._
 import org.apache.james.jmap.model.Invocation
 import org.apache.james.jmap.model.Invocation.MethodName
+import org.apache.james.mailbox.MailboxSession
 import org.reactivestreams.Publisher
 import reactor.core.scala.publisher.SMono
 
-class CoreEcho extends Method {
+class CoreEchoMethod extends Method {
   override val methodName = MethodName("Core/echo")
 
-  override def process(invocation: Invocation): Publisher[Invocation] = SMono.just(invocation)
+  override def process(invocation: Invocation, mailboxSession: MailboxSession): Publisher[Invocation] = SMono.just(invocation)
 }
\ No newline at end of file
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala
index 8ff5b6d..21b33b5 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala
@@ -21,11 +21,12 @@ package org.apache.james.jmap.method
 
 import org.apache.james.jmap.model.Invocation
 import org.apache.james.jmap.model.Invocation.MethodName
+import org.apache.james.mailbox.MailboxSession
 import org.reactivestreams.Publisher
 
 trait Method {
   val methodName: MethodName
 
-  def process(invocation: Invocation): Publisher[Invocation]
+  def process(invocation: Invocation, mailboxSession: MailboxSession): Publisher[Invocation]
 }
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
index 410ad77..478cdcc 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
@@ -35,10 +35,11 @@ import org.apache.james.jmap.exceptions.UnauthorizedException
 import org.apache.james.jmap.http.Authenticator
 import org.apache.james.jmap.http.rfc8621.InjectionKeys
 import org.apache.james.jmap.json.Serializer
-import org.apache.james.jmap.method.CoreEcho
+import org.apache.james.jmap.method.Method
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model.{Invocation, RequestObject, ResponseObject}
 import org.apache.james.jmap.{Endpoint, JMAPRoute, JMAPRoutes}
+import org.apache.james.mailbox.MailboxSession
 import org.slf4j.{Logger, LoggerFactory}
 import play.api.libs.json.{JsError, JsSuccess, Json}
 import reactor.core.publisher.Mono
@@ -46,13 +47,25 @@ import reactor.core.scala.publisher.{SFlux, SMono}
 import reactor.core.scheduler.Schedulers
 import reactor.netty.http.server.{HttpServerRequest, HttpServerResponse}
 
+import scala.collection.mutable
+import scala.jdk.CollectionConverters._
+
 object JMAPApiRoutes {
   val LOGGER: Logger = LoggerFactory.getLogger(classOf[JMAPApiRoutes])
 }
 
-class JMAPApiRoutes @Inject() (@Named(InjectionKeys.RFC_8621) val authenticator: Authenticator,
-                               val serializer: Serializer) extends JMAPRoutes {
-  private val coreEcho = new CoreEcho
+class JMAPApiRoutes (val authenticator: Authenticator,
+                     serializer: Serializer,
+                     methods: Set[Method]) extends JMAPRoutes {
+
+  private val methodsByName: Map[MethodName, Method] = methods.map(method => method.methodName -> method).toMap
+
+  @Inject
+  def this(@Named(InjectionKeys.RFC_8621) authenticator: Authenticator,
+           serializer: Serializer,
+           javaMethods: java.util.Set[Method]) {
+    this(authenticator, serializer, javaMethods.asScala.toSet)
+  }
 
   override def routes(): stream.Stream[JMAPRoute] = Stream.of(
     JMAPRoute.builder
@@ -66,8 +79,8 @@ class JMAPApiRoutes @Inject() (@Named(InjectionKeys.RFC_8621) val authenticator:
 
   private def post(httpServerRequest: HttpServerRequest, httpServerResponse: HttpServerResponse): Mono[Void] =
     SMono(authenticator.authenticate(httpServerRequest))
-      .flatMap(_ => this.requestAsJsonStream(httpServerRequest)
-        .flatMap(requestObject => this.process(requestObject, httpServerResponse)))
+      .flatMap((mailboxSession: MailboxSession) => this.requestAsJsonStream(httpServerRequest)
+        .flatMap(requestObject => this.process(requestObject, httpServerResponse, mailboxSession)))
       .onErrorResume(throwable => handleError(throwable, httpServerResponse))
       .subscribeOn(Schedulers.elastic)
       .asJava()
@@ -87,10 +100,12 @@ class JMAPApiRoutes @Inject() (@Named(InjectionKeys.RFC_8621) val authenticator:
       case JsError(_) => SMono.raiseError(new IllegalArgumentException("Invalid RequestObject"))
     }
 
-  private def process(requestObject: RequestObject, httpServerResponse: HttpServerResponse): SMono[Void] =
+  private def process(requestObject: RequestObject,
+                      httpServerResponse: HttpServerResponse,
+                      mailboxSession: MailboxSession): SMono[Void] =
     requestObject
       .methodCalls
-      .map(this.processMethodWithMatchName)
+      .map(invocation => this.processMethodWithMatchName(invocation, mailboxSession))
       .foldLeft(SFlux.empty[Invocation]) { (flux: SFlux[Invocation], mono: SMono[Invocation]) => flux.mergeWith(mono) }
       .collectSeq()
       .flatMap((invocations: Seq[Invocation]) =>
@@ -103,13 +118,13 @@ class JMAPApiRoutes @Inject() (@Named(InjectionKeys.RFC_8621) val authenticator:
           ).`then`())
       )
 
-  private def processMethodWithMatchName(invocation: Invocation): SMono[Invocation] = invocation.methodName match {
-    case coreEcho.methodName => SMono.fromPublisher(coreEcho.process(invocation))
-    case _ => SMono.just(new Invocation(
-      MethodName("error"),
-      Arguments(Json.obj("type" -> "Not implemented")),
-      invocation.methodCallId))
-  }
+  private def processMethodWithMatchName(invocation: Invocation, mailboxSession: MailboxSession): SMono[Invocation] =
+    SMono.justOrEmpty(methodsByName.get(invocation.methodName))
+      .flatMap(method => SMono.fromPublisher(method.process(invocation, mailboxSession)))
+      .switchIfEmpty(SMono.just(new Invocation(
+        MethodName("error"),
+        Arguments(Json.obj("type" -> "Not implemented")),
+        invocation.methodCallId)))
 
   private def handleError(throwable: Throwable, httpServerResponse: HttpServerResponse): SMono[Void] = throwable match {
     case exception: IllegalArgumentException => SMono.fromPublisher(httpServerResponse.status(SC_BAD_REQUEST)
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoMethodTest.scala
similarity index 84%
rename from server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoTest.scala
rename to server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoMethodTest.scala
index 2d78e5f..583e832 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoMethodTest.scala
@@ -20,25 +20,28 @@ package org.apache.james.jmap.method
 
 import org.apache.james.jmap.json.Fixture.{invocation1, invocation2}
 import org.apache.james.jmap.model.Invocation
+import org.apache.james.mailbox.MailboxSession
+import org.mockito.Mockito.mock
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec
 import reactor.core.scala.publisher.SMono
 
-class CoreEchoTest extends AnyWordSpec with Matchers {
-  private val echoMethod: CoreEcho = new CoreEcho()
+class CoreEchoMethodTest extends AnyWordSpec with Matchers {
+  private val echoMethod: CoreEchoMethod = new CoreEchoMethod()
+  private val mockedSession: MailboxSession = mock(classOf[MailboxSession])
 
   "CoreEcho" should {
     "Process" should {
       "success and return the same with parameters as the invocation request" in {
         val expectedResponse: Invocation = invocation1
-        val dataResponse = SMono.fromPublisher(echoMethod.process(invocation1)).block()
+        val dataResponse = SMono.fromPublisher(echoMethod.process(invocation1, mockedSession)).block()
 
         dataResponse shouldBe expectedResponse
       }
 
       "success and not return anything else different than the original invocation" in {
         val wrongExpected: Invocation = invocation2
-        val dataResponse = SMono.fromPublisher(echoMethod.process(invocation1)).block()
+        val dataResponse = SMono.fromPublisher(echoMethod.process(invocation1, mockedSession)).block()
         
         dataResponse should not be(wrongExpected)
       }
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/JMAPApiRoutesTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/JMAPApiRoutesTest.scala
index f23e835..37ffb9a 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/JMAPApiRoutesTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/JMAPApiRoutesTest.scala
@@ -37,6 +37,7 @@ import org.apache.james.jmap.JMAPUrls.JMAP
 import org.apache.james.jmap._
 import org.apache.james.jmap.http.{Authenticator, BasicAuthenticationStrategy}
 import org.apache.james.jmap.json.Serializer
+import org.apache.james.jmap.method.{CoreEchoMethod, Method}
 import org.apache.james.jmap.routes.JMAPApiRoutesTest._
 import org.apache.james.mailbox.MailboxManager
 import org.apache.james.mailbox.extension.PreDeletionHook
@@ -67,7 +68,10 @@ object JMAPApiRoutesTest {
   private val mailboxManager: MailboxManager = MemoryMailboxManagerProvider.provideMailboxManager(empty_set)
   private val authenticationStrategy: BasicAuthenticationStrategy = new BasicAuthenticationStrategy(usersRepository, mailboxManager)
   private val AUTHENTICATOR: Authenticator = Authenticator.of(new RecordingMetricFactory, authenticationStrategy)
-  private val JMAP_API_ROUTE: JMAPApiRoutes = new JMAPApiRoutes(AUTHENTICATOR, SERIALIZER)
+
+  private val JMAP_METHODS: Set[Method] = Set(new CoreEchoMethod)
+
+  private val JMAP_API_ROUTE: JMAPApiRoutes = new JMAPApiRoutes(AUTHENTICATOR, SERIALIZER, JMAP_METHODS)
   private val ROUTES_HANDLER: ImmutableSet[JMAPRoutesHandler] = ImmutableSet.of(new JMAPRoutesHandler(Version.RFC8621, JMAP_API_ROUTE))
 
   private val userBase64String: String = Base64.getEncoder.encodeToString("user1:password".getBytes(StandardCharsets.UTF_8))


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


[james-project] 08/16: JAMES-3196 CanSendFromImpl: enable sender correlation for SMTP and JMAP

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit e70b8696448b794b55fc46fba8bc028a13fe0f5d
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri May 29 09:17:21 2020 +0700

    JAMES-3196 CanSendFromImpl: enable sender correlation for SMTP and JMAP
    
    Explicitly link the user with the alias when allowing the mail
    submission to ease audit.
---
 .../methods/integration/SetMessagesMethodTest.java |  4 ++--
 .../methods/MailboxSendingNotAllowedException.java | 21 +++++++++++++------
 .../methods/SetMessagesCreationProcessor.java      | 24 ++++++++++++++++------
 .../draft/methods/SetMessagesUpdateProcessor.java  |  5 +++--
 .../SenderAuthIdentifyVerificationRcptHook.java    | 12 ++++++++++-
 5 files changed, 49 insertions(+), 17 deletions(-)

diff --git a/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/SetMessagesMethodTest.java b/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/SetMessagesMethodTest.java
index 46ab5db..38c9e99 100644
--- a/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/SetMessagesMethodTest.java
+++ b/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/SetMessagesMethodTest.java
@@ -2651,7 +2651,7 @@ public abstract class SetMessagesMethodTest {
             .body(ARGUMENTS + ".notCreated", aMapWithSize(1))
             .body(ARGUMENTS + ".notCreated", hasKey(messageCreationId))
             .body(ARGUMENTS + ".notCreated." + messageCreationId + ".type", equalTo("invalidProperties"))
-            .body(ARGUMENTS + ".notCreated." + messageCreationId + ".description", equalTo("Invalid 'from' field. Must be " + USERNAME.asString()));
+            .body(ARGUMENTS + ".notCreated." + messageCreationId + ".description", equalTo("Invalid 'from' field. One accepted value is " + USERNAME.asString()));
     }
 
     @Test
@@ -2999,7 +2999,7 @@ public abstract class SetMessagesMethodTest {
             .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].type", equalTo("invalidProperties"))
             .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].properties", hasSize(1))
             .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].properties", contains("from"))
-            .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].description", endsWith("Invalid 'from' field. Must be username@domain.tld"))
+            .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].description", endsWith("Invalid 'from' field. One accepted value is username@domain.tld"))
             .body(ARGUMENTS + ".created", aMapWithSize(0));
     }
 
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MailboxSendingNotAllowedException.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MailboxSendingNotAllowedException.java
index 19ac45f..8c3f0f5 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MailboxSendingNotAllowedException.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MailboxSendingNotAllowedException.java
@@ -19,18 +19,27 @@
 
 package org.apache.james.jmap.draft.methods;
 
+import java.util.Optional;
+
+import org.apache.james.core.Username;
 import org.apache.james.mailbox.exception.MailboxException;
 
 public class MailboxSendingNotAllowedException extends MailboxException {
 
-    private String allowedFrom;
+    private final Username connectedUser;
+    private final Optional<Username> fromField;
 
-    public MailboxSendingNotAllowedException(String allowedFrom) {
+    public MailboxSendingNotAllowedException(Username connectedUser, Optional<Username> fromField) {
         super();
-        this.allowedFrom = allowedFrom;
+        this.connectedUser = connectedUser;
+        this.fromField = fromField;
+    }
+
+    public Optional<Username> getFromField() {
+        return fromField;
     }
-    
-    public String getAllowedFrom() {
-        return allowedFrom;
+
+    public Username getConnectedUser() {
+        return connectedUser;
     }
 }
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessor.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessor.java
index 77c53b6..a384bd8 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessor.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessor.java
@@ -132,12 +132,14 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
             assertIsUserOwnerOfMailboxes(mailboxIds, mailboxSession);
             performCreate(create, responseBuilder, mailboxSession);
         } catch (MailboxSendingNotAllowedException e) {
+            LOG.debug("{} is not allowed to send a mail using {} identity", e.getConnectedUser().asString(), e.getFromField());
+
             responseBuilder.notCreated(create.getCreationId(),
                     SetError.builder()
                         .type(SetError.Type.INVALID_PROPERTIES)
                         .properties(MessageProperty.from)
-                        .description("Invalid 'from' field. Must be " +
-                                e.getAllowedFrom())
+                        .description("Invalid 'from' field. One accepted value is " +
+                                e.getConnectedUser().asString())
                         .build());
 
         } catch (InvalidDraftKeywordsException e) {
@@ -296,14 +298,24 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
 
     @VisibleForTesting
     void assertUserCanSendFrom(Username connectedUser, Optional<DraftEmailer> from) throws MailboxSendingNotAllowedException {
-        if (!from.flatMap(DraftEmailer::getEmail)
-                .filter(email -> canSendFrom.userCanSendFrom(connectedUser, Username.of(email)))
-                .isPresent()) {
+
+        Optional<Username> maybeFromUser = from.flatMap(DraftEmailer::getEmail)
+            .map(Username::of);
+
+        if (!canSendMailUsingIdentity(connectedUser, maybeFromUser)) {
             String allowedSender = connectedUser.asString();
-            throw new MailboxSendingNotAllowedException(allowedSender);
+            throw new MailboxSendingNotAllowedException(connectedUser, maybeFromUser);
+        } else {
+            LOG.debug("{} is allowed to send a mail using {} identity", connectedUser.asString(), from);
         }
     }
 
+    private boolean canSendMailUsingIdentity(Username connectedUser, Optional<Username> maybeFromUser) {
+        return maybeFromUser
+                .filter(fromUser -> canSendFrom.userCanSendFrom(connectedUser, fromUser))
+                .isPresent();
+    }
+
     private MessageWithId handleDraftMessages(CreationMessageEntry entry, MailboxSession session) throws MailboxException, MessagingException, IOException {
         MetaDataWithContent newMessage = messageAppender.appendMessageInMailboxes(entry, toMailboxIds(entry), session);
         MessageFullView jmapMessage = messageFullViewFactory.fromMetaDataWithContent(newMessage).block();
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessor.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessor.java
index c6f4039..35ba365 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessor.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessor.java
@@ -198,8 +198,9 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
     void assertUserCanSendFrom(Username connectedUser, Optional<Username> fromUser) throws MailboxSendingNotAllowedException {
         if (!fromUser.filter(from -> canSendFrom.userCanSendFrom(connectedUser, from))
             .isPresent()) {
-            String allowedSender = connectedUser.asString();
-            throw new MailboxSendingNotAllowedException(allowedSender);
+            throw new MailboxSendingNotAllowedException(connectedUser, fromUser);
+        } else {
+            LOGGER.debug("{} is allowed to send a mail using {} identity", connectedUser.asString(), fromUser);
         }
     }
 
diff --git a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/SenderAuthIdentifyVerificationRcptHook.java b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/SenderAuthIdentifyVerificationRcptHook.java
index cf6924c..2543fab 100644
--- a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/SenderAuthIdentifyVerificationRcptHook.java
+++ b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/SenderAuthIdentifyVerificationRcptHook.java
@@ -32,11 +32,15 @@ import org.apache.james.protocols.smtp.hook.HookResult;
 import org.apache.james.rrt.api.CanSendFrom;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.james.user.api.UsersRepositoryException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Handler which check if the authenticated user is incorrect
  */
 public class SenderAuthIdentifyVerificationRcptHook extends AbstractSenderAuthIdentifyVerificationRcptHook {
+    private static final Logger LOGGER = LoggerFactory.getLogger(SenderAuthIdentifyVerificationRcptHook.class);
+
     private final DomainList domains;
     private final UsersRepository users;
     private final CanSendFrom canSendFrom;
@@ -78,6 +82,12 @@ public class SenderAuthIdentifyVerificationRcptHook extends AbstractSenderAuthId
 
     @Override
     protected boolean isSenderAllowed(Username connectedUser, Username sender) {
-        return canSendFrom.userCanSendFrom(connectedUser, sender);
+        boolean allowed = canSendFrom.userCanSendFrom(connectedUser, sender);
+        if (allowed) {
+            LOGGER.debug("{} is allowed to send a mail using {} identity", connectedUser.asString(), sender.asString());
+        } else {
+            LOGGER.info("{} is not allowed to send a mail using {} identity", connectedUser.asString(), sender.asString());
+        }
+        return allowed;
     }
 }


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


[james-project] 04/16: JAMES-3196 Add sender/rcpt/helo to SMTP logs context

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit fafbcd9770fe6b734531802fd108e589943724af
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu May 28 12:07:25 2020 +0700

    JAMES-3196 Add sender/rcpt/helo to SMTP logs context
---
 .../netty/BasicChannelUpstreamHandler.java         | 21 +++++-----
 .../apache/james/protocols/netty/NettyServer.java  |  2 +-
 ...Context.java => ProtocolMDCContextFactory.java} | 16 ++++++--
 .../smtp/core/SMTPMDCContextFactory.java}          | 46 ++++++----------------
 .../apache/james/pop3server/netty/POP3Server.java  |  3 +-
 .../netty/SMTPChannelUpstreamHandler.java          |  5 ++-
 6 files changed, 43 insertions(+), 50 deletions(-)

diff --git a/protocols/netty/src/main/java/org/apache/james/protocols/netty/BasicChannelUpstreamHandler.java b/protocols/netty/src/main/java/org/apache/james/protocols/netty/BasicChannelUpstreamHandler.java
index b3246ab..0adcb0c 100644
--- a/protocols/netty/src/main/java/org/apache/james/protocols/netty/BasicChannelUpstreamHandler.java
+++ b/protocols/netty/src/main/java/org/apache/james/protocols/netty/BasicChannelUpstreamHandler.java
@@ -55,15 +55,18 @@ import org.slf4j.LoggerFactory;
 @Sharable
 public class BasicChannelUpstreamHandler extends SimpleChannelUpstreamHandler {
     private static final Logger LOGGER = LoggerFactory.getLogger(BasicChannelUpstreamHandler.class);
+
+    private final ProtocolMDCContextFactory mdcContextFactory;
     protected final Protocol protocol;
     protected final ProtocolHandlerChain chain;
     protected final Encryption secure;
 
-    public BasicChannelUpstreamHandler(Protocol protocol) {
-        this(protocol, null);
+    public BasicChannelUpstreamHandler(ProtocolMDCContextFactory mdcContextFactory, Protocol protocol) {
+        this(mdcContextFactory, protocol, null);
     }
 
-    public BasicChannelUpstreamHandler(Protocol protocol, Encryption secure) {
+    public BasicChannelUpstreamHandler(ProtocolMDCContextFactory mdcContextFactory, Protocol protocol, Encryption secure) {
+        this.mdcContextFactory = mdcContextFactory;
         this.protocol = protocol;
         this.chain = protocol.getProtocolChain();
         this.secure = secure;
@@ -72,7 +75,7 @@ public class BasicChannelUpstreamHandler extends SimpleChannelUpstreamHandler {
 
     @Override
     public void channelBound(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
-        try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) {
+        try (Closeable closeable = mdcContextFactory.from(protocol, ctx)) {
             ctx.setAttachment(createSession(ctx));
             super.channelBound(ctx, e);
         }
@@ -86,7 +89,7 @@ public class BasicChannelUpstreamHandler extends SimpleChannelUpstreamHandler {
     @SuppressWarnings({ "unchecked", "rawtypes" })
     @Override
     public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
-        try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) {
+        try (Closeable closeable = mdcContextFactory.from(protocol, ctx)) {
             List<ConnectHandler> connectHandlers = chain.getHandlers(ConnectHandler.class);
             List<ProtocolHandlerResultHandler> resultHandlers = chain.getHandlers(ProtocolHandlerResultHandler.class);
             ProtocolSession session = (ProtocolSession) ctx.getAttachment();
@@ -116,7 +119,7 @@ public class BasicChannelUpstreamHandler extends SimpleChannelUpstreamHandler {
     @SuppressWarnings({ "rawtypes", "unchecked" })
     @Override
     public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
-        try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) {
+        try (Closeable closeable = mdcContextFactory.from(protocol, ctx)) {
             List<DisconnectHandler> connectHandlers = chain.getHandlers(DisconnectHandler.class);
             ProtocolSession session = (ProtocolSession) ctx.getAttachment();
             if (connectHandlers != null) {
@@ -135,7 +138,7 @@ public class BasicChannelUpstreamHandler extends SimpleChannelUpstreamHandler {
     @SuppressWarnings({ "unchecked", "rawtypes" })
     @Override
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
-        try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) {
+        try (Closeable closeable = mdcContextFactory.from(protocol, ctx)) {
             ProtocolSession pSession = (ProtocolSession) ctx.getAttachment();
             LinkedList<LineHandler> lineHandlers = chain.getHandlers(LineHandler.class);
             LinkedList<ProtocolHandlerResultHandler> resultHandlers = chain.getHandlers(ProtocolHandlerResultHandler.class);
@@ -166,7 +169,7 @@ public class BasicChannelUpstreamHandler extends SimpleChannelUpstreamHandler {
 
     @Override
     public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
-        try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) {
+        try (Closeable closeable = mdcContextFactory.from(protocol, ctx)) {
             ProtocolSession session = (ProtocolSession) ctx.getAttachment();
             LOGGER.info("Connection closed for {}", session.getRemoteAddress().getAddress().getHostAddress());
             cleanup(ctx);
@@ -203,7 +206,7 @@ public class BasicChannelUpstreamHandler extends SimpleChannelUpstreamHandler {
 
     @Override
     public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
-        try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) {
+        try (Closeable closeable = mdcContextFactory.from(protocol, ctx)) {
             Channel channel = ctx.getChannel();
             ProtocolSession session = (ProtocolSession) ctx.getAttachment();
             if (e.getCause() instanceof TooLongFrameException && session != null) {
diff --git a/protocols/netty/src/main/java/org/apache/james/protocols/netty/NettyServer.java b/protocols/netty/src/main/java/org/apache/james/protocols/netty/NettyServer.java
index a523660..27ca9a8 100644
--- a/protocols/netty/src/main/java/org/apache/james/protocols/netty/NettyServer.java
+++ b/protocols/netty/src/main/java/org/apache/james/protocols/netty/NettyServer.java
@@ -100,7 +100,7 @@ public class NettyServer extends AbstractAsyncServer {
     }
     
     protected ChannelUpstreamHandler createCoreHandler() {
-        return new BasicChannelUpstreamHandler(protocol, secure);
+        return new BasicChannelUpstreamHandler(new ProtocolMDCContextFactory.Standard(), protocol, secure);
     }
     
     @Override
diff --git a/protocols/netty/src/main/java/org/apache/james/protocols/netty/ProtocolMDCContext.java b/protocols/netty/src/main/java/org/apache/james/protocols/netty/ProtocolMDCContextFactory.java
similarity index 87%
copy from protocols/netty/src/main/java/org/apache/james/protocols/netty/ProtocolMDCContext.java
copy to protocols/netty/src/main/java/org/apache/james/protocols/netty/ProtocolMDCContextFactory.java
index 810a448..7501530 100644
--- a/protocols/netty/src/main/java/org/apache/james/protocols/netty/ProtocolMDCContext.java
+++ b/protocols/netty/src/main/java/org/apache/james/protocols/netty/ProtocolMDCContextFactory.java
@@ -29,14 +29,22 @@ import org.apache.james.protocols.api.ProtocolSession;
 import org.apache.james.util.MDCBuilder;
 import org.jboss.netty.channel.ChannelHandlerContext;
 
-public class ProtocolMDCContext {
-    public static Closeable from(Protocol protocol, ChannelHandlerContext ctx) {
+public interface ProtocolMDCContextFactory {
+    class Standard implements ProtocolMDCContextFactory {
+        @Override
+        public Closeable from(Protocol protocol, ChannelHandlerContext ctx) {
+            return mdcContext(protocol, ctx).build();
+        }
+    }
+
+    Closeable from(Protocol protocol, ChannelHandlerContext ctx);
+
+    static MDCBuilder mdcContext(Protocol protocol, ChannelHandlerContext ctx) {
         return MDCBuilder.create()
             .addContext(from(ctx.getAttachment()))
             .addContext(MDCBuilder.PROTOCOL, protocol.getName())
             .addContext(MDCBuilder.IP, retrieveIp(ctx))
-            .addContext(MDCBuilder.HOST, retrieveHost(ctx))
-            .build();
+            .addContext(MDCBuilder.HOST, retrieveHost(ctx));
     }
 
     private static String retrieveIp(ChannelHandlerContext ctx) {
diff --git a/protocols/netty/src/main/java/org/apache/james/protocols/netty/ProtocolMDCContext.java b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/SMTPMDCContextFactory.java
similarity index 51%
rename from protocols/netty/src/main/java/org/apache/james/protocols/netty/ProtocolMDCContext.java
rename to protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/SMTPMDCContextFactory.java
index 810a448..cec99ae 100644
--- a/protocols/netty/src/main/java/org/apache/james/protocols/netty/ProtocolMDCContext.java
+++ b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/SMTPMDCContextFactory.java
@@ -17,55 +17,35 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.protocols.netty;
+package org.apache.james.protocols.smtp.core;
 
 import java.io.Closeable;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
 import java.util.Optional;
 
 import org.apache.james.protocols.api.Protocol;
 import org.apache.james.protocols.api.ProtocolSession;
+import org.apache.james.protocols.netty.ProtocolMDCContextFactory;
+import org.apache.james.protocols.smtp.SMTPSession;
 import org.apache.james.util.MDCBuilder;
 import org.jboss.netty.channel.ChannelHandlerContext;
 
-public class ProtocolMDCContext {
-    public static Closeable from(Protocol protocol, ChannelHandlerContext ctx) {
+public class SMTPMDCContextFactory implements ProtocolMDCContextFactory {
+
+    public Closeable from(Protocol protocol, ChannelHandlerContext ctx) {
         return MDCBuilder.create()
+            .addContext(ProtocolMDCContextFactory.mdcContext(protocol, ctx))
             .addContext(from(ctx.getAttachment()))
-            .addContext(MDCBuilder.PROTOCOL, protocol.getName())
-            .addContext(MDCBuilder.IP, retrieveIp(ctx))
-            .addContext(MDCBuilder.HOST, retrieveHost(ctx))
             .build();
     }
 
-    private static String retrieveIp(ChannelHandlerContext ctx) {
-        SocketAddress remoteAddress = ctx.getChannel().getRemoteAddress();
-        if (remoteAddress instanceof InetSocketAddress) {
-            InetSocketAddress address = (InetSocketAddress) remoteAddress;
-            return address.getAddress().getHostAddress();
-        }
-        return remoteAddress.toString();
-    }
-
-    private static String retrieveHost(ChannelHandlerContext ctx) {
-        SocketAddress remoteAddress = ctx.getChannel().getRemoteAddress();
-        if (remoteAddress instanceof InetSocketAddress) {
-            InetSocketAddress address = (InetSocketAddress) remoteAddress;
-            return address.getHostName();
-        }
-        return remoteAddress.toString();
-    }
-
-    private static MDCBuilder from(Object o) {
+    private MDCBuilder from(Object o) {
         return Optional.ofNullable(o)
-            .filter(object -> object instanceof ProtocolSession)
-            .map(object -> (ProtocolSession) object)
+            .filter(object -> object instanceof SMTPSession)
+            .map(object -> (SMTPSession) object)
             .map(protocolSession -> MDCBuilder.create()
-                .addContext(MDCBuilder.SESSION_ID, protocolSession.getSessionID())
-                .addContext(MDCBuilder.CHARSET, protocolSession.getCharset().displayName())
-                .addContext(MDCBuilder.USER, protocolSession.getUsername()))
+                .addContext("ehlo", protocolSession.getAttachment(SMTPSession.CURRENT_HELO_NAME, ProtocolSession.State.Connection))
+                .addContext("sender", protocolSession.getAttachment(SMTPSession.SENDER, ProtocolSession.State.Transaction))
+                .addContext("recipients", protocolSession.getAttachment(SMTPSession.RCPT_LIST, ProtocolSession.State.Transaction)))
             .orElse(MDCBuilder.create());
     }
-
 }
diff --git a/server/protocols/protocols-pop3/src/main/java/org/apache/james/pop3server/netty/POP3Server.java b/server/protocols/protocols-pop3/src/main/java/org/apache/james/pop3server/netty/POP3Server.java
index cd4f4f5..bd235ec 100644
--- a/server/protocols/protocols-pop3/src/main/java/org/apache/james/pop3server/netty/POP3Server.java
+++ b/server/protocols/protocols-pop3/src/main/java/org/apache/james/pop3server/netty/POP3Server.java
@@ -27,6 +27,7 @@ import org.apache.james.protocols.netty.AbstractChannelPipelineFactory;
 import org.apache.james.protocols.netty.BasicChannelUpstreamHandler;
 import org.apache.james.protocols.netty.ChannelHandlerFactory;
 import org.apache.james.protocols.netty.LineDelimiterBasedChannelHandlerFactory;
+import org.apache.james.protocols.netty.ProtocolMDCContextFactory;
 import org.apache.james.protocols.pop3.POP3Protocol;
 import org.jboss.netty.channel.ChannelUpstreamHandler;
 
@@ -75,7 +76,7 @@ public class POP3Server extends AbstractProtocolAsyncServer implements POP3Serve
     protected void preInit() throws Exception {
         super.preInit();
         POP3Protocol protocol = new POP3Protocol(getProtocolHandlerChain(), theConfigData);
-        coreHandler = new BasicChannelUpstreamHandler(protocol, getEncryption());
+        coreHandler = new BasicChannelUpstreamHandler(new ProtocolMDCContextFactory.Standard(), protocol, getEncryption());
     }
 
     @Override
diff --git a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/netty/SMTPChannelUpstreamHandler.java b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/netty/SMTPChannelUpstreamHandler.java
index 6933c2b..fbfcf55 100644
--- a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/netty/SMTPChannelUpstreamHandler.java
+++ b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/netty/SMTPChannelUpstreamHandler.java
@@ -24,6 +24,7 @@ import org.apache.james.protocols.api.Protocol;
 import org.apache.james.protocols.api.ProtocolSession.State;
 import org.apache.james.protocols.netty.BasicChannelUpstreamHandler;
 import org.apache.james.protocols.smtp.SMTPSession;
+import org.apache.james.protocols.smtp.core.SMTPMDCContextFactory;
 import org.apache.james.smtpserver.SMTPConstants;
 import org.jboss.netty.channel.ChannelHandler.Sharable;
 import org.jboss.netty.channel.ChannelHandlerContext;
@@ -40,12 +41,12 @@ public class SMTPChannelUpstreamHandler extends BasicChannelUpstreamHandler {
     private final SmtpMetrics smtpMetrics;
 
     public SMTPChannelUpstreamHandler(Protocol protocol, Encryption encryption, SmtpMetrics smtpMetrics) {
-        super(protocol, encryption);
+        super(new SMTPMDCContextFactory(), protocol, encryption);
         this.smtpMetrics = smtpMetrics;
     }
 
     public SMTPChannelUpstreamHandler(Protocol protocol, SmtpMetrics smtpMetrics) {
-        super(protocol);
+        super(new SMTPMDCContextFactory(), protocol);
         this.smtpMetrics = smtpMetrics;
     }
 


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


[james-project] 02/16: JAMES-3196 IMAP Debug logs should allow reviewing issued commands

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 5dc7c565e17b7e0b7cb81a5af6a243968b2c48f5
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu May 28 11:32:42 2020 +0700

    JAMES-3196 IMAP Debug logs should allow reviewing issued commands
---
 .../org/apache/james/imap/processor/base/AbstractChainedProcessor.java   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/base/AbstractChainedProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/base/AbstractChainedProcessor.java
index 823f4ed..9eaf492 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/base/AbstractChainedProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/base/AbstractChainedProcessor.java
@@ -54,6 +54,7 @@ public abstract class AbstractChainedProcessor<M extends ImapMessage> implements
             M acceptableMessage = (M) message;
             try (Closeable closeable = addContextToMDC(acceptableMessage)) {
                 try {
+                    LOGGER.debug("Processing {}", message.toString());
                     doProcess(acceptableMessage, responder, session);
                 } catch (RuntimeException e) {
                     LOGGER.error("Error while processing IMAP request", e);


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


[james-project] 05/16: JAMES-3196 Carry other SMTP context on DATA hooks

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit deef423282310b32de237bf7dc3186b7f4e55ed5
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu May 28 15:56:16 2020 +0700

    JAMES-3196 Carry other SMTP context on DATA hooks
    
    These hooks are executed on a separate thread thus context is lost
---
 .../protocols/netty/ProtocolMDCContextFactory.java     | 12 ++++++++----
 .../protocols/smtp/core/SMTPMDCContextFactory.java     | 18 ++++++++++++++----
 .../smtpserver/DataLineJamesMessageHookHandler.java    |  4 +++-
 3 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/protocols/netty/src/main/java/org/apache/james/protocols/netty/ProtocolMDCContextFactory.java b/protocols/netty/src/main/java/org/apache/james/protocols/netty/ProtocolMDCContextFactory.java
index 7501530..17a34cc 100644
--- a/protocols/netty/src/main/java/org/apache/james/protocols/netty/ProtocolMDCContextFactory.java
+++ b/protocols/netty/src/main/java/org/apache/james/protocols/netty/ProtocolMDCContextFactory.java
@@ -69,11 +69,15 @@ public interface ProtocolMDCContextFactory {
         return Optional.ofNullable(o)
             .filter(object -> object instanceof ProtocolSession)
             .map(object -> (ProtocolSession) object)
-            .map(protocolSession -> MDCBuilder.create()
-                .addContext(MDCBuilder.SESSION_ID, protocolSession.getSessionID())
-                .addContext(MDCBuilder.CHARSET, protocolSession.getCharset().displayName())
-                .addContext(MDCBuilder.USER, protocolSession.getUsername()))
+            .map(ProtocolMDCContextFactory::forSession)
             .orElse(MDCBuilder.create());
     }
 
+    static MDCBuilder forSession(ProtocolSession protocolSession) {
+        return MDCBuilder.create()
+            .addContext(MDCBuilder.SESSION_ID, protocolSession.getSessionID())
+            .addContext(MDCBuilder.CHARSET, protocolSession.getCharset().displayName())
+            .addContext(MDCBuilder.USER, protocolSession.getUsername());
+    }
+
 }
diff --git a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/SMTPMDCContextFactory.java b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/SMTPMDCContextFactory.java
index cec99ae..87f7d94 100644
--- a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/SMTPMDCContextFactory.java
+++ b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/SMTPMDCContextFactory.java
@@ -38,14 +38,24 @@ public class SMTPMDCContextFactory implements ProtocolMDCContextFactory {
             .build();
     }
 
+    public static MDCBuilder forSession(SMTPSession smtpSession) {
+        return MDCBuilder.create()
+            .addContext(ProtocolMDCContextFactory.forSession(smtpSession))
+            .addContext(forSMTPSession(smtpSession));
+    }
+
     private MDCBuilder from(Object o) {
         return Optional.ofNullable(o)
             .filter(object -> object instanceof SMTPSession)
             .map(object -> (SMTPSession) object)
-            .map(protocolSession -> MDCBuilder.create()
-                .addContext("ehlo", protocolSession.getAttachment(SMTPSession.CURRENT_HELO_NAME, ProtocolSession.State.Connection))
-                .addContext("sender", protocolSession.getAttachment(SMTPSession.SENDER, ProtocolSession.State.Transaction))
-                .addContext("recipients", protocolSession.getAttachment(SMTPSession.RCPT_LIST, ProtocolSession.State.Transaction)))
+            .map(SMTPMDCContextFactory::forSMTPSession)
             .orElse(MDCBuilder.create());
     }
+
+    private static MDCBuilder forSMTPSession(SMTPSession smtpSession) {
+        return MDCBuilder.create()
+            .addContext("ehlo", smtpSession.getAttachment(SMTPSession.CURRENT_HELO_NAME, ProtocolSession.State.Connection))
+            .addContext("sender", smtpSession.getAttachment(SMTPSession.SENDER, ProtocolSession.State.Transaction))
+            .addContext("recipients", smtpSession.getAttachment(SMTPSession.RCPT_LIST, ProtocolSession.State.Transaction));
+    }
 }
diff --git a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/DataLineJamesMessageHookHandler.java b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/DataLineJamesMessageHookHandler.java
index fe3ac7c..4da0261 100644
--- a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/DataLineJamesMessageHookHandler.java
+++ b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/DataLineJamesMessageHookHandler.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.smtpserver;
 
+import java.io.Closeable;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -42,6 +43,7 @@ import org.apache.james.protocols.smtp.SMTPRetCode;
 import org.apache.james.protocols.smtp.SMTPSession;
 import org.apache.james.protocols.smtp.core.AbstractHookableCmdHandler;
 import org.apache.james.protocols.smtp.core.DataLineFilter;
+import org.apache.james.protocols.smtp.core.SMTPMDCContextFactory;
 import org.apache.james.protocols.smtp.dsn.DSNStatus;
 import org.apache.james.protocols.smtp.hook.HookResult;
 import org.apache.james.protocols.smtp.hook.HookResultHook;
@@ -77,7 +79,7 @@ public class DataLineJamesMessageHookHandler implements DataLineFilter, Extensib
         MimeMessageInputStreamSource mmiss = session.getAttachment(SMTPConstants.DATA_MIMEMESSAGE_STREAMSOURCE, State.Transaction)
             .orElseThrow(() -> new RuntimeException("'" + SMTPConstants.DATA_MIMEMESSAGE_STREAMSOURCE.asString() + "' has not been filled."));
 
-        try {
+        try (Closeable closeable = SMTPMDCContextFactory.forSession(session).build()) {
             OutputStream out = mmiss.getWritableOutputStream();
 
             // 46 is "."


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


[james-project] 03/16: JAMES-3196 Add an IMAP SessionId to correlate logs

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 0810b62757648a98ece211550ea0fc9d4cf4828b
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu May 28 11:44:05 2020 +0700

    JAMES-3196 Add an IMAP SessionId to correlate logs
---
 .../apache/james/imap/api/process/ImapSession.java | 50 ++++++++++++++++++++++
 .../apache/james/imap/encode/FakeImapSession.java  |  7 +++
 .../james/imapserver/netty/IMAPMDCContext.java     |  1 +
 .../netty/ImapChannelUpstreamHandler.java          |  6 ++-
 .../james/imapserver/netty/NettyImapSession.java   |  9 +++-
 5 files changed, 70 insertions(+), 3 deletions(-)

diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/process/ImapSession.java b/protocols/imap/src/main/java/org/apache/james/imap/api/process/ImapSession.java
index e8769f9..57ec639 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/api/process/ImapSession.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/api/process/ImapSession.java
@@ -19,8 +19,10 @@
 
 package org.apache.james.imap.api.process;
 
+import java.util.Objects;
 import java.util.Optional;
 
+import org.apache.commons.text.RandomStringGenerator;
 import org.apache.james.core.Username;
 import org.apache.james.imap.api.ImapSessionState;
 import org.apache.james.mailbox.MailboxSession;
@@ -33,9 +35,57 @@ import org.apache.james.mailbox.MailboxSession;
  * @version $Revision: 109034 $
  */
 public interface ImapSession {
+    class SessionId {
+        private static final RandomStringGenerator RANDOM_STRING_GENERATOR = new RandomStringGenerator.Builder()
+            .withinRange('a', 'z')
+            .build();
+        private static final int LENGTH = 12;
+
+        public static SessionId generate() {
+            return new SessionId("SID-" + RANDOM_STRING_GENERATOR.generate(LENGTH));
+        }
+
+        private final String value;
+
+        private SessionId(String value) {
+            this.value = value;
+        }
+
+        public String asString() {
+            return value;
+        }
+
+        @Override
+        public final boolean equals(Object o) {
+            if (o instanceof SessionId) {
+                SessionId sessionId = (SessionId) o;
+
+                return Objects.equals(this.value, sessionId.value);
+            }
+            return false;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(value);
+        }
+
+        @Override
+        public String toString() {
+            return asString();
+        }
+    }
+
     String MAILBOX_SESSION_ATTRIBUTE_SESSION_KEY = "org.apache.james.api.imap.MAILBOX_SESSION_ATTRIBUTE_SESSION_KEY";
 
     /**
+     * @return a unique identifier for this session.
+     *
+     * One of its usage is log correlation.
+     */
+    SessionId sessionId();
+
+    /**
      * Logs out the session. Marks the connection for closure;
      */
     void logout();
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/encode/FakeImapSession.java b/protocols/imap/src/main/java/org/apache/james/imap/encode/FakeImapSession.java
index bd7d053..e2c375e 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/encode/FakeImapSession.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/encode/FakeImapSession.java
@@ -33,12 +33,19 @@ public class FakeImapSession implements ImapSession {
     private SelectedMailbox selectedMailbox = null;
 
     private final Map<String, Object> attributesByKey;
+    private final SessionId sessionId;
 
     public FakeImapSession() {
+        this.sessionId = SessionId.generate();
         this.attributesByKey = new ConcurrentHashMap<>();
     }
 
     @Override
+    public SessionId sessionId() {
+        return sessionId;
+    }
+
+    @Override
     public void logout() {
         closeMailbox();
         state = ImapSessionState.LOGOUT;
diff --git a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPMDCContext.java b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPMDCContext.java
index 56042a7..fe2c26a 100644
--- a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPMDCContext.java
+++ b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPMDCContext.java
@@ -64,6 +64,7 @@ public class IMAPMDCContext {
             ImapSession imapSession = (ImapSession) o;
 
             return MDCBuilder.create()
+                .addContext("sessionId", imapSession.sessionId().asString())
                 .addContext(MDCBuilder.USER, Optional.ofNullable(imapSession.getUserName())
                     .map(Username::asString))
                 .addContext(from(Optional.ofNullable(imapSession.getSelected())));
diff --git a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/ImapChannelUpstreamHandler.java b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/ImapChannelUpstreamHandler.java
index 8f475c9..d1f699d 100644
--- a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/ImapChannelUpstreamHandler.java
+++ b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/ImapChannelUpstreamHandler.java
@@ -29,6 +29,7 @@ import org.apache.james.imap.api.ImapMessage;
 import org.apache.james.imap.api.ImapSessionState;
 import org.apache.james.imap.api.process.ImapProcessor;
 import org.apache.james.imap.api.process.ImapSession;
+import org.apache.james.imap.api.process.ImapSession.SessionId;
 import org.apache.james.imap.encode.ImapEncoder;
 import org.apache.james.imap.encode.ImapResponseComposer;
 import org.apache.james.imap.encode.base.ImapResponseComposerImpl;
@@ -93,9 +94,10 @@ public class ImapChannelUpstreamHandler extends SimpleChannelUpstreamHandler imp
 
     @Override
     public void channelBound(final ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
+        ImapSession imapsession = new NettyImapSession(ctx.getChannel(), context, enabledCipherSuites, compress, plainAuthDisallowed,
+            SessionId.generate());
+        attributes.set(ctx.getChannel(), imapsession);
         try (Closeable closeable = IMAPMDCContext.from(ctx, attributes)) {
-            ImapSession imapsession = new NettyImapSession(ctx.getChannel(), context, enabledCipherSuites, compress, plainAuthDisallowed);
-            attributes.set(ctx.getChannel(), imapsession);
             super.channelBound(ctx, e);
         }
     }
diff --git a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java
index 18b74bf..ea5d670 100644
--- a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java
+++ b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java
@@ -43,13 +43,20 @@ public class NettyImapSession implements ImapSession, NettyConstants {
     private final Channel channel;
     private int handlerCount;
     private final boolean plainAuthDisallowed;
+    private final SessionId sessionId;
 
-    public NettyImapSession(Channel channel, SSLContext sslContext, String[] enabledCipherSuites, boolean compress, boolean plainAuthDisallowed) {
+    public NettyImapSession(Channel channel, SSLContext sslContext, String[] enabledCipherSuites, boolean compress, boolean plainAuthDisallowed, SessionId sessionId) {
         this.channel = channel;
         this.sslContext = sslContext;
         this.enabledCipherSuites = enabledCipherSuites;
         this.compress = compress;
         this.plainAuthDisallowed = plainAuthDisallowed;
+        this.sessionId = sessionId;
+    }
+
+    @Override
+    public SessionId sessionId() {
+        return sessionId;
     }
 
     @Override


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


[james-project] 13/16: JAMES-3171 Mailbox/get all integration tests

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit ac9890279c3b7434703110982f71461241d0a08d
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Fri May 22 21:59:56 2020 +0700

    JAMES-3171 Mailbox/get all integration tests
---
 .../DistributedMailboxGetMethodTest.java           |  51 +++
 .../james/jmap/rfc8621/contract/Fixture.scala      |   2 +
 .../contract/MailboxGetMethodContract.scala        | 476 +++++++++++++++++++++
 .../rfc8621/memory/MemoryMailboxGetMethodTest.java |  38 ++
 4 files changed, 567 insertions(+)

diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxGetMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxGetMethodTest.java
new file mode 100644
index 0000000..d2db630
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxGetMethodTest.java
@@ -0,0 +1,51 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.rfc8621.distributed;
+
+import org.apache.james.CassandraExtension;
+import org.apache.james.CassandraRabbitMQJamesConfiguration;
+import org.apache.james.CassandraRabbitMQJamesServerMain;
+import org.apache.james.DockerElasticSearchExtension;
+import org.apache.james.JamesServerBuilder;
+import org.apache.james.JamesServerExtension;
+import org.apache.james.jmap.rfc8621.contract.MailboxGetMethodContract;
+import org.apache.james.modules.AwsS3BlobStoreExtension;
+import org.apache.james.modules.RabbitMQExtension;
+import org.apache.james.modules.TestJMAPServerModule;
+import org.apache.james.modules.blobstore.BlobStoreConfiguration;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+public class DistributedMailboxGetMethodTest implements MailboxGetMethodContract {
+    @RegisterExtension
+    static JamesServerExtension testExtension = new JamesServerBuilder<CassandraRabbitMQJamesConfiguration>(tmpDir ->
+        CassandraRabbitMQJamesConfiguration.builder()
+            .workingDirectory(tmpDir)
+            .configurationFromClasspath()
+            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .build())
+        .extension(new DockerElasticSearchExtension())
+        .extension(new CassandraExtension())
+        .extension(new RabbitMQExtension())
+        .extension(new AwsS3BlobStoreExtension())
+        .server(configuration -> CassandraRabbitMQJamesServerMain.createServer(configuration)
+            .overrideWith(new TestJMAPServerModule()))
+        .build();
+
+}
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/Fixture.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/Fixture.scala
index 7d22579..799f8f9 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/Fixture.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/Fixture.scala
@@ -69,8 +69,10 @@ object Fixture {
   val DOMAIN_WITH_SPACE: Domain = Domain.of("dom ain.tld")
   val _2_DOT_DOMAIN: Domain = Domain.of("do.main.tld")
   val BOB: Username = Username.fromLocalPartWithDomain("bob", DOMAIN)
+  val ANDRE: Username = Username.fromLocalPartWithDomain("andre", DOMAIN)
   val ALICE: Username = Username.fromLocalPartWithDomain("alice", _2_DOT_DOMAIN)
   val BOB_PASSWORD: String = "bobpassword"
+  val ANDRE_PASSWORD: String = "andrepassword"
   val ALICE_PASSWORD: String = "alicepassword"
 
   val BOB_BASIC_AUTH_HEADER: Header = new Header(AUTHORIZATION_HEADER, s"Basic ${toBase64(s"${BOB.asString}:$BOB_PASSWORD")}")
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
new file mode 100644
index 0000000..0748914
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
@@ -0,0 +1,476 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.jmap.rfc8621.contract
+
+import java.io.ByteArrayInputStream
+import java.nio.charset.StandardCharsets
+import java.util.Date
+
+import io.netty.handler.codec.http.HttpHeaderNames.ACCEPT
+import io.restassured.RestAssured._
+import io.restassured.http.ContentType.JSON
+import javax.mail.Flags
+import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
+import org.apache.http.HttpStatus.{SC_INTERNAL_SERVER_ERROR, SC_OK}
+import org.apache.james.GuiceJamesServer
+import org.apache.james.core.quota.{QuotaCountLimit, QuotaSizeLimit}
+import org.apache.james.jmap.http.UserCredential
+import org.apache.james.jmap.rfc8621.contract.Fixture._
+import org.apache.james.jmap.rfc8621.contract.tags.CategoryTags
+import org.apache.james.mailbox.MessageManager.AppendCommand
+import org.apache.james.mailbox.model.MailboxACL.Right
+import org.apache.james.mailbox.model.{MailboxACL, MailboxId, MailboxPath}
+import org.apache.james.mailbox.{DefaultMailboxes, Role}
+import org.apache.james.mime4j.dom.Message
+import org.apache.james.modules.{ACLProbeImpl, MailboxProbeImpl, QuotaProbesImpl}
+import org.apache.james.utils.DataProbeImpl
+import org.hamcrest.Matchers._
+import org.junit.jupiter.api.{BeforeEach, Tag, Test}
+
+object MailboxGetMethodContract {
+  private val ARGUMENTS: String = "methodResponses[0][1]"
+  private val FIRST_MAILBOX: String = ARGUMENTS + ".list[0]"
+  private val SECOND_MAILBOX: String = ARGUMENTS + ".list[1]"
+
+  private val LOOKUP: String = Right.Lookup.asCharacter.toString
+  private val READ: String = Right.Read.asCharacter.toString
+  private val ADMINISTER: String = Right.Administer.asCharacter.toString
+
+  private val GET_ALL_MAILBOXES_REQUEST: String =
+    """{
+    |  "using": [
+    |    "urn:ietf:params:jmap:core",
+    |    "urn:ietf:params:jmap:mail"],
+    |  "methodCalls": [[
+    |      "Mailbox/get",
+    |      {
+    |        "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+    |        "ids": null
+    |      },
+    |      "c1"]]
+    |}""".stripMargin
+}
+
+trait MailboxGetMethodContract {
+  import MailboxGetMethodContract._
+
+  @BeforeEach
+  def setUp(server: GuiceJamesServer): Unit = {
+    server.getProbe(classOf[DataProbeImpl])
+      .fluent
+      .addDomain(DOMAIN.asString)
+      .addUser(BOB.asString, BOB_PASSWORD)
+      .addUser(ANDRE.asString, ANDRE_PASSWORD)
+
+    requestSpecification = baseRequestSpecBuilder(server)
+      .setAuth(authScheme(UserCredential(BOB, BOB_PASSWORD)))
+      .build
+  }
+
+  @Test
+  def getMailboxesShouldReturnExistingMailbox(server: GuiceJamesServer): Unit = {
+    val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(MailboxPath.forUser(BOB, "custom"))
+      .serialize
+
+    val response: String = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(GET_ALL_MAILBOXES_REQUEST)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response).isEqualTo(
+      s"""{
+         |  "sessionState": "75128aab4b1b",
+         |  "methodResponses": [[
+         |    "Mailbox/get",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "state": "000001",
+         |      "list": [
+         |        {
+         |          "id": "${mailboxId}",
+         |          "name": "custom",
+         |          "sortOrder": 1000,
+         |          "totalEmails": 0,
+         |          "unreadEmails": 0,
+         |          "totalThreads": 0,
+         |          "unreadThreads": 0,
+         |          "myRights": {
+         |            "mayReadItems": true,
+         |            "mayAddItems": true,
+         |            "mayRemoveItems": true,
+         |            "maySetSeen": true,
+         |            "maySetKeywords": true,
+         |            "mayCreateChild": true,
+         |            "mayRename": true,
+         |            "mayDelete": true,
+         |            "maySubmit": true
+         |          },
+         |          "isSubscribed": false,
+         |          "namespace": "Personal",
+         |          "rights": {},
+         |          "quotas": {
+         |            "#private&bob@domain.tld": {
+         |              "Storage": { "used": 0},
+         |              "Message": {"used": 0}
+         |            }
+         |          }
+         |        }
+         |      ],
+         |      "notFound": []
+         |    },
+         |    "c1"]]
+         |}""".stripMargin)
+  }
+
+  @Test
+  @Tag(CategoryTags.BASIC_FEATURE)
+  def getMailboxesShouldReturnEmptyWhenNone(): Unit = {
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(GET_ALL_MAILBOXES_REQUEST)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .body(s"$ARGUMENTS.list", empty)
+  }
+
+  @Test
+  @Tag(CategoryTags.BASIC_FEATURE)
+  def getMailboxesShouldReturnAllExistingMailboxes(server: GuiceJamesServer): Unit = {
+    val firstMailboxName: String = "custom"
+    val mailboxId1: String = server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(MailboxPath.forUser(BOB, firstMailboxName))
+      .serialize
+
+    val secondMailboxName: String = "othercustom"
+    val mailboxId2: String = server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(MailboxPath.forUser(BOB, secondMailboxName))
+      .serialize
+
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(GET_ALL_MAILBOXES_REQUEST)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .body(s"$ARGUMENTS.list", hasSize(2))
+      .body(s"$FIRST_MAILBOX.id", equalTo(mailboxId1))
+      .body(s"$FIRST_MAILBOX.name", equalTo(firstMailboxName))
+      .body(s"$SECOND_MAILBOX.id", equalTo(mailboxId2))
+      .body(s"$SECOND_MAILBOX.name", equalTo(secondMailboxName))
+  }
+
+  @Test
+  def getMailboxesShouldReturnOnlyMailboxesOfCurrentUser(server: GuiceJamesServer): Unit = {
+    val mailboxName: String = "custom"
+    val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(MailboxPath.forUser(BOB, "custom"))
+      .serialize
+    server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(MailboxPath.forUser(ANDRE, "andrecustom"))
+
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(GET_ALL_MAILBOXES_REQUEST)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .body(s"$ARGUMENTS.list", hasSize(1))
+      .body(s"$FIRST_MAILBOX.id", equalTo(mailboxId))
+      .body(s"$FIRST_MAILBOX.name", equalTo(mailboxName))
+  }
+
+  @Test
+  def getMailboxesShouldReturnSharedRights(server: GuiceJamesServer): Unit = {
+    val targetUser1: String = "touser1@" + DOMAIN.asString
+    val targetUser2: String = "touser2@" + DOMAIN.asString
+    val mailboxName: String = "myMailbox"
+    server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(MailboxPath.forUser(BOB, mailboxName))
+      .serialize
+
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(MailboxPath.forUser(BOB, mailboxName), targetUser1, new MailboxACL.Rfc4314Rights(Right.Lookup, Right.Administer))
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(MailboxPath.forUser(BOB, mailboxName), targetUser2, new MailboxACL.Rfc4314Rights(Right.Read, Right.Lookup))
+
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(GET_ALL_MAILBOXES_REQUEST)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .body(s"$ARGUMENTS.list", hasSize(1))
+      .body(s"$FIRST_MAILBOX.name", equalTo(mailboxName))
+      .body(s"$FIRST_MAILBOX.rights['$targetUser1']", contains(ADMINISTER, LOOKUP))
+      .body(s"$FIRST_MAILBOX.rights['$targetUser2']", contains(LOOKUP, READ))
+  }
+
+  @Test
+  @Tag(CategoryTags.BASIC_FEATURE)
+  def getMailboxesShouldReturnDelegatedNamespaceWhenSharedMailbox(server: GuiceJamesServer): Unit = {
+    val sharedMailboxName = "AndreShared"
+    val andreMailboxPath = MailboxPath.forUser(ANDRE, sharedMailboxName)
+    server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(andreMailboxPath)
+      .serialize
+
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(andreMailboxPath, BOB.asString, new MailboxACL.Rfc4314Rights(Right.Lookup))
+
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(GET_ALL_MAILBOXES_REQUEST)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .body(s"$ARGUMENTS.list", hasSize(1))
+      .body(s"$FIRST_MAILBOX.name", equalTo(sharedMailboxName))
+      .body(s"$FIRST_MAILBOX.namespace", equalTo(s"Delegated[${ANDRE.asString}]"))
+      .body(s"$FIRST_MAILBOX.rights['${BOB.asString}']", contains(LOOKUP))
+  }
+
+  @Test
+  @Tag(CategoryTags.BASIC_FEATURE)
+  def getMailboxesShouldNotReturnOtherPeopleRightsAsSharee(server: GuiceJamesServer): Unit = {
+    val toUser1: String = "touser1@" + DOMAIN.asString
+    val sharedMailboxName: String = "AndreShared"
+    val andreMailboxPath: MailboxPath = MailboxPath.forUser(ANDRE, sharedMailboxName)
+    server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(andreMailboxPath)
+      .serialize
+
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(andreMailboxPath, BOB.asString, new MailboxACL.Rfc4314Rights(Right.Lookup, Right.Read))
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(andreMailboxPath, toUser1, new MailboxACL.Rfc4314Rights(Right.Lookup, Right.Read))
+
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(GET_ALL_MAILBOXES_REQUEST)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .body(s"$ARGUMENTS.list", hasSize(1))
+      .body(s"$FIRST_MAILBOX.name", equalTo(sharedMailboxName))
+      .body(s"$FIRST_MAILBOX.rights['${BOB.asString}']", contains(LOOKUP, READ))
+      .body(s"$FIRST_MAILBOX.rights['$toUser1']", nullValue)
+  }
+
+  @Test
+  def getMailboxesShouldReturnPartiallyAllowedMayPropertiesWhenDelegated(server: GuiceJamesServer): Unit = {
+    val toUser1: String = "touser1@" + DOMAIN.asString
+    val sharedMailboxName: String = "AndreShared"
+    val andreMailboxPath: MailboxPath = MailboxPath.forUser(ANDRE, sharedMailboxName)
+    server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(andreMailboxPath)
+      .serialize
+
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(andreMailboxPath, BOB.asString, new MailboxACL.Rfc4314Rights(Right.Lookup, Right.Read))
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(andreMailboxPath, toUser1, new MailboxACL.Rfc4314Rights(Right.Lookup, Right.Read))
+
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(GET_ALL_MAILBOXES_REQUEST)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .body(s"$ARGUMENTS.list", hasSize(1))
+      .body(s"$FIRST_MAILBOX.name", equalTo(sharedMailboxName))
+      .body(s"$FIRST_MAILBOX.myRights.mayReadItems", equalTo(true))
+      .body(s"$FIRST_MAILBOX.myRights.mayAddItems", equalTo(false))
+      .body(s"$FIRST_MAILBOX.myRights.mayRemoveItems", equalTo(false))
+      .body(s"$FIRST_MAILBOX.myRights.mayCreateChild", equalTo(false))
+      .body(s"$FIRST_MAILBOX.myRights.mayDelete", equalTo(false))
+      .body(s"$FIRST_MAILBOX.myRights.mayRename", equalTo(false))
+      .body(s"$FIRST_MAILBOX.myRights.maySubmit", equalTo(false))
+      .body(s"$FIRST_MAILBOX.myRights.maySetSeen", equalTo(false))
+      .body(s"$FIRST_MAILBOX.myRights.maySetKeywords", equalTo(false))
+  }
+
+  @Test
+  @Tag(CategoryTags.BASIC_FEATURE)
+  def getMailboxesShouldNotReturnInboxRoleToShareeWhenDelegatedInbox(server: GuiceJamesServer): Unit = {
+    val andreMailboxPath = MailboxPath.forUser(ANDRE, DefaultMailboxes.INBOX)
+    server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(andreMailboxPath)
+      .serialize
+
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(andreMailboxPath, BOB.asString, new MailboxACL.Rfc4314Rights(Right.Lookup))
+
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(GET_ALL_MAILBOXES_REQUEST)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .body(s"$ARGUMENTS.list", hasSize(1))
+      .body(s"$FIRST_MAILBOX.name", equalTo(DefaultMailboxes.INBOX))
+      .body(s"$FIRST_MAILBOX.role", nullValue)
+      .body(s"$FIRST_MAILBOX.sortOrder", equalTo(1000))
+  }
+
+  @Test
+  def getMailboxesShouldReturnCorrectMailboxRole(server: GuiceJamesServer): Unit = {
+    server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(MailboxPath.forUser(BOB, DefaultMailboxes.INBOX))
+      .serialize
+
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(GET_ALL_MAILBOXES_REQUEST)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .body(s"$ARGUMENTS.list", hasSize(1))
+      .body(s"$FIRST_MAILBOX.name", equalTo(DefaultMailboxes.INBOX))
+      .body(s"$FIRST_MAILBOX.role", equalTo(Role.INBOX.serialize))
+      .body(s"$FIRST_MAILBOX.sortOrder", equalTo(10))
+  }
+
+  @Test
+  @Tag(CategoryTags.BASIC_FEATURE)
+  def getMailboxesShouldReturnUpdatedQuotasForInboxWhenMailReceived(server: GuiceJamesServer): Unit = {
+    val message: Message = Message.Builder
+      .of
+      .setSubject("test")
+      .setBody("testmail", StandardCharsets.UTF_8)
+      .build
+
+    server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(MailboxPath.forUser(BOB, DefaultMailboxes.INBOX))
+      .serialize
+
+    server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, MailboxPath.forUser(BOB, DefaultMailboxes.INBOX), AppendCommand.from(message))
+    server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, MailboxPath.forUser(BOB, DefaultMailboxes.INBOX),
+        new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags(Flags.Flag.SEEN))
+
+    Thread.sleep(1000) //dirty fix for distributed integration test...
+
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(GET_ALL_MAILBOXES_REQUEST)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .body(s"$ARGUMENTS.list", hasSize(1))
+      .body(s"$FIRST_MAILBOX.name", equalTo(DefaultMailboxes.INBOX))
+      .body(s"$FIRST_MAILBOX.quotas['#private&bob@domain.tld']['Storage'].used", equalTo(110))
+      .body(s"$FIRST_MAILBOX.quotas['#private&bob@domain.tld']['Storage'].max", nullValue)
+      .body(s"$FIRST_MAILBOX.quotas['#private&bob@domain.tld']['Message'].used", equalTo(2))
+      .body(s"$FIRST_MAILBOX.quotas['#private&bob@domain.tld']['Message'].max", nullValue)
+  }
+
+  @Test
+  def getMailboxesShouldReturnMaximumQuotasWhenSet(server: GuiceJamesServer): Unit = {
+    server.getProbe(classOf[QuotaProbesImpl])
+      .setGlobalMaxStorage(QuotaSizeLimit.size(142))
+    server.getProbe(classOf[QuotaProbesImpl])
+      .setGlobalMaxMessageCount(QuotaCountLimit.count(31))
+
+    server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(MailboxPath.forUser(BOB, DefaultMailboxes.INBOX))
+      .serialize
+
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(GET_ALL_MAILBOXES_REQUEST)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .body(s"$ARGUMENTS.list", hasSize(1))
+      .body(s"$FIRST_MAILBOX.name", equalTo(DefaultMailboxes.INBOX))
+      .body(s"$FIRST_MAILBOX.quotas['#private&bob@domain.tld']['Storage'].max", equalTo(142))
+      .body(s"$FIRST_MAILBOX.quotas['#private&bob@domain.tld']['Message'].max", equalTo(31))
+  }
+
+  @Test
+  def getMailboxesShouldReturnMailboxesInSorteredOrder(server: GuiceJamesServer): Unit = {
+    val mailboxId1: String = server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(MailboxPath.forUser(BOB, DefaultMailboxes.TRASH))
+      .serialize
+    val mailboxId2: String = server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(MailboxPath.forUser(BOB, DefaultMailboxes.INBOX))
+      .serialize
+
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(GET_ALL_MAILBOXES_REQUEST)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .body(s"$ARGUMENTS.list", hasSize(2))
+      .body(s"$FIRST_MAILBOX.id", equalTo(mailboxId2))
+      .body(s"$FIRST_MAILBOX.name", equalTo(DefaultMailboxes.INBOX))
+      .body(s"$FIRST_MAILBOX.sortOrder", equalTo(10))
+      .body(s"$SECOND_MAILBOX.id", equalTo(mailboxId1))
+      .body(s"$SECOND_MAILBOX.name", equalTo(DefaultMailboxes.TRASH))
+      .body(s"$SECOND_MAILBOX.sortOrder", equalTo(60))
+  }
+
+  @Test
+  def getMailboxesByIdsShouldNotBeImplementedYet(server: GuiceJamesServer): Unit = {
+    val mailboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(MailboxPath.forUser(BOB, "custom"))
+
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(s"""{
+              |  "using": [
+              |    "urn:ietf:params:jmap:core",
+              |    "urn:ietf:params:jmap:mail"],
+              |  "methodCalls": [[
+              |      "Mailbox/get",
+              |      {
+              |        "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+              |        "ids": ["${mailboxId}"]
+              |      },
+              |      "c1"]]
+              |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_INTERNAL_SERVER_ERROR)
+  }
+}
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryMailboxGetMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryMailboxGetMethodTest.java
new file mode 100644
index 0000000..70db27f
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryMailboxGetMethodTest.java
@@ -0,0 +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.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.rfc8621.memory;
+
+import static org.apache.james.MemoryJamesServerMain.IN_MEMORY_SERVER_AGGREGATE_MODULE;
+
+import org.apache.james.GuiceJamesServer;
+import org.apache.james.JamesServerBuilder;
+import org.apache.james.JamesServerExtension;
+import org.apache.james.jmap.rfc8621.contract.MailboxGetMethodContract;
+import org.apache.james.modules.TestJMAPServerModule;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+public class MemoryMailboxGetMethodTest implements MailboxGetMethodContract {
+    @RegisterExtension
+    static JamesServerExtension testExtension = new JamesServerBuilder<>(JamesServerBuilder.defaultConfigurationProvider())
+        .server(configuration -> GuiceJamesServer.forConfiguration(configuration)
+            .combineWith(IN_MEMORY_SERVER_AGGREGATE_MODULE)
+            .overrideWith(new TestJMAPServerModule()))
+        .build();
+}


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


[james-project] 07/16: JAMES-3196 CanSendFromImpl: log unexpected exception

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 24fbe6844f8b25d03435f5bb61b2888697b95d90
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri May 29 09:13:19 2020 +0700

    JAMES-3196 CanSendFromImpl: log unexpected exception
    
    RRT resolution fails upon reads if the address feeded is invalid, or
    if a user has too much recursive mappings level.
    
    In any case this is some valuable audit information that could allow
    diagnosing alias related issues early, we should emit a WARN log for
    the admin to be aware of it.
---
 .../src/main/java/org/apache/james/rrt/lib/CanSendFromImpl.java  | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/server/data/data-library/src/main/java/org/apache/james/rrt/lib/CanSendFromImpl.java b/server/data/data-library/src/main/java/org/apache/james/rrt/lib/CanSendFromImpl.java
index 8f7f70a..0293914 100644
--- a/server/data/data-library/src/main/java/org/apache/james/rrt/lib/CanSendFromImpl.java
+++ b/server/data/data-library/src/main/java/org/apache/james/rrt/lib/CanSendFromImpl.java
@@ -35,6 +35,8 @@ import org.apache.james.rrt.api.AliasReverseResolver;
 import org.apache.james.rrt.api.CanSendFrom;
 import org.apache.james.rrt.api.RecipientRewriteTable;
 import org.apache.james.rrt.api.RecipientRewriteTableException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class CanSendFromImpl implements CanSendFrom {
 
@@ -43,7 +45,9 @@ public class CanSendFromImpl implements CanSendFrom {
         List<Domain> fetch(Username user);
     }
 
-    public static final EnumSet<Mapping.Type> ALIAS_TYPES_ACCEPTED_IN_FROM = EnumSet.of(Alias, DomainAlias);
+    private static final Logger LOGGER = LoggerFactory.getLogger(CanSendFromImpl.class);
+    private static final EnumSet<Mapping.Type> ALIAS_TYPES_ACCEPTED_IN_FROM = EnumSet.of(Alias, DomainAlias);
+
     private final RecipientRewriteTable recipientRewriteTable;
     private final AliasReverseResolver aliasReverseResolver;
 
@@ -58,6 +62,9 @@ public class CanSendFromImpl implements CanSendFrom {
         try {
             return connectedUser.equals(fromUser) || emailIsAnAliasOfTheConnectedUser(connectedUser, fromUser);
         } catch (RecipientRewriteTableException | RecipientRewriteTable.ErrorMappingException e) {
+            LOGGER.warn("Error upon {} mapping resolution for {}. You might want to audit mapping content for this mapping entry. ",
+                fromUser.asString(),
+                connectedUser.asString());
             return false;
         }
     }


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


[james-project] 06/16: JAMES-3196 MailetContainer: Log correlation for sender aliases

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 07a3f487c0092c13e0acad040efdb8ee6cd52558
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri May 29 09:06:42 2020 +0700

    JAMES-3196 MailetContainer: Log correlation for sender aliases
    
    Debugging can be hard when sender relies on aliases: we can not easily
    link them to their main address within the mailet-container. As a result,
    it is easy to miss user-related emails upon audit.
    
    This log ensures the sender alias could be correlated with the main user
    address.
---
 .../org/apache/james/mailetcontainer/impl/LocalResources.java     | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/LocalResources.java b/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/LocalResources.java
index bd41f84..a883a75 100644
--- a/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/LocalResources.java
+++ b/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/LocalResources.java
@@ -37,12 +37,15 @@ import org.apache.james.rrt.api.RecipientRewriteTableException;
 import org.apache.james.rrt.lib.Mapping;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.james.user.api.UsersRepositoryException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.github.fge.lambdas.Throwing;
 import com.github.steveash.guavate.Guavate;
 
 class LocalResources {
     private static final EnumSet<Mapping.Type> ALIAS_TYPES = EnumSet.of(Mapping.Type.Alias, Mapping.Type.DomainAlias);
+    public static final Logger LOGGER = LoggerFactory.getLogger(LocalResources.class);
 
     private final UsersRepository localUsers;
     private final DomainList domains;
@@ -130,6 +133,9 @@ class LocalResources {
             .asStream()
             .map(mapping -> mapping.asMailAddress()
                 .orElseThrow(() -> new IllegalStateException(String.format("Can not compute address for mapping %s", mapping.asString()))))
-            .anyMatch(Throwing.predicate(this::isLocaluser).sneakyThrow());
+            .filter(Throwing.predicate(this::isLocaluser).sneakyThrow())
+            .peek(ownerAddress -> LOGGER.debug("{} belongs to {} local user", mailAddress.asString(), ownerAddress.asString()))
+            .findFirst()
+            .isPresent();
     }
 }


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


[james-project] 01/16: JAMES-3196 Add toString() for IMAP commands

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a984dbf9fd1c7ed156be0abf5daa8cd55d92717d
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu May 28 11:28:50 2020 +0700

    JAMES-3196 Add toString() for IMAP commands
---
 .../james/imap/api/message/request/SearchOperation.java   |  1 +
 .../apache/james/imap/message/request/AppendRequest.java  | 11 +++++++++++
 .../james/imap/message/request/AuthenticateRequest.java   |  9 +++++++++
 .../apache/james/imap/message/request/CopyRequest.java    | 11 +++++++++++
 .../apache/james/imap/message/request/CreateRequest.java  |  9 +++++++++
 .../james/imap/message/request/DeleteACLRequest.java      |  9 +++++++++
 .../apache/james/imap/message/request/DeleteRequest.java  |  9 +++++++++
 .../apache/james/imap/message/request/EnableRequest.java  |  9 +++++++++
 .../apache/james/imap/message/request/ExamineRequest.java | 14 ++++++++++++++
 .../apache/james/imap/message/request/ExpungeRequest.java |  8 ++++++++
 .../apache/james/imap/message/request/FetchRequest.java   | 10 ++++++++++
 .../apache/james/imap/message/request/GetACLRequest.java  |  8 ++++++++
 .../james/imap/message/request/GetAnnotationRequest.java  | 11 +++++++++++
 .../james/imap/message/request/GetQuotaRequest.java       |  8 ++++++++
 .../james/imap/message/request/GetQuotaRootRequest.java   |  8 ++++++++
 .../apache/james/imap/message/request/ListRequest.java    | 10 ++++++++++
 .../james/imap/message/request/ListRightsRequest.java     |  9 +++++++++
 .../apache/james/imap/message/request/LoginRequest.java   |  9 +++++++++
 .../apache/james/imap/message/request/LsubRequest.java    | 10 ++++++++++
 .../apache/james/imap/message/request/MoveRequest.java    | 10 ++++++++++
 .../james/imap/message/request/MyRightsRequest.java       |  9 +++++++++
 .../apache/james/imap/message/request/RenameRequest.java  | 10 ++++++++++
 .../apache/james/imap/message/request/SearchRequest.java  | 10 ++++++++++
 .../apache/james/imap/message/request/SelectRequest.java  | 15 +++++++++++++++
 .../apache/james/imap/message/request/SetACLRequest.java  | 10 ++++++++++
 .../james/imap/message/request/SetAnnotationRequest.java  |  8 ++++++++
 .../james/imap/message/request/SetQuotaRequest.java       |  8 ++++++++
 .../apache/james/imap/message/request/StatusRequest.java  | 10 ++++++++++
 .../james/imap/message/request/SubscribeRequest.java      |  9 +++++++++
 .../james/imap/message/request/UnselectRequest.java       |  8 ++++++++
 .../james/imap/message/request/UnsubscribeRequest.java    |  9 +++++++++
 31 files changed, 289 insertions(+)

diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/message/request/SearchOperation.java b/protocols/imap/src/main/java/org/apache/james/imap/api/message/request/SearchOperation.java
index 44558b2..5333ac1 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/api/message/request/SearchOperation.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/api/message/request/SearchOperation.java
@@ -46,6 +46,7 @@ public final class SearchOperation {
     public String toString() {
         return MoreObjects.toStringHelper(this)
             .add("key", key)
+            .add("options", options)
             .toString();
     }
  
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/AppendRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/AppendRequest.java
index 839bcea..f60bdaa 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/AppendRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/AppendRequest.java
@@ -27,6 +27,8 @@ import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.request.ImapRequest;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * {@link ImapRequest} which request the append of a message to a mailbox
  */
@@ -80,4 +82,13 @@ public class AppendRequest extends AbstractImapRequest {
         return message;
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("mailboxName", mailboxName)
+            .add("flags", flags)
+            .add("datetime", datetime)
+            .add("message", message)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/AuthenticateRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/AuthenticateRequest.java
index 6bb79f8..7bb2df1 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/AuthenticateRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/AuthenticateRequest.java
@@ -21,6 +21,8 @@ package org.apache.james.imap.message.request;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 
+import com.google.common.base.MoreObjects;
+
 public class AuthenticateRequest extends AbstractImapRequest {
     private final String authType;
 
@@ -32,4 +34,11 @@ public class AuthenticateRequest extends AbstractImapRequest {
     public final String getAuthType() {
         return authType;
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("authType", authType)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/CopyRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/CopyRequest.java
index c136aaf..2329b27 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/CopyRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/CopyRequest.java
@@ -23,6 +23,8 @@ import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.request.ImapRequest;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * {@link ImapRequest} which request the copy of messages
  */
@@ -30,4 +32,13 @@ public class CopyRequest extends AbstractMessageRangeRequest {
     public CopyRequest(IdRange[] idSet, String mailboxName, boolean useUids, Tag tag) {
         super(ImapConstants.COPY_COMMAND, idSet, mailboxName, useUids, tag);
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("idSet", getIdSet())
+            .add("mailboxName", getMailboxName())
+            .add("useUids", isUseUids())
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/CreateRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/CreateRequest.java
index 6f85aa1..780c0e1 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/CreateRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/CreateRequest.java
@@ -22,6 +22,8 @@ import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.request.ImapRequest;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * {@link ImapRequest} which request the creation of a mailbox
  */
@@ -41,4 +43,11 @@ public class CreateRequest extends AbstractImapRequest {
     public final String getMailboxName() {
         return mailboxName;
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("mailboxName", mailboxName)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/DeleteACLRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/DeleteACLRequest.java
index ea7f824..192133c 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/DeleteACLRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/DeleteACLRequest.java
@@ -22,6 +22,8 @@ package org.apache.james.imap.message.request;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * DELETEACL Request.
  */
@@ -43,4 +45,11 @@ public class DeleteACLRequest extends AbstractImapRequest {
         return mailboxName;
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("mailboxName", mailboxName)
+            .add("identifier", identifier)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/DeleteRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/DeleteRequest.java
index 20900b6..4d9a42d 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/DeleteRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/DeleteRequest.java
@@ -22,6 +22,8 @@ import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.request.ImapRequest;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * {@link ImapRequest} which request the deletion of a mailbox
  */
@@ -41,4 +43,11 @@ public class DeleteRequest extends AbstractImapRequest {
     public final String getMailboxName() {
         return mailboxName;
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("mailboxName", mailboxName)
+            .toString();
+    }
 }
\ No newline at end of file
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/EnableRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/EnableRequest.java
index e9e1efe..d92296e 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/EnableRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/EnableRequest.java
@@ -24,6 +24,8 @@ import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.Capability;
 
+import com.google.common.base.MoreObjects;
+
 public class EnableRequest extends AbstractImapRequest {
 
     private final List<Capability> capabilities;
@@ -41,4 +43,11 @@ public class EnableRequest extends AbstractImapRequest {
     public List<Capability> getCapabilities() {
         return capabilities;
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("capabilities", capabilities)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ExamineRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ExamineRequest.java
index 208c0d9..969f6b5 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ExamineRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ExamineRequest.java
@@ -23,9 +23,23 @@ import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.UidRange;
 
+import com.google.common.base.MoreObjects;
+
 public class ExamineRequest extends AbstractMailboxSelectionRequest {
     public ExamineRequest(String mailboxName, boolean condstore, ClientSpecifiedUidValidity lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
         super(ImapConstants.EXAMINE_COMMAND, mailboxName, condstore, lastKnownUidValidity, knownModSeq, uidSet, knownUidSet, knownSequenceSet, tag);
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("mailboxName", getMailboxName())
+            .add("condstore", getCondstore())
+            .add("lastKnownUidValidity", getLastKnownUidValidity())
+            .add("knownModSeq", getKnownModSeq())
+            .add("uidSet", getUidSet())
+            .add("knownUidSet", getKnownUidSet())
+            .add("knownSequenceSet", getKnownSequenceSet())
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ExpungeRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ExpungeRequest.java
index 461415e..fd31e27 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ExpungeRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ExpungeRequest.java
@@ -23,6 +23,8 @@ import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.request.ImapRequest;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * {@link ImapRequest} which requests expunge of deleted messages
  */
@@ -44,4 +46,10 @@ public class ExpungeRequest extends AbstractImapRequest {
         return uidRange;
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("uidRange", uidRange)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/FetchRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/FetchRequest.java
index 9dce8ee..b5e088f 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/FetchRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/FetchRequest.java
@@ -23,6 +23,8 @@ import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.FetchData;
 import org.apache.james.imap.api.message.IdRange;
 
+import com.google.common.base.MoreObjects;
+
 public class FetchRequest extends AbstractImapRequest {
     private final boolean useUids;
     private final IdRange[] idSet;
@@ -47,4 +49,12 @@ public class FetchRequest extends AbstractImapRequest {
         return useUids;
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("useUids", useUids)
+            .add("idSet", idSet)
+            .add("fetch", fetch)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetACLRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetACLRequest.java
index d11262b..4a0da7f 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetACLRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetACLRequest.java
@@ -22,6 +22,8 @@ package org.apache.james.imap.message.request;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * GETACL Request.
  */
@@ -37,4 +39,10 @@ public class GetACLRequest extends AbstractImapRequest {
         return mailboxName;
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("mailboxName", mailboxName)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetAnnotationRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetAnnotationRequest.java
index 84f2902..46b6102 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetAnnotationRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetAnnotationRequest.java
@@ -26,6 +26,7 @@ import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 import org.apache.james.mailbox.model.MailboxAnnotationKey;
 
+import com.google.common.base.MoreObjects;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
 
@@ -150,4 +151,14 @@ public class GetAnnotationRequest extends AbstractImapRequest {
             throw new IllegalArgumentException("Cannot lookup Depth data for: " + code);
         }
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("mailboxName", mailboxName)
+            .add("keys", keys)
+            .add("maxsize", maxsize)
+            .add("depth", depth)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetQuotaRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetQuotaRequest.java
index 1760066..0e2d401 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetQuotaRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetQuotaRequest.java
@@ -22,6 +22,8 @@ package org.apache.james.imap.message.request;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * GETQUOTA Request
  */
@@ -37,4 +39,10 @@ public class GetQuotaRequest extends AbstractImapRequest {
         return quotaRoot;
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("quotaRoot", quotaRoot)
+            .toString();
+    }
 }
\ No newline at end of file
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetQuotaRootRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetQuotaRootRequest.java
index b4c3d1f..cd1d4b6 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetQuotaRootRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetQuotaRootRequest.java
@@ -22,6 +22,8 @@ package org.apache.james.imap.message.request;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * GETQUOTAROOT Request
  */
@@ -37,4 +39,10 @@ public class GetQuotaRootRequest extends AbstractImapRequest {
         return mailboxName;
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("mailboxName", mailboxName)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRequest.java
index 31b73e5..e2e5e78 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRequest.java
@@ -21,6 +21,8 @@ package org.apache.james.imap.message.request;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 
+import com.google.common.base.MoreObjects;
+
 public class ListRequest extends AbstractImapRequest {
     private final String baseReferenceName;
 
@@ -39,4 +41,12 @@ public class ListRequest extends AbstractImapRequest {
     public final String getMailboxPattern() {
         return mailboxPattern;
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("baseReferenceName", baseReferenceName)
+            .add("mailboxPattern", mailboxPattern)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRightsRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRightsRequest.java
index e63312f..a4e406f 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRightsRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRightsRequest.java
@@ -22,6 +22,8 @@ package org.apache.james.imap.message.request;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * LISTRIGHTS Request.
  */
@@ -43,4 +45,11 @@ public class ListRightsRequest extends AbstractImapRequest {
         return mailboxName;
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("identifier", identifier)
+            .add("mailboxName", mailboxName)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/LoginRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/LoginRequest.java
index ed14d73..4a92b52 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/LoginRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/LoginRequest.java
@@ -23,6 +23,8 @@ import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.request.ImapRequest;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * {@link ImapRequest} which requests the login of a user
  */
@@ -54,4 +56,11 @@ public class LoginRequest extends AbstractImapRequest {
     public final Username getUserid() {
         return userid;
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("userid", userid)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/LsubRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/LsubRequest.java
index 7113dbf..dff4d7b 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/LsubRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/LsubRequest.java
@@ -21,6 +21,8 @@ package org.apache.james.imap.message.request;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 
+import com.google.common.base.MoreObjects;
+
 public class LsubRequest extends AbstractImapRequest {
     private final String baseReferenceName;
 
@@ -39,4 +41,12 @@ public class LsubRequest extends AbstractImapRequest {
     public final String getMailboxPattern() {
         return mailboxPattern;
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("baseReferenceName", baseReferenceName)
+            .add("mailboxPattern", mailboxPattern)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/MoveRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/MoveRequest.java
index 0d303f1..c0c03af 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/MoveRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/MoveRequest.java
@@ -24,6 +24,8 @@ import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.request.ImapRequest;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * {@link ImapRequest} which request the move of messages
  */
@@ -33,4 +35,12 @@ public class MoveRequest extends AbstractMessageRangeRequest {
         super(ImapConstants.MOVE_COMMAND, idSet, mailboxName, useUids, tag);
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("idSet", getIdSet())
+            .add("mailboxName", getMailboxName())
+            .add("useUids", isUseUids())
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/MyRightsRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/MyRightsRequest.java
index fa6f2d0..421024b 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/MyRightsRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/MyRightsRequest.java
@@ -22,6 +22,8 @@ package org.apache.james.imap.message.request;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * MYRIGHTS Request.
  */
@@ -36,4 +38,11 @@ public class MyRightsRequest extends AbstractImapRequest {
     public String getMailboxName() {
         return mailboxName;
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("mailboxName", mailboxName)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/RenameRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/RenameRequest.java
index 984a4e3..4d24475 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/RenameRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/RenameRequest.java
@@ -21,6 +21,8 @@ package org.apache.james.imap.message.request;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 
+import com.google.common.base.MoreObjects;
+
 public class RenameRequest extends AbstractImapRequest {
     private final String existingName;
     private final String newName;
@@ -38,4 +40,12 @@ public class RenameRequest extends AbstractImapRequest {
     public final String getNewName() {
         return newName;
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("existingName", existingName)
+            .add("newName", newName)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SearchRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SearchRequest.java
index 6fa9176..df5ba78 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SearchRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SearchRequest.java
@@ -22,6 +22,8 @@ import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.request.SearchOperation;
 
+import com.google.common.base.MoreObjects;
+
 public class SearchRequest extends AbstractImapRequest {
     private final SearchOperation operation;
     private final boolean useUids;
@@ -39,4 +41,12 @@ public class SearchRequest extends AbstractImapRequest {
     public final boolean isUseUids() {
         return useUids;
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("operation", operation)
+            .add("useUids", useUids)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SelectRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SelectRequest.java
index d7b98ad..da11ea3 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SelectRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SelectRequest.java
@@ -23,8 +23,23 @@ import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.UidRange;
 
+import com.google.common.base.MoreObjects;
+
 public class SelectRequest extends AbstractMailboxSelectionRequest {
     public SelectRequest(String mailboxName, boolean condstore, ClientSpecifiedUidValidity lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
         super(ImapConstants.SELECT_COMMAND, mailboxName, condstore, lastKnownUidValidity, knownModSeq, uidSet, knownUidSet, knownSequenceSet, tag);
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("mailboxName", getMailboxName())
+            .add("condstore", getCondstore())
+            .add("lastKnownUidValidity", getLastKnownUidValidity())
+            .add("knownModSeq", getKnownModSeq())
+            .add("uidSet", getUidSet())
+            .add("knownUidSet", getKnownUidSet())
+            .add("knownSequenceSet", getKnownSequenceSet())
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SetACLRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SetACLRequest.java
index 6364234..1b34bb6 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SetACLRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SetACLRequest.java
@@ -22,6 +22,8 @@ package org.apache.james.imap.message.request;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * SETACL Request.
  */
@@ -49,4 +51,12 @@ public class SetACLRequest extends AbstractImapRequest {
         return rights;
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("identifier", identifier)
+            .add("mailboxName", mailboxName)
+            .add("rights", rights)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SetAnnotationRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SetAnnotationRequest.java
index d9dc574..8a4535f 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SetAnnotationRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SetAnnotationRequest.java
@@ -25,6 +25,7 @@ import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 import org.apache.james.mailbox.model.MailboxAnnotation;
 
+import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableList;
 
 public class SetAnnotationRequest extends AbstractImapRequest {
@@ -45,4 +46,11 @@ public class SetAnnotationRequest extends AbstractImapRequest {
         return mailboxAnnotations;
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("mailboxName", mailboxName)
+            .add("mailboxAnnotations", mailboxAnnotations)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SetQuotaRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SetQuotaRequest.java
index 756c19e..6cff465 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SetQuotaRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SetQuotaRequest.java
@@ -79,4 +79,12 @@ public class SetQuotaRequest extends AbstractImapRequest {
     public String getQuotaRoot() {
         return quotaRoot;
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("quotaRoot", quotaRoot)
+            .add("resourceLimits", resourceLimits)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/StatusRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/StatusRequest.java
index 950a024..692e34c 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/StatusRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/StatusRequest.java
@@ -22,6 +22,8 @@ import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.StatusDataItems;
 
+import com.google.common.base.MoreObjects;
+
 public class StatusRequest extends AbstractImapRequest {
     private final String mailboxName;
 
@@ -40,4 +42,12 @@ public class StatusRequest extends AbstractImapRequest {
     public final StatusDataItems getStatusDataItems() {
         return statusDataItems;
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("mailboxName", mailboxName)
+            .add("statusDataItems", statusDataItems)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SubscribeRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SubscribeRequest.java
index 828c5aa..ddaf5f4 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SubscribeRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SubscribeRequest.java
@@ -21,6 +21,8 @@ package org.apache.james.imap.message.request;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 
+import com.google.common.base.MoreObjects;
+
 public class SubscribeRequest extends AbstractImapRequest {
     private final String mailboxName;
 
@@ -32,4 +34,11 @@ public class SubscribeRequest extends AbstractImapRequest {
     public final String getMailboxName() {
         return mailboxName;
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("mailboxName", mailboxName)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/UnselectRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/UnselectRequest.java
index 7d89894..b87a422 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/UnselectRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/UnselectRequest.java
@@ -21,8 +21,16 @@ package org.apache.james.imap.message.request;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 
+import com.google.common.base.MoreObjects;
+
 public class UnselectRequest extends AbstractImapRequest {
     public UnselectRequest(Tag tag) {
         super(tag, ImapConstants.UNSELECT_COMMAND);
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .toString();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/UnsubscribeRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/UnsubscribeRequest.java
index f585c74..21c287a 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/UnsubscribeRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/UnsubscribeRequest.java
@@ -21,6 +21,8 @@ package org.apache.james.imap.message.request;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 
+import com.google.common.base.MoreObjects;
+
 public class UnsubscribeRequest extends AbstractImapRequest {
     private final String mailboxName;
 
@@ -32,4 +34,11 @@ public class UnsubscribeRequest extends AbstractImapRequest {
     public final String getMailboxName() {
         return mailboxName;
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("mailboxName", mailboxName)
+            .toString();
+    }
 }


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


[james-project] 10/16: JAMES-3171 Port MailboxFactory to jmap-rfc8621

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 24482cc489fbeb65be1cf57de51dd63390fc4f14
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Thu May 21 10:18:20 2020 +0700

    JAMES-3171 Port MailboxFactory to jmap-rfc8621
---
 .../scala/org/apache/james/jmap/mail/Mailbox.scala |  48 ++++++--
 .../apache/james/jmap/model/MailboxFactory.scala   | 125 +++++++++++++++++++++
 .../james/jmap/json/MailboxSerializationTest.scala |   2 +-
 3 files changed, 162 insertions(+), 13 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Mailbox.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Mailbox.scala
index aaff40e..fc03a7b 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Mailbox.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Mailbox.scala
@@ -19,11 +19,12 @@
 
 package org.apache.james.jmap.mail
 
+import eu.timepit.refined
 import eu.timepit.refined.api.Refined
 import eu.timepit.refined.auto._
 import eu.timepit.refined.collection.NonEmpty
 import org.apache.james.core.Username
-import org.apache.james.jmap.mail.Mailbox.MailboxName
+import org.apache.james.jmap.mail.MailboxName.MailboxName
 import org.apache.james.jmap.model.UnsignedInt.UnsignedInt
 import org.apache.james.mailbox.Role
 import org.apache.james.mailbox.model.MailboxId
@@ -38,15 +39,29 @@ case class MayRename(value: Boolean) extends AnyVal
 case class MayDelete(value: Boolean) extends AnyVal
 case class MaySubmit(value: Boolean) extends AnyVal
 
+object MailboxRights {
+  val FULL: MailboxRights = MailboxRights(
+    mayReadItems = MayReadItems(true),
+    mayAddItems = MayAddItems(true),
+    mayRemoveItems = MayRemoveItems(true),
+    maySetSeen = MaySetSeen(true),
+    maySetKeywords = MaySetKeywords(true),
+    mayCreateChild = MayCreateChild(true),
+    mayRename = MayRename(true),
+    mayDelete = MayDelete(true),
+    maySubmit = MaySubmit(true),
+  )
+}
+
 case class MailboxRights(mayReadItems: MayReadItems,
-                               mayAddItems: MayAddItems,
-                               mayRemoveItems: MayRemoveItems,
-                               maySetSeen: MaySetSeen,
-                               maySetKeywords: MaySetKeywords,
-                               mayCreateChild: MayCreateChild,
-                               mayRename: MayRename,
-                               mayDelete: MayDelete,
-                               maySubmit: MaySubmit)
+                         mayAddItems: MayAddItems,
+                         mayRemoveItems: MayRemoveItems,
+                         maySetSeen: MaySetSeen,
+                         maySetKeywords: MaySetKeywords,
+                         mayCreateChild: MayCreateChild,
+                         mayRename: MayRename,
+                         mayDelete: MayDelete,
+                         maySubmit: MaySubmit)
 
 object MailboxNamespace {
   def delegated(owner: Username) = DelegatedNamespace(owner)
@@ -61,6 +76,8 @@ case class PersonalNamespace() extends MailboxNamespace
 case class DelegatedNamespace(owner: Username) extends MailboxNamespace
 
 object SortOrder {
+  val defaultSortOrder: SortOrder = SortOrder(1000L)
+
   private val defaultSortOrders = Map(
       Role.INBOX -> SortOrder(10L),
       Role.ARCHIVE -> SortOrder(20L),
@@ -71,7 +88,7 @@ object SortOrder {
       Role.SPAM -> SortOrder(70L),
       Role.TEMPLATES -> SortOrder(80L),
       Role.RESTORED_MESSAGES -> SortOrder(90L))
-    .withDefaultValue(SortOrder(1000L))
+    .withDefaultValue(defaultSortOrder)
 
   def getSortOrder(role: Role): SortOrder = defaultSortOrders(role)
 }
@@ -97,8 +114,15 @@ sealed trait QuotasExtension extends MailboxExtensionAdditionalFields {
   def quotas: Quotas
 }
 
-object Mailbox {
-  type MailboxName = String Refined NonEmpty
+object MailboxName {
+  type MailboxNameConstraint = NonEmpty
+  type MailboxName = String Refined MailboxNameConstraint
+
+  def liftOrThrow(value: String): MailboxName =
+    refined.refineV[MailboxNameConstraint](value) match {
+      case scala.util.Right(value) => value
+      case Left(error) => throw new IllegalArgumentException(error)
+    }
 }
 
 case class Mailbox(id: MailboxId,
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/MailboxFactory.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/MailboxFactory.scala
new file mode 100644
index 0000000..caf9046
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/MailboxFactory.scala
@@ -0,0 +1,125 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.model
+
+import javax.inject.Inject
+import org.apache.james.jmap.mail.MailboxName.MailboxName
+import org.apache.james.jmap.mail._
+import org.apache.james.jmap.utils.quotas.QuotaLoader
+import org.apache.james.mailbox.model.MailboxACL.EntryKey
+import org.apache.james.mailbox.model.{MailboxCounters, MailboxId, MailboxMetaData, MailboxPath, MailboxACL => JavaMailboxACL}
+import org.apache.james.mailbox.{MailboxSession, Role, SubscriptionManager}
+import reactor.core.scala.publisher.SMono
+
+import scala.jdk.CollectionConverters._
+import scala.jdk.OptionConverters._
+
+sealed trait MailboxConstructionOrder
+
+class Factory
+
+class MailboxFactory @Inject() (subscriptionManager: SubscriptionManager) {
+
+  def create(mailboxMetaData: MailboxMetaData,
+             mailboxSession: MailboxSession,
+             allMailboxesMetadata: Seq[MailboxMetaData],
+             quotaLoader: QuotaLoader): SMono[Mailbox] = {
+
+    val id: MailboxId = mailboxMetaData.getId
+
+    val name: MailboxName = MailboxName.liftOrThrow(mailboxMetaData.getPath
+      .getName
+      .split(mailboxSession.getPathDelimiter)
+      .last)
+
+    val role: Option[Role] = Role.from(mailboxMetaData.getPath.getName)
+      .filter(_ => mailboxMetaData.getPath.belongsTo(mailboxSession)).toScala
+    val sortOrder: SortOrder = role.map(SortOrder.getSortOrder).getOrElse(SortOrder.defaultSortOrder)
+    val quotas: SMono[Quotas] = quotaLoader.getQuotas(mailboxMetaData.getPath)
+    val rights: Rights = Rights.fromACL(MailboxACL.fromJava(mailboxMetaData.getResolvedAcls))
+
+    val sanitizedCounters: MailboxCounters = mailboxMetaData.getCounters.sanitize()
+    val unreadEmails: UnreadEmails = UnreadEmails(UnsignedInt.liftOrThrow(sanitizedCounters.getUnseen))
+    val unreadThreads: UnreadThreads = UnreadThreads(UnsignedInt.liftOrThrow(sanitizedCounters.getUnseen))
+    val totalEmails: TotalEmails = TotalEmails(UnsignedInt.liftOrThrow(sanitizedCounters.getCount))
+    val totalThreads: TotalThreads = TotalThreads(UnsignedInt.liftOrThrow(sanitizedCounters.getCount))
+
+    val isOwner = mailboxMetaData.getPath.belongsTo(mailboxSession)
+    val aclEntryKey: EntryKey = EntryKey.createUserEntryKey(mailboxSession.getUser)
+
+    val namespace: MailboxNamespace = if (isOwner) {
+      PersonalNamespace()
+    } else {
+      DelegatedNamespace(mailboxMetaData.getPath.getUser)
+    }
+
+    val parentPath: Option[MailboxPath] =
+      mailboxMetaData.getPath
+        .getHierarchyLevels(mailboxSession.getPathDelimiter)
+        .asScala
+        .reverse
+        .drop(1)
+        .headOption
+
+    val parentId: Option[MailboxId] = allMailboxesMetadata.filter(otherMetadata => parentPath.contains(otherMetadata.getPath))
+      .map(_.getId)
+      .headOption
+
+    val myRights: MailboxRights = if (isOwner) {
+      MailboxRights.FULL
+    } else {
+      val rights = Rfc4314Rights.fromJava(mailboxMetaData.getResolvedAcls
+        .getEntries
+        .getOrDefault(aclEntryKey, JavaMailboxACL.NO_RIGHTS))
+        .toRights
+      MailboxRights(
+        mayReadItems = MayReadItems(rights.contains(Right.Read)),
+        mayAddItems = MayAddItems(rights.contains(Right.Insert)),
+        mayRemoveItems = MayRemoveItems(rights.contains(Right.DeleteMessages)),
+        maySetSeen = MaySetSeen(rights.contains(Right.Seen)),
+        maySetKeywords = MaySetKeywords(rights.contains(Right.Write)),
+        mayCreateChild = MayCreateChild(false),
+        mayRename = MayRename(false),
+        mayDelete = MayDelete(false),
+        maySubmit = MaySubmit(false))
+    }
+
+    def retrieveIsSubscribed: IsSubscribed = IsSubscribed(subscriptionManager
+      .subscriptions(mailboxSession)
+      .contains(mailboxMetaData.getPath.getName))
+
+    SMono.fromPublisher(quotas)
+      .map(quotas => Mailbox(
+        id = id,
+        name = name,
+        parentId = parentId,
+        role = role,
+        sortOrder = sortOrder,
+        unreadEmails = unreadEmails,
+        totalEmails = totalEmails,
+        unreadThreads = unreadThreads,
+        totalThreads = totalThreads,
+        myRights = myRights,
+        namespace = namespace,
+        rights = rights,
+        quotas = quotas,
+        isSubscribed = retrieveIsSubscribed))
+  }
+}
\ No newline at end of file
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxSerializationTest.scala
index 940d29a..c5b49d8 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxSerializationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxSerializationTest.scala
@@ -23,7 +23,7 @@ import eu.timepit.refined.auto._
 import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
 import org.apache.james.core.{Domain, Username}
 import org.apache.james.jmap.json.MailboxSerializationTest.MAILBOX
-import org.apache.james.jmap.mail.Mailbox.MailboxName
+import org.apache.james.jmap.mail.MailboxName.MailboxName
 import org.apache.james.jmap.mail.{IsSubscribed, Mailbox, MailboxNamespace, MailboxRights, MayAddItems, MayCreateChild, MayDelete, MayReadItems, MayRemoveItems, MayRename, MaySetKeywords, MaySetSeen, MaySubmit, PersonalNamespace, Quota, QuotaId, QuotaRoot, Quotas, Right, Rights, SortOrder, TotalEmails, TotalThreads, UnreadEmails, UnreadThreads, Value}
 import org.apache.james.mailbox.Role
 import org.apache.james.mailbox.model.{MailboxId, TestId}


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


[james-project] 14/16: JAMES-3171 Add metrics to MailboxGetMethod process

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 613a87b7d4cf17a879c170084375fa507b3cfce1
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Wed May 27 14:21:44 2020 +0700

    JAMES-3171 Add metrics to MailboxGetMethod process
---
 .../scala/org/apache/james/jmap/method/MailboxGetMethod.scala    | 9 ++++++---
 .../src/main/scala/org/apache/james/jmap/method/Method.scala     | 2 ++
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
index ca9f2e7..1ccb921 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
@@ -30,6 +30,7 @@ import org.apache.james.jmap.utils.quotas.QuotaLoaderWithPreloadedDefaultFactory
 import org.apache.james.mailbox.model.MailboxMetaData
 import org.apache.james.mailbox.model.search.MailboxQuery
 import org.apache.james.mailbox.{MailboxManager, MailboxSession}
+import org.apache.james.metrics.api.MetricFactory
 import org.reactivestreams.Publisher
 import play.api.libs.json.{JsError, JsObject, JsSuccess, Json}
 import reactor.core.scala.publisher.{SFlux, SMono}
@@ -38,11 +39,13 @@ import reactor.core.scheduler.Schedulers
 class MailboxGetMethod @Inject() (serializer: Serializer,
                                   mailboxManager: MailboxManager,
                                   quotaFactory : QuotaLoaderWithPreloadedDefaultFactory,
-                                  mailboxFactory: MailboxFactory) extends Method {
+                                  mailboxFactory: MailboxFactory,
+                                  metricFactory: MetricFactory) extends Method {
   override val methodName: MethodName = MethodName("Mailbox/get")
 
   override def process(invocation: Invocation, mailboxSession: MailboxSession): Publisher[Invocation] = {
-    asMailboxGetRequest(invocation.arguments)
+    metricFactory.runPublishingTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value,
+      asMailboxGetRequest(invocation.arguments)
         .flatMap(mailboxGetRequest => getMailboxes(mailboxGetRequest, mailboxSession)
           .collectSeq()
           .map(_.sortBy(_.sortOrder))
@@ -54,7 +57,7 @@ class MailboxGetMethod @Inject() (serializer: Serializer,
           .map(mailboxGetResponse => Invocation(
             methodName = methodName,
             arguments = Arguments(serializer.serialize(mailboxGetResponse).as[JsObject]),
-            methodCallId = invocation.methodCallId)))
+            methodCallId = invocation.methodCallId))))
   }
 
   private def asMailboxGetRequest(arguments: Arguments): SMono[MailboxGetRequest] = {
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala
index 21b33b5..02751dc 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala
@@ -25,6 +25,8 @@ import org.apache.james.mailbox.MailboxSession
 import org.reactivestreams.Publisher
 
 trait Method {
+  val JMAP_RFC8621_PREFIX: String = "JMAP-RFC8621-"
+
   val methodName: MethodName
 
   def process(invocation: Invocation, mailboxSession: MailboxSession): Publisher[Invocation]


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


[james-project] 12/16: JAMES-3171 Mailbox/get all implementation

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit d307d391ed2b32c5485749006ee72da08923f78e
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Fri May 22 16:47:09 2020 +0700

    JAMES-3171 Mailbox/get all implementation
---
 .../james/jmap/rfc8621/RFC8621MethodsModule.java   |  2 +
 .../org/apache/james/jmap/json/Serializer.scala    |  4 +
 .../james/jmap/method/MailboxGetMethod.scala       | 89 ++++++++++++++++++++++
 .../org/apache/james/jmap/model/Session.scala      |  2 +-
 .../apache/james/jmap/routes/JMAPApiRoutes.scala   |  3 +-
 5 files changed, 97 insertions(+), 3 deletions(-)

diff --git a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java
index 8d855de..5cd2117 100644
--- a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java
+++ b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java
@@ -28,6 +28,7 @@ import org.apache.james.jmap.http.rfc8621.InjectionKeys;
 import org.apache.james.jmap.json.Serializer;
 import org.apache.james.jmap.jwt.JWTAuthenticationStrategy;
 import org.apache.james.jmap.method.CoreEchoMethod;
+import org.apache.james.jmap.method.MailboxGetMethod;
 import org.apache.james.jmap.method.Method;
 import org.apache.james.jmap.routes.JMAPApiRoutes;
 import org.apache.james.metrics.api.MetricFactory;
@@ -48,6 +49,7 @@ public class RFC8621MethodsModule extends AbstractModule {
 
         Multibinder<Method> methods = Multibinder.newSetBinder(binder(), Method.class);
         methods.addBinding().to(CoreEchoMethod.class);
+        methods.addBinding().to(MailboxGetMethod.class);
     }
 
     @ProvidesIntoSet
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala
index 7e24a1a..6d374ed 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala
@@ -244,4 +244,8 @@ class Serializer @Inject() (mailboxIdFactory: MailboxId.Factory) {
   def deserializeMailboxGetRequest(input: String): JsResult[MailboxGetRequest] = {
     Json.parse(input).validate[MailboxGetRequest]
   }
+
+  def deserializeMailboxGetRequest(input: JsValue): JsResult[MailboxGetRequest] = {
+    Json.fromJson[MailboxGetRequest](input)
+  }
 }
\ No newline at end of file
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
new file mode 100644
index 0000000..ca9f2e7
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
@@ -0,0 +1,89 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.method
+
+import eu.timepit.refined.auto._
+import javax.inject.Inject
+import org.apache.james.jmap.json.Serializer
+import org.apache.james.jmap.mail._
+import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
+import org.apache.james.jmap.model.State.INSTANCE
+import org.apache.james.jmap.model.{Invocation, MailboxFactory}
+import org.apache.james.jmap.utils.quotas.QuotaLoaderWithPreloadedDefaultFactory
+import org.apache.james.mailbox.model.MailboxMetaData
+import org.apache.james.mailbox.model.search.MailboxQuery
+import org.apache.james.mailbox.{MailboxManager, MailboxSession}
+import org.reactivestreams.Publisher
+import play.api.libs.json.{JsError, JsObject, JsSuccess, Json}
+import reactor.core.scala.publisher.{SFlux, SMono}
+import reactor.core.scheduler.Schedulers
+
+class MailboxGetMethod @Inject() (serializer: Serializer,
+                                  mailboxManager: MailboxManager,
+                                  quotaFactory : QuotaLoaderWithPreloadedDefaultFactory,
+                                  mailboxFactory: MailboxFactory) extends Method {
+  override val methodName: MethodName = MethodName("Mailbox/get")
+
+  override def process(invocation: Invocation, mailboxSession: MailboxSession): Publisher[Invocation] = {
+    asMailboxGetRequest(invocation.arguments)
+        .flatMap(mailboxGetRequest => getMailboxes(mailboxGetRequest, mailboxSession)
+          .collectSeq()
+          .map(_.sortBy(_.sortOrder))
+          .map(mailboxes => MailboxGetResponse(
+            accountId = mailboxGetRequest.accountId,
+            state = INSTANCE,
+            list = mailboxes.toList,
+            notFound = NotFound(Nil)))
+          .map(mailboxGetResponse => Invocation(
+            methodName = methodName,
+            arguments = Arguments(serializer.serialize(mailboxGetResponse).as[JsObject]),
+            methodCallId = invocation.methodCallId)))
+  }
+
+  private def asMailboxGetRequest(arguments: Arguments): SMono[MailboxGetRequest] = {
+    serializer.deserializeMailboxGetRequest(arguments.value) match {
+      case JsSuccess(mailboxGetRequest, _) => SMono.just(mailboxGetRequest)
+      case JsError(errors) => SMono.raiseError(new IllegalArgumentException("Invalid MailboxGetRequest")) //FIXME MOB
+    }
+  }
+
+  private def getMailboxes(mailboxGetRequest: MailboxGetRequest, mailboxSession: MailboxSession): SFlux[Mailbox] = mailboxGetRequest.ids match {
+    case None => getAllMailboxes(mailboxSession)
+    case _ => SFlux.raiseError(new NotImplementedError("Getting mailboxes by Ids is not supported yet"))
+  }
+
+  private def getAllMailboxes(mailboxSession: MailboxSession): SFlux[Mailbox] = {
+    quotaFactory.loadFor(mailboxSession)
+      .subscribeOn(Schedulers.elastic)
+      .flatMapMany(quotaLoader =>
+        getAllMailboxesMetaData(mailboxSession).flatMapMany(mailboxesMetaData =>
+          SFlux.fromIterable(mailboxesMetaData)
+            .flatMap(mailboxMetaData =>
+              mailboxFactory.create(
+                mailboxMetaData = mailboxMetaData,
+                mailboxSession = mailboxSession,
+                allMailboxesMetadata = mailboxesMetaData,
+                quotaLoader = quotaLoader))))
+  }
+
+  private def getAllMailboxesMetaData(mailboxSession: MailboxSession): SMono[Seq[MailboxMetaData]] =
+    SFlux.fromPublisher(mailboxManager.searchReactive(MailboxQuery.builder.matchesAllMailboxNames.build, mailboxSession))
+      .collectSeq()
+}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Session.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Session.scala
index 1b09946..fb53ae3 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Session.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Session.scala
@@ -75,7 +75,7 @@ final case class Account private(accountId: AccountId,
                                  accountCapabilities: Set[_ <: Capability])
 
 object State {
-  private[model] val INSTANCE: State = "000001"
+  val INSTANCE: State = "000001"
 
   type State = String Refined NonEmpty
 }
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
index 478cdcc..dc918c6 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
@@ -47,7 +47,6 @@ import reactor.core.scala.publisher.{SFlux, SMono}
 import reactor.core.scheduler.Schedulers
 import reactor.netty.http.server.{HttpServerRequest, HttpServerResponse}
 
-import scala.collection.mutable
 import scala.jdk.CollectionConverters._
 
 object JMAPApiRoutes {
@@ -130,7 +129,7 @@ class JMAPApiRoutes (val authenticator: Authenticator,
     case exception: IllegalArgumentException => SMono.fromPublisher(httpServerResponse.status(SC_BAD_REQUEST)
       .header(CONTENT_TYPE, JSON_CONTENT_TYPE)
       .sendString(SMono.fromCallable(() => exception.getMessage), StandardCharsets.UTF_8)
-      .`then`())
+      .`then`)
     case exception: UnauthorizedException => SMono(handleAuthenticationFailure(httpServerResponse, JMAPApiRoutes.LOGGER, exception))
     case _ => SMono.fromPublisher(handleInternalError(httpServerResponse, throwable))
   }


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


[james-project] 15/16: JAMES-3171 JsError serialization

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit eef1f7e92565d37d2be2fe853802f276d7530d69
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Fri May 29 11:38:30 2020 +0700

    JAMES-3171 JsError serialization
---
 .../org/apache/james/jmap/json/Serializer.scala    | 64 +++++++++++----------
 .../james/jmap/method/MailboxGetMethod.scala       |  2 +-
 .../james/jmap/json/JsErrorSerializationTest.scala | 66 ++++++++++++++++++++++
 3 files changed, 100 insertions(+), 32 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala
index 6d374ed..76c6863 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala
@@ -34,6 +34,7 @@ import org.apache.james.mailbox.model.{MailboxACL, MailboxId}
 import play.api.libs.functional.syntax._
 import play.api.libs.json._
 
+import scala.collection.{Seq => LegacySeq}
 import scala.util.Try
 
 class Serializer @Inject() (mailboxIdFactory: MailboxId.Factory) {
@@ -158,7 +159,7 @@ class Serializer @Inject() (mailboxIdFactory: MailboxId.Factory) {
   private implicit val mailboxRightsWrites: Writes[MailboxRights] = Json.writes[MailboxRights]
 
   private implicit val mailboxNamespaceWrites: Writes[MailboxNamespace] = {
-    case personal: PersonalNamespace => JsString("Personal")
+    case _: PersonalNamespace => JsString("Personal")
     case delegated: DelegatedNamespace => JsString(s"Delegated[${delegated.owner.asString}]")
   }
 
@@ -209,43 +210,44 @@ class Serializer @Inject() (mailboxIdFactory: MailboxId.Factory) {
   private implicit val notFoundWrites: Writes[NotFound] = Json.valueWrites[NotFound]
   private implicit val mailboxGetResponseWrites: Writes[MailboxGetResponse] = Json.writes[MailboxGetResponse]
 
-  def serialize(session: Session): JsValue = {
-    Json.toJson(session)
-  }
+  private implicit val jsonValidationErrorWrites: Writes[JsonValidationError] = error => JsString(error.message)
 
-  def serialize(requestObject: RequestObject): JsValue = {
-    Json.toJson(requestObject)
-  }
+  private implicit def jsonValidationErrorsWrites(implicit jsonValidationErrorWrites: Writes[JsonValidationError]): Writes[LegacySeq[JsonValidationError]] =
+    (errors: LegacySeq[JsonValidationError]) => {
+      JsArray(errors.map(error => jsonValidationErrorWrites.writes(error)).toArray[JsValue])
+    }
 
-  def serialize(responseObject: ResponseObject): JsValue = {
-    Json.toJson(responseObject)
-  }
+  private implicit def errorsWrites(implicit jsonValidationErrorsWrites: Writes[LegacySeq[JsonValidationError]]): Writes[LegacySeq[(JsPath, LegacySeq[JsonValidationError])]] =
+    (errors: LegacySeq[(JsPath, LegacySeq[JsonValidationError])]) => {
+      errors.foldLeft(JsArray.empty)((jsArray, jsError) => {
+        val (path: JsPath, list: LegacySeq[JsonValidationError]) = jsError
+        jsArray:+ JsObject(Seq(
+          "path" -> JsString(path.toJsonString),
+          "messages" -> jsonValidationErrorsWrites.writes(list)))
+      })
+    }
 
-  def serialize(mailbox: Mailbox): JsValue = {
-    Json.toJson(mailbox)
-  }
+  private implicit def jsErrorWrites: Writes[JsError] = Json.writes[JsError]
 
-  def serialize(mailboxGetResponse: MailboxGetResponse): JsValue = {
-    Json.toJson(mailboxGetResponse)
-  }
+  def serialize(session: Session): JsValue = Json.toJson(session)
 
-  def deserializeRequestObject(input: String): JsResult[RequestObject] = {
-    Json.parse(input).validate[RequestObject]
-  }
+  def serialize(requestObject: RequestObject): JsValue = Json.toJson(requestObject)
 
-  def deserializeRequestObject(input: InputStream): JsResult[RequestObject] = {
-    Json.parse(input).validate[RequestObject]
-  }
+  def serialize(responseObject: ResponseObject): JsValue = Json.toJson(responseObject)
 
-  def deserializeResponseObject(input: String): JsResult[ResponseObject] = {
-    Json.parse(input).validate[ResponseObject]
-  }
+  def serialize(mailbox: Mailbox): JsValue = Json.toJson(mailbox)
 
-  def deserializeMailboxGetRequest(input: String): JsResult[MailboxGetRequest] = {
-    Json.parse(input).validate[MailboxGetRequest]
-  }
+  def serialize(mailboxGetResponse: MailboxGetResponse): JsValue = Json.toJson(mailboxGetResponse)
 
-  def deserializeMailboxGetRequest(input: JsValue): JsResult[MailboxGetRequest] = {
-    Json.fromJson[MailboxGetRequest](input)
-  }
+  def serialize(errors: JsError): JsValue = Json.toJson(errors)
+
+  def deserializeRequestObject(input: String): JsResult[RequestObject] = Json.parse(input).validate[RequestObject]
+
+  def deserializeRequestObject(input: InputStream): JsResult[RequestObject] = Json.parse(input).validate[RequestObject]
+
+  def deserializeResponseObject(input: String): JsResult[ResponseObject] = Json.parse(input).validate[ResponseObject]
+
+  def deserializeMailboxGetRequest(input: String): JsResult[MailboxGetRequest] = Json.parse(input).validate[MailboxGetRequest]
+
+  def deserializeMailboxGetRequest(input: JsValue): JsResult[MailboxGetRequest] = Json.fromJson[MailboxGetRequest](input)
 }
\ No newline at end of file
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
index 1ccb921..22c38c6 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
@@ -63,7 +63,7 @@ class MailboxGetMethod @Inject() (serializer: Serializer,
   private def asMailboxGetRequest(arguments: Arguments): SMono[MailboxGetRequest] = {
     serializer.deserializeMailboxGetRequest(arguments.value) match {
       case JsSuccess(mailboxGetRequest, _) => SMono.just(mailboxGetRequest)
-      case JsError(errors) => SMono.raiseError(new IllegalArgumentException("Invalid MailboxGetRequest")) //FIXME MOB
+      case errors: JsError => SMono.raiseError(new IllegalArgumentException(serializer.serialize(errors).toString))
     }
   }
 
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/JsErrorSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/JsErrorSerializationTest.scala
new file mode 100644
index 0000000..cd980aa
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/JsErrorSerializationTest.scala
@@ -0,0 +1,66 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.json
+
+import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
+import org.apache.james.mailbox.model.TestId
+import org.scalatest.matchers.should.Matchers
+import org.scalatest.wordspec.AnyWordSpec
+import play.api.libs.json.{JsError, JsPath, Json, JsonValidationError}
+
+object JsErrorSerializationTest {
+  private val SERIALIZER: Serializer = new Serializer(new TestId.Factory)
+}
+
+class JsErrorSerializationTest extends AnyWordSpec with Matchers {
+  import JsErrorSerializationTest.SERIALIZER
+
+  "Serialize JsError" should {
+    "succeed " in {
+      val errors: JsError = JsError(Seq(
+        (
+          JsPath.\("name"),
+          Seq(JsonValidationError("validate.error.expected.jsstring"), JsonValidationError("error.maxLength"))
+        ),
+        (
+          JsPath.\("id"),
+          Seq(JsonValidationError("error.path.missing"))
+        )))
+
+      val expectedJson: String =
+        """
+          |{
+          |  "errors": [
+          |    {
+          |      "path": "obj.name",
+          |      "messages": ["validate.error.expected.jsstring", "error.maxLength"]
+          |    },
+          |    {
+          |      "path": "obj.id",
+          |      "messages": ["error.path.missing"]
+          |    }
+          |  ]
+          |}
+          |""".stripMargin
+
+      assertThatJson(Json.stringify(SERIALIZER.serialize(errors))).isEqualTo(expectedJson)
+    }
+  }
+}


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