You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2020/11/03 02:24:56 UTC

[james-project] branch master updated (b3b1974 -> b94374f)

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

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


    from b3b1974  Merge pull request #258 from apache/JAMES-1902-extract-some-guice-modules
     new 4aa9a49  JAMES-3437 Provide a bash script wrapper for James Webadmin Cli
     new 7622c3f  [REFACTORING] Relocate SessionRoutes in toutes package
     new 83320c4  [REFACTORING] Relocate Vacation models in vacation package
     new dcfdca4  [REFACTORING] Rename model package to core
     new 04c26c5  [REFACTORING] Relocate Keywords in mail package
     new 88f6d21  [REFACTORING] Split EmailSetMethod and extract create/update/destroy
     new 429bcbf  [REFACTORING] Split MailboxSetMethod and extract create/update/destroy
     new 6c4e6b0  [REFACTORING] Relocate SessionRoutes in toutes package
     new 4aba016  [REFACTORING] Remove wildcards on some James scala imports
     new 11ae532  JAMES-3436 Email/set create should support specific headers
     new b94374f  JAMES-2366 Demonstrate forward with local copy works with SMTP

The 11 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:
 .../james/jmap/rfc8621/RFC8621MethodsModule.java   |   6 +-
 .../contract/EmailQueryMethodContract.scala        |   2 +-
 .../rfc8621/contract/EmailSetMethodContract.scala  | 252 +++++++++++-
 .../james/jmap/{model => core}/Capabilities.scala  |   4 +-
 .../james/jmap/{model => core}/Capability.scala    |  10 +-
 .../org/apache/james/jmap/{model => core}/Id.scala |   2 +-
 .../james/jmap/{model => core}/Invocation.scala    |   4 +-
 .../{model => core}/JmapRfc8621Configuration.scala |   4 +-
 .../jmap/{model => core}/ProblemDetails.scala      |   6 +-
 .../james/jmap/{model => core}/Properties.scala    |   2 +-
 .../apache/james/jmap/{model => core}/Query.scala  |   2 +-
 .../{model => core}/RequestLevelErrorType.scala    |   4 +-
 .../james/jmap/{model => core}/RequestObject.scala |   6 +-
 .../jmap/{model => core}/ResponseObject.scala      |   4 +-
 .../james/jmap/{model => core}/Session.scala       |   8 +-
 .../james/jmap/{model => core}/SetError.scala      |   4 +-
 .../james/jmap/{model => core}/StatusCode.scala    |   2 +-
 .../james/jmap/{model => core}/UTCDate.scala       |   4 +-
 .../james/jmap/{model => core}/UnsignedInt.scala   |   2 +-
 .../jmap/json/BackReferenceDeserializer.scala      |   2 +-
 .../james/jmap/json/EmailGetSerializer.scala       |   4 +-
 .../james/jmap/json/EmailQuerySerializer.scala     |   4 +-
 .../james/jmap/json/EmailSetSerializer.scala       | 122 +++++-
 .../james/jmap/json/MailboxQuerySerializer.scala   |   2 +-
 .../apache/james/jmap/json/MailboxSerializer.scala |   4 +-
 .../james/jmap/json/ResponseSerializer.scala       |  18 +-
 .../james/jmap/json/VacationSerializer.scala       |   8 +-
 .../scala/org/apache/james/jmap/json/package.scala |   6 +-
 .../scala/org/apache/james/jmap/mail/Email.scala   |  17 +-
 .../apache/james/jmap/mail/EmailAddressGroup.scala |  11 +-
 .../org/apache/james/jmap/mail/EmailBodyPart.scala |   2 +-
 .../org/apache/james/jmap/mail/EmailGet.scala      |  19 +-
 .../org/apache/james/jmap/mail/EmailHeader.scala   |  73 +++-
 .../org/apache/james/jmap/mail/EmailQuery.scala    |   6 +-
 .../org/apache/james/jmap/mail/EmailSet.scala      |  59 ++-
 .../james/jmap/{model => mail}/Keyword.scala       |   4 +-
 .../james/jmap/{model => mail}/Keywords.scala      |   4 +-
 .../jmap/{model => mail}/KeywordsCombiner.scala    |   2 +-
 .../scala/org/apache/james/jmap/mail/Mailbox.scala |   6 +-
 .../jmap/{model => mail}/MailboxFactory.scala      |   4 +-
 .../org/apache/james/jmap/mail/MailboxGet.scala    |   4 +-
 .../org/apache/james/jmap/mail/MailboxQuery.scala  |   6 +-
 .../org/apache/james/jmap/mail/MailboxSet.scala    |   8 +-
 .../scala/org/apache/james/jmap/mail/Quotas.scala  |   2 +-
 .../apache/james/jmap/method/CoreEchoMethod.scala  |   4 +-
 .../apache/james/jmap/method/EmailGetMethod.scala  |  10 +-
 .../james/jmap/method/EmailQueryMethod.scala       |  12 +-
 .../jmap/method/EmailSetCreatePerformer.scala      | 102 +++++
 .../jmap/method/EmailSetDeletePerformer.scala      |  99 +++++
 .../apache/james/jmap/method/EmailSetMethod.scala  | 367 +----------------
 .../jmap/method/EmailSetUpdatePerformer.scala      | 249 ++++++++++++
 .../james/jmap/method/MailboxGetMethod.scala       |  12 +-
 .../james/jmap/method/MailboxQueryMethod.scala     |   8 +-
 .../jmap/method/MailboxSetCreatePerformer.scala    | 206 ++++++++++
 .../jmap/method/MailboxSetDeletePerformer.scala    | 109 +++++
 .../james/jmap/method/MailboxSetMethod.scala       | 448 +--------------------
 .../jmap/method/MailboxSetUpdatePerformer.scala    | 222 ++++++++++
 .../org/apache/james/jmap/method/Method.scala      |  10 +-
 .../jmap/method/VacationResponseGetMethod.scala    |  14 +-
 .../jmap/method/VacationResponseSetMethod.scala    |  10 +-
 .../apache/james/jmap/routes/DownloadRoutes.scala  |   5 +-
 .../apache/james/jmap/routes/JMAPApiRoutes.scala   |  10 +-
 .../james/jmap/routes/ProcessingContext.scala      |   4 +-
 .../jmap/{http => routes}/SessionRoutes.scala      |   7 +-
 .../jmap/{http => routes}/SessionSupplier.scala    |   6 +-
 .../apache/james/jmap/routes/UploadRoutes.scala    |   4 +-
 .../james/jmap/utils/quotas/QuotaReader.scala      |   6 +-
 .../james/jmap/utils/search/MailboxFilter.scala    |   4 +-
 .../jmap/{mail => vacation}/VacationResponse.scala |   7 +-
 .../{mail => vacation}/VacationResponseGet.scala   |   8 +-
 .../james/jmap/vacation/VacationResponseSet.scala  |   6 +-
 .../JmapRfc8621ConfigurationTest.scala             |   6 +-
 .../james/jmap/{model => core}/KeywordTest.scala   |   4 +-
 .../{model => core}/KeywordsCombinerTest.scala     |   6 +-
 .../james/jmap/{model => core}/KeywordsTest.scala  |   9 +-
 .../{model => core}/MailboxValidationTest.scala    |   6 +-
 .../james/jmap/{model => core}/UTCDateTest.scala   |   2 +-
 .../apache/james/jmap/json/BackReferenceTest.scala |   2 +-
 .../scala/org/apache/james/jmap/json/Fixture.scala |   8 +-
 .../jmap/json/MailboxGetSerializationTest.scala    |   4 +-
 .../jmap/json/RequestObjectSerializationTest.scala |   2 +-
 .../json/ResponseObjectSerializationTest.scala     |   2 +-
 .../james/jmap/json/SessionSerializationTest.scala |  10 +-
 .../VacationResponseGetSerializationTest.scala     |   6 +-
 .../json/VacationResponseSerializationTest.scala   |   8 +-
 .../james/jmap/method/CoreEchoMethodTest.scala     |   4 +-
 .../james/jmap/routes/JMAPApiRoutesTest.scala      |  10 +-
 .../jmap/{http => routes}/SessionRoutesTest.scala  |  11 +-
 .../{http => routes}/SessionSupplierTest.scala     |   6 +-
 server/protocols/webadmin-cli/james-cli            |   3 +
 .../webadmin-integration-test-common/pom.xml       |   4 +
 .../integration/ForwardIntegrationTest.java        |  38 ++
 92 files changed, 1788 insertions(+), 1037 deletions(-)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => core}/Capabilities.scala (93%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => core}/Capability.scala (93%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => core}/Id.scala (97%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => core}/Invocation.scala (96%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => core}/JmapRfc8621Configuration.scala (95%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => core}/ProblemDetails.scala (92%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => core}/Properties.scala (98%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => core}/Query.scala (98%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => core}/RequestLevelErrorType.scala (97%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => core}/RequestObject.scala (92%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => core}/ResponseObject.scala (94%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => core}/Session.scala (94%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => core}/SetError.scala (95%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => core}/StatusCode.scala (97%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => core}/UTCDate.scala (94%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => core}/UnsignedInt.scala (98%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => mail}/Keyword.scala (97%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => mail}/Keywords.scala (98%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => mail}/KeywordsCombiner.scala (98%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{model => mail}/MailboxFactory.scala (99%)
 create mode 100644 server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetCreatePerformer.scala
 create mode 100644 server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetDeletePerformer.scala
 create mode 100644 server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetUpdatePerformer.scala
 create mode 100644 server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetCreatePerformer.scala
 create mode 100644 server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetDeletePerformer.scala
 create mode 100644 server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{http => routes}/SessionRoutes.scala (94%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{http => routes}/SessionSupplier.scala (90%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{mail => vacation}/VacationResponse.scala (94%)
 rename server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/{mail => vacation}/VacationResponseGet.scala (89%)
 rename server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/{model => core}/JmapRfc8621ConfigurationTest.scala (93%)
 rename server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/{model => core}/KeywordTest.scala (98%)
 rename server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/{model => core}/KeywordsCombinerTest.scala (95%)
 rename server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/{model => core}/KeywordsTest.scala (96%)
 rename server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/{model => core}/MailboxValidationTest.scala (95%)
 rename server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/{model => core}/UTCDateTest.scala (98%)
 rename server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/{http => routes}/SessionRoutesTest.scala (95%)
 rename server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/{http => routes}/SessionSupplierTest.scala (92%)
 create mode 100755 server/protocols/webadmin-cli/james-cli


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


[james-project] 05/11: [REFACTORING] Relocate Keywords in mail package

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

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

commit 04c26c582f555ce9f6ff245bec4bf94651ab70e7
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Nov 2 10:15:51 2020 +0700

    [REFACTORING] Relocate Keywords in mail package
---
 .../james/jmap/json/BackReferenceDeserializer.scala    |  2 +-
 .../apache/james/jmap/json/EmailGetSerializer.scala    |  4 ++--
 .../apache/james/jmap/json/EmailQuerySerializer.scala  |  4 ++--
 .../apache/james/jmap/json/EmailSetSerializer.scala    |  8 ++++----
 .../james/jmap/json/MailboxQuerySerializer.scala       |  2 +-
 .../org/apache/james/jmap/json/MailboxSerializer.scala |  4 ++--
 .../apache/james/jmap/json/ResponseSerializer.scala    | 18 +++++++++---------
 .../apache/james/jmap/json/VacationSerializer.scala    |  8 ++++----
 .../scala/org/apache/james/jmap/json/package.scala     |  6 +++---
 .../main/scala/org/apache/james/jmap/mail/Email.scala  |  4 ++--
 .../org/apache/james/jmap/mail/EmailBodyPart.scala     |  2 +-
 .../scala/org/apache/james/jmap/mail/EmailGet.scala    |  4 ++--
 .../scala/org/apache/james/jmap/mail/EmailHeader.scala |  2 +-
 .../scala/org/apache/james/jmap/mail/EmailQuery.scala  |  6 +++---
 .../scala/org/apache/james/jmap/mail/EmailSet.scala    |  6 +++---
 .../apache/james/jmap/{model => mail}/Keyword.scala    |  4 ++--
 .../apache/james/jmap/{model => mail}/Keywords.scala   |  4 ++--
 .../james/jmap/{model => mail}/KeywordsCombiner.scala  |  2 +-
 .../scala/org/apache/james/jmap/mail/Mailbox.scala     |  6 +++---
 .../james/jmap/{model => mail}/MailboxFactory.scala    |  4 ++--
 .../scala/org/apache/james/jmap/mail/MailboxGet.scala  |  4 ++--
 .../org/apache/james/jmap/mail/MailboxQuery.scala      |  6 +++---
 .../scala/org/apache/james/jmap/mail/MailboxSet.scala  |  8 ++++----
 .../main/scala/org/apache/james/jmap/mail/Quotas.scala |  2 +-
 .../org/apache/james/jmap/method/CoreEchoMethod.scala  |  4 ++--
 .../org/apache/james/jmap/method/EmailGetMethod.scala  |  8 ++++----
 .../apache/james/jmap/method/EmailQueryMethod.scala    | 10 +++++-----
 .../org/apache/james/jmap/method/EmailSetMethod.scala  | 10 +++++-----
 .../apache/james/jmap/method/MailboxGetMethod.scala    |  8 ++++----
 .../apache/james/jmap/method/MailboxQueryMethod.scala  |  6 +++---
 .../apache/james/jmap/method/MailboxSetMethod.scala    |  8 ++++----
 .../scala/org/apache/james/jmap/method/Method.scala    |  6 +++---
 .../james/jmap/method/VacationResponseGetMethod.scala  | 12 ++++++------
 .../james/jmap/method/VacationResponseSetMethod.scala  |  8 ++++----
 .../org/apache/james/jmap/routes/DownloadRoutes.scala  |  5 ++---
 .../org/apache/james/jmap/routes/JMAPApiRoutes.scala   |  8 ++++----
 .../apache/james/jmap/routes/ProcessingContext.scala   |  4 ++--
 .../org/apache/james/jmap/routes/SessionRoutes.scala   |  2 +-
 .../org/apache/james/jmap/routes/SessionSupplier.scala |  4 ++--
 .../org/apache/james/jmap/routes/UploadRoutes.scala    |  4 ++--
 .../apache/james/jmap/utils/quotas/QuotaReader.scala   |  4 ++--
 .../apache/james/jmap/utils/search/MailboxFilter.scala |  4 ++--
 .../apache/james/jmap/core/KeywordsCombinerTest.scala  |  2 +-
 .../org/apache/james/jmap/core/KeywordsTest.scala      |  2 +-
 .../org/apache/james/jmap/json/BackReferenceTest.scala |  2 +-
 .../scala/org/apache/james/jmap/json/Fixture.scala     |  8 ++++----
 .../james/jmap/json/MailboxGetSerializationTest.scala  |  2 +-
 .../jmap/json/RequestObjectSerializationTest.scala     |  2 +-
 .../jmap/json/ResponseObjectSerializationTest.scala    |  2 +-
 .../james/jmap/json/SessionSerializationTest.scala     | 10 +++++-----
 .../json/VacationResponseGetSerializationTest.scala    |  6 +++---
 .../jmap/json/VacationResponseSerializationTest.scala  |  8 +++++---
 .../apache/james/jmap/method/CoreEchoMethodTest.scala  |  4 ++--
 .../apache/james/jmap/routes/JMAPApiRoutesTest.scala   |  6 +++---
 .../apache/james/jmap/routes/SessionRoutesTest.scala   |  4 ++--
 .../apache/james/jmap/routes/SessionSupplierTest.scala |  2 +-
 56 files changed, 148 insertions(+), 147 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/BackReferenceDeserializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/BackReferenceDeserializer.scala
index cf3fb3d..3b81240 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/BackReferenceDeserializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/BackReferenceDeserializer.scala
@@ -19,7 +19,7 @@
 
 package org.apache.james.jmap.json
 
-import org.apache.james.jmap.model.Invocation.{MethodCallId, MethodName}
+import org.apache.james.jmap.core.Invocation.{MethodCallId, MethodName}
 import org.apache.james.jmap.routes.{BackReference, JsonPath}
 import play.api.libs.json.{Format, JsError, JsResult, JsString, JsSuccess, JsValue, Json, Reads}
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
index e8f3c54..e24e728 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
@@ -20,8 +20,8 @@
 package org.apache.james.jmap.json
 
 import org.apache.james.jmap.api.model.Preview
-import org.apache.james.jmap.mail.{AddressesHeaderValue, BlobId, Charset, DateHeaderValue, Disposition, EmailAddress, EmailAddressGroup, EmailBody, EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailFastView, EmailFullView, EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, FetchHTMLBodyValues, FetchTextBodyValues, Group [...]
-import org.apache.james.jmap.model._
+import org.apache.james.jmap.core._
+import org.apache.james.jmap.mail.{AddressesHeaderValue, BlobId, Charset, DateHeaderValue, Disposition, EmailAddress, EmailAddressGroup, EmailBody, EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailFastView, EmailFullView, EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, FetchHTMLBodyValues, FetchTextBodyValues, Group [...]
 import org.apache.james.mailbox.model.{Cid, MailboxId, MessageId}
 import play.api.libs.functional.syntax._
 import play.api.libs.json._
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailQuerySerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailQuerySerializer.scala
index 72ad05e..ab020df 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailQuerySerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailQuerySerializer.scala
@@ -20,8 +20,8 @@
 package org.apache.james.jmap.json
 
 import javax.inject.Inject
-import org.apache.james.jmap.mail.{AllInThreadHaveKeywordSortProperty, Anchor, AnchorOffset, And, Bcc, Body, Cc, CollapseThreads, Collation, Comparator, EmailQueryRequest, EmailQueryResponse, FilterCondition, FilterOperator, FilterQuery, From, FromSortProperty, HasAttachment, HasKeywordSortProperty, Header, HeaderContains, HeaderExist, IsAscending, Not, Operator, Or, ReceivedAtSortProperty, SentAtSortProperty, SizeSortProperty, SomeInThreadHaveKeywordSortProperty, SortProperty, Subject,  [...]
-import org.apache.james.jmap.model.{AccountId, CanCalculateChanges, Keyword, LimitUnparsed, PositionUnparsed, QueryState}
+import org.apache.james.jmap.core.{AccountId, CanCalculateChanges, LimitUnparsed, PositionUnparsed, QueryState}
+import org.apache.james.jmap.mail.{AllInThreadHaveKeywordSortProperty, Anchor, AnchorOffset, And, Bcc, Body, Cc, CollapseThreads, Collation, Comparator, EmailQueryRequest, EmailQueryResponse, FilterCondition, FilterOperator, FilterQuery, From, FromSortProperty, HasAttachment, HasKeywordSortProperty, Header, HeaderContains, HeaderExist, IsAscending, Keyword, Not, Operator, Or, ReceivedAtSortProperty, SentAtSortProperty, SizeSortProperty, SomeInThreadHaveKeywordSortProperty, SortProperty,  [...]
 import org.apache.james.mailbox.model.{MailboxId, MessageId}
 import play.api.libs.json._
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSetSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSetSerializer.scala
index 77ce173..3113b86 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSetSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSetSerializer.scala
@@ -22,11 +22,11 @@ package org.apache.james.jmap.json
 import cats.implicits._
 import eu.timepit.refined.refineV
 import javax.inject.Inject
+import org.apache.james.jmap.core.Id.IdConstraint
+import org.apache.james.jmap.core.{Id, SetError}
 import org.apache.james.jmap.mail.EmailSet.{EmailCreationId, UnparsedMessageId, UnparsedMessageIdConstraint}
-import org.apache.james.jmap.mail.{AddressesHeaderValue, ClientEmailBodyValue, ClientHtmlBody, ClientPartId, DestroyIds, EmailAddress, EmailCreationRequest, EmailCreationResponse, EmailSetRequest, EmailSetResponse, EmailSetUpdate, EmailerName, HeaderMessageId, IsEncodingProblem, IsTruncated, MailboxIds, MessageIdsHeaderValue, Subject, Type}
-import org.apache.james.jmap.model.Id.IdConstraint
-import org.apache.james.jmap.model.KeywordsFactory.STRICT_KEYWORDS_FACTORY
-import org.apache.james.jmap.model.{Id, Keyword, Keywords, SetError}
+import org.apache.james.jmap.mail.KeywordsFactory.STRICT_KEYWORDS_FACTORY
+import org.apache.james.jmap.mail.{AddressesHeaderValue, ClientEmailBodyValue, ClientHtmlBody, ClientPartId, DestroyIds, EmailAddress, EmailCreationRequest, EmailCreationResponse, EmailSetRequest, EmailSetResponse, EmailSetUpdate, EmailerName, HeaderMessageId, IsEncodingProblem, IsTruncated, Keyword, Keywords, MailboxIds, MessageIdsHeaderValue, Subject, Type}
 import org.apache.james.mailbox.model.{MailboxId, MessageId}
 import play.api.libs.json.{JsArray, JsBoolean, JsError, JsNull, JsObject, JsResult, JsString, JsSuccess, JsValue, Json, OWrites, Reads, Writes}
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxQuerySerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxQuerySerializer.scala
index 26eb2bb..d67c9a9 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxQuerySerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxQuerySerializer.scala
@@ -19,8 +19,8 @@
 
 package org.apache.james.jmap.json
 
+import org.apache.james.jmap.core._
 import org.apache.james.jmap.mail.{MailboxFilter, MailboxQueryRequest, MailboxQueryResponse}
-import org.apache.james.jmap.model._
 import org.apache.james.mailbox.Role
 import org.apache.james.mailbox.model.MailboxId
 import play.api.libs.json._
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala
index a445741..28fcb34 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala
@@ -23,11 +23,11 @@ import eu.timepit.refined._
 import eu.timepit.refined.collection.NonEmpty
 import javax.inject.Inject
 import org.apache.james.core.{Domain, Username}
+import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
+import org.apache.james.jmap.core._
 import org.apache.james.jmap.mail.MailboxGet.{UnparsedMailboxId, UnparsedMailboxIdConstraint}
 import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId
 import org.apache.james.jmap.mail.{DelegatedNamespace, Ids, IsSubscribed, Mailbox, MailboxCreationRequest, MailboxCreationResponse, MailboxGetRequest, MailboxGetResponse, MailboxNamespace, MailboxPatchObject, MailboxRights, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, MayAddItems, MayCreateChild, MayDelete, MayReadItems, MayRemoveItems, MayRename, MaySetKeywords, MaySetSeen, MaySubmit, NotFound, PersonalNamespace, Quota, QuotaId, QuotaRoot, Quotas, RemoveEmailsOnDestroy, [...]
-import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.model._
 import org.apache.james.mailbox.Role
 import org.apache.james.mailbox.model.MailboxACL.{Right => JavaRight}
 import org.apache.james.mailbox.model.{MailboxACL, MailboxId}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/ResponseSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/ResponseSerializer.scala
index 9ba12a7..b6038e0 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/ResponseSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/ResponseSerializer.scala
@@ -23,11 +23,11 @@ import java.io.InputStream
 import java.net.URL
 
 import org.apache.james.core.Username
-import org.apache.james.jmap.model
-import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.model.Invocation.{Arguments, MethodCallId, MethodName}
-import org.apache.james.jmap.model.SetError.SetErrorDescription
-import org.apache.james.jmap.model.{Account, Invocation, Session, _}
+import org.apache.james.jmap.core
+import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
+import org.apache.james.jmap.core.Invocation.{Arguments, MethodCallId, MethodName}
+import org.apache.james.jmap.core.SetError.SetErrorDescription
+import org.apache.james.jmap.core.{Account, Invocation, Session, _}
 import play.api.libs.functional.syntax._
 import play.api.libs.json._
 
@@ -61,10 +61,10 @@ object ResponseSerializer {
   private implicit val argumentFormat: Format[Arguments] = Json.valueFormat[Arguments]
   private implicit val methodCallIdFormat: Format[MethodCallId] = Json.valueFormat[MethodCallId]
   private implicit val invocationRead: Reads[Invocation] = (
-    (JsPath \ model.Invocation.METHOD_NAME).read[MethodName] and
-      (JsPath \ model.Invocation.ARGUMENTS).read[Arguments] and
-      (JsPath \ model.Invocation.METHOD_CALL).read[MethodCallId]
-    ) (model.Invocation.apply _)
+    (JsPath \ core.Invocation.METHOD_NAME).read[MethodName] and
+      (JsPath \ core.Invocation.ARGUMENTS).read[Arguments] and
+      (JsPath \ core.Invocation.METHOD_CALL).read[MethodCallId]
+    ) (core.Invocation.apply _)
 
   private implicit val invocationWrite: Writes[Invocation] = (invocation: Invocation) =>
     Json.arr(invocation.methodName, invocation.arguments, invocation.methodCallId)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala
index 38c830c..2ee807e 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala
@@ -21,10 +21,10 @@ package org.apache.james.jmap.json
 
 import java.time.format.DateTimeFormatter
 
-import org.apache.james.jmap.mail.VacationResponse.{UnparsedVacationResponseId, VACATION_RESPONSE_ID}
-import org.apache.james.jmap.mail.{FromDate, HtmlBody, IsEnabled, Subject, TextBody, ToDate, VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseId, VacationResponseIds, VacationResponseNotFound}
-import org.apache.james.jmap.model._
-import org.apache.james.jmap.vacation.{VacationResponsePatchObject, VacationResponseSetError, VacationResponseSetRequest, VacationResponseSetResponse, VacationResponseUpdateResponse}
+import org.apache.james.jmap.core._
+import org.apache.james.jmap.mail.Subject
+import org.apache.james.jmap.vacation.VacationResponse.{UnparsedVacationResponseId, VACATION_RESPONSE_ID}
+import org.apache.james.jmap.vacation.{FromDate, HtmlBody, IsEnabled, TextBody, ToDate, VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseId, VacationResponseIds, VacationResponseNotFound, VacationResponsePatchObject, VacationResponseSetError, VacationResponseSetRequest, VacationResponseSetResponse, VacationResponseUpdateResponse}
 import play.api.libs.json._
 
 import scala.language.implicitConversions
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/package.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/package.scala
index 2c4e5c2..8b05ba9 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/package.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/package.scala
@@ -24,9 +24,9 @@ import java.time.format.DateTimeFormatter
 
 import eu.timepit.refined.api.{RefType, Validate}
 import org.apache.james.core.MailAddress
-import org.apache.james.jmap.model.Id.Id
-import org.apache.james.jmap.model.SetError.SetErrorDescription
-import org.apache.james.jmap.model.{AccountId, Properties, SetError, UTCDate}
+import org.apache.james.jmap.core.Id.Id
+import org.apache.james.jmap.core.SetError.SetErrorDescription
+import org.apache.james.jmap.core.{AccountId, Properties, SetError, UTCDate}
 import play.api.libs.json._
 
 import scala.util.{Failure, Success, Try}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
index fb93168..bf91c0f 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
@@ -34,11 +34,11 @@ import eu.timepit.refined.types.string.NonEmptyString
 import javax.inject.Inject
 import org.apache.james.jmap.api.model.Preview
 import org.apache.james.jmap.api.projections.{MessageFastViewPrecomputedProperties, MessageFastViewProjection}
+import org.apache.james.jmap.core.{Properties, UTCDate}
 import org.apache.james.jmap.mail.BracketHeader.sanitize
 import org.apache.james.jmap.mail.Email.{Size, sanitizeSize}
+import org.apache.james.jmap.mail.KeywordsFactory.LENIENT_KEYWORDS_FACTORY
 import org.apache.james.jmap.method.ZoneIdProvider
-import org.apache.james.jmap.model.KeywordsFactory.LENIENT_KEYWORDS_FACTORY
-import org.apache.james.jmap.model.{Keywords, Properties, UTCDate}
 import org.apache.james.mailbox.model.FetchGroup.{FULL_CONTENT, HEADERS, MINIMAL}
 import org.apache.james.mailbox.model.{FetchGroup, MailboxId, MessageId, MessageResult}
 import org.apache.james.mailbox.{MailboxSession, MessageIdManager}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailBodyPart.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailBodyPart.scala
index 72adc2b..46b77f2 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailBodyPart.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailBodyPart.scala
@@ -28,10 +28,10 @@ import eu.timepit.refined.auto._
 import eu.timepit.refined.numeric.NonNegative
 import eu.timepit.refined.refineV
 import org.apache.commons.io.IOUtils
+import org.apache.james.jmap.core.Properties
 import org.apache.james.jmap.mail.Email.Size
 import org.apache.james.jmap.mail.EmailBodyPart.{MULTIPART_ALTERNATIVE, TEXT_HTML, TEXT_PLAIN}
 import org.apache.james.jmap.mail.PartId.PartIdValue
-import org.apache.james.jmap.model.Properties
 import org.apache.james.mailbox.model.{Cid, MessageId, MessageResult}
 import org.apache.james.mime4j.codec.DecodeMonitor
 import org.apache.james.mime4j.dom.{Entity, Message, Multipart, TextBody => Mime4JTextBody}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
index 96f3ea7..fce39f5 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
@@ -25,12 +25,12 @@ import eu.timepit.refined.api.Refined
 import eu.timepit.refined.auto._
 import eu.timepit.refined.numeric.NonNegative
 import eu.timepit.refined.types.string.NonEmptyString
+import org.apache.james.jmap.core.State.State
+import org.apache.james.jmap.core.{AccountId, Properties}
 import org.apache.james.jmap.mail.Email.UnparsedEmailId
 import org.apache.james.jmap.mail.EmailGetRequest.MaxBodyValueBytes
 import org.apache.james.jmap.mail.EmailHeaders.SPECIFIC_HEADER_PREFIX
 import org.apache.james.jmap.method.WithAccountId
-import org.apache.james.jmap.model.State.State
-import org.apache.james.jmap.model.{AccountId, Properties}
 import org.apache.james.mime4j.dom.Message
 import org.apache.james.mime4j.stream.Field
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
index 9b877fb..7330fc7 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
@@ -23,7 +23,7 @@ import java.nio.charset.StandardCharsets.US_ASCII
 import java.time.ZoneId
 
 import org.apache.commons.lang3.StringUtils
-import org.apache.james.jmap.model.UTCDate
+import org.apache.james.jmap.core.UTCDate
 import org.apache.james.mime4j.codec.{DecodeMonitor, DecoderUtil}
 import org.apache.james.mime4j.dom.address.{AddressList, Group, Address => Mime4jAddress, Mailbox => Mime4jMailbox}
 import org.apache.james.mime4j.field.{AddressListFieldImpl, ContentLocationFieldImpl, DateTimeFieldImpl}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailQuery.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailQuery.scala
index a178a11..7f154e1 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailQuery.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailQuery.scala
@@ -20,12 +20,12 @@
 package org.apache.james.jmap.mail
 
 import cats.implicits._
+import org.apache.james.jmap.core.Limit.Limit
+import org.apache.james.jmap.core.Position.Position
+import org.apache.james.jmap.core.{AccountId, CanCalculateChanges, LimitUnparsed, PositionUnparsed, QueryState, UTCDate}
 import org.apache.james.jmap.mail.Email.Size
 import org.apache.james.jmap.mail.IsAscending.ASCENDING
 import org.apache.james.jmap.method.WithAccountId
-import org.apache.james.jmap.model.Limit.Limit
-import org.apache.james.jmap.model.Position.Position
-import org.apache.james.jmap.model.{AccountId, CanCalculateChanges, Keyword, LimitUnparsed, PositionUnparsed, QueryState, UTCDate}
 import org.apache.james.mailbox.model.SearchQuery.Sort.Order.{NATURAL, REVERSE}
 import org.apache.james.mailbox.model.SearchQuery.Sort.SortClause
 import org.apache.james.mailbox.model.{MailboxId, MessageId, SearchQuery}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala
index 89c7c98..2176302 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala
@@ -24,11 +24,11 @@ import java.util.Date
 import eu.timepit.refined
 import eu.timepit.refined.api.Refined
 import eu.timepit.refined.collection.NonEmpty
+import org.apache.james.jmap.core.Id.Id
+import org.apache.james.jmap.core.State.State
+import org.apache.james.jmap.core.{AccountId, SetError, UTCDate}
 import org.apache.james.jmap.mail.EmailSet.{EmailCreationId, UnparsedMessageId}
 import org.apache.james.jmap.method.WithAccountId
-import org.apache.james.jmap.model.Id.Id
-import org.apache.james.jmap.model.State.State
-import org.apache.james.jmap.model.{AccountId, Keywords, SetError, UTCDate}
 import org.apache.james.mailbox.model.MessageId
 import org.apache.james.mime4j.dom.Message
 import org.apache.james.mime4j.dom.field.FieldName
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Keyword.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Keyword.scala
similarity index 97%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Keyword.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Keyword.scala
index f63a424..6553332 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Keyword.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Keyword.scala
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations      *
  * under the License.                                           *
  * **************************************************************/
-package org.apache.james.jmap.model
+package org.apache.james.jmap.mail
 
 import java.util.Locale
 
@@ -56,7 +56,7 @@ object Keyword {
 
   def of(flagName: String): Try[Keyword] = parse(flagName) match {
     case Left(errorMessage: String) => Failure(new IllegalArgumentException(errorMessage))
-    case Right(keyword: Keyword) => Success(keyword)
+    case scala.Right(keyword: Keyword) => Success(keyword)
   }
 
   def fromFlag(flag: Flags.Flag): Option[Keyword] = IMAP_SYSTEM_FLAGS.get(flag)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Keywords.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Keywords.scala
similarity index 98%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Keywords.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Keywords.scala
index 813938b..155e064 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Keywords.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Keywords.scala
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations      *
  * under the License.                                           *
  ****************************************************************/
-package org.apache.james.jmap.model
+package org.apache.james.jmap.mail
 
 import javax.mail.Flags
 import org.apache.james.mailbox.FlagsBuilder
@@ -42,7 +42,7 @@ object ToKeyword {
 
   val LENIENT: ToKeyword = (value: String) => Keyword.parse(value) match {
     case Left(_) => None
-    case Right(value: Keyword) => Some(value)
+    case scala.Right(value: Keyword) => Some(value)
   }
 }
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/KeywordsCombiner.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/KeywordsCombiner.scala
similarity index 98%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/KeywordsCombiner.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/KeywordsCombiner.scala
index 88257f0..a876ee1 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/KeywordsCombiner.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/KeywordsCombiner.scala
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations      *
  * under the License.                                           *
  ****************************************************************/
-package org.apache.james.jmap.model
+package org.apache.james.jmap.mail
 
 import java.util.function.BinaryOperator
 
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 29396ee..0e503de 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
@@ -24,10 +24,10 @@ 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.core.CapabilityIdentifier.CapabilityIdentifier
+import org.apache.james.jmap.core.UnsignedInt.UnsignedInt
+import org.apache.james.jmap.core.{CapabilityIdentifier, Properties}
 import org.apache.james.jmap.mail.MailboxName.MailboxName
-import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.model.UnsignedInt.UnsignedInt
-import org.apache.james.jmap.model.{CapabilityIdentifier, Properties}
 import org.apache.james.mailbox.Role
 import org.apache.james.mailbox.exception.MailboxNameException
 import org.apache.james.mailbox.model.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/mail/MailboxFactory.scala
similarity index 99%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/MailboxFactory.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxFactory.scala
index 4ed2cfb..58c7fd2 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/mail/MailboxFactory.scala
@@ -17,11 +17,11 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.mail
 
 import javax.inject.Inject
+import org.apache.james.jmap.core.UnsignedInt
 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._
 import org.apache.james.mailbox.exception.MailboxNameException
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
index bd7b8ba..971cfcb 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
@@ -22,10 +22,10 @@ package org.apache.james.jmap.mail
 import eu.timepit.refined
 import eu.timepit.refined.api.Refined
 import eu.timepit.refined.collection.NonEmpty
+import org.apache.james.jmap.core.State.State
+import org.apache.james.jmap.core.{AccountId, Properties}
 import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
 import org.apache.james.jmap.method.WithAccountId
-import org.apache.james.jmap.model.State.State
-import org.apache.james.jmap.model.{AccountId, Properties}
 import org.apache.james.mailbox.model.MailboxId
 
 import scala.util.{Failure, Try}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxQuery.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxQuery.scala
index f271308..873a7af 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxQuery.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxQuery.scala
@@ -19,10 +19,10 @@
 
 package org.apache.james.jmap.mail
 
+import org.apache.james.jmap.core.Limit.Limit
+import org.apache.james.jmap.core.Position.Position
+import org.apache.james.jmap.core.{AccountId, CanCalculateChanges, QueryState}
 import org.apache.james.jmap.method.WithAccountId
-import org.apache.james.jmap.model.Limit.Limit
-import org.apache.james.jmap.model.Position.Position
-import org.apache.james.jmap.model.{AccountId, CanCalculateChanges, Position, QueryState}
 import org.apache.james.mailbox.Role
 import org.apache.james.mailbox.model.MailboxId
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala
index 20801bd..021139d 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala
@@ -25,16 +25,16 @@ import eu.timepit.refined.collection.NonEmpty
 import eu.timepit.refined.refineV
 import eu.timepit.refined.types.string.NonEmptyString
 import org.apache.james.core.Username
+import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
+import org.apache.james.jmap.core.SetError.{SetErrorDescription, SetErrorType}
+import org.apache.james.jmap.core.State.State
+import org.apache.james.jmap.core.{AccountId, CapabilityIdentifier, Properties, SetError}
 import org.apache.james.jmap.json.MailboxSerializer
 import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
 import org.apache.james.jmap.mail.MailboxName.MailboxName
 import org.apache.james.jmap.mail.MailboxPatchObject.MailboxPatchObjectKey
 import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId
 import org.apache.james.jmap.method.{MailboxCreationParseException, WithAccountId}
-import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.model.SetError.{SetErrorDescription, SetErrorType}
-import org.apache.james.jmap.model.State.State
-import org.apache.james.jmap.model.{AccountId, CapabilityIdentifier, Properties, SetError}
 import org.apache.james.mailbox.model.{MailboxId, MailboxACL => JavaMailboxACL}
 import org.apache.james.mailbox.{MailboxSession, Role}
 import play.api.libs.json.{JsBoolean, JsError, JsNull, JsObject, JsString, JsSuccess, JsValue}
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 1f41ec3..7fcb91c 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
@@ -20,7 +20,7 @@
 package org.apache.james.jmap.mail
 
 import org.apache.james.core.Domain
-import org.apache.james.jmap.model.UnsignedInt.UnsignedInt
+import org.apache.james.jmap.core.UnsignedInt.UnsignedInt
 import org.apache.james.mailbox.model.{QuotaRoot => ModelQuotaRoot}
 
 import scala.compat.java8.OptionConverters._
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEchoMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEchoMethod.scala
index c93e05c..17afc3d 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEchoMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEchoMethod.scala
@@ -20,8 +20,8 @@ package org.apache.james.jmap.method
 
 
 import eu.timepit.refined.auto._
-import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE}
-import org.apache.james.jmap.model.Invocation.MethodName
+import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE}
+import org.apache.james.jmap.core.Invocation.MethodName
 import org.apache.james.mailbox.MailboxSession
 import org.reactivestreams.Publisher
 import reactor.core.scala.publisher.SMono
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
index 734a6ba..f228e54 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
@@ -23,13 +23,13 @@ import java.time.ZoneId
 import eu.timepit.refined.auto._
 import eu.timepit.refined.types.string.NonEmptyString
 import javax.inject.Inject
+import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
+import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
+import org.apache.james.jmap.core.State.INSTANCE
+import org.apache.james.jmap.core.{AccountId, ErrorCode, Invocation, Properties}
 import org.apache.james.jmap.json.{EmailGetSerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.Email.UnparsedEmailId
 import org.apache.james.jmap.mail.{Email, EmailBodyPart, EmailGetRequest, EmailGetResponse, EmailIds, EmailNotFound, EmailView, EmailViewReaderFactory, SpecificHeaderRequest}
-import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, 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.{AccountId, ErrorCode, Invocation, Properties}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.mailbox.model.MessageId
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
index da20d5f..895d890 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
@@ -21,13 +21,13 @@ package org.apache.james.jmap.method
 import cats.implicits._
 import eu.timepit.refined.auto._
 import javax.inject.Inject
+import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
+import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
+import org.apache.james.jmap.core.Limit.Limit
+import org.apache.james.jmap.core.Position.Position
+import org.apache.james.jmap.core.{CanCalculateChanges, Invocation, Limit, Position, QueryState}
 import org.apache.james.jmap.json.{EmailQuerySerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.{Comparator, EmailQueryRequest, EmailQueryResponse, UnsupportedRequestParameterException}
-import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
-import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
-import org.apache.james.jmap.model.Limit.Limit
-import org.apache.james.jmap.model.Position.Position
-import org.apache.james.jmap.model.{CanCalculateChanges, Invocation, Limit, Position, QueryState}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.jmap.utils.search.MailboxFilter
 import org.apache.james.jmap.utils.search.MailboxFilter.QueryFilter
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
index c1f87fc..5a2dd36 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
@@ -26,14 +26,14 @@ import com.google.common.collect.ImmutableList
 import eu.timepit.refined.auto._
 import javax.inject.Inject
 import javax.mail.Flags
+import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
+import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
+import org.apache.james.jmap.core.SetError.SetErrorDescription
+import org.apache.james.jmap.core.{ClientId, Id, Invocation, ServerId, SetError, State, UTCDate}
 import org.apache.james.jmap.json.{EmailSetSerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.EmailSet.{EmailCreationId, UnparsedMessageId}
+import org.apache.james.jmap.mail.KeywordsFactory.LENIENT_KEYWORDS_FACTORY
 import org.apache.james.jmap.mail.{DestroyIds, EmailCreationRequest, EmailCreationResponse, EmailSet, EmailSetRequest, EmailSetResponse, MailboxIds, ValidatedEmailSetUpdate}
-import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
-import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
-import org.apache.james.jmap.model.KeywordsFactory.LENIENT_KEYWORDS_FACTORY
-import org.apache.james.jmap.model.SetError.SetErrorDescription
-import org.apache.james.jmap.model.{ClientId, Id, Invocation, ServerId, SetError, State, UTCDate}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MessageManager.{AppendCommand, FlagsUpdateMode}
 import org.apache.james.mailbox.exception.MailboxNotFoundException
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 ba5baa2..ca4ea06 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
@@ -21,13 +21,13 @@ package org.apache.james.jmap.method
 
 import eu.timepit.refined.auto._
 import javax.inject.Inject
+import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
+import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
+import org.apache.james.jmap.core.State.INSTANCE
+import org.apache.james.jmap.core.{AccountId, CapabilityIdentifier, ErrorCode, Invocation, Properties}
 import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
 import org.apache.james.jmap.mail._
-import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, 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.{AccountId, CapabilityIdentifier, ErrorCode, Invocation, MailboxFactory, Properties, Subscriptions}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.jmap.utils.quotas.{QuotaLoader, QuotaLoaderWithPreloadedDefaultFactory}
 import org.apache.james.mailbox.exception.MailboxNotFoundException
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
index 2c3d5a8..e247a10 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
@@ -20,11 +20,11 @@ package org.apache.james.jmap.method
 
 import eu.timepit.refined.auto._
 import javax.inject.Inject
+import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
+import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
+import org.apache.james.jmap.core._
 import org.apache.james.jmap.json.{MailboxQuerySerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.{MailboxQueryRequest, MailboxQueryResponse}
-import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
-import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
-import org.apache.james.jmap.model._
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.{MailboxSession, SystemMailboxesProvider}
 import org.apache.james.metrics.api.MetricFactory
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
index d5ab55d..e8c4265 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
@@ -21,14 +21,14 @@ package org.apache.james.jmap.method
 
 import eu.timepit.refined.auto._
 import javax.inject.Inject
+import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
+import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
+import org.apache.james.jmap.core.SetError.SetErrorDescription
+import org.apache.james.jmap.core.{ClientId, Id, Invocation, Properties, ServerId, SetError, State}
 import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
 import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId
 import org.apache.james.jmap.mail.{InvalidPatchException, InvalidPropertyException, InvalidUpdateException, IsSubscribed, MailboxCreationRequest, MailboxCreationResponse, MailboxGet, MailboxPatchObject, MailboxRights, MailboxSetError, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, NameUpdate, ParentIdUpdate, RemoveEmailsOnDestroy, ServerSetPropertyException, SortOrder, TotalEmails, TotalThreads, UnreadEmails, UnreadThreads, UnsupportedPropertyUpdatedException, ValidatedMai [...]
-import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
-import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
-import org.apache.james.jmap.model.SetError.SetErrorDescription
-import org.apache.james.jmap.model.{ClientId, Id, Invocation, Properties, ServerId, SetError, State}
 import org.apache.james.jmap.routes.{ProcessingContext, SessionSupplier}
 import org.apache.james.jmap.utils.quotas.QuotaLoaderWithPreloadedDefaultFactory
 import org.apache.james.mailbox.MailboxManager.RenameOption
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 e14f870..bcb7edb 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
@@ -18,10 +18,10 @@
  ****************************************************************/
 package org.apache.james.jmap.method
 
+import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
+import org.apache.james.jmap.core.Invocation.MethodName
+import org.apache.james.jmap.core.{AccountId, ErrorCode, Invocation, Session}
 import org.apache.james.jmap.mail.{UnsupportedFilterException, UnsupportedNestingException, UnsupportedRequestParameterException, UnsupportedSortException}
-import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.model.Invocation.MethodName
-import org.apache.james.jmap.model.{AccountId, ErrorCode, Invocation, Session}
 import org.apache.james.jmap.routes.{ProcessingContext, SessionSupplier}
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.mailbox.exception.MailboxNotFoundException
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
index 770443f..1c8d780 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
@@ -22,14 +22,14 @@ package org.apache.james.jmap.method
 import eu.timepit.refined.auto._
 import javax.inject.Inject
 import org.apache.james.jmap.api.vacation.{VacationRepository, AccountId => JavaAccountId}
+import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE}
+import org.apache.james.jmap.core.Invocation.{Arguments, MethodCallId, MethodName}
+import org.apache.james.jmap.core.State.INSTANCE
+import org.apache.james.jmap.core.{AccountId, ErrorCode, Invocation, MissingCapabilityException, Properties}
 import org.apache.james.jmap.json.{ResponseSerializer, VacationSerializer}
-import org.apache.james.jmap.mail.VacationResponse.{UNPARSED_SINGLETON, UnparsedVacationResponseId}
-import org.apache.james.jmap.mail.{VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseNotFound}
-import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE}
-import org.apache.james.jmap.model.Invocation.{Arguments, MethodCallId, MethodName}
-import org.apache.james.jmap.model.State.INSTANCE
-import org.apache.james.jmap.model.{AccountId, ErrorCode, Invocation, MissingCapabilityException, Properties}
 import org.apache.james.jmap.routes.SessionSupplier
+import org.apache.james.jmap.vacation.VacationResponse.{UNPARSED_SINGLETON, UnparsedVacationResponseId}
+import org.apache.james.jmap.vacation.{VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseNotFound}
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.metrics.api.MetricFactory
 import play.api.libs.json.{JsError, JsObject, JsSuccess}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
index f7d2d0f..46b9bf5 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
@@ -22,12 +22,12 @@ package org.apache.james.jmap.method
 import eu.timepit.refined.auto._
 import javax.inject.Inject
 import org.apache.james.jmap.api.vacation.{AccountId, VacationPatch, VacationRepository}
+import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE}
+import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
+import org.apache.james.jmap.core.SetError.SetErrorDescription
+import org.apache.james.jmap.core.{Invocation, State}
 import org.apache.james.jmap.json.{ResponseSerializer, VacationSerializer}
 import org.apache.james.jmap.method.VacationResponseSetMethod.VACATION_RESPONSE_PATCH_OBJECT_KEY
-import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE}
-import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
-import org.apache.james.jmap.model.SetError.SetErrorDescription
-import org.apache.james.jmap.model.{Invocation, State}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.jmap.vacation.{VacationResponseSetError, VacationResponseSetRequest, VacationResponseSetResponse, VacationResponseUpdateResponse}
 import org.apache.james.mailbox.MailboxSession
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/DownloadRoutes.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/DownloadRoutes.scala
index 702fce3..6188757 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/DownloadRoutes.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/DownloadRoutes.scala
@@ -31,16 +31,15 @@ import io.netty.handler.codec.http.HttpResponseStatus.OK
 import io.netty.handler.codec.http.{HttpMethod, QueryStringDecoder}
 import javax.inject.{Inject, Named}
 import org.apache.http.HttpStatus.SC_NOT_FOUND
+import org.apache.james.jmap.core.Id.Id
+import org.apache.james.jmap.core.{AccountId, Id}
 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.mail.Email.Size
 import org.apache.james.jmap.mail.{BlobId, EmailBodyPart, PartId}
-import org.apache.james.jmap.model.Id.Id
-import org.apache.james.jmap.model.{AccountId, Id}
 import org.apache.james.jmap.routes.DownloadRoutes.{BUFFER_SIZE, LOGGER}
 import org.apache.james.jmap.{Endpoint, JMAPRoute, JMAPRoutes}
-import org.apache.james.mailbox.exception.AttachmentNotFoundException
 import org.apache.james.mailbox.model.{AttachmentId, AttachmentMetadata, ContentType, FetchGroup, MessageId, MessageResult}
 import org.apache.james.mailbox.{AttachmentManager, MailboxSession, MessageIdManager}
 import org.apache.james.mime4j.codec.EncoderUtil
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 37d3d5d..4fab40c 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
@@ -31,15 +31,15 @@ import javax.inject.{Inject, Named}
 import org.apache.http.HttpStatus.SC_BAD_REQUEST
 import org.apache.james.jmap.HttpConstants.JSON_CONTENT_TYPE
 import org.apache.james.jmap.JMAPUrls.JMAP
+import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
+import org.apache.james.jmap.core.Invocation.MethodName
+import org.apache.james.jmap.core.ProblemDetails.{notJSONProblem, notRequestProblem, unknownCapabilityProblem}
+import org.apache.james.jmap.core._
 import org.apache.james.jmap.exceptions.UnauthorizedException
 import org.apache.james.jmap.http.rfc8621.InjectionKeys
 import org.apache.james.jmap.http.{Authenticator, MailboxesProvisioner, UserProvisioning}
 import org.apache.james.jmap.json.ResponseSerializer
 import org.apache.james.jmap.method.{InvocationWithContext, Method}
-import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.model.Invocation.MethodName
-import org.apache.james.jmap.model.ProblemDetails.{notJSONProblem, notRequestProblem, unknownCapabilityProblem}
-import org.apache.james.jmap.model._
 import org.apache.james.jmap.{Endpoint, JMAPRoute, JMAPRoutes}
 import org.apache.james.mailbox.MailboxSession
 import org.slf4j.{Logger, LoggerFactory}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/ProcessingContext.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/ProcessingContext.scala
index a814df4..d3f6609 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/ProcessingContext.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/ProcessingContext.scala
@@ -22,9 +22,9 @@ package org.apache.james.jmap.routes
 import eu.timepit.refined.numeric.NonNegative
 import eu.timepit.refined.refineV
 import eu.timepit.refined.types.numeric.NonNegInt
+import org.apache.james.jmap.core.Invocation.{Arguments, MethodCallId, MethodName}
+import org.apache.james.jmap.core.{ClientId, Id, Invocation, ServerId}
 import org.apache.james.jmap.json.BackReferenceDeserializer
-import org.apache.james.jmap.model.Invocation.{Arguments, MethodCallId, MethodName}
-import org.apache.james.jmap.model.{ClientId, Id, Invocation, ServerId}
 import play.api.libs.json.{JsArray, JsError, JsObject, JsResult, JsString, JsSuccess, JsValue, Reads}
 
 import scala.util.Try
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionRoutes.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionRoutes.scala
index a637cbc..3f9772b 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionRoutes.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionRoutes.scala
@@ -27,11 +27,11 @@ import io.netty.handler.codec.http.HttpResponseStatus.OK
 import javax.inject.{Inject, Named}
 import org.apache.james.jmap.HttpConstants.JSON_CONTENT_TYPE_UTF8
 import org.apache.james.jmap.JMAPRoutes.CORS_CONTROL
+import org.apache.james.jmap.core.Session
 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.ResponseSerializer
-import org.apache.james.jmap.model.Session
 import org.apache.james.jmap.{Endpoint, JMAPRoute, JMAPRoutes}
 import org.slf4j.LoggerFactory
 import play.api.libs.json.Json
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionSupplier.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionSupplier.scala
index 2f6d12b..38388b0 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionSupplier.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionSupplier.scala
@@ -21,8 +21,8 @@ package org.apache.james.jmap.routes
 
 import javax.inject.Inject
 import org.apache.james.core.Username
-import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.model._
+import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
+import org.apache.james.jmap.core._
 import reactor.core.scala.publisher.SMono
 
 class SessionSupplier @Inject() (val configuration: JmapRfc8621Configuration){
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/UploadRoutes.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/UploadRoutes.scala
index d8aa79c..9bb9f78 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/UploadRoutes.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/UploadRoutes.scala
@@ -32,14 +32,14 @@ import io.netty.handler.codec.http.HttpMethod
 import io.netty.handler.codec.http.HttpResponseStatus.{BAD_REQUEST, CREATED}
 import javax.inject.{Inject, Named}
 import org.apache.commons.fileupload.util.LimitedInputStream
+import org.apache.james.jmap.core.Id.Id
+import org.apache.james.jmap.core.{AccountId, Id, JmapRfc8621Configuration}
 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.UploadSerializer
 import org.apache.james.jmap.mail.BlobId
 import org.apache.james.jmap.mail.Email.Size
-import org.apache.james.jmap.model.Id.Id
-import org.apache.james.jmap.model.{AccountId, Id, JmapRfc8621Configuration}
 import org.apache.james.jmap.routes.UploadRoutes.{LOGGER, sanitizeSize}
 import org.apache.james.jmap.{Endpoint, JMAPRoute, JMAPRoutes}
 import org.apache.james.mailbox.model.{AttachmentMetadata, ContentType}
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
index 2545c83..01ba28d 100644
--- 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
@@ -21,9 +21,9 @@ package org.apache.james.jmap.utils.quotas
 
 import javax.inject.Inject
 import org.apache.james.core.quota.{QuotaLimitValue, QuotaUsageValue}
+import org.apache.james.jmap.core.UnsignedInt
+import org.apache.james.jmap.core.UnsignedInt.UnsignedInt
 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
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
index c76e3ff..25914f2 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
@@ -21,9 +21,9 @@ package org.apache.james.jmap.utils.search
 import java.util.Date
 
 import cats.implicits._
+import org.apache.james.jmap.core.CapabilityIdentifier
+import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.mail.{And, EmailQueryRequest, FilterCondition, FilterOperator, FilterQuery, HeaderContains, HeaderExist, Not, Operator, Or, UnsupportedFilterException}
-import org.apache.james.jmap.model.CapabilityIdentifier
-import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery.{AccessibleNamespace, Namespace, PersonalNamespace}
 import org.apache.james.mailbox.model.SearchQuery.DateResolution.Second
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordsCombinerTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordsCombinerTest.scala
index 94f0b8b..a16859d 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordsCombinerTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordsCombinerTest.scala
@@ -20,7 +20,7 @@
 package org.apache.james.jmap.core
 
 import org.apache.james.jmap.mail.KeywordsFactory.LENIENT_KEYWORDS_FACTORY
-import org.apache.james.jmap.mail.{Keywords, KeywordsCombiner}
+import org.apache.james.jmap.mail.{Keyword, Keywords, KeywordsCombiner}
 import org.apache.james.util.CommutativityChecker
 import org.assertj.core.api.Assertions.assertThat
 import org.scalatest.matchers.must.Matchers
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordsTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordsTest.scala
index 0708919..8786024 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordsTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordsTest.scala
@@ -22,8 +22,8 @@ package org.apache.james.jmap.core
 import javax.mail.Flags
 import javax.mail.Flags.Flag
 import nl.jqno.equalsverifier.EqualsVerifier
-import org.apache.james.jmap.mail.Keywords
 import org.apache.james.jmap.mail.KeywordsFactory.{LENIENT_KEYWORDS_FACTORY, STRICT_KEYWORDS_FACTORY}
+import org.apache.james.jmap.mail.{Keyword, Keywords}
 import org.apache.james.mailbox.FlagsBuilder
 import org.assertj.core.api.Assertions.assertThat
 import org.scalatest.matchers.must.Matchers
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/BackReferenceTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/BackReferenceTest.scala
index 6d31491..c9d5e56 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/BackReferenceTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/BackReferenceTest.scala
@@ -20,7 +20,7 @@
 package org.apache.james.jmap.json
 
 import eu.timepit.refined.auto._
-import org.apache.james.jmap.model.Invocation.{MethodCallId, MethodName}
+import org.apache.james.jmap.core.Invocation.{MethodCallId, MethodName}
 import org.apache.james.jmap.routes.{ArrayElementPart, BackReference, JsonPath, PlainPart}
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/Fixture.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/Fixture.scala
index e249535..e43d96c 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/Fixture.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/Fixture.scala
@@ -20,10 +20,10 @@
 package org.apache.james.jmap.json
 
 import eu.timepit.refined.auto._
-import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.model.Id.Id
-import org.apache.james.jmap.model.Invocation.{Arguments, MethodCallId, MethodName}
-import org.apache.james.jmap.model.{ClientId, CreatedIds, Invocation, ResponseObject, ServerId}
+import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
+import org.apache.james.jmap.core.Id.Id
+import org.apache.james.jmap.core.Invocation.{Arguments, MethodCallId, MethodName}
+import org.apache.james.jmap.core.{ClientId, CreatedIds, Invocation, ResponseObject, ServerId}
 import play.api.libs.json.Json
 
 object Fixture {
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala
index c2841fc..491dbd2 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala
@@ -21,12 +21,12 @@ package org.apache.james.jmap.json
 
 import eu.timepit.refined.auto._
 import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
+import org.apache.james.jmap.core.{AccountId, Properties}
 import org.apache.james.jmap.json.Fixture._
 import org.apache.james.jmap.json.MailboxGetSerializationTest._
 import org.apache.james.jmap.json.MailboxSerializationTest.MAILBOX
 import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
 import org.apache.james.jmap.mail._
-import org.apache.james.jmap.model.{AccountId, Properties}
 import org.apache.james.mailbox.model.{MailboxId, TestId}
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/RequestObjectSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/RequestObjectSerializationTest.scala
index c614cc9..e53cd3a 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/RequestObjectSerializationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/RequestObjectSerializationTest.scala
@@ -19,8 +19,8 @@
 
 package org.apache.james.jmap.json
 
+import org.apache.james.jmap.core.RequestObject
 import org.apache.james.jmap.json.Fixture._
-import org.apache.james.jmap.model.RequestObject
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec
 import play.api.libs.json._
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/ResponseObjectSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/ResponseObjectSerializationTest.scala
index dd14faa..3a4a532 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/ResponseObjectSerializationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/ResponseObjectSerializationTest.scala
@@ -20,8 +20,8 @@
 package org.apache.james.jmap.json
 
 import eu.timepit.refined.auto._
+import org.apache.james.jmap.core.ResponseObject
 import org.apache.james.jmap.json.Fixture._
-import org.apache.james.jmap.model.ResponseObject
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec
 import play.api.libs.json._
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/SessionSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/SessionSerializationTest.scala
index 1983b95..627ca9d 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/SessionSerializationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/SessionSerializationTest.scala
@@ -23,12 +23,12 @@ import java.net.URL
 
 import eu.timepit.refined.auto._
 import org.apache.james.core.Username
+import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
+import org.apache.james.jmap.core.CoreCapabilityProperties.CollationAlgorithm
+import org.apache.james.jmap.core.MailCapability.EmailQuerySortOption
+import org.apache.james.jmap.core.State.State
+import org.apache.james.jmap.core._
 import org.apache.james.jmap.json.SessionSerializationTest.SESSION
-import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.model.CoreCapabilityProperties.CollationAlgorithm
-import org.apache.james.jmap.model.MailCapability.EmailQuerySortOption
-import org.apache.james.jmap.model.State.State
-import org.apache.james.jmap.model._
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec
 import play.api.libs.json.Json
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/VacationResponseGetSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/VacationResponseGetSerializationTest.scala
index 35a0e4e..b37910f 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/VacationResponseGetSerializationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/VacationResponseGetSerializationTest.scala
@@ -21,12 +21,12 @@ package org.apache.james.jmap.json
 
 import eu.timepit.refined.auto._
 import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
+import org.apache.james.jmap.core.{AccountId, Properties}
 import org.apache.james.jmap.json.Fixture.id
 import org.apache.james.jmap.json.VacationResponseGetSerializationTest.{ACCOUNT_ID, PROPERTIES, SINGLETON_ID}
 import org.apache.james.jmap.json.VacationResponseSerializationTest.VACATION_RESPONSE
-import org.apache.james.jmap.mail.VacationResponse.UnparsedVacationResponseId
-import org.apache.james.jmap.mail.{VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseIds, VacationResponseNotFound}
-import org.apache.james.jmap.model.{AccountId, Properties}
+import org.apache.james.jmap.vacation.VacationResponse.UnparsedVacationResponseId
+import org.apache.james.jmap.vacation.{VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseIds, VacationResponseNotFound}
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec
 import play.api.libs.json.{JsSuccess, Json}
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/VacationResponseSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/VacationResponseSerializationTest.scala
index dba41c3..b805538 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/VacationResponseSerializationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/VacationResponseSerializationTest.scala
@@ -22,9 +22,11 @@ package org.apache.james.jmap.json
 import java.time.ZonedDateTime
 
 import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
+import org.apache.james.jmap.core.UTCDate
 import org.apache.james.jmap.json.VacationResponseSerializationTest.VACATION_RESPONSE
-import org.apache.james.jmap.mail.{FromDate, HtmlBody, IsEnabled, Subject, TextBody, ToDate, VacationResponse, VacationResponseId}
-import org.apache.james.jmap.model.UTCDate
+import org.apache.james.jmap.mail.Subject
+import org.apache.james.jmap.vacation
+import org.apache.james.jmap.vacation.{FromDate, HtmlBody, IsEnabled, TextBody, ToDate, VacationResponse, VacationResponseId}
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec
 import play.api.libs.json.Json
@@ -41,7 +43,7 @@ object VacationResponseSerializationTest {
   private val TEXT_BODY: Option[TextBody] = Some(TextBody("text is required when enabled"))
   private val HTML_BODY: Option[HtmlBody] = Some(HtmlBody("<b>HTML body</b>"))
 
-  val VACATION_RESPONSE: VacationResponse = VacationResponse(
+  val VACATION_RESPONSE: VacationResponse = vacation.VacationResponse(
     id = VACATION_RESPONSE_ID,
     isEnabled = IS_ENABLED,
     fromDate = FROM_DATE,
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoMethodTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoMethodTest.scala
index 18a2624..2dc0314 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoMethodTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoMethodTest.scala
@@ -18,9 +18,9 @@
  * ***************************************************************/
 package org.apache.james.jmap.method
 
+import org.apache.james.jmap.core.CapabilityIdentifier
+import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.json.Fixture.{invocation1, invocation2}
-import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.model.{CapabilityIdentifier, Invocation}
 import org.apache.james.jmap.routes.ProcessingContext
 import org.apache.james.mailbox.MailboxSession
 import org.mockito.Mockito.mock
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 dd2240a..52ce1cd 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
@@ -36,11 +36,11 @@ import org.apache.james.dnsservice.api.DNSService
 import org.apache.james.domainlist.memory.MemoryDomainList
 import org.apache.james.jmap.JMAPUrls.JMAP
 import org.apache.james.jmap._
+import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE}
+import org.apache.james.jmap.core.Invocation.MethodName
+import org.apache.james.jmap.core.{JmapRfc8621Configuration, RequestLevelErrorType}
 import org.apache.james.jmap.http.{Authenticator, BasicAuthenticationStrategy, MailboxesProvisioner, UserProvisioning}
 import org.apache.james.jmap.method.{CoreEchoMethod, Method}
-import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE}
-import org.apache.james.jmap.model.Invocation.MethodName
-import org.apache.james.jmap.model.{JmapRfc8621Configuration, RequestLevelErrorType}
 import org.apache.james.jmap.routes.JMAPApiRoutesTest._
 import org.apache.james.mailbox.extension.PreDeletionHook
 import org.apache.james.mailbox.inmemory.{InMemoryMailboxManager, MemoryMailboxManagerProvider}
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala
index 4c47291..d0cad56 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala
@@ -31,9 +31,9 @@ import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
 import org.apache.http.HttpStatus
 import org.apache.james.core.Username
 import org.apache.james.jmap._
+import org.apache.james.jmap.core.JmapRfc8621Configuration
+import org.apache.james.jmap.core.JmapRfc8621Configuration.LOCALHOST_URL_PREFIX
 import org.apache.james.jmap.http.Authenticator
-import org.apache.james.jmap.model.JmapRfc8621Configuration
-import org.apache.james.jmap.model.JmapRfc8621Configuration.LOCALHOST_URL_PREFIX
 import org.apache.james.jmap.routes.SessionRoutesTest.{BOB, TEST_CONFIGURATION}
 import org.apache.james.mailbox.MailboxSession
 import org.mockito.ArgumentMatchers.any
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionSupplierTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionSupplierTest.scala
index 56e6a6d..01e53ce 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionSupplierTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionSupplierTest.scala
@@ -20,7 +20,7 @@
 package org.apache.james.jmap.routes
 
 import org.apache.james.core.Username
-import org.apache.james.jmap.model.JmapRfc8621Configuration
+import org.apache.james.jmap.core.JmapRfc8621Configuration
 import org.apache.james.jmap.routes.SessionSupplierTest.USERNAME
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec


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


[james-project] 07/11: [REFACTORING] Split MailboxSetMethod and extract create/update/destroy

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

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

commit 429bcbfb8bf138418ebaccd1f28bfb40ac4f5767
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Nov 2 11:03:05 2020 +0700

    [REFACTORING] Split MailboxSetMethod and extract create/update/destroy
---
 .../jmap/method/MailboxSetCreatePerformer.scala    | 206 ++++++++++
 .../jmap/method/MailboxSetDeletePerformer.scala    | 109 +++++
 .../james/jmap/method/MailboxSetMethod.scala       | 443 +--------------------
 .../jmap/method/MailboxSetUpdatePerformer.scala    | 222 +++++++++++
 4 files changed, 556 insertions(+), 424 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetCreatePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetCreatePerformer.scala
new file mode 100644
index 0000000..4fe260a
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetCreatePerformer.scala
@@ -0,0 +1,206 @@
+/****************************************************************
+ * 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.core.SetError.SetErrorDescription
+import org.apache.james.jmap.core.{ClientId, Id, Properties, ServerId, SetError}
+import org.apache.james.jmap.json.MailboxSerializer
+import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId
+import org.apache.james.jmap.mail.{IsSubscribed, MailboxCreationRequest, MailboxCreationResponse, MailboxRights, MailboxSetRequest, SortOrder, TotalEmails, TotalThreads, UnreadEmails, UnreadThreads}
+import org.apache.james.jmap.method.MailboxSetCreatePerformer.{MailboxCreationFailure, MailboxCreationResult, MailboxCreationResults, MailboxCreationSuccess}
+import org.apache.james.jmap.routes.{ProcessingContext, SessionSupplier}
+import org.apache.james.jmap.utils.quotas.QuotaLoaderWithPreloadedDefaultFactory
+import org.apache.james.mailbox.exception.{InsufficientRightsException, MailboxExistsException, MailboxNameException, MailboxNotFoundException}
+import org.apache.james.mailbox.model.{MailboxId, MailboxPath}
+import org.apache.james.mailbox.{MailboxManager, MailboxSession, SubscriptionManager}
+import org.apache.james.metrics.api.MetricFactory
+import play.api.libs.json.{JsError, JsObject, JsPath, JsSuccess, Json, JsonValidationError}
+import reactor.core.scala.publisher.{SFlux, SMono}
+import reactor.core.scheduler.Schedulers
+
+import scala.util.Try
+
+object MailboxSetCreatePerformer {
+  sealed trait MailboxCreationResult {
+    def mailboxCreationId: MailboxCreationId
+  }
+  case class MailboxCreationSuccess(mailboxCreationId: MailboxCreationId, mailboxCreationResponse: MailboxCreationResponse) extends MailboxCreationResult
+  case class MailboxCreationFailure(mailboxCreationId: MailboxCreationId, exception: Exception) extends MailboxCreationResult {
+    def asMailboxSetError: SetError = exception match {
+      case e: MailboxNotFoundException => SetError.invalidArguments(SetErrorDescription(e.getMessage), Some(Properties("parentId")))
+      case e: MailboxExistsException => SetError.invalidArguments(SetErrorDescription(e.getMessage), Some(Properties("name")))
+      case e: MailboxNameException => SetError.invalidArguments(SetErrorDescription(e.getMessage), Some(Properties("name")))
+      case e: MailboxCreationParseException => e.setError
+      case _: InsufficientRightsException => SetError.forbidden(SetErrorDescription("Insufficient rights"), Properties("parentId"))
+      case _ => SetError.serverFail(SetErrorDescription(exception.getMessage))
+    }
+  }
+  case class MailboxCreationResults(created: Seq[MailboxCreationResult]) {
+    def retrieveCreated: Map[MailboxCreationId, MailboxCreationResponse] = created
+      .flatMap(result => result match {
+        case success: MailboxCreationSuccess => Some(success.mailboxCreationId, success.mailboxCreationResponse)
+        case _ => None
+      })
+      .toMap
+      .map(creation => (creation._1, creation._2))
+
+    def retrieveErrors: Map[MailboxCreationId, SetError] = created
+      .flatMap(result => result match {
+        case failure: MailboxCreationFailure => Some(failure.mailboxCreationId, failure.asMailboxSetError)
+        case _ => None
+      })
+      .toMap
+  }
+}
+
+class MailboxSetCreatePerformer @Inject()(serializer: MailboxSerializer,
+                                          mailboxManager: MailboxManager,
+                                          subscriptionManager: SubscriptionManager,
+                                          mailboxIdFactory: MailboxId.Factory,
+                                          quotaFactory : QuotaLoaderWithPreloadedDefaultFactory,
+                                          val metricFactory: MetricFactory,
+                                          val sessionSupplier: SessionSupplier) {
+
+
+
+  def createMailboxes(mailboxSession: MailboxSession,
+                              mailboxSetRequest: MailboxSetRequest,
+                              processingContext: ProcessingContext): SMono[(MailboxCreationResults, ProcessingContext)] = {
+    SFlux.fromIterable(mailboxSetRequest.create
+      .getOrElse(Map.empty)
+      .view)
+      .foldLeft((MailboxCreationResults(Nil), processingContext)){
+        (acc : (MailboxCreationResults, ProcessingContext), elem: (MailboxCreationId, JsObject)) => {
+          val (mailboxCreationId, jsObject) = elem
+          val (creationResult, updatedProcessingContext) = createMailbox(mailboxSession, mailboxCreationId, jsObject, acc._2)
+          (MailboxCreationResults(acc._1.created :+ creationResult), updatedProcessingContext)
+        }
+      }
+      .subscribeOn(Schedulers.elastic())
+  }
+
+  private def createMailbox(mailboxSession: MailboxSession,
+                            mailboxCreationId: MailboxCreationId,
+                            jsObject: JsObject,
+                            processingContext: ProcessingContext): (MailboxCreationResult, ProcessingContext) = {
+    parseCreate(jsObject)
+      .flatMap(mailboxCreationRequest => resolvePath(mailboxSession, mailboxCreationRequest)
+        .flatMap(path => createMailbox(mailboxSession = mailboxSession,
+          path = path,
+          mailboxCreationRequest = mailboxCreationRequest)))
+      .flatMap(creationResponse => recordCreationIdInProcessingContext(mailboxCreationId, processingContext, creationResponse.id)
+        .map(context => (creationResponse, context)))
+      .fold(e => (MailboxCreationFailure(mailboxCreationId, e), processingContext),
+        creationResponseWithUpdatedContext => {
+          (MailboxCreationSuccess(mailboxCreationId, creationResponseWithUpdatedContext._1), creationResponseWithUpdatedContext._2)
+        })
+  }
+
+  private def parseCreate(jsObject: JsObject): Either[MailboxCreationParseException, MailboxCreationRequest] =
+    MailboxCreationRequest.validateProperties(jsObject)
+      .flatMap(validJsObject => Json.fromJson(validJsObject)(serializer.mailboxCreationRequest) match {
+        case JsSuccess(creationRequest, _) => Right(creationRequest)
+        case JsError(errors) => Left(MailboxCreationParseException(mailboxSetError(errors)))
+      })
+
+  private def resolvePath(mailboxSession: MailboxSession,
+                          mailboxCreationRequest: MailboxCreationRequest): Either[Exception, MailboxPath] = {
+    if (mailboxCreationRequest.name.value.contains(mailboxSession.getPathDelimiter)) {
+      return Left(new MailboxNameException(s"The mailbox '${mailboxCreationRequest.name.value}' contains an illegal character: '${mailboxSession.getPathDelimiter}'"))
+    }
+    mailboxCreationRequest.parentId
+      .map(maybeParentId => for {
+        parentId <- Try(mailboxIdFactory.fromString(maybeParentId.value))
+          .toEither
+          .left
+          .map(e => new IllegalArgumentException(e.getMessage, e))
+        parentPath <- retrievePath(parentId, mailboxSession)
+      } yield {
+        parentPath.child(mailboxCreationRequest.name, mailboxSession.getPathDelimiter)
+      })
+      .getOrElse(Right(MailboxPath.forUser(mailboxSession.getUser, mailboxCreationRequest.name)))
+  }
+
+  private def retrievePath(mailboxId: MailboxId, mailboxSession: MailboxSession): Either[Exception, MailboxPath] = try {
+    Right(mailboxManager.getMailbox(mailboxId, mailboxSession).getMailboxPath)
+  } catch {
+    case e: Exception => Left(e)
+  }
+
+  private def recordCreationIdInProcessingContext(mailboxCreationId: MailboxCreationId,
+                                                  processingContext: ProcessingContext,
+                                                  mailboxId: MailboxId): Either[IllegalArgumentException, ProcessingContext] = {
+    for {
+      creationId <- Id.validate(mailboxCreationId)
+      serverAssignedId <- Id.validate(mailboxId.serialize())
+    } yield {
+      processingContext.recordCreatedId(ClientId(creationId), ServerId(serverAssignedId))
+    }
+  }
+  private def mailboxSetError(errors: collection.Seq[(JsPath, collection.Seq[JsonValidationError])]): SetError =
+    errors.head match {
+      case (path, Seq()) => SetError.invalidArguments(SetErrorDescription(s"'$path' property in mailbox object is not valid"))
+      case (path, Seq(JsonValidationError(Seq("error.path.missing")))) => SetError.invalidArguments(SetErrorDescription(s"Missing '$path' property in mailbox object"))
+      case (path, Seq(JsonValidationError(Seq(message)))) => SetError.invalidArguments(SetErrorDescription(s"'$path' property in mailbox object is not valid: $message"))
+      case (path, _) => SetError.invalidArguments(SetErrorDescription(s"Unknown error on property '$path'"))
+    }
+
+  private def createMailbox(mailboxSession: MailboxSession,
+                            path: MailboxPath,
+                            mailboxCreationRequest: MailboxCreationRequest): Either[Exception, MailboxCreationResponse] = {
+    try {
+      //can safely do a get as the Optional is empty only if the mailbox name is empty which is forbidden by the type constraint on MailboxName
+      val mailboxId = mailboxManager.createMailbox(path, mailboxSession).get()
+
+      val defaultSubscribed = IsSubscribed(true)
+      if (mailboxCreationRequest.isSubscribed.getOrElse(defaultSubscribed).value) {
+        subscriptionManager.subscribe(mailboxSession, path.getName)
+      }
+
+      mailboxCreationRequest.rights
+        .foreach(rights => mailboxManager.setRights(mailboxId, rights.toMailboxAcl.asJava, mailboxSession))
+
+      val quotas = quotaFactory.loadFor(mailboxSession)
+        .flatMap(quotaLoader => quotaLoader.getQuotas(path))
+        .block()
+
+      Right(MailboxCreationResponse(
+        id = mailboxId,
+        sortOrder = SortOrder.defaultSortOrder,
+        role = None,
+        totalEmails = TotalEmails(0L),
+        unreadEmails = UnreadEmails(0L),
+        totalThreads = TotalThreads(0L),
+        unreadThreads = UnreadThreads(0L),
+        myRights = MailboxRights.FULL,
+        quotas = Some(quotas),
+        isSubscribed =  if (mailboxCreationRequest.isSubscribed.isEmpty) {
+          Some(defaultSubscribed)
+        } else {
+          None
+        }))
+    } catch {
+      case error: Exception => Left(error)
+    }
+  }
+
+}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetDeletePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetDeletePerformer.scala
new file mode 100644
index 0000000..3ccd8f4
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetDeletePerformer.scala
@@ -0,0 +1,109 @@
+/****************************************************************
+ * 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 javax.inject.Inject
+import org.apache.james.jmap.core.SetError
+import org.apache.james.jmap.core.SetError.SetErrorDescription
+import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
+import org.apache.james.jmap.mail.{MailboxGet, MailboxSetError, MailboxSetRequest, RemoveEmailsOnDestroy}
+import org.apache.james.jmap.method.MailboxSetDeletePerformer.{MailboxDeletionFailure, MailboxDeletionResult, MailboxDeletionResults, MailboxDeletionSuccess}
+import org.apache.james.mailbox.exception.MailboxNotFoundException
+import org.apache.james.mailbox.model.{FetchGroup, MailboxId, MessageRange}
+import org.apache.james.mailbox.{MailboxManager, MailboxSession, MessageManager, Role, SubscriptionManager}
+import reactor.core.scala.publisher.{SFlux, SMono}
+import reactor.core.scheduler.Schedulers
+
+object MailboxSetDeletePerformer {
+  sealed trait MailboxDeletionResult
+  case class MailboxDeletionSuccess(mailboxId: MailboxId) extends MailboxDeletionResult
+  case class MailboxDeletionFailure(mailboxId: UnparsedMailboxId, exception: Throwable) extends MailboxDeletionResult {
+    def asMailboxSetError: SetError = exception match {
+      case e: MailboxNotFoundException => SetError.notFound(SetErrorDescription(e.getMessage))
+      case e: MailboxHasMailException => MailboxSetError.mailboxHasEmail(SetErrorDescription(s"${e.mailboxId.serialize} is not empty"))
+      case e: MailboxHasChildException => MailboxSetError.mailboxHasChild(SetErrorDescription(s"${e.mailboxId.serialize} has child mailboxes"))
+      case e: SystemMailboxChangeException => SetError.invalidArguments(SetErrorDescription("System mailboxes cannot be destroyed"))
+      case e: IllegalArgumentException => SetError.invalidArguments(SetErrorDescription(s"${mailboxId} is not a mailboxId: ${e.getMessage}"))
+      case _ => SetError.serverFail(SetErrorDescription(exception.getMessage))
+    }
+  }
+  case class MailboxDeletionResults(results: Seq[MailboxDeletionResult]) {
+    def destroyed: Seq[MailboxId] =
+      results.flatMap(result => result match {
+        case success: MailboxDeletionSuccess => Some(success)
+        case _ => None
+      }).map(_.mailboxId)
+
+    def retrieveErrors: Map[UnparsedMailboxId, SetError] =
+      results.flatMap(result => result match {
+        case failure: MailboxDeletionFailure => Some(failure.mailboxId, failure.asMailboxSetError)
+        case _ => None
+      })
+        .toMap
+  }
+}
+
+class MailboxSetDeletePerformer @Inject()(mailboxManager: MailboxManager,
+                                          subscriptionManager: SubscriptionManager,
+                                          mailboxIdFactory: MailboxId.Factory) {
+
+  def deleteMailboxes(mailboxSession: MailboxSession, mailboxSetRequest: MailboxSetRequest): SMono[MailboxDeletionResults] = {
+    SFlux.fromIterable(mailboxSetRequest.destroy.getOrElse(Seq()))
+      .flatMap(id => delete(mailboxSession, id, mailboxSetRequest.onDestroyRemoveEmails.getOrElse(RemoveEmailsOnDestroy(false)))
+        .onErrorRecover(e => MailboxDeletionFailure(id, e)))
+      .collectSeq()
+      .map(MailboxDeletionResults)
+  }
+
+  private def delete(mailboxSession: MailboxSession, id: UnparsedMailboxId, onDestroy: RemoveEmailsOnDestroy): SMono[MailboxDeletionResult] = {
+    MailboxGet.parse(mailboxIdFactory)(id)
+      .fold(e => SMono.raiseError(e),
+        id => SMono.fromCallable(() => doDelete(mailboxSession, id, onDestroy))
+          .subscribeOn(Schedulers.elastic())
+          .`then`(SMono.just[MailboxDeletionResult](MailboxDeletionSuccess(id))))
+
+  }
+
+  private def doDelete(mailboxSession: MailboxSession, id: MailboxId, onDestroy: RemoveEmailsOnDestroy): Unit = {
+    val mailbox = mailboxManager.getMailbox(id, mailboxSession)
+
+    if (isASystemMailbox(mailbox)) {
+      throw SystemMailboxChangeException(id)
+    }
+
+    if (mailboxManager.hasChildren(mailbox.getMailboxPath, mailboxSession)) {
+      throw MailboxHasChildException(id)
+    }
+
+    if (onDestroy.value) {
+      val deletedMailbox = mailboxManager.deleteMailbox(id, mailboxSession)
+      subscriptionManager.unsubscribe(mailboxSession, deletedMailbox.getName)
+    } else {
+      if (mailbox.getMessages(MessageRange.all(), FetchGroup.MINIMAL, mailboxSession).hasNext) {
+        throw MailboxHasMailException(id)
+      }
+
+      val deletedMailbox = mailboxManager.deleteMailbox(id, mailboxSession)
+      subscriptionManager.unsubscribe(mailboxSession, deletedMailbox.getName)
+    }
+  }
+
+  private def isASystemMailbox(mailbox: MessageManager): Boolean = Role.from(mailbox.getMailboxPath.getName).isPresent
+}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
index e8c4265..723c208 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
@@ -23,25 +23,18 @@ import eu.timepit.refined.auto._
 import javax.inject.Inject
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
-import org.apache.james.jmap.core.SetError.SetErrorDescription
-import org.apache.james.jmap.core.{ClientId, Id, Invocation, Properties, ServerId, SetError, State}
+import org.apache.james.jmap.core.{Invocation, SetError, State}
 import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer}
-import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
-import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId
-import org.apache.james.jmap.mail.{InvalidPatchException, InvalidPropertyException, InvalidUpdateException, IsSubscribed, MailboxCreationRequest, MailboxCreationResponse, MailboxGet, MailboxPatchObject, MailboxRights, MailboxSetError, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, NameUpdate, ParentIdUpdate, RemoveEmailsOnDestroy, ServerSetPropertyException, SortOrder, TotalEmails, TotalThreads, UnreadEmails, UnreadThreads, UnsupportedPropertyUpdatedException, ValidatedMai [...]
-import org.apache.james.jmap.routes.{ProcessingContext, SessionSupplier}
-import org.apache.james.jmap.utils.quotas.QuotaLoaderWithPreloadedDefaultFactory
-import org.apache.james.mailbox.MailboxManager.RenameOption
-import org.apache.james.mailbox.exception.{InsufficientRightsException, MailboxExistsException, MailboxNameException, MailboxNotFoundException}
-import org.apache.james.mailbox.model.{FetchGroup, MailboxId, MailboxPath, MessageRange}
-import org.apache.james.mailbox.{MailboxManager, MailboxSession, MessageManager, Role, SubscriptionManager}
+import org.apache.james.jmap.mail.{MailboxSetRequest, MailboxSetResponse}
+import org.apache.james.jmap.method.MailboxSetCreatePerformer.MailboxCreationResults
+import org.apache.james.jmap.method.MailboxSetDeletePerformer.MailboxDeletionResults
+import org.apache.james.jmap.method.MailboxSetUpdatePerformer.MailboxUpdateResults
+import org.apache.james.jmap.routes.SessionSupplier
+import org.apache.james.mailbox.MailboxSession
+import org.apache.james.mailbox.model.MailboxId
 import org.apache.james.metrics.api.MetricFactory
-import play.api.libs.json.{JsError, JsObject, JsPath, JsSuccess, Json, JsonValidationError}
-import reactor.core.scala.publisher.{SFlux, SMono}
-import reactor.core.scheduler.Schedulers
-
-import scala.jdk.CollectionConverters._
-import scala.util.Try
+import play.api.libs.json.{JsError, JsObject, JsSuccess}
+import reactor.core.scala.publisher.SMono
 
 case class MailboxHasMailException(mailboxId: MailboxId) extends Exception
 case class SystemMailboxChangeException(mailboxId: MailboxId) extends Exception
@@ -49,427 +42,29 @@ case class LoopInMailboxGraphException(mailboxId: MailboxId) extends Exception
 case class MailboxHasChildException(mailboxId: MailboxId) extends Exception
 case class MailboxCreationParseException(setError: SetError) extends Exception
 
-sealed trait CreationResult {
-  def mailboxCreationId: MailboxCreationId
-}
-case class CreationSuccess(mailboxCreationId: MailboxCreationId, mailboxCreationResponse: MailboxCreationResponse) extends CreationResult
-case class CreationFailure(mailboxCreationId: MailboxCreationId, exception: Exception) extends CreationResult {
-  def asMailboxSetError: SetError = exception match {
-    case e: MailboxNotFoundException => SetError.invalidArguments(SetErrorDescription(e.getMessage), Some(Properties("parentId")))
-    case e: MailboxExistsException => SetError.invalidArguments(SetErrorDescription(e.getMessage), Some(Properties("name")))
-    case e: MailboxNameException => SetError.invalidArguments(SetErrorDescription(e.getMessage), Some(Properties("name")))
-    case e: MailboxCreationParseException => e.setError
-    case _: InsufficientRightsException => SetError.forbidden(SetErrorDescription("Insufficient rights"), Properties("parentId"))
-    case _ => SetError.serverFail(SetErrorDescription(exception.getMessage))
-  }
-}
-case class CreationResults(created: Seq[CreationResult]) {
-  def retrieveCreated: Map[MailboxCreationId, MailboxCreationResponse] = created
-    .flatMap(result => result match {
-      case success: CreationSuccess => Some(success.mailboxCreationId, success.mailboxCreationResponse)
-      case _ => None
-    })
-    .toMap
-    .map(creation => (creation._1, creation._2))
-
-  def retrieveErrors: Map[MailboxCreationId, SetError] = created
-    .flatMap(result => result match {
-      case failure: CreationFailure => Some(failure.mailboxCreationId, failure.asMailboxSetError)
-      case _ => None
-    })
-    .toMap
-}
-
-sealed trait DeletionResult
-case class DeletionSuccess(mailboxId: MailboxId) extends DeletionResult
-case class DeletionFailure(mailboxId: UnparsedMailboxId, exception: Throwable) extends DeletionResult {
-  def asMailboxSetError: SetError = exception match {
-    case e: MailboxNotFoundException => SetError.notFound(SetErrorDescription(e.getMessage))
-    case e: MailboxHasMailException => MailboxSetError.mailboxHasEmail(SetErrorDescription(s"${e.mailboxId.serialize} is not empty"))
-    case e: MailboxHasChildException => MailboxSetError.mailboxHasChild(SetErrorDescription(s"${e.mailboxId.serialize} has child mailboxes"))
-    case e: SystemMailboxChangeException => SetError.invalidArguments(SetErrorDescription("System mailboxes cannot be destroyed"))
-    case e: IllegalArgumentException => SetError.invalidArguments(SetErrorDescription(s"${mailboxId} is not a mailboxId: ${e.getMessage}"))
-    case _ => SetError.serverFail(SetErrorDescription(exception.getMessage))
-  }
-}
-case class DeletionResults(results: Seq[DeletionResult]) {
-  def destroyed: Seq[MailboxId] =
-    results.flatMap(result => result match {
-      case success: DeletionSuccess => Some(success)
-      case _ => None
-    }).map(_.mailboxId)
-
-  def retrieveErrors: Map[UnparsedMailboxId, SetError] =
-    results.flatMap(result => result match {
-      case failure: DeletionFailure => Some(failure.mailboxId, failure.asMailboxSetError)
-      case _ => None
-    })
-      .toMap
-}
-
-sealed trait UpdateResult
-case class UpdateSuccess(mailboxId: MailboxId) extends UpdateResult
-case class UpdateFailure(mailboxId: UnparsedMailboxId, exception: Throwable, patch: Option[ValidatedMailboxPatchObject]) extends UpdateResult {
-  def filter(acceptableProperties: Properties): Option[Properties] = Some(patch
-    .map(_.updatedProperties.intersect(acceptableProperties))
-    .getOrElse(acceptableProperties))
-
-  def asMailboxSetError: SetError = exception match {
-    case e: MailboxNotFoundException => SetError.notFound(SetErrorDescription(e.getMessage))
-    case e: MailboxNameException => SetError.invalidArguments(SetErrorDescription(e.getMessage), filter(Properties("name", "parentId")))
-    case e: MailboxExistsException => SetError.invalidArguments(SetErrorDescription(e.getMessage), filter(Properties("name", "parentId")))
-    case e: UnsupportedPropertyUpdatedException => SetError.invalidArguments(SetErrorDescription(s"${e.property} property do not exist thus cannot be updated"), Some(Properties(e.property)))
-    case e: InvalidUpdateException => SetError.invalidArguments(SetErrorDescription(s"${e.cause}"), Some(Properties(e.property)))
-    case e: ServerSetPropertyException => SetError.invalidArguments(SetErrorDescription("Can not modify server-set properties"), Some(Properties(e.property)))
-    case e: InvalidPropertyException => SetError.invalidPatch(SetErrorDescription(s"${e.cause}"))
-    case e: InvalidPatchException => SetError.invalidPatch(SetErrorDescription(s"${e.cause}"))
-    case e: SystemMailboxChangeException => SetError.invalidArguments(SetErrorDescription("Invalid change to a system mailbox"), filter(Properties("name", "parentId")))
-    case e: LoopInMailboxGraphException => SetError.invalidArguments(SetErrorDescription("A mailbox parentId property can not be set to itself or one of its child"), Some(Properties("parentId")))
-    case e: InsufficientRightsException => SetError.invalidArguments(SetErrorDescription("Invalid change to a delegated mailbox"))
-    case e: MailboxHasChildException => SetError.invalidArguments(SetErrorDescription(s"${e.mailboxId.serialize()} parentId property cannot be updated as this mailbox has child mailboxes"), Some(Properties("parentId")))
-    case e: IllegalArgumentException => SetError.invalidArguments(SetErrorDescription(e.getMessage), None)
-    case _ => SetError.serverFail(SetErrorDescription(exception.getMessage))
-  }
-}
-case class UpdateResults(results: Seq[UpdateResult]) {
-  def updated: Map[MailboxId, MailboxUpdateResponse] =
-    results.flatMap(result => result match {
-      case success: UpdateSuccess => Some((success.mailboxId, MailboxSetResponse.empty))
-      case _ => None
-    }).toMap
-  def notUpdated: Map[UnparsedMailboxId, SetError] = results.flatMap(result => result match {
-    case failure: UpdateFailure => Some(failure.mailboxId, failure.asMailboxSetError)
-    case _ => None
-  }).toMap
-}
-
 class MailboxSetMethod @Inject()(serializer: MailboxSerializer,
-                                 mailboxManager: MailboxManager,
-                                 subscriptionManager: SubscriptionManager,
-                                 mailboxIdFactory: MailboxId.Factory,
-                                 quotaFactory : QuotaLoaderWithPreloadedDefaultFactory,
+                                 createPerformer: MailboxSetCreatePerformer,
+                                 deletePerformer: MailboxSetDeletePerformer,
+                                 updatePerformer: MailboxSetUpdatePerformer,
                                  val metricFactory: MetricFactory,
                                  val sessionSupplier: SessionSupplier) extends MethodRequiringAccountId[MailboxSetRequest] {
   override val methodName: MethodName = MethodName("Mailbox/set")
   override val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE, JMAP_MAIL)
 
   override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: MailboxSetRequest): SMono[InvocationWithContext] = for {
-    creationResultsWithUpdatedProcessingContext <- createMailboxes(mailboxSession, request, invocation.processingContext)
-    deletionResults <- deleteMailboxes(mailboxSession, request)
-    updateResults <- updateMailboxes(mailboxSession, request, invocation.processingContext, capabilities)
+    creationResultsWithUpdatedProcessingContext <- createPerformer.createMailboxes(mailboxSession, request, invocation.processingContext)
+    deletionResults <- deletePerformer.deleteMailboxes(mailboxSession, request)
+    updateResults <- updatePerformer.updateMailboxes(mailboxSession, request, capabilities)
   } yield InvocationWithContext(createResponse(capabilities, invocation.invocation, request, creationResultsWithUpdatedProcessingContext._1, deletionResults, updateResults), creationResultsWithUpdatedProcessingContext._2)
 
   override def getRequest(mailboxSession: MailboxSession, invocation: Invocation): SMono[MailboxSetRequest] = asMailboxSetRequest(invocation.arguments)
 
-  private def updateMailboxes(mailboxSession: MailboxSession,
-                              mailboxSetRequest: MailboxSetRequest,
-                              processingContext: ProcessingContext,
-                              capabilities: Set[CapabilityIdentifier]): SMono[UpdateResults] = {
-    SFlux.fromIterable(mailboxSetRequest.update.getOrElse(Seq()))
-      .flatMap({
-        case (unparsedMailboxId: UnparsedMailboxId, patch: MailboxPatchObject) =>
-          MailboxGet.parse(mailboxIdFactory)(unparsedMailboxId)
-            .fold(
-              e => SMono.just(UpdateFailure(unparsedMailboxId, e, None)),
-              mailboxId => updateMailbox(mailboxSession, mailboxId, unparsedMailboxId, patch, capabilities))
-            .onErrorResume(e => SMono.just(UpdateFailure(unparsedMailboxId, e, None)))
-      })
-      .collectSeq()
-      .map(UpdateResults)
-  }
-
-  private def updateMailbox(mailboxSession: MailboxSession,
-                            mailboxId: MailboxId,
-                            unparsedMailboxId: UnparsedMailboxId,
-                            patch: MailboxPatchObject,
-                            capabilities: Set[CapabilityIdentifier]): SMono[UpdateResult] = {
-    patch.validate(mailboxIdFactory, serializer, capabilities, mailboxSession)
-      .fold(e => SMono.raiseError(e), validatedPatch =>
-        updateMailboxRights(mailboxId, validatedPatch, mailboxSession)
-          .`then`(updateSubscription(mailboxId, validatedPatch, mailboxSession))
-          .`then`(updateMailboxPath(mailboxId, unparsedMailboxId, validatedPatch, mailboxSession)))
-  }
-
-  private def updateSubscription(mailboxId: MailboxId, validatedPatch: ValidatedMailboxPatchObject, mailboxSession: MailboxSession): SMono[UpdateResult] = {
-    validatedPatch.isSubscribedUpdate.map(isSubscribedUpdate => {
-      SMono.fromCallable(() => {
-        val mailbox = mailboxManager.getMailbox(mailboxId, mailboxSession)
-        val isOwner = mailbox.getMailboxPath.belongsTo(mailboxSession)
-        val shouldSubscribe = isSubscribedUpdate.isSubscribed.map(_.value).getOrElse(isOwner)
-
-        if (shouldSubscribe) {
-          subscriptionManager.subscribe(mailboxSession, mailbox.getMailboxPath.getName)
-        } else {
-          subscriptionManager.unsubscribe(mailboxSession, mailbox.getMailboxPath.getName)
-        }
-      }).`then`(SMono.just[UpdateResult](UpdateSuccess(mailboxId)))
-        .subscribeOn(Schedulers.elastic())
-    })
-      .getOrElse(SMono.just[UpdateResult](UpdateSuccess(mailboxId)))
-  }
-
-  private def updateMailboxPath(mailboxId: MailboxId,
-                                unparsedMailboxId: UnparsedMailboxId,
-                                validatedPatch: ValidatedMailboxPatchObject,
-                                mailboxSession: MailboxSession): SMono[UpdateResult] = {
-    if (validatedPatch.shouldUpdateMailboxPath) {
-      SMono.fromCallable[UpdateResult](() => {
-        try {
-          val mailbox = mailboxManager.getMailbox(mailboxId, mailboxSession)
-          if (isASystemMailbox(mailbox)) {
-            throw SystemMailboxChangeException(mailboxId)
-          }
-          if (validatedPatch.parentIdUpdate.flatMap(_.newId).contains(mailboxId)) {
-            throw LoopInMailboxGraphException(mailboxId)
-          }
-          val oldPath = mailbox.getMailboxPath
-          val newPath = applyParentIdUpdate(mailboxId, validatedPatch.parentIdUpdate, mailboxSession)
-            .andThen(applyNameUpdate(validatedPatch.nameUpdate, mailboxSession))
-            .apply(oldPath)
-          if (!oldPath.equals(newPath)) {
-            mailboxManager.renameMailbox(mailboxId,
-              newPath,
-              RenameOption.RENAME_SUBSCRIPTIONS,
-              mailboxSession)
-          }
-          UpdateSuccess(mailboxId)
-        } catch {
-          case e: Exception => UpdateFailure(unparsedMailboxId, e, Some(validatedPatch))
-        }
-      })
-        .subscribeOn(Schedulers.elastic())
-    } else {
-      SMono.just[UpdateResult](UpdateSuccess(mailboxId))
-    }
-  }
-
-  private def applyParentIdUpdate(mailboxId: MailboxId, maybeParentIdUpdate: Option[ParentIdUpdate], mailboxSession: MailboxSession): MailboxPath => MailboxPath = {
-    maybeParentIdUpdate.map(parentIdUpdate => applyParentIdUpdate(mailboxId, parentIdUpdate, mailboxSession))
-      .getOrElse(x => x)
-  }
-
-  private def applyNameUpdate(maybeNameUpdate: Option[NameUpdate], mailboxSession: MailboxSession): MailboxPath => MailboxPath = {
-    originalPath => maybeNameUpdate.map(nameUpdate => {
-      val originalParentPath: Option[MailboxPath] = originalPath.getHierarchyLevels(mailboxSession.getPathDelimiter)
-        .asScala
-        .reverse
-        .drop(1)
-        .headOption
-      originalParentPath.map(_.child(nameUpdate.newName, mailboxSession.getPathDelimiter))
-        .getOrElse(MailboxPath.forUser(mailboxSession.getUser, nameUpdate.newName))
-    }).getOrElse(originalPath)
-  }
-
-  private def applyParentIdUpdate(mailboxId: MailboxId, parentIdUpdate: ParentIdUpdate, mailboxSession: MailboxSession): MailboxPath => MailboxPath = {
-    originalPath => {
-      val currentName = originalPath.getName(mailboxSession.getPathDelimiter)
-      parentIdUpdate.newId
-        .map(id => {
-          if (mailboxManager.hasChildren(originalPath, mailboxSession)) {
-            throw MailboxHasChildException(mailboxId)
-          }
-          val parentPath = mailboxManager.getMailbox(id, mailboxSession).getMailboxPath
-          parentPath.child(currentName, mailboxSession.getPathDelimiter)
-        })
-        .getOrElse(MailboxPath.forUser(originalPath.getUser, currentName))
-    }
-  }
-
-  private def updateMailboxRights(mailboxId: MailboxId,
-                                  validatedPatch: ValidatedMailboxPatchObject,
-                                  mailboxSession: MailboxSession): SMono[UpdateResult] = {
-
-    val resetOperation: SMono[Unit] = validatedPatch.rightsReset.map(sharedWithResetUpdate => {
-      SMono.fromCallable(() => {
-        mailboxManager.setRights(mailboxId, sharedWithResetUpdate.rights.toMailboxAcl.asJava, mailboxSession)
-      }).`then`()
-    }).getOrElse(SMono.empty)
-
-    val partialUpdatesOperation: SMono[Unit] = SFlux.fromIterable(validatedPatch.rightsPartialUpdates)
-      .flatMap(partialUpdate => SMono.fromCallable(() => {
-        mailboxManager.applyRightsCommand(mailboxId, partialUpdate.asACLCommand(), mailboxSession)
-      }))
-      .`then`()
-
-    SFlux.merge(Seq(resetOperation, partialUpdatesOperation))
-      .`then`()
-      .`then`(SMono.just[UpdateResult](UpdateSuccess(mailboxId)))
-      .subscribeOn(Schedulers.elastic())
-
-  }
-
-
-  private def deleteMailboxes(mailboxSession: MailboxSession, mailboxSetRequest: MailboxSetRequest): SMono[DeletionResults] = {
-    SFlux.fromIterable(mailboxSetRequest.destroy.getOrElse(Seq()))
-      .flatMap(id => delete(mailboxSession, id, mailboxSetRequest.onDestroyRemoveEmails.getOrElse(RemoveEmailsOnDestroy(false)))
-        .onErrorRecover(e => DeletionFailure(id, e)))
-      .collectSeq()
-      .map(DeletionResults)
-  }
-
-  private def delete(mailboxSession: MailboxSession, id: UnparsedMailboxId, onDestroy: RemoveEmailsOnDestroy): SMono[DeletionResult] = {
-    MailboxGet.parse(mailboxIdFactory)(id)
-        .fold(e => SMono.raiseError(e),
-          id => SMono.fromCallable(() => doDelete(mailboxSession, id, onDestroy))
-            .subscribeOn(Schedulers.elastic())
-            .`then`(SMono.just[DeletionResult](DeletionSuccess(id))))
-
-  }
-
-  private def doDelete(mailboxSession: MailboxSession, id: MailboxId, onDestroy: RemoveEmailsOnDestroy): Unit = {
-    val mailbox = mailboxManager.getMailbox(id, mailboxSession)
-
-    if (isASystemMailbox(mailbox)) {
-      throw SystemMailboxChangeException(id)
-    }
-
-    if (mailboxManager.hasChildren(mailbox.getMailboxPath, mailboxSession)) {
-      throw MailboxHasChildException(id)
-    }
-
-    if (onDestroy.value) {
-      val deletedMailbox = mailboxManager.deleteMailbox(id, mailboxSession)
-      subscriptionManager.unsubscribe(mailboxSession, deletedMailbox.getName)
-    } else {
-      if (mailbox.getMessages(MessageRange.all(), FetchGroup.MINIMAL, mailboxSession).hasNext) {
-        throw MailboxHasMailException(id)
-      }
-
-      val deletedMailbox = mailboxManager.deleteMailbox(id, mailboxSession)
-      subscriptionManager.unsubscribe(mailboxSession, deletedMailbox.getName)
-    }
-  }
-
-  private def isASystemMailbox(mailbox: MessageManager): Boolean = Role.from(mailbox.getMailboxPath.getName).isPresent
-
-  private def createMailboxes(mailboxSession: MailboxSession,
-                              mailboxSetRequest: MailboxSetRequest,
-                              processingContext: ProcessingContext): SMono[(CreationResults, ProcessingContext)] = {
-    SFlux.fromIterable(mailboxSetRequest.create
-      .getOrElse(Map.empty)
-      .view)
-      .foldLeft((CreationResults(Nil), processingContext)){
-         (acc : (CreationResults, ProcessingContext), elem: (MailboxCreationId, JsObject)) => {
-           val (mailboxCreationId, jsObject) = elem
-           val (creationResult, updatedProcessingContext) = createMailbox(mailboxSession, mailboxCreationId, jsObject, acc._2)
-           (CreationResults(acc._1.created :+ creationResult), updatedProcessingContext)
-          }
-      }
-      .subscribeOn(Schedulers.elastic())
-  }
-
-  private def createMailbox(mailboxSession: MailboxSession,
-                            mailboxCreationId: MailboxCreationId,
-                            jsObject: JsObject,
-                            processingContext: ProcessingContext): (CreationResult, ProcessingContext) = {
-    parseCreate(jsObject)
-      .flatMap(mailboxCreationRequest => resolvePath(mailboxSession, mailboxCreationRequest)
-        .flatMap(path => createMailbox(mailboxSession = mailboxSession,
-          path = path,
-          mailboxCreationRequest = mailboxCreationRequest)))
-      .flatMap(creationResponse => recordCreationIdInProcessingContext(mailboxCreationId, processingContext, creationResponse.id)
-        .map(context => (creationResponse, context)))
-      .fold(e => (CreationFailure(mailboxCreationId, e), processingContext),
-        creationResponseWithUpdatedContext => {
-          (CreationSuccess(mailboxCreationId, creationResponseWithUpdatedContext._1), creationResponseWithUpdatedContext._2)
-        })
-  }
-
-  private def parseCreate(jsObject: JsObject): Either[MailboxCreationParseException, MailboxCreationRequest] =
-    MailboxCreationRequest.validateProperties(jsObject)
-      .flatMap(validJsObject => Json.fromJson(validJsObject)(serializer.mailboxCreationRequest) match {
-        case JsSuccess(creationRequest, _) => Right(creationRequest)
-        case JsError(errors) => Left(MailboxCreationParseException(mailboxSetError(errors)))
-      })
-
-  private def mailboxSetError(errors: collection.Seq[(JsPath, collection.Seq[JsonValidationError])]): SetError =
-    errors.head match {
-      case (path, Seq()) => SetError.invalidArguments(SetErrorDescription(s"'$path' property in mailbox object is not valid"))
-      case (path, Seq(JsonValidationError(Seq("error.path.missing")))) => SetError.invalidArguments(SetErrorDescription(s"Missing '$path' property in mailbox object"))
-      case (path, Seq(JsonValidationError(Seq(message)))) => SetError.invalidArguments(SetErrorDescription(s"'$path' property in mailbox object is not valid: $message"))
-      case (path, _) => SetError.invalidArguments(SetErrorDescription(s"Unknown error on property '$path'"))
-    }
-
-  private def createMailbox(mailboxSession: MailboxSession,
-                            path: MailboxPath,
-                            mailboxCreationRequest: MailboxCreationRequest): Either[Exception, MailboxCreationResponse] = {
-    try {
-      //can safely do a get as the Optional is empty only if the mailbox name is empty which is forbidden by the type constraint on MailboxName
-      val mailboxId = mailboxManager.createMailbox(path, mailboxSession).get()
-
-      val defaultSubscribed = IsSubscribed(true)
-      if (mailboxCreationRequest.isSubscribed.getOrElse(defaultSubscribed).value) {
-        subscriptionManager.subscribe(mailboxSession, path.getName)
-      }
-
-      mailboxCreationRequest.rights
-        .foreach(rights => mailboxManager.setRights(mailboxId, rights.toMailboxAcl.asJava, mailboxSession))
-
-      val quotas = quotaFactory.loadFor(mailboxSession)
-        .flatMap(quotaLoader => quotaLoader.getQuotas(path))
-        .block()
-
-      Right(MailboxCreationResponse(
-        id = mailboxId,
-        sortOrder = SortOrder.defaultSortOrder,
-        role = None,
-        totalEmails = TotalEmails(0L),
-        unreadEmails = UnreadEmails(0L),
-        totalThreads = TotalThreads(0L),
-        unreadThreads = UnreadThreads(0L),
-        myRights = MailboxRights.FULL,
-        quotas = Some(quotas),
-        isSubscribed =  if (mailboxCreationRequest.isSubscribed.isEmpty) {
-          Some(defaultSubscribed)
-        } else {
-          None
-        }))
-    } catch {
-      case error: Exception => Left(error)
-    }
-  }
-
-  private def recordCreationIdInProcessingContext(mailboxCreationId: MailboxCreationId,
-                                                  processingContext: ProcessingContext,
-                                                  mailboxId: MailboxId): Either[IllegalArgumentException, ProcessingContext] = {
-    for {
-      creationId <- Id.validate(mailboxCreationId)
-      serverAssignedId <- Id.validate(mailboxId.serialize())
-    } yield {
-      processingContext.recordCreatedId(ClientId(creationId), ServerId(serverAssignedId))
-    }
-  }
-
-  private def resolvePath(mailboxSession: MailboxSession,
-                          mailboxCreationRequest: MailboxCreationRequest): Either[Exception, MailboxPath] = {
-    if (mailboxCreationRequest.name.value.contains(mailboxSession.getPathDelimiter)) {
-      return Left(new MailboxNameException(s"The mailbox '${mailboxCreationRequest.name.value}' contains an illegal character: '${mailboxSession.getPathDelimiter}'"))
-    }
-    mailboxCreationRequest.parentId
-      .map(maybeParentId => for {
-        parentId <- Try(mailboxIdFactory.fromString(maybeParentId.value))
-          .toEither
-          .left
-          .map(e => new IllegalArgumentException(e.getMessage, e))
-        parentPath <- retrievePath(parentId, mailboxSession)
-      } yield {
-        parentPath.child(mailboxCreationRequest.name, mailboxSession.getPathDelimiter)
-      })
-      .getOrElse(Right(MailboxPath.forUser(mailboxSession.getUser, mailboxCreationRequest.name)))
-  }
-
-  private def retrievePath(mailboxId: MailboxId, mailboxSession: MailboxSession): Either[Exception, MailboxPath] = try {
-    Right(mailboxManager.getMailbox(mailboxId, mailboxSession).getMailboxPath)
-  } catch {
-    case e: Exception => Left(e)
-  }
-
   private def createResponse(capabilities: Set[CapabilityIdentifier],
                              invocation: Invocation,
                              mailboxSetRequest: MailboxSetRequest,
-                             creationResults: CreationResults,
-                             deletionResults: DeletionResults,
-                             updateResults: UpdateResults): Invocation = {
+                             creationResults: MailboxCreationResults,
+                             deletionResults: MailboxDeletionResults,
+                             updateResults: MailboxUpdateResults): Invocation = {
     val response = MailboxSetResponse(
       mailboxSetRequest.accountId,
       oldState = None,
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala
new file mode 100644
index 0000000..176324f
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala
@@ -0,0 +1,222 @@
+/****************************************************************
+ * 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.core.CapabilityIdentifier.CapabilityIdentifier
+import org.apache.james.jmap.core.SetError.SetErrorDescription
+import org.apache.james.jmap.core.{Properties, SetError}
+import org.apache.james.jmap.json.MailboxSerializer
+import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
+import org.apache.james.jmap.mail.{InvalidPatchException, InvalidPropertyException, InvalidUpdateException, MailboxGet, MailboxPatchObject, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, NameUpdate, ParentIdUpdate, ServerSetPropertyException, UnsupportedPropertyUpdatedException, ValidatedMailboxPatchObject}
+import org.apache.james.jmap.method.MailboxSetUpdatePerformer.{MailboxUpdateFailure, MailboxUpdateResult, MailboxUpdateResults, MailboxUpdateSuccess}
+import org.apache.james.mailbox.MailboxManager.RenameOption
+import org.apache.james.mailbox.exception.{InsufficientRightsException, MailboxExistsException, MailboxNameException, MailboxNotFoundException}
+import org.apache.james.mailbox.model.{MailboxId, MailboxPath}
+import org.apache.james.mailbox.{MailboxManager, MailboxSession, MessageManager, Role, SubscriptionManager}
+import reactor.core.scala.publisher.{SFlux, SMono}
+import reactor.core.scheduler.Schedulers
+
+import scala.jdk.CollectionConverters._
+
+object MailboxSetUpdatePerformer {
+
+  sealed trait MailboxUpdateResult
+  case class MailboxUpdateSuccess(mailboxId: MailboxId) extends MailboxUpdateResult
+  case class MailboxUpdateFailure(mailboxId: UnparsedMailboxId, exception: Throwable, patch: Option[ValidatedMailboxPatchObject]) extends MailboxUpdateResult {
+    def filter(acceptableProperties: Properties): Option[Properties] = Some(patch
+      .map(_.updatedProperties.intersect(acceptableProperties))
+      .getOrElse(acceptableProperties))
+
+    def asMailboxSetError: SetError = exception match {
+      case e: MailboxNotFoundException => SetError.notFound(SetErrorDescription(e.getMessage))
+      case e: MailboxNameException => SetError.invalidArguments(SetErrorDescription(e.getMessage), filter(Properties("name", "parentId")))
+      case e: MailboxExistsException => SetError.invalidArguments(SetErrorDescription(e.getMessage), filter(Properties("name", "parentId")))
+      case e: UnsupportedPropertyUpdatedException => SetError.invalidArguments(SetErrorDescription(s"${e.property} property do not exist thus cannot be updated"), Some(Properties(e.property)))
+      case e: InvalidUpdateException => SetError.invalidArguments(SetErrorDescription(s"${e.cause}"), Some(Properties(e.property)))
+      case e: ServerSetPropertyException => SetError.invalidArguments(SetErrorDescription("Can not modify server-set properties"), Some(Properties(e.property)))
+      case e: InvalidPropertyException => SetError.invalidPatch(SetErrorDescription(s"${e.cause}"))
+      case e: InvalidPatchException => SetError.invalidPatch(SetErrorDescription(s"${e.cause}"))
+      case e: SystemMailboxChangeException => SetError.invalidArguments(SetErrorDescription("Invalid change to a system mailbox"), filter(Properties("name", "parentId")))
+      case e: LoopInMailboxGraphException => SetError.invalidArguments(SetErrorDescription("A mailbox parentId property can not be set to itself or one of its child"), Some(Properties("parentId")))
+      case e: InsufficientRightsException => SetError.invalidArguments(SetErrorDescription("Invalid change to a delegated mailbox"))
+      case e: MailboxHasChildException => SetError.invalidArguments(SetErrorDescription(s"${e.mailboxId.serialize()} parentId property cannot be updated as this mailbox has child mailboxes"), Some(Properties("parentId")))
+      case e: IllegalArgumentException => SetError.invalidArguments(SetErrorDescription(e.getMessage), None)
+      case _ => SetError.serverFail(SetErrorDescription(exception.getMessage))
+    }
+  }
+  case class MailboxUpdateResults(results: Seq[MailboxUpdateResult]) {
+    def updated: Map[MailboxId, MailboxUpdateResponse] =
+      results.flatMap(result => result match {
+        case success: MailboxUpdateSuccess => Some((success.mailboxId, MailboxSetResponse.empty))
+        case _ => None
+      }).toMap
+    def notUpdated: Map[UnparsedMailboxId, SetError] = results.flatMap(result => result match {
+      case failure: MailboxUpdateFailure => Some(failure.mailboxId, failure.asMailboxSetError)
+      case _ => None
+    }).toMap
+  }
+}
+
+class MailboxSetUpdatePerformer @Inject()(serializer: MailboxSerializer,
+                                          mailboxManager: MailboxManager,
+                                          subscriptionManager: SubscriptionManager,
+                                          mailboxIdFactory: MailboxId.Factory) {
+
+  def updateMailboxes(mailboxSession: MailboxSession,
+                              mailboxSetRequest: MailboxSetRequest,
+                              capabilities: Set[CapabilityIdentifier]): SMono[MailboxUpdateResults] = {
+    SFlux.fromIterable(mailboxSetRequest.update.getOrElse(Seq()))
+      .flatMap({
+        case (unparsedMailboxId: UnparsedMailboxId, patch: MailboxPatchObject) =>
+          MailboxGet.parse(mailboxIdFactory)(unparsedMailboxId)
+            .fold(
+              e => SMono.just(MailboxUpdateFailure(unparsedMailboxId, e, None)),
+              mailboxId => updateMailbox(mailboxSession, mailboxId, unparsedMailboxId, patch, capabilities))
+            .onErrorResume(e => SMono.just(MailboxUpdateFailure(unparsedMailboxId, e, None)))
+      })
+      .collectSeq()
+      .map(MailboxUpdateResults)
+  }
+
+  private def updateMailbox(mailboxSession: MailboxSession,
+                            mailboxId: MailboxId,
+                            unparsedMailboxId: UnparsedMailboxId,
+                            patch: MailboxPatchObject,
+                            capabilities: Set[CapabilityIdentifier]): SMono[MailboxUpdateResult] = {
+    patch.validate(mailboxIdFactory, serializer, capabilities, mailboxSession)
+      .fold(e => SMono.raiseError(e), validatedPatch =>
+        updateMailboxRights(mailboxId, validatedPatch, mailboxSession)
+          .`then`(updateSubscription(mailboxId, validatedPatch, mailboxSession))
+          .`then`(updateMailboxPath(mailboxId, unparsedMailboxId, validatedPatch, mailboxSession)))
+  }
+
+  private def updateSubscription(mailboxId: MailboxId, validatedPatch: ValidatedMailboxPatchObject, mailboxSession: MailboxSession): SMono[MailboxUpdateResult] = {
+    validatedPatch.isSubscribedUpdate.map(isSubscribedUpdate => {
+      SMono.fromCallable(() => {
+        val mailbox = mailboxManager.getMailbox(mailboxId, mailboxSession)
+        val isOwner = mailbox.getMailboxPath.belongsTo(mailboxSession)
+        val shouldSubscribe = isSubscribedUpdate.isSubscribed.map(_.value).getOrElse(isOwner)
+
+        if (shouldSubscribe) {
+          subscriptionManager.subscribe(mailboxSession, mailbox.getMailboxPath.getName)
+        } else {
+          subscriptionManager.unsubscribe(mailboxSession, mailbox.getMailboxPath.getName)
+        }
+      }).`then`(SMono.just[MailboxUpdateResult](MailboxUpdateSuccess(mailboxId)))
+        .subscribeOn(Schedulers.elastic())
+    })
+      .getOrElse(SMono.just[MailboxUpdateResult](MailboxUpdateSuccess(mailboxId)))
+  }
+
+  private def updateMailboxPath(mailboxId: MailboxId,
+                                unparsedMailboxId: UnparsedMailboxId,
+                                validatedPatch: ValidatedMailboxPatchObject,
+                                mailboxSession: MailboxSession): SMono[MailboxUpdateResult] = {
+    if (validatedPatch.shouldUpdateMailboxPath) {
+      SMono.fromCallable[MailboxUpdateResult](() => {
+        try {
+          val mailbox = mailboxManager.getMailbox(mailboxId, mailboxSession)
+          if (isASystemMailbox(mailbox)) {
+            throw SystemMailboxChangeException(mailboxId)
+          }
+          if (validatedPatch.parentIdUpdate.flatMap(_.newId).contains(mailboxId)) {
+            throw LoopInMailboxGraphException(mailboxId)
+          }
+          val oldPath = mailbox.getMailboxPath
+          val newPath = applyParentIdUpdate(mailboxId, validatedPatch.parentIdUpdate, mailboxSession)
+            .andThen(applyNameUpdate(validatedPatch.nameUpdate, mailboxSession))
+            .apply(oldPath)
+          if (!oldPath.equals(newPath)) {
+            mailboxManager.renameMailbox(mailboxId,
+              newPath,
+              RenameOption.RENAME_SUBSCRIPTIONS,
+              mailboxSession)
+          }
+          MailboxUpdateSuccess(mailboxId)
+        } catch {
+          case e: Exception => MailboxUpdateFailure(unparsedMailboxId, e, Some(validatedPatch))
+        }
+      })
+        .subscribeOn(Schedulers.elastic())
+    } else {
+      SMono.just[MailboxUpdateResult](MailboxUpdateSuccess(mailboxId))
+    }
+  }
+
+  private def applyParentIdUpdate(mailboxId: MailboxId, maybeParentIdUpdate: Option[ParentIdUpdate], mailboxSession: MailboxSession): MailboxPath => MailboxPath = {
+    maybeParentIdUpdate.map(parentIdUpdate => applyParentIdUpdate(mailboxId, parentIdUpdate, mailboxSession))
+      .getOrElse(x => x)
+  }
+
+  private def applyNameUpdate(maybeNameUpdate: Option[NameUpdate], mailboxSession: MailboxSession): MailboxPath => MailboxPath = {
+    originalPath => maybeNameUpdate.map(nameUpdate => {
+      val originalParentPath: Option[MailboxPath] = originalPath.getHierarchyLevels(mailboxSession.getPathDelimiter)
+        .asScala
+        .reverse
+        .drop(1)
+        .headOption
+      originalParentPath.map(_.child(nameUpdate.newName, mailboxSession.getPathDelimiter))
+        .getOrElse(MailboxPath.forUser(mailboxSession.getUser, nameUpdate.newName))
+    }).getOrElse(originalPath)
+  }
+
+  private def applyParentIdUpdate(mailboxId: MailboxId, parentIdUpdate: ParentIdUpdate, mailboxSession: MailboxSession): MailboxPath => MailboxPath = {
+    originalPath => {
+      val currentName = originalPath.getName(mailboxSession.getPathDelimiter)
+      parentIdUpdate.newId
+        .map(id => {
+          if (mailboxManager.hasChildren(originalPath, mailboxSession)) {
+            throw MailboxHasChildException(mailboxId)
+          }
+          val parentPath = mailboxManager.getMailbox(id, mailboxSession).getMailboxPath
+          parentPath.child(currentName, mailboxSession.getPathDelimiter)
+        })
+        .getOrElse(MailboxPath.forUser(originalPath.getUser, currentName))
+    }
+  }
+
+  private def updateMailboxRights(mailboxId: MailboxId,
+                                  validatedPatch: ValidatedMailboxPatchObject,
+                                  mailboxSession: MailboxSession): SMono[MailboxUpdateResult] = {
+
+    val resetOperation: SMono[Unit] = validatedPatch.rightsReset.map(sharedWithResetUpdate => {
+      SMono.fromCallable(() => {
+        mailboxManager.setRights(mailboxId, sharedWithResetUpdate.rights.toMailboxAcl.asJava, mailboxSession)
+      }).`then`()
+    }).getOrElse(SMono.empty)
+
+    val partialUpdatesOperation: SMono[Unit] = SFlux.fromIterable(validatedPatch.rightsPartialUpdates)
+      .flatMap(partialUpdate => SMono.fromCallable(() => {
+        mailboxManager.applyRightsCommand(mailboxId, partialUpdate.asACLCommand(), mailboxSession)
+      }))
+      .`then`()
+
+    SFlux.merge(Seq(resetOperation, partialUpdatesOperation))
+      .`then`()
+      .`then`(SMono.just[MailboxUpdateResult](MailboxUpdateSuccess(mailboxId)))
+      .subscribeOn(Schedulers.elastic())
+
+  }
+
+  private def isASystemMailbox(mailbox: MessageManager): Boolean = Role.from(mailbox.getMailboxPath.getName).isPresent
+
+}


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


[james-project] 03/11: [REFACTORING] Relocate Vacation models in vacation package

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

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

commit 83320c4c61e48af5f3bb39bfbb298ce153e76b2c
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Nov 2 10:11:53 2020 +0700

    [REFACTORING] Relocate Vacation models in vacation package
---
 .../apache/james/jmap/{mail => vacation}/VacationResponse.scala   | 7 ++++---
 .../james/jmap/{mail => vacation}/VacationResponseGet.scala       | 8 ++++----
 .../org/apache/james/jmap/vacation/VacationResponseSet.scala      | 6 +++---
 3 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponse.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponse.scala
similarity index 94%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponse.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponse.scala
index d25c603..c1c44d5 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponse.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponse.scala
@@ -17,14 +17,15 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.mail
+package org.apache.james.jmap.vacation
 
 import eu.timepit.refined.api.Refined
 import eu.timepit.refined.auto._
 import eu.timepit.refined.collection.NonEmpty
 import org.apache.james.jmap.api.vacation.Vacation
-import org.apache.james.jmap.model.Id.Id
-import org.apache.james.jmap.model.{Properties, UTCDate}
+import org.apache.james.jmap.core.Id.Id
+import org.apache.james.jmap.core.{Properties, UTCDate}
+import org.apache.james.jmap.mail.Subject
 
 import scala.compat.java8.OptionConverters._
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponseGet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponseGet.scala
similarity index 89%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponseGet.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponseGet.scala
index 5509e18..6b9f6e7 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponseGet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponseGet.scala
@@ -17,12 +17,12 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.mail
+package org.apache.james.jmap.vacation
 
-import org.apache.james.jmap.mail.VacationResponse.UnparsedVacationResponseId
+import org.apache.james.jmap.core.State.State
+import org.apache.james.jmap.core.{AccountId, Properties}
 import org.apache.james.jmap.method.WithAccountId
-import org.apache.james.jmap.model.State.State
-import org.apache.james.jmap.model.{AccountId, Properties}
+import org.apache.james.jmap.vacation.VacationResponse.UnparsedVacationResponseId
 
 case class VacationResponseIds(value: List[UnparsedVacationResponseId])
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponseSet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponseSet.scala
index 6a68a53..6cfe717 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponseSet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponseSet.scala
@@ -24,10 +24,10 @@ import java.time.format.DateTimeFormatter
 
 import org.apache.james.jmap.api.vacation.Vacation.ID
 import org.apache.james.jmap.api.vacation.VacationPatch
+import org.apache.james.jmap.core.AccountId
+import org.apache.james.jmap.core.SetError.{SetErrorDescription, SetErrorType, invalidArgumentValue, serverFailValue}
+import org.apache.james.jmap.core.State.State
 import org.apache.james.jmap.method.WithAccountId
-import org.apache.james.jmap.model.AccountId
-import org.apache.james.jmap.model.SetError.{SetErrorDescription, SetErrorType, invalidArgumentValue, serverFailValue}
-import org.apache.james.jmap.model.State.State
 import org.apache.james.util.ValuePatch
 import play.api.libs.json.{JsBoolean, JsNull, JsObject, JsString, JsValue}
 


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


[james-project] 10/11: JAMES-3436 Email/set create should support specific headers

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

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

commit 11ae5320f1ddb9fcfdc7cb1029ddf6758ad21d45
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Oct 30 14:10:01 2020 +0700

    JAMES-3436 Email/set create should support specific headers
    
    This enables for instance to request read receipts
    
    The following restrictions applies:
    
     - There MUST NOT be two properties that represent the same header field
        (e.g., header:from and from) within the Email or particular EmailBodyPart.
     - Header fields MUST NOT be specified in parsed forms that are forbidden for that particular field.
     - Header fields beginning with Content- MUST NOT be specified on the Email object, only on EmailBodyPart objects.
---
 .../rfc8621/contract/EmailSetMethodContract.scala  | 250 +++++++++++++++++++++
 .../james/jmap/json/EmailSetSerializer.scala       | 118 +++++++++-
 .../scala/org/apache/james/jmap/mail/Email.scala   |  13 ++
 .../apache/james/jmap/mail/EmailAddressGroup.scala |  11 +-
 .../org/apache/james/jmap/mail/EmailGet.scala      |  15 +-
 .../org/apache/james/jmap/mail/EmailHeader.scala   |  71 ++++--
 .../org/apache/james/jmap/mail/EmailSet.scala      |  53 +++--
 7 files changed, 492 insertions(+), 39 deletions(-)

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/EmailSetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSetMethodContract.scala
index 4fa93d0..f800f45 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSetMethodContract.scala
@@ -128,6 +128,256 @@ trait EmailSetMethodContract {
       """.stripMargin, messageId.serialize))
   }
 
+  @ParameterizedTest
+  @ValueSource(strings = Array(
+    """"header:aheader": " a value"""",
+    """"header:aheader:asRaw": " a value"""",
+    """"header:aheader:asText": "a value"""",
+    """"header:aheader:asDate": "2020-10-29T06:39:04Z"""",
+    """"header:aheader:asAddresses": [{"email": "rcpt1@apache.org"}, {"email": "rcpt2@apache.org"}]""",
+    """"header:aheader:asURLs": ["url1", "url2"]""",
+    """"header:aheader:asMessageIds": ["id1@domain.tld", "id2@domain.tld"]""",
+    """"header:aheader:asGroupedAddresses": [{"name": null,"addresses": [{"name": "user1","email": "user1@domain.tld" },{"name": "user2", "email": "user2@domain.tld"}]},{"name": "Friends","addresses": [{"email": "user3@domain.tld"},{"name": "user4","email": "user4@domain.tld"}]}]"""
+  ))
+  def createShouldPositionSpecificHeaders(specificHeader: String, server: GuiceJamesServer): Unit = {
+    val bobPath = MailboxPath.inbox(BOB)
+    val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+    val propertyEnd = specificHeader.substring(1).indexOf('"')
+    val property = specificHeader.substring(1, propertyEnd + 1)
+
+    val request =
+      s"""{
+         |  "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [
+         |    ["Email/set", {
+         |      "accountId": "$ACCOUNT_ID",
+         |      "create": {
+         |        "aaaaaa":{
+         |          "mailboxIds": {
+         |             "${mailboxId.serialize}": true
+         |          },
+         |          $specificHeader
+         |        }
+         |      }
+         |    }, "c1"],
+         |    ["Email/get",
+         |     {
+         |       "accountId": "$ACCOUNT_ID",
+         |       "ids": ["#aaaaaa"],
+         |       "properties": ["mailboxIds", "$property"]
+         |     },
+         |     "c2"]]
+         |}""".stripMargin
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(request)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response)
+      .whenIgnoringPaths("methodResponses[0][1].created.aaaaaa.id")
+      .inPath("methodResponses[0][1].created.aaaaaa")
+      .isEqualTo("{}".stripMargin)
+
+    assertThatJson(response)
+      .whenIgnoringPaths("methodResponses[1][1].list[0].id")
+      .inPath(s"methodResponses[1][1].list")
+      .isEqualTo(s"""[{
+                    |  "mailboxIds": {
+                    |    "${mailboxId.serialize}": true
+                    |  },
+                    |  $specificHeader
+                    |}]""".stripMargin)
+  }
+
+  @Test
+  def specificHeaderShouldMatchSupportedType(server: GuiceJamesServer): Unit = {
+    val bobPath = MailboxPath.inbox(BOB)
+    val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+
+    val request =
+      s"""{
+         |  "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [
+         |    ["Email/set", {
+         |      "accountId": "$ACCOUNT_ID",
+         |      "create": {
+         |        "aaaaaa":{
+         |          "mailboxIds": {
+         |             "${mailboxId.serialize}": true
+         |          },
+         |          "header:To:asMessageId": ["mid@domain.tld"]
+         |        }
+         |      }
+         |    }, "c1"]]
+         |}""".stripMargin
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(request)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response)
+      .inPath("methodResponses[0][1].notCreated")
+      .isEqualTo(
+        s"""{
+           |  "aaaaaa": {
+           |    "type": "invalidArguments",
+           |    "description": "List((,List(JsonValidationError(List(header:To:asMessageId is an invalid specific header),ArraySeq()))))"
+           |  }
+           |}""".stripMargin)
+  }
+
+  @Test
+  def specificHeadersCannotOverrideConvenienceHeader(server: GuiceJamesServer): Unit = {
+    val bobPath = MailboxPath.inbox(BOB)
+    val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+
+    val request =
+      s"""{
+         |  "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [
+         |    ["Email/set", {
+         |      "accountId": "$ACCOUNT_ID",
+         |      "create": {
+         |        "aaaaaa":{
+         |          "mailboxIds": {
+         |             "${mailboxId.serialize}": true
+         |          },
+         |          "header:To:asAddresses": [{"email": "rcpt1@apache.org"}, {"email": "rcpt2@apache.org"}],
+         |          "to": [{"email": "rcpt1@apache.org"}, {"email": "rcpt2@apache.org"}]
+         |        }
+         |      }
+         |    }, "c1"]]
+         |}""".stripMargin
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(request)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response)
+      .inPath("methodResponses[0][1].notCreated")
+      .isEqualTo(
+        s"""{
+           |  "aaaaaa": {
+           |    "type": "invalidArguments",
+           |    "description": "To was already defined by convenience headers"
+           |  }
+           |}""".stripMargin)
+  }
+
+  @Test
+  def createShouldFailWhenBadJsonPayloadForSpecificHeader(server: GuiceJamesServer): Unit = {
+    val bobPath = MailboxPath.inbox(BOB)
+    val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+
+    val request =
+      s"""{
+         |  "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [
+         |    ["Email/set", {
+         |      "accountId": "$ACCOUNT_ID",
+         |      "create": {
+         |        "aaaaaa":{
+         |          "mailboxIds": {
+         |             "${mailboxId.serialize}": true
+         |          },
+         |          "header:To:asAddresses": "invalid"
+         |        }
+         |      }
+         |    }, "c1"]]
+         |}""".stripMargin
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(request)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response)
+      .inPath("methodResponses[0][1].notCreated")
+      .isEqualTo(
+        s"""{
+           |  "aaaaaa": {
+           |    "type": "invalidArguments",
+           |    "description": "List((,List(JsonValidationError(List(List((,List(JsonValidationError(List(error.expected.jsarray),ArraySeq()))))),ArraySeq()))))"
+           |  }
+           |}""".stripMargin)
+  }
+
+  @Test
+  def specificContentHeadersShouldBeRejected(server: GuiceJamesServer): Unit = {
+    val bobPath = MailboxPath.inbox(BOB)
+    val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+
+    val request =
+      s"""{
+         |  "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [
+         |    ["Email/set", {
+         |      "accountId": "$ACCOUNT_ID",
+         |      "create": {
+         |        "aaaaaa":{
+         |          "mailboxIds": {
+         |             "${mailboxId.serialize}": true
+         |          },
+         |          "header:Content-Type:asText": "text/plain"
+         |        }
+         |      }
+         |    }, "c1"]]
+         |}""".stripMargin
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(request)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response)
+      .inPath("methodResponses[0][1].notCreated")
+      .isEqualTo(
+        s"""{
+           |  "aaaaaa": {
+           |    "type": "invalidArguments",
+           |    "description": "Header fields beginning with `Content-` MUST NOT be specified on the Email object, only on EmailBodyPart objects."
+           |  }
+           |}""".stripMargin)
+  }
+
   @Test
   def shouldNotResetKeywordWhenFalseValue(server: GuiceJamesServer): Unit = {
     val message: Message = Fixture.createTestMessage
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSetSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSetSerializer.scala
index 3113b86..3d455b5 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSetSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSetSerializer.scala
@@ -20,13 +20,15 @@
 package org.apache.james.jmap.json
 
 import cats.implicits._
+import eu.timepit.refined.collection.NonEmpty
 import eu.timepit.refined.refineV
+import eu.timepit.refined.types.string.NonEmptyString
 import javax.inject.Inject
 import org.apache.james.jmap.core.Id.IdConstraint
-import org.apache.james.jmap.core.{Id, SetError}
+import org.apache.james.jmap.core.{Id, SetError, UTCDate}
 import org.apache.james.jmap.mail.EmailSet.{EmailCreationId, UnparsedMessageId, UnparsedMessageIdConstraint}
 import org.apache.james.jmap.mail.KeywordsFactory.STRICT_KEYWORDS_FACTORY
-import org.apache.james.jmap.mail.{AddressesHeaderValue, ClientEmailBodyValue, ClientHtmlBody, ClientPartId, DestroyIds, EmailAddress, EmailCreationRequest, EmailCreationResponse, EmailSetRequest, EmailSetResponse, EmailSetUpdate, EmailerName, HeaderMessageId, IsEncodingProblem, IsTruncated, Keyword, Keywords, MailboxIds, MessageIdsHeaderValue, Subject, Type}
+import org.apache.james.jmap.mail.{AddressesHeaderValue, AsAddresses, AsDate, AsGroupedAddresses, AsMessageIds, AsRaw, AsText, AsURLs, ClientEmailBodyValue, ClientHtmlBody, ClientPartId, DateHeaderValue, DestroyIds, EmailAddress, EmailAddressGroup, EmailCreationRequest, EmailCreationResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailSetRequest, EmailSetResponse, EmailSetUpdate, EmailerName, GroupName, GroupedAddressesHeaderValue, HeaderMessageId, HeaderURL, IsEncodingProblem, [...]
 import org.apache.james.mailbox.model.{MailboxId, MessageId}
 import play.api.libs.json.{JsArray, JsBoolean, JsError, JsNull, JsObject, JsResult, JsString, JsSuccess, JsValue, Json, OWrites, Reads, Writes}
 
@@ -260,7 +262,117 @@ class EmailSetSerializer @Inject()(messageIdFactory: MessageId.Factory, mailboxI
     readMapEntry[ClientPartId, ClientEmailBodyValue](s => Id.validate(s).fold(e => Left(e.getMessage), partId => Right(ClientPartId(partId))),
       clientEmailBodyValueReads)
 
-  private implicit val emailCreationRequestReads: Reads[EmailCreationRequest] = Json.reads[EmailCreationRequest]
+  case class EmailCreationRequestWithoutHeaders(mailboxIds: MailboxIds,
+                                  messageId: Option[MessageIdsHeaderValue],
+                                  references: Option[MessageIdsHeaderValue],
+                                  inReplyTo: Option[MessageIdsHeaderValue],
+                                  from: Option[AddressesHeaderValue],
+                                  to: Option[AddressesHeaderValue],
+                                  cc: Option[AddressesHeaderValue],
+                                  bcc: Option[AddressesHeaderValue],
+                                  sender: Option[AddressesHeaderValue],
+                                  replyTo: Option[AddressesHeaderValue],
+                                  subject: Option[Subject],
+                                  sentAt: Option[UTCDate],
+                                  keywords: Option[Keywords],
+                                  receivedAt: Option[UTCDate],
+                                  htmlBody: Option[List[ClientHtmlBody]],
+                                  bodyValues: Option[Map[ClientPartId, ClientEmailBodyValue]]) {
+    def toCreationRequest(specificHeaders: List[EmailHeader]): EmailCreationRequest = EmailCreationRequest(
+      mailboxIds = mailboxIds,
+      messageId = messageId,
+      references = references,
+      inReplyTo = inReplyTo,
+      from = from,
+      to = to,
+      cc = cc,
+      bcc = bcc,
+      sender = sender,
+      replyTo = replyTo,
+      subject = subject,
+      sentAt = sentAt,
+      keywords = keywords,
+      receivedAt = receivedAt,
+      specificHeaders = specificHeaders,
+      bodyValues = bodyValues,
+      htmlBody = htmlBody)
+  }
+
+  private implicit val headerUrlReads: Reads[HeaderURL] = Json.valueReads[HeaderURL]
+  private implicit val groupNameReads: Reads[GroupName] = Json.valueReads[GroupName]
+  private implicit val groupReads: Reads[EmailAddressGroup] = Json.reads[EmailAddressGroup]
+
+  private implicit val dateReads: Reads[DateHeaderValue] = {
+    case JsNull => JsSuccess(DateHeaderValue(None))
+    case json: JsValue => UTCDateReads.reads(json).map(date => DateHeaderValue(Some(date)))
+  }
+
+  sealed trait HeaderValueReads extends Reads[EmailHeaderValue]
+  case object RawReads extends HeaderValueReads {
+    val rawReads: Reads[RawHeaderValue] = Json.valueReads[RawHeaderValue]
+    override def reads(json: JsValue): JsResult[EmailHeaderValue] = rawReads.reads(json)
+  }
+  case object TextReads extends HeaderValueReads {
+    val textReads: Reads[TextHeaderValue] = Json.valueReads[TextHeaderValue]
+    override def reads(json: JsValue): JsResult[TextHeaderValue] = textReads.reads(json)
+  }
+  case object AddressesReads extends HeaderValueReads {
+    override def reads(json: JsValue): JsResult[AddressesHeaderValue] = addressesHeaderValueReads.reads(json)
+  }
+  case object DateReads extends HeaderValueReads {
+    override def reads(json: JsValue): JsResult[DateHeaderValue] = dateReads.reads(json)
+  }
+  case object MessageIdReads extends HeaderValueReads {
+    override def reads(json: JsValue): JsResult[MessageIdsHeaderValue] = messageIdsHeaderValueReads.reads(json)
+  }
+  case object URLReads extends HeaderValueReads {
+    val urlsReads: Reads[URLsHeaderValue] = {
+      case JsNull => JsSuccess(URLsHeaderValue(None))
+      case JsArray(value) => value.map(headerUrlReads.reads).map(_.asEither).toList.sequence
+        .fold(e => JsError(e), urls => JsSuccess(URLsHeaderValue(Some(urls))))
+      case _ => JsError("Expecting a JsArray")
+    }
+    override def reads(json: JsValue): JsResult[URLsHeaderValue] = urlsReads.reads(json)
+  }
+  case object GroupedAddressReads extends HeaderValueReads {
+    val groupsReads: Reads[GroupedAddressesHeaderValue] = Json.valueReads[GroupedAddressesHeaderValue]
+    override def reads(json: JsValue): JsResult[GroupedAddressesHeaderValue] = groupsReads.reads(json)
+  }
+
+  def asReads(parseOption: ParseOption): Reads[EmailHeaderValue] = parseOption match {
+      case AsRaw => RawReads
+      case AsText => TextReads
+      case AsAddresses => AddressesReads
+      case AsDate => DateReads
+      case AsMessageIds => MessageIdReads
+      case AsURLs => URLReads
+      case AsGroupedAddresses => GroupedAddressReads
+    }
+
+  private implicit val emailCreationRequestWithoutHeadersReads: Reads[EmailCreationRequestWithoutHeaders] = Json.reads[EmailCreationRequestWithoutHeaders]
+  private implicit val emailCreationRequestReads: Reads[EmailCreationRequest] = {
+    case o: JsObject =>
+      val withoutHeader = emailCreationRequestWithoutHeadersReads.reads(o)
+
+      val specificHeadersEither: Either[IllegalArgumentException, List[EmailHeader]] = o.value.toList
+        .filter {
+          case (name, _) => name.startsWith("header:")
+        }.map {
+          case (name, value) =>
+            val refinedName: Either[String, NonEmptyString] = refineV[NonEmpty](name)
+            refinedName.left.map(e => new IllegalArgumentException(e))
+              .flatMap(property => SpecificHeaderRequest.from(property)
+                .left.map(_ => new IllegalArgumentException(s"$name is an invalid specific header")))
+              .flatMap(_.validate)
+              .flatMap(specificHeaderRequest => asReads(specificHeaderRequest.parseOption.getOrElse(AsRaw))
+                .reads(value).asEither.left.map(e => new IllegalArgumentException(e.toString()))
+                .map(headerValue => EmailHeader(EmailHeaderName(specificHeaderRequest.headerName), headerValue)))
+        }.sequence
+
+      specificHeadersEither.fold(e => JsError(e.getMessage),
+        specificHeaders => withoutHeader.map(_.toCreationRequest(specificHeaders)))
+    case _ => JsError("Expecting a JsObject to represent a creation request")
+  }
 
   def deserialize(input: JsValue): JsResult[EmailSetRequest] = Json.fromJson[EmailSetRequest](input)
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
index bf91c0f..e978537 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
@@ -37,6 +37,7 @@ import org.apache.james.jmap.api.projections.{MessageFastViewPrecomputedProperti
 import org.apache.james.jmap.core.{Properties, UTCDate}
 import org.apache.james.jmap.mail.BracketHeader.sanitize
 import org.apache.james.jmap.mail.Email.{Size, sanitizeSize}
+import org.apache.james.jmap.mail.EmailHeaderName.{ADDRESSES_NAMES, DATE, MESSAGE_ID_NAMES}
 import org.apache.james.jmap.mail.KeywordsFactory.LENIENT_KEYWORDS_FACTORY
 import org.apache.james.jmap.method.ZoneIdProvider
 import org.apache.james.mailbox.model.FetchGroup.{FULL_CONTENT, HEADERS, MINIMAL}
@@ -184,6 +185,8 @@ object ParseOptions {
 
 sealed trait ParseOption {
   def extractHeaderValue(field: Field): EmailHeaderValue
+
+  def forbiddenHeaderNames: Set[EmailHeaderName] = Set()
 }
 case object AsRaw extends ParseOption {
   override def extractHeaderValue(field: Field): EmailHeaderValue = RawHeaderValue.from(field)
@@ -193,20 +196,30 @@ case object AsText extends ParseOption {
 }
 case object AsAddresses extends ParseOption {
   override def extractHeaderValue(field: Field): EmailHeaderValue = AddressesHeaderValue.from(field)
+
+  override def forbiddenHeaderNames: Set[EmailHeaderName] = MESSAGE_ID_NAMES + DATE
 }
 case object AsGroupedAddresses extends ParseOption {
   override def extractHeaderValue(field: Field): EmailHeaderValue = GroupedAddressesHeaderValue.from(field)
+
+  override def forbiddenHeaderNames: Set[EmailHeaderName] = MESSAGE_ID_NAMES + DATE
 }
 case object AsMessageIds extends ParseOption {
   override def extractHeaderValue(field: Field): EmailHeaderValue = MessageIdsHeaderValue.from(field)
+
+  override def forbiddenHeaderNames: Set[EmailHeaderName] = ADDRESSES_NAMES + DATE
 }
 case object AsDate extends ParseOption {
   override def extractHeaderValue(field: Field): EmailHeaderValue = DateHeaderValue.from(field, ZoneId.systemDefault())
 
   def extractHeaderValue(field: Field, zoneId: ZoneId): EmailHeaderValue = DateHeaderValue.from(field, zoneId)
+
+  override def forbiddenHeaderNames: Set[EmailHeaderName] = ADDRESSES_NAMES ++ MESSAGE_ID_NAMES
 }
 case object AsURLs extends ParseOption {
   override def extractHeaderValue(field: Field): EmailHeaderValue = URLsHeaderValue.from(field)
+
+  override def forbiddenHeaderNames: Set[EmailHeaderName] = ADDRESSES_NAMES ++ MESSAGE_ID_NAMES + DATE
 }
 
 case class HeaderMessageId(value: String) extends AnyVal
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailAddressGroup.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailAddressGroup.scala
index b423996..e363bd6 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailAddressGroup.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailAddressGroup.scala
@@ -19,6 +19,15 @@
 
 package org.apache.james.jmap.mail
 
+import org.apache.james.mime4j.dom.address.{Address, Group, MailboxList}
+
+import scala.jdk.CollectionConverters._
+
 case class GroupName(value: String) extends AnyVal
 
-case class EmailAddressGroup(name: Option[GroupName], addresses: List[EmailAddress])
+case class EmailAddressGroup(name: Option[GroupName], addresses: List[EmailAddress]) {
+  val asAddress: List[Address] = name.map(aName => new Group(aName.value,
+    new MailboxList(addresses.map(_.asMime4JMailbox).asJava)))
+    .map(List(_))
+    .getOrElse(addresses.map(_.asMime4JMailbox))
+}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
index fce39f5..ae59d9e 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
@@ -86,13 +86,13 @@ case class EmailGetResponse(accountId: AccountId,
                             list: List[EmailView],
                             notFound: EmailNotFound)
 
-case class SpecificHeaderRequest(headerName: NonEmptyString, property: String, parseOption: Option[ParseOption]) {
+case class SpecificHeaderRequest(property: NonEmptyString, headerName: String, parseOption: Option[ParseOption]) {
   def retrieveHeader(zoneId: ZoneId, message: Message): (String, Option[EmailHeaderValue]) = {
-    val field: Option[Field] = Option(message.getHeader.getFields(property))
+    val field: Option[Field] = Option(message.getHeader.getFields(headerName))
       .map(_.asScala)
       .flatMap(fields => fields.reverse.headOption)
 
-    (headerName, field.map({
+    (property.value, field.map({
       val option = parseOption.getOrElse(AsRaw)
         option match {
           case AsDate => AsDate.extractHeaderValue(_, zoneId)
@@ -100,4 +100,13 @@ case class SpecificHeaderRequest(headerName: NonEmptyString, property: String, p
         }
     }))
   }
+
+  def validate: Either[IllegalArgumentException, SpecificHeaderRequest] = {
+    val forbiddenNames = parseOption.map(_.forbiddenHeaderNames).getOrElse(Set())
+    if (forbiddenNames.contains(EmailHeaderName(headerName))) {
+      Left(new IllegalArgumentException(s"$property is forbidden with $parseOption"))
+    } else {
+      scala.Right(this)
+    }
+  }
 }
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
index 7330fc7..a1d3ade 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
@@ -21,13 +21,14 @@ package org.apache.james.jmap.mail
 
 import java.nio.charset.StandardCharsets.US_ASCII
 import java.time.ZoneId
+import java.util.Date
 
 import org.apache.commons.lang3.StringUtils
 import org.apache.james.jmap.core.UTCDate
 import org.apache.james.mime4j.codec.{DecodeMonitor, DecoderUtil}
 import org.apache.james.mime4j.dom.address.{AddressList, Group, Address => Mime4jAddress, Mailbox => Mime4jMailbox}
-import org.apache.james.mime4j.field.{AddressListFieldImpl, ContentLocationFieldImpl, DateTimeFieldImpl}
-import org.apache.james.mime4j.stream.Field
+import org.apache.james.mime4j.field.{AddressListFieldImpl, ContentLocationFieldImpl, DateTimeFieldImpl, Fields}
+import org.apache.james.mime4j.stream.{Field, RawField}
 import org.apache.james.mime4j.util.MimeUtil
 
 import scala.jdk.CollectionConverters._
@@ -36,19 +37,19 @@ object EmailHeader {
   def apply(field: Field): EmailHeader = EmailHeader(EmailHeaderName(field.getName), RawHeaderValue.from(field))
 }
 
-object RawHeaderValue extends EmailHeaderValue {
+object RawHeaderValue {
   def from(field: Field): RawHeaderValue = RawHeaderValue(new String(field.getRaw.toByteArray, US_ASCII).substring(field.getName.length + 1))
 }
 
-object TextHeaderValue extends EmailHeaderValue {
+object TextHeaderValue {
   def from(field: Field): TextHeaderValue = TextHeaderValue(MimeUtil.unfold(DecoderUtil.decodeEncodedWords(field.getBody, DecodeMonitor.SILENT)).stripLeading())
 }
 
-object AddressesHeaderValue extends EmailHeaderValue {
+object AddressesHeaderValue {
   def from(field: Field): AddressesHeaderValue = AddressesHeaderValue(EmailAddress.from(AddressListFieldImpl.PARSER.parse(field, DecodeMonitor.SILENT).getAddressList))
 }
 
-object GroupedAddressesHeaderValue extends EmailHeaderValue {
+object GroupedAddressesHeaderValue {
   def from(field: Field): GroupedAddressesHeaderValue = {
     val addresses: List[Mime4jAddress] =
       Option(AddressListFieldImpl.PARSER.parse(field, DecodeMonitor.SILENT).getAddressList)
@@ -95,14 +96,14 @@ object MessageIdsHeaderValue {
   }
 }
 
-object DateHeaderValue extends EmailHeaderValue {
+object DateHeaderValue {
   def from(field: Field, zoneId: ZoneId): DateHeaderValue =
     Option(DateTimeFieldImpl.PARSER.parse(field, DecodeMonitor.SILENT).getDate)
       .map(date => DateHeaderValue(Some(UTCDate.from(date, zoneId))))
       .getOrElse(DateHeaderValue(None))
 }
 
-object URLsHeaderValue extends EmailHeaderValue {
+object URLsHeaderValue {
   def from(field: Field): URLsHeaderValue = {
     val url: Option[List[HeaderURL]] = Option(ContentLocationFieldImpl.PARSER.parse(field, DecodeMonitor.SILENT).getLocation)
       .map(urls => urls.split(',')
@@ -119,22 +120,62 @@ object URLsHeaderValue extends EmailHeaderValue {
   }
 }
 
+object EmailHeaderName {
+  val DATE: EmailHeaderName = EmailHeaderName("Date")
+  val TO: EmailHeaderName = EmailHeaderName("To")
+  val FROM: EmailHeaderName = EmailHeaderName("From")
+  val CC: EmailHeaderName = EmailHeaderName("Cc")
+  val BCC: EmailHeaderName = EmailHeaderName("Bcc")
+  val SENDER: EmailHeaderName = EmailHeaderName("Sender")
+  val REPLY_TO: EmailHeaderName = EmailHeaderName("Reply-To")
+  val REFERENCES: EmailHeaderName = EmailHeaderName("References")
+  val MESSAGE_ID: EmailHeaderName = EmailHeaderName("Message-Id")
+  val IN_REPLY_TO: EmailHeaderName = EmailHeaderName("In-Reply-To")
+
+  val MESSAGE_ID_NAMES = Set(REFERENCES, MESSAGE_ID, IN_REPLY_TO)
+  val ADDRESSES_NAMES = Set(SENDER, FROM, TO, CC, BCC, REPLY_TO)
+}
+
 case class EmailHeaderName(value: String) extends AnyVal
 
-sealed trait EmailHeaderValue
-case class RawHeaderValue(value: String) extends EmailHeaderValue
-case class TextHeaderValue(value: String) extends EmailHeaderValue
+sealed trait EmailHeaderValue {
+  def asField(name: EmailHeaderName): Field
+}
+case class RawHeaderValue(value: String) extends EmailHeaderValue {
+  override def asField(name: EmailHeaderName): Field = new RawField(name.value, value.substring(1))
+}
+case class TextHeaderValue(value: String) extends EmailHeaderValue {
+  override def asField(name: EmailHeaderName): Field = new RawField(name.value, value)
+}
 case class AddressesHeaderValue(value: List[EmailAddress]) extends EmailHeaderValue {
   def asMime4JMailboxList: Option[List[Mime4jMailbox]] = Some(value.map(_.asMime4JMailbox)).filter(_.nonEmpty)
+
+  override def asField(name: EmailHeaderName): Field = Fields.addressList(name.value, asMime4JMailboxList.getOrElse(List()).asJava)
+}
+case class GroupedAddressesHeaderValue(value: List[EmailAddressGroup]) extends EmailHeaderValue {
+  override def asField(name: EmailHeaderName): Field = Fields.addressList(name.value, value.flatMap(_.asAddress).asJava)
 }
-case class GroupedAddressesHeaderValue(value: List[EmailAddressGroup]) extends EmailHeaderValue
 case class MessageIdsHeaderValue(value: Option[List[HeaderMessageId]]) extends EmailHeaderValue {
   def asString: Option[String] = value.map(messageIds => messageIds
     .map(_.value)
     .map(messageId => s"<${messageId}>")
     .mkString(" "))
+
+  override def asField(name: EmailHeaderName): Field = new RawField(name.value, asString.getOrElse(""))
+}
+case class DateHeaderValue(value: Option[UTCDate]) extends EmailHeaderValue {
+  override def asField(name: EmailHeaderName): Field = Fields.date(name.value,
+    value.map(_.asUTC.toInstant)
+      .map(Date.from)
+      .orNull)
+}
+case class URLsHeaderValue(value: Option[List[HeaderURL]]) extends EmailHeaderValue {
+  override def asField(name: EmailHeaderName): Field = new RawField(name.value,
+    value
+      .map(list => list.map("<" + _.value + ">").mkString(", "))
+      .getOrElse(""))
 }
-case class DateHeaderValue(value: Option[UTCDate]) extends EmailHeaderValue
-case class URLsHeaderValue(value: Option[List[HeaderURL]]) extends EmailHeaderValue
 
-case class EmailHeader(name: EmailHeaderName, value: EmailHeaderValue)
+case class EmailHeader(name: EmailHeaderName, value: EmailHeaderValue) {
+  def asField: Field = value.asField(name)
+}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala
index 2176302..dacc307 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala
@@ -21,6 +21,7 @@ package org.apache.james.jmap.mail
 import java.nio.charset.StandardCharsets
 import java.util.Date
 
+import cats.implicits._
 import eu.timepit.refined
 import eu.timepit.refined.api.Refined
 import eu.timepit.refined.collection.NonEmpty
@@ -76,24 +77,30 @@ case class EmailCreationRequest(mailboxIds: MailboxIds,
                                 keywords: Option[Keywords],
                                 receivedAt: Option[UTCDate],
                                 htmlBody: Option[List[ClientHtmlBody]],
-                                bodyValues: Option[Map[ClientPartId, ClientEmailBodyValue]]) {
+                                bodyValues: Option[Map[ClientPartId, ClientEmailBodyValue]],
+                                specificHeaders: List[EmailHeader]) {
   def toMime4JMessage: Either[IllegalArgumentException, Message] =
-    validateHtmlBody.map(maybeHtmlBody => {
-      val builder = Message.Builder.of
-      val htmlSubType = "html"
-      references.flatMap(_.asString).map(new RawField("References", _)).foreach(builder.setField)
-      inReplyTo.flatMap(_.asString).map(new RawField("In-Reply-To", _)).foreach(builder.setField)
-      messageId.flatMap(_.asString).map(new RawField(FieldName.MESSAGE_ID, _)).foreach(builder.setField)
-      subject.foreach(value => builder.setSubject(value.value))
-      from.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setFrom)
-      to.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setTo)
-      cc.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setCc)
-      bcc.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setBcc)
-      sender.flatMap(_.asMime4JMailboxList).map(_.asJava).map(Fields.addressList(FieldName.SENDER, _)).foreach(builder.setField)
-      replyTo.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setReplyTo)
-      sentAt.map(_.asUTC).map(_.toInstant).map(Date.from).foreach(builder.setDate)
-      builder.setBody(maybeHtmlBody.getOrElse(""), htmlSubType, StandardCharsets.UTF_8)
-      builder.build()
+    validateHtmlBody
+      .flatMap(maybeHtmlBody => {
+        val builder = Message.Builder.of
+        val htmlSubType = "html"
+        references.flatMap(_.asString).map(new RawField("References", _)).foreach(builder.setField)
+        inReplyTo.flatMap(_.asString).map(new RawField("In-Reply-To", _)).foreach(builder.setField)
+        messageId.flatMap(_.asString).map(new RawField(FieldName.MESSAGE_ID, _)).foreach(builder.setField)
+        subject.foreach(value => builder.setSubject(value.value))
+        from.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setFrom)
+        to.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setTo)
+        cc.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setCc)
+        bcc.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setBcc)
+        sender.flatMap(_.asMime4JMailboxList).map(_.asJava).map(Fields.addressList(FieldName.SENDER, _)).foreach(builder.setField)
+        replyTo.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setReplyTo)
+        sentAt.map(_.asUTC).map(_.toInstant).map(Date.from).foreach(builder.setDate)
+        validateSpecificHeaders(builder)
+          .map(_ => {
+            specificHeaders.map(_.asField).foreach(builder.addField)
+            builder.setBody(maybeHtmlBody.getOrElse(""), htmlSubType, StandardCharsets.UTF_8)
+            builder.build()
+          })
     })
 
   def validateHtmlBody: Either[IllegalArgumentException, Option[String]] = htmlBody match {
@@ -109,6 +116,18 @@ case class EmailCreationRequest(mailboxIds: MailboxIds,
       .getOrElse(Left(new IllegalArgumentException("Expecting bodyValues to contain the part specified in htmlBody")))
     case _ => Left(new IllegalArgumentException("Expecting htmlBody to contains only 1 part"))
   }
+
+  private def validateSpecificHeaders(message: Message.Builder): Either[IllegalArgumentException, Unit] = {
+    specificHeaders.map(header => {
+      if (Option(message.getField(header.name.value)).isDefined) {
+        Left(new IllegalArgumentException(s"${header.name.value} was already defined by convenience headers"))
+      } else if (header.name.value.startsWith("Content-")) {
+        Left(new IllegalArgumentException(s"Header fields beginning with `Content-` MUST NOT be specified on the Email object, only on EmailBodyPart objects."))
+      } else {
+        scala.Right(())
+      }
+    }).sequence.map(_ => ())
+  }
 }
 
 case class DestroyIds(value: Seq[UnparsedMessageId])


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


[james-project] 02/11: [REFACTORING] Relocate SessionRoutes in toutes package

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

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

commit 7622c3f6f9b1cbcf0a07125e3f4d2cd02d36868e
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Nov 2 10:07:08 2020 +0700

    [REFACTORING] Relocate SessionRoutes in toutes package
---
 .../java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java     | 2 +-
 .../src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala | 2 +-
 .../main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala   | 2 +-
 .../src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala | 2 +-
 .../main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala   | 2 +-
 .../main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala | 2 +-
 .../main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala   | 3 +--
 .../src/main/scala/org/apache/james/jmap/method/Method.scala         | 4 +---
 .../org/apache/james/jmap/method/VacationResponseGetMethod.scala     | 2 +-
 .../org/apache/james/jmap/method/VacationResponseSetMethod.scala     | 2 +-
 .../src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala  | 2 +-
 .../scala/org/apache/james/jmap/{http => routes}/SessionRoutes.scala | 4 ++--
 .../org/apache/james/jmap/{http => routes}/SessionSupplier.scala     | 2 +-
 .../test/scala/org/apache/james/jmap/routes/JMAPApiRoutesTest.scala  | 2 +-
 .../org/apache/james/jmap/{http => routes}/SessionRoutesTest.scala   | 5 +++--
 .../org/apache/james/jmap/{http => routes}/SessionSupplierTest.scala | 4 ++--
 16 files changed, 20 insertions(+), 22 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 d8c2771..f2c599b 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
@@ -30,7 +30,6 @@ import org.apache.james.jmap.JMAPRoutesHandler;
 import org.apache.james.jmap.Version;
 import org.apache.james.jmap.http.Authenticator;
 import org.apache.james.jmap.http.BasicAuthenticationStrategy;
-import org.apache.james.jmap.http.SessionRoutes;
 import org.apache.james.jmap.http.rfc8621.InjectionKeys;
 import org.apache.james.jmap.jwt.JWTAuthenticationStrategy;
 import org.apache.james.jmap.method.CoreEchoMethod;
@@ -48,6 +47,7 @@ import org.apache.james.jmap.method.ZoneIdProvider;
 import org.apache.james.jmap.model.JmapRfc8621Configuration;
 import org.apache.james.jmap.routes.DownloadRoutes;
 import org.apache.james.jmap.routes.JMAPApiRoutes;
+import org.apache.james.jmap.routes.SessionRoutes;
 import org.apache.james.jmap.routes.UploadRoutes;
 import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.utils.PropertiesProvider;
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
index 5201c5a..734a6ba 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
@@ -23,7 +23,6 @@ import java.time.ZoneId
 import eu.timepit.refined.auto._
 import eu.timepit.refined.types.string.NonEmptyString
 import javax.inject.Inject
-import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{EmailGetSerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.Email.UnparsedEmailId
 import org.apache.james.jmap.mail.{Email, EmailBodyPart, EmailGetRequest, EmailGetResponse, EmailIds, EmailNotFound, EmailView, EmailViewReaderFactory, SpecificHeaderRequest}
@@ -31,6 +30,7 @@ import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, J
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model.State.INSTANCE
 import org.apache.james.jmap.model.{AccountId, ErrorCode, Invocation, Properties}
+import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.mailbox.model.MessageId
 import org.apache.james.metrics.api.MetricFactory
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
index 06fa529..da20d5f 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
@@ -21,7 +21,6 @@ package org.apache.james.jmap.method
 import cats.implicits._
 import eu.timepit.refined.auto._
 import javax.inject.Inject
-import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{EmailQuerySerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.{Comparator, EmailQueryRequest, EmailQueryResponse, UnsupportedRequestParameterException}
 import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
@@ -29,6 +28,7 @@ import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model.Limit.Limit
 import org.apache.james.jmap.model.Position.Position
 import org.apache.james.jmap.model.{CanCalculateChanges, Invocation, Limit, Position, QueryState}
+import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.jmap.utils.search.MailboxFilter
 import org.apache.james.jmap.utils.search.MailboxFilter.QueryFilter
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
index bdf9722..c1f87fc 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
@@ -26,7 +26,6 @@ import com.google.common.collect.ImmutableList
 import eu.timepit.refined.auto._
 import javax.inject.Inject
 import javax.mail.Flags
-import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{EmailSetSerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.EmailSet.{EmailCreationId, UnparsedMessageId}
 import org.apache.james.jmap.mail.{DestroyIds, EmailCreationRequest, EmailCreationResponse, EmailSet, EmailSetRequest, EmailSetResponse, MailboxIds, ValidatedEmailSetUpdate}
@@ -35,6 +34,7 @@ import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model.KeywordsFactory.LENIENT_KEYWORDS_FACTORY
 import org.apache.james.jmap.model.SetError.SetErrorDescription
 import org.apache.james.jmap.model.{ClientId, Id, Invocation, ServerId, SetError, State, UTCDate}
+import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MessageManager.{AppendCommand, FlagsUpdateMode}
 import org.apache.james.mailbox.exception.MailboxNotFoundException
 import org.apache.james.mailbox.model.{ComposedMessageIdWithMetaData, DeleteResult, MailboxId, MessageId, MessageRange}
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 0b41593..ba5baa2 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
@@ -21,7 +21,6 @@ package org.apache.james.jmap.method
 
 import eu.timepit.refined.auto._
 import javax.inject.Inject
-import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
 import org.apache.james.jmap.mail._
@@ -29,6 +28,7 @@ import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, J
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model.State.INSTANCE
 import org.apache.james.jmap.model.{AccountId, CapabilityIdentifier, ErrorCode, Invocation, MailboxFactory, Properties, Subscriptions}
+import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.jmap.utils.quotas.{QuotaLoader, QuotaLoaderWithPreloadedDefaultFactory}
 import org.apache.james.mailbox.exception.MailboxNotFoundException
 import org.apache.james.mailbox.model.search.MailboxQuery
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
index d390d0f..2c3d5a8 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
@@ -20,12 +20,12 @@ package org.apache.james.jmap.method
 
 import eu.timepit.refined.auto._
 import javax.inject.Inject
-import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{MailboxQuerySerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.{MailboxQueryRequest, MailboxQueryResponse}
 import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model._
+import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.{MailboxSession, SystemMailboxesProvider}
 import org.apache.james.metrics.api.MetricFactory
 import play.api.libs.json.{JsError, JsSuccess}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
index ff59c84..d5ab55d 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
@@ -21,7 +21,6 @@ package org.apache.james.jmap.method
 
 import eu.timepit.refined.auto._
 import javax.inject.Inject
-import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
 import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId
@@ -30,7 +29,7 @@ import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, J
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model.SetError.SetErrorDescription
 import org.apache.james.jmap.model.{ClientId, Id, Invocation, Properties, ServerId, SetError, State}
-import org.apache.james.jmap.routes.ProcessingContext
+import org.apache.james.jmap.routes.{ProcessingContext, SessionSupplier}
 import org.apache.james.jmap.utils.quotas.QuotaLoaderWithPreloadedDefaultFactory
 import org.apache.james.mailbox.MailboxManager.RenameOption
 import org.apache.james.mailbox.exception.{InsufficientRightsException, MailboxExistsException, MailboxNameException, MailboxNotFoundException}
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 c2e52d9..e14f870 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
@@ -18,13 +18,11 @@
  ****************************************************************/
 package org.apache.james.jmap.method
 
-
-import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.mail.{UnsupportedFilterException, UnsupportedNestingException, UnsupportedRequestParameterException, UnsupportedSortException}
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.Invocation.MethodName
 import org.apache.james.jmap.model.{AccountId, ErrorCode, Invocation, Session}
-import org.apache.james.jmap.routes.ProcessingContext
+import org.apache.james.jmap.routes.{ProcessingContext, SessionSupplier}
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.mailbox.exception.MailboxNotFoundException
 import org.apache.james.metrics.api.MetricFactory
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
index 05cbfdb..770443f 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
@@ -22,7 +22,6 @@ package org.apache.james.jmap.method
 import eu.timepit.refined.auto._
 import javax.inject.Inject
 import org.apache.james.jmap.api.vacation.{VacationRepository, AccountId => JavaAccountId}
-import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{ResponseSerializer, VacationSerializer}
 import org.apache.james.jmap.mail.VacationResponse.{UNPARSED_SINGLETON, UnparsedVacationResponseId}
 import org.apache.james.jmap.mail.{VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseNotFound}
@@ -30,6 +29,7 @@ import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, J
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodCallId, MethodName}
 import org.apache.james.jmap.model.State.INSTANCE
 import org.apache.james.jmap.model.{AccountId, ErrorCode, Invocation, MissingCapabilityException, Properties}
+import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.metrics.api.MetricFactory
 import play.api.libs.json.{JsError, JsObject, JsSuccess}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
index c43978b..f7d2d0f 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
@@ -22,13 +22,13 @@ package org.apache.james.jmap.method
 import eu.timepit.refined.auto._
 import javax.inject.Inject
 import org.apache.james.jmap.api.vacation.{AccountId, VacationPatch, VacationRepository}
-import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{ResponseSerializer, VacationSerializer}
 import org.apache.james.jmap.method.VacationResponseSetMethod.VACATION_RESPONSE_PATCH_OBJECT_KEY
 import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE}
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model.SetError.SetErrorDescription
 import org.apache.james.jmap.model.{Invocation, State}
+import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.jmap.vacation.{VacationResponseSetError, VacationResponseSetRequest, VacationResponseSetResponse, VacationResponseUpdateResponse}
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.metrics.api.MetricFactory
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 d58912b..37d3d5d 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
@@ -33,7 +33,7 @@ import org.apache.james.jmap.HttpConstants.JSON_CONTENT_TYPE
 import org.apache.james.jmap.JMAPUrls.JMAP
 import org.apache.james.jmap.exceptions.UnauthorizedException
 import org.apache.james.jmap.http.rfc8621.InjectionKeys
-import org.apache.james.jmap.http.{Authenticator, MailboxesProvisioner, SessionSupplier, UserProvisioning}
+import org.apache.james.jmap.http.{Authenticator, MailboxesProvisioner, UserProvisioning}
 import org.apache.james.jmap.json.ResponseSerializer
 import org.apache.james.jmap.method.{InvocationWithContext, Method}
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionRoutes.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionRoutes.scala
similarity index 97%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionRoutes.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionRoutes.scala
index 33e9598..a637cbc 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionRoutes.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionRoutes.scala
@@ -17,7 +17,7 @@
  * under the License.                                           *
  * ***************************************************************/
 
-package org.apache.james.jmap.http
+package org.apache.james.jmap.routes
 
 import java.util.stream.Stream
 
@@ -28,7 +28,7 @@ import javax.inject.{Inject, Named}
 import org.apache.james.jmap.HttpConstants.JSON_CONTENT_TYPE_UTF8
 import org.apache.james.jmap.JMAPRoutes.CORS_CONTROL
 import org.apache.james.jmap.exceptions.UnauthorizedException
-import org.apache.james.jmap.http.SessionRoutes.{JMAP_SESSION, LOGGER, WELL_KNOWN_JMAP}
+import org.apache.james.jmap.http.Authenticator
 import org.apache.james.jmap.http.rfc8621.InjectionKeys
 import org.apache.james.jmap.json.ResponseSerializer
 import org.apache.james.jmap.model.Session
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionSupplier.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionSupplier.scala
similarity index 98%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionSupplier.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionSupplier.scala
index 0aa6bbc..2f6d12b 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionSupplier.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionSupplier.scala
@@ -17,7 +17,7 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.http
+package org.apache.james.jmap.routes
 
 import javax.inject.Inject
 import org.apache.james.core.Username
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 379a71f..dd2240a 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
@@ -36,7 +36,7 @@ import org.apache.james.dnsservice.api.DNSService
 import org.apache.james.domainlist.memory.MemoryDomainList
 import org.apache.james.jmap.JMAPUrls.JMAP
 import org.apache.james.jmap._
-import org.apache.james.jmap.http.{Authenticator, BasicAuthenticationStrategy, MailboxesProvisioner, SessionSupplier, UserProvisioning}
+import org.apache.james.jmap.http.{Authenticator, BasicAuthenticationStrategy, MailboxesProvisioner, UserProvisioning}
 import org.apache.james.jmap.method.{CoreEchoMethod, Method}
 import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE}
 import org.apache.james.jmap.model.Invocation.MethodName
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionRoutesTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala
similarity index 98%
rename from server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionRoutesTest.scala
rename to server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala
index 1f3083c..4c47291 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionRoutesTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala
@@ -17,7 +17,7 @@
  * under the License.                                           *
  * ***************************************************************/
 
-package org.apache.james.jmap.http
+package org.apache.james.jmap.routes
 
 import java.nio.charset.StandardCharsets
 
@@ -31,9 +31,10 @@ import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
 import org.apache.http.HttpStatus
 import org.apache.james.core.Username
 import org.apache.james.jmap._
-import org.apache.james.jmap.http.SessionRoutesTest.{BOB, TEST_CONFIGURATION}
+import org.apache.james.jmap.http.Authenticator
 import org.apache.james.jmap.model.JmapRfc8621Configuration
 import org.apache.james.jmap.model.JmapRfc8621Configuration.LOCALHOST_URL_PREFIX
+import org.apache.james.jmap.routes.SessionRoutesTest.{BOB, TEST_CONFIGURATION}
 import org.apache.james.mailbox.MailboxSession
 import org.mockito.ArgumentMatchers.any
 import org.mockito.Mockito._
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionSupplierTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionSupplierTest.scala
similarity index 95%
rename from server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionSupplierTest.scala
rename to server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionSupplierTest.scala
index 71f00d0..56e6a6d 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionSupplierTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionSupplierTest.scala
@@ -17,11 +17,11 @@
  * under the License.                                           *
  * ***************************************************************/
 
-package org.apache.james.jmap.http
+package org.apache.james.jmap.routes
 
 import org.apache.james.core.Username
-import org.apache.james.jmap.http.SessionSupplierTest.USERNAME
 import org.apache.james.jmap.model.JmapRfc8621Configuration
+import org.apache.james.jmap.routes.SessionSupplierTest.USERNAME
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec
 


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


[james-project] 01/11: JAMES-3437 Provide a bash script wrapper for James Webadmin Cli

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

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

commit 4aa9a499ad6444cbd5773ddcbbd8a188fe61fbe7
Author: quanth <hq...@linagora.com>
AuthorDate: Wed Oct 28 13:52:10 2020 +0700

    JAMES-3437 Provide a bash script wrapper for James Webadmin Cli
---
 server/protocols/webadmin-cli/james-cli | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/server/protocols/webadmin-cli/james-cli b/server/protocols/webadmin-cli/james-cli
new file mode 100755
index 0000000..d7c277a
--- /dev/null
+++ b/server/protocols/webadmin-cli/james-cli
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+java -jar target/james-cli.jar "$@"
\ No newline at end of file


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


[james-project] 08/11: [REFACTORING] Relocate SessionRoutes in toutes package

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

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

commit 6c4e6b0d2c3f413d32b1cb02b45dff5afd6597ce
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Nov 2 11:06:43 2020 +0700

    [REFACTORING] Relocate SessionRoutes in toutes package
---
 .../src/main/scala/org/apache/james/jmap/routes/SessionRoutes.scala      | 1 +
 1 file changed, 1 insertion(+)

diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionRoutes.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionRoutes.scala
index 3f9772b..080029d 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionRoutes.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionRoutes.scala
@@ -32,6 +32,7 @@ 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.ResponseSerializer
+import org.apache.james.jmap.routes.SessionRoutes.{JMAP_SESSION, LOGGER, WELL_KNOWN_JMAP}
 import org.apache.james.jmap.{Endpoint, JMAPRoute, JMAPRoutes}
 import org.slf4j.LoggerFactory
 import play.api.libs.json.Json


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


[james-project] 09/11: [REFACTORING] Remove wildcards on some James scala imports

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

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

commit 4aba016f117fe4ad5f69e540f66e0fcf427c69b3
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Mon Nov 2 14:06:39 2020 +0700

    [REFACTORING] Remove wildcards on some James scala imports
---
 .../src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala  | 2 +-
 .../main/scala/org/apache/james/jmap/json/MailboxQuerySerializer.scala  | 2 +-
 .../src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala   | 2 +-
 .../src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala  | 2 +-
 .../src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala  | 2 +-
 .../main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala    | 2 +-
 .../src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala     | 2 +-
 .../src/main/scala/org/apache/james/jmap/routes/SessionSupplier.scala   | 2 +-
 .../src/main/scala/org/apache/james/jmap/utils/quotas/QuotaReader.scala | 2 +-
 .../test/scala/org/apache/james/jmap/core/MailboxValidationTest.scala   | 2 +-
 .../scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala  | 2 +-
 .../scala/org/apache/james/jmap/json/SessionSerializationTest.scala     | 2 +-
 .../src/test/scala/org/apache/james/jmap/routes/JMAPApiRoutesTest.scala | 2 +-
 .../src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala | 2 +-
 14 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
index e24e728..fb89158 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
@@ -20,7 +20,7 @@
 package org.apache.james.jmap.json
 
 import org.apache.james.jmap.api.model.Preview
-import org.apache.james.jmap.core._
+import org.apache.james.jmap.core.Properties
 import org.apache.james.jmap.mail.{AddressesHeaderValue, BlobId, Charset, DateHeaderValue, Disposition, EmailAddress, EmailAddressGroup, EmailBody, EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailFastView, EmailFullView, EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, FetchHTMLBodyValues, FetchTextBodyValues, Group [...]
 import org.apache.james.mailbox.model.{Cid, MailboxId, MessageId}
 import play.api.libs.functional.syntax._
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxQuerySerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxQuerySerializer.scala
index d67c9a9..a10a316 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxQuerySerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxQuerySerializer.scala
@@ -19,7 +19,7 @@
 
 package org.apache.james.jmap.json
 
-import org.apache.james.jmap.core._
+import org.apache.james.jmap.core.{AccountId, CanCalculateChanges, QueryState}
 import org.apache.james.jmap.mail.{MailboxFilter, MailboxQueryRequest, MailboxQueryResponse}
 import org.apache.james.mailbox.Role
 import org.apache.james.mailbox.model.MailboxId
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala
index 28fcb34..8a0970e 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala
@@ -24,7 +24,7 @@ import eu.timepit.refined.collection.NonEmpty
 import javax.inject.Inject
 import org.apache.james.core.{Domain, Username}
 import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.core._
+import org.apache.james.jmap.core.{ClientId, Properties, SetError}
 import org.apache.james.jmap.mail.MailboxGet.{UnparsedMailboxId, UnparsedMailboxIdConstraint}
 import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId
 import org.apache.james.jmap.mail.{DelegatedNamespace, Ids, IsSubscribed, Mailbox, MailboxCreationRequest, MailboxCreationResponse, MailboxGetRequest, MailboxGetResponse, MailboxNamespace, MailboxPatchObject, MailboxRights, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, MayAddItems, MayCreateChild, MayDelete, MayReadItems, MayRemoveItems, MayRename, MaySetKeywords, MaySetSeen, MaySubmit, NotFound, PersonalNamespace, Quota, QuotaId, QuotaRoot, Quotas, RemoveEmailsOnDestroy, [...]
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala
index 2ee807e..3a3ecbd 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala
@@ -21,7 +21,7 @@ package org.apache.james.jmap.json
 
 import java.time.format.DateTimeFormatter
 
-import org.apache.james.jmap.core._
+import org.apache.james.jmap.core.{Properties, UTCDate}
 import org.apache.james.jmap.mail.Subject
 import org.apache.james.jmap.vacation.VacationResponse.{UnparsedVacationResponseId, VACATION_RESPONSE_ID}
 import org.apache.james.jmap.vacation.{FromDate, HtmlBody, IsEnabled, TextBody, ToDate, VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseId, VacationResponseIds, VacationResponseNotFound, VacationResponsePatchObject, VacationResponseSetError, VacationResponseSetRequest, VacationResponseSetResponse, VacationResponseUpdateResponse}
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 ca4ea06..c433d17 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
@@ -27,7 +27,7 @@ import org.apache.james.jmap.core.State.INSTANCE
 import org.apache.james.jmap.core.{AccountId, CapabilityIdentifier, ErrorCode, Invocation, Properties}
 import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
-import org.apache.james.jmap.mail._
+import org.apache.james.jmap.mail.{Mailbox, MailboxFactory, MailboxGet, MailboxGetRequest, MailboxGetResponse, NotFound, PersonalNamespace, Subscriptions}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.jmap.utils.quotas.{QuotaLoader, QuotaLoaderWithPreloadedDefaultFactory}
 import org.apache.james.mailbox.exception.MailboxNotFoundException
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
index e247a10..123e7aa 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
@@ -22,7 +22,7 @@ import eu.timepit.refined.auto._
 import javax.inject.Inject
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
-import org.apache.james.jmap.core._
+import org.apache.james.jmap.core.{CanCalculateChanges, ErrorCode, Invocation, Limit, Position, QueryState}
 import org.apache.james.jmap.json.{MailboxQuerySerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.{MailboxQueryRequest, MailboxQueryResponse}
 import org.apache.james.jmap.routes.SessionSupplier
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 4fab40c..42efd8f 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
@@ -34,7 +34,7 @@ import org.apache.james.jmap.JMAPUrls.JMAP
 import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.core.Invocation.MethodName
 import org.apache.james.jmap.core.ProblemDetails.{notJSONProblem, notRequestProblem, unknownCapabilityProblem}
-import org.apache.james.jmap.core._
+import org.apache.james.jmap.core.{DefaultCapabilities, ErrorCode, Invocation, MissingCapabilityException, ProblemDetails, RequestObject, ResponseObject}
 import org.apache.james.jmap.exceptions.UnauthorizedException
 import org.apache.james.jmap.http.rfc8621.InjectionKeys
 import org.apache.james.jmap.http.{Authenticator, MailboxesProvisioner, UserProvisioning}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionSupplier.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionSupplier.scala
index 38388b0..760d95c 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionSupplier.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionSupplier.scala
@@ -22,7 +22,7 @@ package org.apache.james.jmap.routes
 import javax.inject.Inject
 import org.apache.james.core.Username
 import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.core._
+import org.apache.james.jmap.core.{Account, AccountId, DefaultCapabilities, IsPersonal, IsReadOnly, JmapRfc8621Configuration, Session}
 import reactor.core.scala.publisher.SMono
 
 class SessionSupplier @Inject() (val configuration: JmapRfc8621Configuration){
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
index 01ba28d..3c8a7a6 100644
--- 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
@@ -23,7 +23,7 @@ import javax.inject.Inject
 import org.apache.james.core.quota.{QuotaLimitValue, QuotaUsageValue}
 import org.apache.james.jmap.core.UnsignedInt
 import org.apache.james.jmap.core.UnsignedInt.UnsignedInt
-import org.apache.james.jmap.mail._
+import org.apache.james.jmap.mail.{Quota, QuotaId, QuotaRoot, Quotas, Value}
 import org.apache.james.mailbox.exception.MailboxException
 import org.apache.james.mailbox.model.{Quota => ModelQuota}
 import org.apache.james.mailbox.quota.QuotaManager
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/MailboxValidationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/MailboxValidationTest.scala
index 69ff7d0..cd69f53 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/MailboxValidationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/MailboxValidationTest.scala
@@ -23,7 +23,7 @@ import eu.timepit.refined.auto._
 import org.apache.james.core.Username
 import org.apache.james.jmap.core.UnsignedInt.UnsignedInt
 import org.apache.james.jmap.mail.MailboxName.MailboxName
-import org.apache.james.jmap.mail._
+import org.apache.james.jmap.mail.{MailboxValidation, TotalEmails, TotalThreads, UnreadEmails, UnreadThreads}
 import org.apache.james.mailbox.model.MailboxPath
 import org.scalatest.matchers.must.Matchers
 import org.scalatest.wordspec.AnyWordSpec
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala
index 491dbd2..f39412c 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala
@@ -26,7 +26,7 @@ import org.apache.james.jmap.json.Fixture._
 import org.apache.james.jmap.json.MailboxGetSerializationTest._
 import org.apache.james.jmap.json.MailboxSerializationTest.MAILBOX
 import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
-import org.apache.james.jmap.mail._
+import org.apache.james.jmap.mail.{Ids, Mailbox, MailboxGetRequest, MailboxGetResponse, NotFound}
 import org.apache.james.mailbox.model.{MailboxId, TestId}
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/SessionSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/SessionSerializationTest.scala
index 627ca9d..6d9931e 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/SessionSerializationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/SessionSerializationTest.scala
@@ -27,7 +27,7 @@ import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.core.CoreCapabilityProperties.CollationAlgorithm
 import org.apache.james.jmap.core.MailCapability.EmailQuerySortOption
 import org.apache.james.jmap.core.State.State
-import org.apache.james.jmap.core._
+import org.apache.james.jmap.core.{Account, Capabilities, CoreCapability, CoreCapabilityProperties, IsPersonal, IsReadOnly, MailCapability, MailCapabilityProperties, MaxCallsInRequest, MaxConcurrentRequests, MaxConcurrentUpload, MaxMailboxDepth, MaxMailboxesPerEmail, MaxObjectsInGet, MaxObjectsInSet, MaxSizeAttachmentsPerEmail, MaxSizeMailboxName, MaxSizeRequest, MaxSizeUpload, MayCreateTopLevelMailbox, QuotaCapability, Session, SharesCapability, VacationResponseCapability}
 import org.apache.james.jmap.json.SessionSerializationTest.SESSION
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec
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 52ce1cd..526da86 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
@@ -35,13 +35,13 @@ import org.apache.james.core.{Domain, Username}
 import org.apache.james.dnsservice.api.DNSService
 import org.apache.james.domainlist.memory.MemoryDomainList
 import org.apache.james.jmap.JMAPUrls.JMAP
-import org.apache.james.jmap._
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE}
 import org.apache.james.jmap.core.Invocation.MethodName
 import org.apache.james.jmap.core.{JmapRfc8621Configuration, RequestLevelErrorType}
 import org.apache.james.jmap.http.{Authenticator, BasicAuthenticationStrategy, MailboxesProvisioner, UserProvisioning}
 import org.apache.james.jmap.method.{CoreEchoMethod, Method}
 import org.apache.james.jmap.routes.JMAPApiRoutesTest._
+import org.apache.james.jmap.{JMAPConfiguration, JMAPRoutesHandler, JMAPServer, Version, VersionParser}
 import org.apache.james.mailbox.extension.PreDeletionHook
 import org.apache.james.mailbox.inmemory.{InMemoryMailboxManager, MemoryMailboxManagerProvider}
 import org.apache.james.mailbox.store.StoreSubscriptionManager
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala
index d0cad56..6cab55a 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala
@@ -30,11 +30,11 @@ import io.restassured.http.ContentType
 import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
 import org.apache.http.HttpStatus
 import org.apache.james.core.Username
-import org.apache.james.jmap._
 import org.apache.james.jmap.core.JmapRfc8621Configuration
 import org.apache.james.jmap.core.JmapRfc8621Configuration.LOCALHOST_URL_PREFIX
 import org.apache.james.jmap.http.Authenticator
 import org.apache.james.jmap.routes.SessionRoutesTest.{BOB, TEST_CONFIGURATION}
+import org.apache.james.jmap.{JMAPConfiguration, JMAPRoutesHandler, JMAPServer, Version, VersionParser}
 import org.apache.james.mailbox.MailboxSession
 import org.mockito.ArgumentMatchers.any
 import org.mockito.Mockito._


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


[james-project] 06/11: [REFACTORING] Split EmailSetMethod and extract create/update/destroy

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

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

commit 88f6d210a6f766c6bc0f3c3634372803fbbcc685
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Nov 2 10:35:04 2020 +0700

    [REFACTORING] Split EmailSetMethod and extract create/update/destroy
---
 .../jmap/method/EmailSetCreatePerformer.scala      | 102 ++++++
 .../jmap/method/EmailSetDeletePerformer.scala      |  99 ++++++
 .../apache/james/jmap/method/EmailSetMethod.scala  | 361 +--------------------
 .../jmap/method/EmailSetUpdatePerformer.scala      | 249 ++++++++++++++
 4 files changed, 463 insertions(+), 348 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetCreatePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetCreatePerformer.scala
new file mode 100644
index 0000000..b7abebe
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetCreatePerformer.scala
@@ -0,0 +1,102 @@
+/****************************************************************
+ * 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 java.time.ZonedDateTime
+import java.util.Date
+
+import javax.inject.Inject
+import javax.mail.Flags
+import org.apache.james.jmap.core.SetError.SetErrorDescription
+import org.apache.james.jmap.core.{SetError, UTCDate}
+import org.apache.james.jmap.json.EmailSetSerializer
+import org.apache.james.jmap.mail.EmailSet.EmailCreationId
+import org.apache.james.jmap.mail.{EmailCreationRequest, EmailCreationResponse, EmailSetRequest}
+import org.apache.james.jmap.method.EmailSetCreatePerformer.{CreationFailure, CreationResult, CreationResults, CreationSuccess}
+import org.apache.james.mailbox.MessageManager.AppendCommand
+import org.apache.james.mailbox.exception.MailboxNotFoundException
+import org.apache.james.mailbox.model.MailboxId
+import org.apache.james.mailbox.{MailboxManager, MailboxSession}
+import reactor.core.scala.publisher.{SFlux, SMono}
+import reactor.core.scheduler.Schedulers
+
+object EmailSetCreatePerformer {
+  case class CreationResults(results: Seq[CreationResult]) {
+    def created: Option[Map[EmailCreationId, EmailCreationResponse]] =
+      Option(results.flatMap{
+        case result: CreationSuccess => Some((result.clientId, result.response))
+        case _ => None
+      }.toMap)
+        .filter(_.nonEmpty)
+
+    def notCreated: Option[Map[EmailCreationId, SetError]] = {
+      Option(results.flatMap{
+        case failure: CreationFailure => Some((failure.clientId, failure.asMessageSetError))
+        case _ => None
+      }
+        .toMap)
+        .filter(_.nonEmpty)
+    }
+  }
+  trait CreationResult
+  case class CreationSuccess(clientId: EmailCreationId, response: EmailCreationResponse) extends CreationResult
+  case class CreationFailure(clientId: EmailCreationId, e: Throwable) extends CreationResult {
+    def asMessageSetError: SetError = e match {
+      case e: IllegalArgumentException => SetError.invalidArguments(SetErrorDescription(e.getMessage))
+      case e: MailboxNotFoundException => SetError.notFound(SetErrorDescription("Mailbox " + e.getMessage))
+      case _ => SetError.serverFail(SetErrorDescription(e.getMessage))
+    }
+  }
+}
+
+class EmailSetCreatePerformer @Inject()(serializer: EmailSetSerializer,
+                                        mailboxManager: MailboxManager) {
+
+  def create(request: EmailSetRequest, mailboxSession: MailboxSession): SMono[CreationResults] =
+    SFlux.fromIterable(request.create.getOrElse(Map()))
+      .concatMap {
+        case (clientId, json) => serializer.deserializeCreationRequest(json)
+          .fold(e => SMono.just[CreationResult](CreationFailure(clientId, new IllegalArgumentException(e.toString))),
+            creationRequest => create(clientId, creationRequest, mailboxSession))
+      }.collectSeq()
+      .map(CreationResults)
+
+  private def create(clientId: EmailCreationId, request: EmailCreationRequest, mailboxSession: MailboxSession): SMono[CreationResult] = {
+    val mailboxIds: List[MailboxId] = request.mailboxIds.value
+    if (mailboxIds.size != 1) {
+      SMono.just(CreationFailure(clientId, new IllegalArgumentException("mailboxIds need to have size 1")))
+    } else {
+      request.toMime4JMessage
+        .fold(e => SMono.just(CreationFailure(clientId, e)),
+          message => SMono.fromCallable[CreationResult](() => {
+            val appendResult = mailboxManager.getMailbox(mailboxIds.head, mailboxSession)
+              .appendMessage(AppendCommand.builder()
+                .recent()
+                .withFlags(request.keywords.map(_.asFlags).getOrElse(new Flags()))
+                .withInternalDate(Date.from(request.receivedAt.getOrElse(UTCDate(ZonedDateTime.now())).asUTC.toInstant))
+                .build(message),
+                mailboxSession)
+            CreationSuccess(clientId, EmailCreationResponse(appendResult.getId.getMessageId))
+          })
+            .subscribeOn(Schedulers.elastic())
+            .onErrorResume(e => SMono.just[CreationResult](CreationFailure(clientId, e))))
+    }
+  }
+}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetDeletePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetDeletePerformer.scala
new file mode 100644
index 0000000..da56ab5
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetDeletePerformer.scala
@@ -0,0 +1,99 @@
+/****************************************************************
+ * 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 javax.inject.Inject
+import org.apache.james.jmap.core.SetError
+import org.apache.james.jmap.core.SetError.SetErrorDescription
+import org.apache.james.jmap.mail.EmailSet.UnparsedMessageId
+import org.apache.james.jmap.mail.{DestroyIds, EmailSet, EmailSetRequest}
+import org.apache.james.jmap.method.EmailSetDeletePerformer.{DestroyFailure, DestroyResult, DestroyResults}
+import org.apache.james.mailbox.model.{DeleteResult, MessageId}
+import org.apache.james.mailbox.{MailboxSession, MessageIdManager}
+import reactor.core.scala.publisher.SMono
+import reactor.core.scheduler.Schedulers
+
+import scala.jdk.CollectionConverters._
+
+object EmailSetDeletePerformer {
+  case class DestroyResults(results: Seq[DestroyResult]) {
+    def destroyed: Option[DestroyIds] =
+      Option(results.flatMap{
+        case result: DestroySuccess => Some(result.messageId)
+        case _ => None
+      }.map(EmailSet.asUnparsed))
+        .filter(_.nonEmpty)
+        .map(DestroyIds)
+
+    def notDestroyed: Option[Map[UnparsedMessageId, SetError]] =
+      Option(results.flatMap{
+        case failure: DestroyFailure => Some((failure.unparsedMessageId, failure.asMessageSetError))
+        case _ => None
+      }.toMap)
+        .filter(_.nonEmpty)
+  }
+  object DestroyResult {
+    def from(deleteResult: DeleteResult): Seq[DestroyResult] = {
+      val success: Seq[DestroySuccess] = deleteResult.getDestroyed.asScala.toSeq
+        .map(DestroySuccess)
+      val notFound: Seq[DestroyResult] = deleteResult.getNotFound.asScala.toSeq
+        .map(id => DestroyFailure(EmailSet.asUnparsed(id), MessageNotFoundExeception(id)))
+
+      success ++ notFound
+    }
+  }
+  trait DestroyResult
+  case class DestroySuccess(messageId: MessageId) extends DestroyResult
+  case class DestroyFailure(unparsedMessageId: UnparsedMessageId, e: Throwable) extends DestroyResult {
+    def asMessageSetError: SetError = e match {
+      case e: IllegalArgumentException => SetError.invalidArguments(SetErrorDescription(s"$unparsedMessageId is not a messageId: ${e.getMessage}"))
+      case e: MessageNotFoundExeception => SetError.notFound(SetErrorDescription(s"Cannot find message with messageId: ${e.messageId.serialize()}"))
+      case _ => SetError.serverFail(SetErrorDescription(e.getMessage))
+    }
+  }
+}
+
+class EmailSetDeletePerformer @Inject()(messageIdManager: MessageIdManager,
+                                        messageIdFactory: MessageId.Factory) {
+  def destroy(emailSetRequest: EmailSetRequest, mailboxSession: MailboxSession): SMono[DestroyResults] = {
+    if (emailSetRequest.destroy.isDefined) {
+      val messageIdsValidation: Seq[Either[DestroyFailure, MessageId]] = emailSetRequest.destroy.get.value
+        .map(unparsedId => EmailSet.parse(messageIdFactory)(unparsedId).toEither
+          .left.map(e => DestroyFailure(unparsedId, e)))
+      val messageIds: Seq[MessageId] = messageIdsValidation.flatMap {
+        case Right(messageId) => Some(messageId)
+        case _ => None
+      }
+      val parsingErrors: Seq[DestroyFailure] = messageIdsValidation.flatMap {
+        case Left(e) => Some(e)
+        case _ => None
+      }
+
+      SMono.fromCallable(() => messageIdManager.delete(messageIds.toList.asJava, mailboxSession))
+        .map(DestroyResult.from)
+        .subscribeOn(Schedulers.elastic())
+        .onErrorResume(e => SMono.just(messageIds.map(id => DestroyFailure(EmailSet.asUnparsed(id), e))))
+        .map(_ ++ parsingErrors)
+        .map(DestroyResults)
+    } else {
+      SMono.just(DestroyResults(Seq()))
+    }
+  }
+}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
index 5a2dd36..1bcc308 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
@@ -18,142 +18,36 @@
  ****************************************************************/
 package org.apache.james.jmap.method
 
-import java.time.ZonedDateTime
-import java.util.Date
-import java.util.function.Consumer
-
-import com.google.common.collect.ImmutableList
 import eu.timepit.refined.auto._
 import javax.inject.Inject
-import javax.mail.Flags
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
-import org.apache.james.jmap.core.SetError.SetErrorDescription
-import org.apache.james.jmap.core.{ClientId, Id, Invocation, ServerId, SetError, State, UTCDate}
+import org.apache.james.jmap.core.{ClientId, Id, Invocation, ServerId, State}
 import org.apache.james.jmap.json.{EmailSetSerializer, ResponseSerializer}
-import org.apache.james.jmap.mail.EmailSet.{EmailCreationId, UnparsedMessageId}
-import org.apache.james.jmap.mail.KeywordsFactory.LENIENT_KEYWORDS_FACTORY
-import org.apache.james.jmap.mail.{DestroyIds, EmailCreationRequest, EmailCreationResponse, EmailSet, EmailSetRequest, EmailSetResponse, MailboxIds, ValidatedEmailSetUpdate}
+import org.apache.james.jmap.mail.{EmailSetRequest, EmailSetResponse}
 import org.apache.james.jmap.routes.SessionSupplier
-import org.apache.james.mailbox.MessageManager.{AppendCommand, FlagsUpdateMode}
-import org.apache.james.mailbox.exception.MailboxNotFoundException
-import org.apache.james.mailbox.model.{ComposedMessageIdWithMetaData, DeleteResult, MailboxId, MessageId, MessageRange}
-import org.apache.james.mailbox.{MailboxManager, MailboxSession, MessageIdManager, MessageManager}
+import org.apache.james.mailbox.MailboxSession
+import org.apache.james.mailbox.model.MessageId
 import org.apache.james.metrics.api.MetricFactory
-import play.api.libs.json.{JsError, JsObject, JsSuccess}
-import reactor.core.scala.publisher.{SFlux, SMono}
-import reactor.core.scheduler.Schedulers
-
-import scala.jdk.CollectionConverters._
+import play.api.libs.json.{JsError, JsSuccess}
+import reactor.core.scala.publisher.SMono
 
 case class MessageNotFoundExeception(messageId: MessageId) extends Exception
 
 class EmailSetMethod @Inject()(serializer: EmailSetSerializer,
-                               messageIdManager: MessageIdManager,
-                               mailboxManager: MailboxManager,
-                               messageIdFactory: MessageId.Factory,
                                val metricFactory: MetricFactory,
-                               val sessionSupplier: SessionSupplier) extends MethodRequiringAccountId[EmailSetRequest] {
-
-  case class DestroyResults(results: Seq[DestroyResult]) {
-    def destroyed: Option[DestroyIds] =
-      Option(results.flatMap{
-        case result: DestroySuccess => Some(result.messageId)
-        case _ => None
-      }.map(EmailSet.asUnparsed))
-        .filter(_.nonEmpty)
-        .map(DestroyIds)
-
-    def notDestroyed: Option[Map[UnparsedMessageId, SetError]] =
-      Option(results.flatMap{
-        case failure: DestroyFailure => Some((failure.unparsedMessageId, failure.asMessageSetError))
-        case _ => None
-      }.toMap)
-        .filter(_.nonEmpty)
-  }
-
-  object DestroyResult {
-    def from(deleteResult: DeleteResult): Seq[DestroyResult] = {
-      val success: Seq[DestroySuccess] = deleteResult.getDestroyed.asScala.toSeq
-        .map(DestroySuccess)
-      val notFound: Seq[DestroyResult] = deleteResult.getNotFound.asScala.toSeq
-        .map(id => DestroyFailure(EmailSet.asUnparsed(id), MessageNotFoundExeception(id)))
-
-      success ++ notFound
-    }
-  }
-
-  trait DestroyResult
-  case class DestroySuccess(messageId: MessageId) extends DestroyResult
-  case class DestroyFailure(unparsedMessageId: UnparsedMessageId, e: Throwable) extends DestroyResult {
-    def asMessageSetError: SetError = e match {
-      case e: IllegalArgumentException => SetError.invalidArguments(SetErrorDescription(s"$unparsedMessageId is not a messageId: ${e.getMessage}"))
-      case e: MessageNotFoundExeception => SetError.notFound(SetErrorDescription(s"Cannot find message with messageId: ${e.messageId.serialize()}"))
-      case _ => SetError.serverFail(SetErrorDescription(e.getMessage))
-    }
-  }
-
-  case class CreationResults(results: Seq[CreationResult]) {
-    def created: Option[Map[EmailCreationId, EmailCreationResponse]] =
-      Option(results.flatMap{
-        case result: CreationSuccess => Some((result.clientId, result.response))
-        case _ => None
-      }.toMap)
-        .filter(_.nonEmpty)
-
-    def notCreated: Option[Map[EmailCreationId, SetError]] = {
-      Option(results.flatMap{
-        case failure: CreationFailure => Some((failure.clientId, failure.asMessageSetError))
-        case _ => None
-      }
-        .toMap)
-        .filter(_.nonEmpty)
-    }
-  }
-  trait CreationResult
-  case class CreationSuccess(clientId: EmailCreationId, response: EmailCreationResponse) extends CreationResult
-  case class CreationFailure(clientId: EmailCreationId, e: Throwable) extends CreationResult {
-    def asMessageSetError: SetError = e match {
-      case e: IllegalArgumentException => SetError.invalidArguments(SetErrorDescription(e.getMessage))
-      case e: MailboxNotFoundException => SetError.notFound(SetErrorDescription("Mailbox " + e.getMessage))
-      case _ => SetError.serverFail(SetErrorDescription(e.getMessage))
-    }
-  }
-
-  trait UpdateResult
-  case class UpdateSuccess(messageId: MessageId) extends UpdateResult
-  case class UpdateFailure(unparsedMessageId: UnparsedMessageId, e: Throwable) extends UpdateResult {
-    def asMessageSetError: SetError = e match {
-      case e: IllegalArgumentException => SetError.invalidPatch(SetErrorDescription(s"Message $unparsedMessageId update is invalid: ${e.getMessage}"))
-      case _: MailboxNotFoundException => SetError.notFound(SetErrorDescription(s"Mailbox not found"))
-      case e: MessageNotFoundExeception => SetError.notFound(SetErrorDescription(s"Cannot find message with messageId: ${e.messageId.serialize()}"))
-      case _ => SetError.serverFail(SetErrorDescription(e.getMessage))
-    }
-  }
-  case class UpdateResults(results: Seq[UpdateResult]) {
-    def updated: Option[Map[MessageId, Unit]] =
-      Option(results.flatMap{
-        case result: UpdateSuccess => Some(result.messageId, ())
-        case _ => None
-      }.toMap)
-        .filter(_.nonEmpty)
-
-    def notUpdated: Option[Map[UnparsedMessageId, SetError]] =
-      Option(results.flatMap{
-        case failure: UpdateFailure => Some((failure.unparsedMessageId, failure.asMessageSetError))
-        case _ => None
-      }.toMap)
-        .filter(_.nonEmpty)
-  }
-
+                               val sessionSupplier: SessionSupplier,
+                               createPerformer: EmailSetCreatePerformer,
+                               deletePerformer: EmailSetDeletePerformer,
+                               updatePerformer: EmailSetUpdatePerformer) extends MethodRequiringAccountId[EmailSetRequest] {
   override val methodName: MethodName = MethodName("Email/set")
   override val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE, JMAP_MAIL)
 
   override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: EmailSetRequest): SMono[InvocationWithContext] = {
     for {
-      destroyResults <- destroy(request, mailboxSession)
-      updateResults <- update(request, mailboxSession)
-      created <- create(request, mailboxSession)
+      destroyResults <- deletePerformer.destroy(request, mailboxSession)
+      updateResults <- updatePerformer.update(request, mailboxSession)
+      created <- createPerformer.create(request, mailboxSession)
     } yield InvocationWithContext(
       invocation = Invocation(
         methodName = invocation.invocation.methodName,
@@ -183,233 +77,4 @@ class EmailSetMethod @Inject()(serializer: EmailSetSerializer,
       case JsSuccess(emailSetRequest, _) => SMono.just(emailSetRequest)
       case errors: JsError => SMono.raiseError(new IllegalArgumentException(ResponseSerializer.serialize(errors).toString))
     }
-
-  private def destroy(emailSetRequest: EmailSetRequest, mailboxSession: MailboxSession): SMono[DestroyResults] = {
-    if (emailSetRequest.destroy.isDefined) {
-      val messageIdsValidation: Seq[Either[DestroyFailure, MessageId]] = emailSetRequest.destroy.get.value
-        .map(unparsedId => EmailSet.parse(messageIdFactory)(unparsedId).toEither
-          .left.map(e => DestroyFailure(unparsedId, e)))
-      val messageIds: Seq[MessageId] = messageIdsValidation.flatMap {
-        case Right(messageId) => Some(messageId)
-        case _ => None
-      }
-      val parsingErrors: Seq[DestroyFailure] = messageIdsValidation.flatMap {
-        case Left(e) => Some(e)
-        case _ => None
-      }
-
-      SMono.fromCallable(() => messageIdManager.delete(messageIds.toList.asJava, mailboxSession))
-        .map(DestroyResult.from)
-        .subscribeOn(Schedulers.elastic())
-        .onErrorResume(e => SMono.just(messageIds.map(id => DestroyFailure(EmailSet.asUnparsed(id), e))))
-        .map(_ ++ parsingErrors)
-        .map(DestroyResults)
-    } else {
-      SMono.just(DestroyResults(Seq()))
-    }
-  }
-
-  private def create(request: EmailSetRequest, mailboxSession: MailboxSession): SMono[CreationResults] =
-    SFlux.fromIterable(request.create.getOrElse(Map()))
-      .concatMap {
-        case (clientId, json) => serializer.deserializeCreationRequest(json)
-          .fold(e => SMono.just[CreationResult](CreationFailure(clientId, new IllegalArgumentException(e.toString))),
-            creationRequest => create(clientId, creationRequest, mailboxSession))
-      }.collectSeq()
-      .map(CreationResults)
-
-  private def create(clientId: EmailCreationId, request: EmailCreationRequest, mailboxSession: MailboxSession): SMono[CreationResult] = {
-    val mailboxIds: List[MailboxId] = request.mailboxIds.value
-    if (mailboxIds.size != 1) {
-      SMono.just(CreationFailure(clientId, new IllegalArgumentException("mailboxIds need to have size 1")))
-    } else {
-      request.toMime4JMessage
-        .fold(e => SMono.just(CreationFailure(clientId, e)),
-          message => SMono.fromCallable[CreationResult](() => {
-            val appendResult = mailboxManager.getMailbox(mailboxIds.head, mailboxSession)
-              .appendMessage(AppendCommand.builder()
-                .recent()
-                .withFlags(request.keywords.map(_.asFlags).getOrElse(new Flags()))
-                .withInternalDate(Date.from(request.receivedAt.getOrElse(UTCDate(ZonedDateTime.now())).asUTC.toInstant))
-                .build(message),
-                mailboxSession)
-            CreationSuccess(clientId, EmailCreationResponse(appendResult.getId.getMessageId))
-          })
-            .subscribeOn(Schedulers.elastic())
-            .onErrorResume(e => SMono.just[CreationResult](CreationFailure(clientId, e))))
-    }
-  }
-
-  private def update(emailSetRequest: EmailSetRequest, mailboxSession: MailboxSession): SMono[UpdateResults] = {
-    emailSetRequest.update
-      .filter(_.nonEmpty)
-      .map(update(_, mailboxSession))
-      .getOrElse(SMono.just(UpdateResults(Seq())))
-  }
-
-  private def update(updates: Map[UnparsedMessageId, JsObject], session: MailboxSession): SMono[UpdateResults] = {
-    val validatedUpdates: List[Either[UpdateFailure, (MessageId, ValidatedEmailSetUpdate)]] = updates
-      .map({
-        case (unparsedMessageId, json) => EmailSet.parse(messageIdFactory)(unparsedMessageId)
-          .toEither
-          .left.map(e => UpdateFailure(unparsedMessageId, e))
-          .flatMap(id => serializer.deserializeEmailSetUpdate(json)
-            .asEither.left.map(e => new IllegalArgumentException(e.toString))
-            .flatMap(_.validate)
-            .fold(e => Left(UpdateFailure(unparsedMessageId, e)),
-              emailSetUpdate => Right((id, emailSetUpdate))))
-      })
-      .toList
-    val failures: List[UpdateFailure] = validatedUpdates.flatMap({
-      case Left(e) => Some(e)
-      case _ => None
-    })
-    val validUpdates: List[(MessageId, ValidatedEmailSetUpdate)] = validatedUpdates.flatMap({
-      case Right(pair) => Some(pair)
-      case _ => None
-    })
-
-    for {
-      updates <- SFlux.fromPublisher(messageIdManager.messagesMetadata(validUpdates.map(_._1).asJavaCollection, session))
-        .collectMultimap(metaData => metaData.getComposedMessageId.getMessageId)
-        .flatMap(metaData => {
-          doUpdate(validUpdates, metaData, session)
-        })
-    } yield {
-      UpdateResults(updates ++ failures)
-    }
-  }
-
-  private def doUpdate(validUpdates: List[(MessageId, ValidatedEmailSetUpdate)],
-                       metaData: Map[MessageId, Traversable[ComposedMessageIdWithMetaData]],
-                       session: MailboxSession): SMono[Seq[UpdateResult]] = {
-    val sameUpdate: Boolean = validUpdates.map(_._2).distinctBy(_.update).size == 1
-    val singleMailbox: Boolean = metaData.values.flatten.map(_.getComposedMessageId.getMailboxId).toSet.size == 1
-
-    if (sameUpdate && singleMailbox && validUpdates.size > 3) {
-      val update: ValidatedEmailSetUpdate = validUpdates.map(_._2).headOption.get
-      val ranges: List[MessageRange] = asRanges(metaData)
-      val mailboxId: MailboxId = metaData.values.flatten.map(_.getComposedMessageId.getMailboxId).headOption.get
-
-      if (update.update.isOnlyFlagAddition) {
-        updateFlagsByRange(mailboxId, update.update.keywordsToAdd.get.asFlags, ranges, metaData, FlagsUpdateMode.ADD, session)
-      } else if (update.update.isOnlyFlagRemoval) {
-        updateFlagsByRange(mailboxId, update.update.keywordsToRemove.get.asFlags, ranges, metaData, FlagsUpdateMode.REMOVE, session)
-      } else if (update.update.isOnlyMove) {
-        moveByRange(mailboxId, update, ranges, metaData, session)
-      } else {
-        updateEachMessage(validUpdates, metaData, session)
-      }
-    } else {
-      updateEachMessage(validUpdates, metaData, session)
-    }
-  }
-
-  private def asRanges(metaData: Map[MessageId, Traversable[ComposedMessageIdWithMetaData]]) =
-    MessageRange.toRanges(metaData.values
-      .flatten.map(_.getComposedMessageId.getUid)
-      .toList.asJava)
-      .asScala.toList
-
-  private def updateFlagsByRange(mailboxId: MailboxId,
-                                 flags: Flags,
-                                 ranges: List[MessageRange],
-                                 metaData: Map[MessageId, Traversable[ComposedMessageIdWithMetaData]],
-                                 updateMode: FlagsUpdateMode,
-                                 session: MailboxSession): SMono[Seq[UpdateResult]] = {
-    val mailboxMono: SMono[MessageManager] = SMono.fromCallable(() => mailboxManager.getMailbox(mailboxId, session))
-
-    mailboxMono.flatMap(mailbox => updateByRange(ranges, metaData,
-      range => mailbox.setFlags(flags, updateMode, range, session)))
-      .subscribeOn(Schedulers.elastic())
-  }
-
-  private def moveByRange(mailboxId: MailboxId,
-                          update: ValidatedEmailSetUpdate,
-                          ranges: List[MessageRange],
-                          metaData: Map[MessageId, Traversable[ComposedMessageIdWithMetaData]],
-                          session: MailboxSession): SMono[Seq[UpdateResult]] = {
-    val targetId: MailboxId = update.update.mailboxIds.get.value.headOption.get
-
-    updateByRange(ranges, metaData,
-      range => mailboxManager.moveMessages(range, mailboxId, targetId, session))
-  }
-
-  private def updateByRange(ranges: List[MessageRange],
-                            metaData: Map[MessageId, Traversable[ComposedMessageIdWithMetaData]],
-                            operation: Consumer[MessageRange]): SMono[Seq[UpdateResult]] = {
-
-    SFlux.fromIterable(ranges)
-      .concatMap(range => {
-        val messageIds = metaData.filter(entry => entry._2.exists(composedId => range.includes(composedId.getComposedMessageId.getUid)))
-          .keys
-          .toSeq
-        SMono.fromCallable[Seq[UpdateResult]](() => {
-          operation.accept(range)
-          messageIds.map(UpdateSuccess)
-        })
-          .onErrorResume(e => SMono.just(messageIds.map(id => UpdateFailure(EmailSet.asUnparsed(id), e))))
-          .subscribeOn(Schedulers.elastic())
-      })
-      .reduce(Seq(), _ ++ _)
-  }
-
-  private def updateEachMessage(validUpdates: List[(MessageId, ValidatedEmailSetUpdate)],
-                                metaData: Map[MessageId, Traversable[ComposedMessageIdWithMetaData]],
-                                session: MailboxSession): SMono[Seq[UpdateResult]] =
-    SFlux.fromIterable(validUpdates)
-      .concatMap[UpdateResult]({
-        case (messageId, updatePatch) =>
-          updateSingleMessage(messageId, updatePatch, metaData.get(messageId).toList.flatten, session)
-      })
-      .collectSeq()
-
-  private def updateSingleMessage(messageId: MessageId, update: ValidatedEmailSetUpdate, storedMetaData: List[ComposedMessageIdWithMetaData], session: MailboxSession): SMono[UpdateResult] = {
-    val mailboxIds: MailboxIds = MailboxIds(storedMetaData.map(metaData => metaData.getComposedMessageId.getMailboxId))
-    val originFlags: Flags = storedMetaData
-      .foldLeft[Flags](new Flags())((flags: Flags, m: ComposedMessageIdWithMetaData) => {
-        flags.add(m.getFlags)
-        flags
-      })
-
-    if (mailboxIds.value.isEmpty) {
-      SMono.just[UpdateResult](UpdateFailure(EmailSet.asUnparsed(messageId), MessageNotFoundExeception(messageId)))
-    } else {
-      updateFlags(messageId, update, mailboxIds, originFlags, session)
-        .flatMap {
-          case failure: UpdateFailure => SMono.just[UpdateResult](failure)
-          case _: UpdateSuccess => updateMailboxIds(messageId, update, mailboxIds, session)
-        }
-        .onErrorResume(e => SMono.just[UpdateResult](UpdateFailure(EmailSet.asUnparsed(messageId), e)))
-        .switchIfEmpty(SMono.just[UpdateResult](UpdateSuccess(messageId)))
-    }
-  }
-
-  private def updateMailboxIds(messageId: MessageId, update: ValidatedEmailSetUpdate, mailboxIds: MailboxIds, session: MailboxSession): SMono[UpdateResult] = {
-    val targetIds = update.mailboxIdsTransformation.apply(mailboxIds)
-    if (targetIds.equals(mailboxIds)) {
-      SMono.just[UpdateResult](UpdateSuccess(messageId))
-    } else {
-      SMono.fromCallable(() => messageIdManager.setInMailboxes(messageId, targetIds.value.asJava, session))
-        .subscribeOn(Schedulers.elastic())
-        .`then`(SMono.just[UpdateResult](UpdateSuccess(messageId)))
-        .onErrorResume(e => SMono.just[UpdateResult](UpdateFailure(EmailSet.asUnparsed(messageId), e)))
-        .switchIfEmpty(SMono.just[UpdateResult](UpdateSuccess(messageId)))
-    }
-  }
-
-  private def updateFlags(messageId: MessageId, update: ValidatedEmailSetUpdate, mailboxIds: MailboxIds, originalFlags: Flags, session: MailboxSession): SMono[UpdateResult] = {
-    val newFlags = update.keywordsTransformation
-      .apply(LENIENT_KEYWORDS_FACTORY.fromFlags(originalFlags).get)
-      .asFlagsWithRecentAndDeletedFrom(originalFlags)
-
-    if (newFlags.equals(originalFlags)) {
-      SMono.just[UpdateResult](UpdateSuccess(messageId))
-    } else {
-      SMono.fromCallable(() =>
-        messageIdManager.setFlags(newFlags, FlagsUpdateMode.REPLACE, messageId, ImmutableList.copyOf(mailboxIds.value.asJavaCollection), session))
-        .subscribeOn(Schedulers.elastic())
-        .`then`(SMono.just[UpdateResult](UpdateSuccess(messageId)))
-    }
-  }
 }
\ No newline at end of file
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetUpdatePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetUpdatePerformer.scala
new file mode 100644
index 0000000..662435d
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetUpdatePerformer.scala
@@ -0,0 +1,249 @@
+/****************************************************************
+ * 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 java.util.function.Consumer
+
+import com.google.common.collect.ImmutableList
+import javax.inject.Inject
+import javax.mail.Flags
+import org.apache.james.jmap.core.SetError
+import org.apache.james.jmap.core.SetError.SetErrorDescription
+import org.apache.james.jmap.json.EmailSetSerializer
+import org.apache.james.jmap.mail.EmailSet.UnparsedMessageId
+import org.apache.james.jmap.mail.KeywordsFactory.LENIENT_KEYWORDS_FACTORY
+import org.apache.james.jmap.mail.{EmailSet, EmailSetRequest, MailboxIds, ValidatedEmailSetUpdate}
+import org.apache.james.jmap.method.EmailSetUpdatePerformer.{EmailUpdateFailure, EmailUpdateResult, EmailUpdateResults, EmailUpdateSuccess}
+import org.apache.james.mailbox.MessageManager.FlagsUpdateMode
+import org.apache.james.mailbox.exception.MailboxNotFoundException
+import org.apache.james.mailbox.model.{ComposedMessageIdWithMetaData, MailboxId, MessageId, MessageRange}
+import org.apache.james.mailbox.{MailboxManager, MailboxSession, MessageIdManager, MessageManager}
+import play.api.libs.json.JsObject
+import reactor.core.scala.publisher.{SFlux, SMono}
+import reactor.core.scheduler.Schedulers
+
+import scala.jdk.CollectionConverters._
+
+object EmailSetUpdatePerformer {
+  trait EmailUpdateResult
+  case class EmailUpdateSuccess(messageId: MessageId) extends EmailUpdateResult
+  case class EmailUpdateFailure(unparsedMessageId: UnparsedMessageId, e: Throwable) extends EmailUpdateResult {
+    def asMessageSetError: SetError = e match {
+      case e: IllegalArgumentException => SetError.invalidPatch(SetErrorDescription(s"Message $unparsedMessageId update is invalid: ${e.getMessage}"))
+      case _: MailboxNotFoundException => SetError.notFound(SetErrorDescription(s"Mailbox not found"))
+      case e: MessageNotFoundExeception => SetError.notFound(SetErrorDescription(s"Cannot find message with messageId: ${e.messageId.serialize()}"))
+      case _ => SetError.serverFail(SetErrorDescription(e.getMessage))
+    }
+  }
+  case class EmailUpdateResults(results: Seq[EmailUpdateResult]) {
+    def updated: Option[Map[MessageId, Unit]] =
+      Option(results.flatMap{
+        case result: EmailUpdateSuccess => Some(result.messageId, ())
+        case _ => None
+      }.toMap)
+        .filter(_.nonEmpty)
+
+    def notUpdated: Option[Map[UnparsedMessageId, SetError]] =
+      Option(results.flatMap{
+        case failure: EmailUpdateFailure => Some((failure.unparsedMessageId, failure.asMessageSetError))
+        case _ => None
+      }.toMap)
+        .filter(_.nonEmpty)
+  }
+}
+
+class EmailSetUpdatePerformer @Inject() (serializer: EmailSetSerializer,
+                              messageIdManager: MessageIdManager,
+                              mailboxManager: MailboxManager,
+                              messageIdFactory: MessageId.Factory) {
+
+  def update(emailSetRequest: EmailSetRequest, mailboxSession: MailboxSession): SMono[EmailUpdateResults] = {
+    emailSetRequest.update
+      .filter(_.nonEmpty)
+      .map(update(_, mailboxSession))
+      .getOrElse(SMono.just(EmailUpdateResults(Seq())))
+  }
+
+  private def update(updates: Map[UnparsedMessageId, JsObject], session: MailboxSession): SMono[EmailUpdateResults] = {
+    val validatedUpdates: List[Either[EmailUpdateFailure, (MessageId, ValidatedEmailSetUpdate)]] = updates
+      .map({
+        case (unparsedMessageId, json) => EmailSet.parse(messageIdFactory)(unparsedMessageId)
+          .toEither
+          .left.map(e => EmailUpdateFailure(unparsedMessageId, e))
+          .flatMap(id => serializer.deserializeEmailSetUpdate(json)
+            .asEither.left.map(e => new IllegalArgumentException(e.toString))
+            .flatMap(_.validate)
+            .fold(e => Left(EmailUpdateFailure(unparsedMessageId, e)),
+              emailSetUpdate => Right((id, emailSetUpdate))))
+      })
+      .toList
+    val failures: List[EmailUpdateFailure] = validatedUpdates.flatMap({
+      case Left(e) => Some(e)
+      case _ => None
+    })
+    val validUpdates: List[(MessageId, ValidatedEmailSetUpdate)] = validatedUpdates.flatMap({
+      case Right(pair) => Some(pair)
+      case _ => None
+    })
+
+    for {
+      updates <- SFlux.fromPublisher(messageIdManager.messagesMetadata(validUpdates.map(_._1).asJavaCollection, session))
+        .collectMultimap(metaData => metaData.getComposedMessageId.getMessageId)
+        .flatMap(metaData => {
+          doUpdate(validUpdates, metaData, session)
+        })
+    } yield {
+      EmailUpdateResults(updates ++ failures)
+    }
+  }
+
+  private def doUpdate(validUpdates: List[(MessageId, ValidatedEmailSetUpdate)],
+                       metaData: Map[MessageId, Traversable[ComposedMessageIdWithMetaData]],
+                       session: MailboxSession): SMono[Seq[EmailUpdateResult]] = {
+    val sameUpdate: Boolean = validUpdates.map(_._2).distinctBy(_.update).size == 1
+    val singleMailbox: Boolean = metaData.values.flatten.map(_.getComposedMessageId.getMailboxId).toSet.size == 1
+
+    if (sameUpdate && singleMailbox && validUpdates.size > 3) {
+      val update: ValidatedEmailSetUpdate = validUpdates.map(_._2).headOption.get
+      val ranges: List[MessageRange] = asRanges(metaData)
+      val mailboxId: MailboxId = metaData.values.flatten.map(_.getComposedMessageId.getMailboxId).headOption.get
+
+      if (update.update.isOnlyFlagAddition) {
+        updateFlagsByRange(mailboxId, update.update.keywordsToAdd.get.asFlags, ranges, metaData, FlagsUpdateMode.ADD, session)
+      } else if (update.update.isOnlyFlagRemoval) {
+        updateFlagsByRange(mailboxId, update.update.keywordsToRemove.get.asFlags, ranges, metaData, FlagsUpdateMode.REMOVE, session)
+      } else if (update.update.isOnlyMove) {
+        moveByRange(mailboxId, update, ranges, metaData, session)
+      } else {
+        updateEachMessage(validUpdates, metaData, session)
+      }
+    } else {
+      updateEachMessage(validUpdates, metaData, session)
+    }
+  }
+
+  private def asRanges(metaData: Map[MessageId, Traversable[ComposedMessageIdWithMetaData]]) =
+    MessageRange.toRanges(metaData.values
+      .flatten.map(_.getComposedMessageId.getUid)
+      .toList.asJava)
+      .asScala.toList
+
+  private def updateFlagsByRange(mailboxId: MailboxId,
+                                 flags: Flags,
+                                 ranges: List[MessageRange],
+                                 metaData: Map[MessageId, Traversable[ComposedMessageIdWithMetaData]],
+                                 updateMode: FlagsUpdateMode,
+                                 session: MailboxSession): SMono[Seq[EmailUpdateResult]] = {
+    val mailboxMono: SMono[MessageManager] = SMono.fromCallable(() => mailboxManager.getMailbox(mailboxId, session))
+
+    mailboxMono.flatMap(mailbox => updateByRange(ranges, metaData,
+      range => mailbox.setFlags(flags, updateMode, range, session)))
+      .subscribeOn(Schedulers.elastic())
+  }
+
+  private def moveByRange(mailboxId: MailboxId,
+                          update: ValidatedEmailSetUpdate,
+                          ranges: List[MessageRange],
+                          metaData: Map[MessageId, Traversable[ComposedMessageIdWithMetaData]],
+                          session: MailboxSession): SMono[Seq[EmailUpdateResult]] = {
+    val targetId: MailboxId = update.update.mailboxIds.get.value.headOption.get
+
+    updateByRange(ranges, metaData,
+      range => mailboxManager.moveMessages(range, mailboxId, targetId, session))
+  }
+
+  private def updateByRange(ranges: List[MessageRange],
+                            metaData: Map[MessageId, Traversable[ComposedMessageIdWithMetaData]],
+                            operation: Consumer[MessageRange]): SMono[Seq[EmailUpdateResult]] = {
+
+    SFlux.fromIterable(ranges)
+      .concatMap(range => {
+        val messageIds = metaData.filter(entry => entry._2.exists(composedId => range.includes(composedId.getComposedMessageId.getUid)))
+          .keys
+          .toSeq
+        SMono.fromCallable[Seq[EmailUpdateResult]](() => {
+          operation.accept(range)
+          messageIds.map(EmailUpdateSuccess)
+        })
+          .onErrorResume(e => SMono.just(messageIds.map(id => EmailUpdateFailure(EmailSet.asUnparsed(id), e))))
+          .subscribeOn(Schedulers.elastic())
+      })
+      .reduce(Seq(), _ ++ _)
+  }
+
+  private def updateEachMessage(validUpdates: List[(MessageId, ValidatedEmailSetUpdate)],
+                                metaData: Map[MessageId, Traversable[ComposedMessageIdWithMetaData]],
+                                session: MailboxSession): SMono[Seq[EmailUpdateResult]] =
+    SFlux.fromIterable(validUpdates)
+      .concatMap[EmailUpdateResult]({
+        case (messageId, updatePatch) =>
+          updateSingleMessage(messageId, updatePatch, metaData.get(messageId).toList.flatten, session)
+      })
+      .collectSeq()
+
+  private def updateSingleMessage(messageId: MessageId, update: ValidatedEmailSetUpdate, storedMetaData: List[ComposedMessageIdWithMetaData], session: MailboxSession): SMono[EmailUpdateResult] = {
+    val mailboxIds: MailboxIds = MailboxIds(storedMetaData.map(metaData => metaData.getComposedMessageId.getMailboxId))
+    val originFlags: Flags = storedMetaData
+      .foldLeft[Flags](new Flags())((flags: Flags, m: ComposedMessageIdWithMetaData) => {
+        flags.add(m.getFlags)
+        flags
+      })
+
+    if (mailboxIds.value.isEmpty) {
+      SMono.just[EmailUpdateResult](EmailUpdateFailure(EmailSet.asUnparsed(messageId), MessageNotFoundExeception(messageId)))
+    } else {
+      updateFlags(messageId, update, mailboxIds, originFlags, session)
+        .flatMap {
+          case failure: EmailUpdateFailure => SMono.just[EmailUpdateResult](failure)
+          case _: EmailUpdateSuccess => updateMailboxIds(messageId, update, mailboxIds, session)
+        }
+        .onErrorResume(e => SMono.just[EmailUpdateResult](EmailUpdateFailure(EmailSet.asUnparsed(messageId), e)))
+        .switchIfEmpty(SMono.just[EmailUpdateResult](EmailUpdateSuccess(messageId)))
+    }
+  }
+
+  private def updateMailboxIds(messageId: MessageId, update: ValidatedEmailSetUpdate, mailboxIds: MailboxIds, session: MailboxSession): SMono[EmailUpdateResult] = {
+    val targetIds = update.mailboxIdsTransformation.apply(mailboxIds)
+    if (targetIds.equals(mailboxIds)) {
+      SMono.just[EmailUpdateResult](EmailUpdateSuccess(messageId))
+    } else {
+      SMono.fromCallable(() => messageIdManager.setInMailboxes(messageId, targetIds.value.asJava, session))
+        .subscribeOn(Schedulers.elastic())
+        .`then`(SMono.just[EmailUpdateResult](EmailUpdateSuccess(messageId)))
+        .onErrorResume(e => SMono.just[EmailUpdateResult](EmailUpdateFailure(EmailSet.asUnparsed(messageId), e)))
+        .switchIfEmpty(SMono.just[EmailUpdateResult](EmailUpdateSuccess(messageId)))
+    }
+  }
+
+  private def updateFlags(messageId: MessageId, update: ValidatedEmailSetUpdate, mailboxIds: MailboxIds, originalFlags: Flags, session: MailboxSession): SMono[EmailUpdateResult] = {
+    val newFlags = update.keywordsTransformation
+      .apply(LENIENT_KEYWORDS_FACTORY.fromFlags(originalFlags).get)
+      .asFlagsWithRecentAndDeletedFrom(originalFlags)
+
+    if (newFlags.equals(originalFlags)) {
+      SMono.just[EmailUpdateResult](EmailUpdateSuccess(messageId))
+    } else {
+      SMono.fromCallable(() =>
+        messageIdManager.setFlags(newFlags, FlagsUpdateMode.REPLACE, messageId, ImmutableList.copyOf(mailboxIds.value.asJavaCollection), session))
+        .subscribeOn(Schedulers.elastic())
+        .`then`(SMono.just[EmailUpdateResult](EmailUpdateSuccess(messageId)))
+    }
+  }
+}


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


[james-project] 04/11: [REFACTORING] Rename model package to core

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

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

commit dcfdca4f3480404a5c5fcd45d991920730fc1c3e
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Nov 2 10:13:51 2020 +0700

    [REFACTORING] Rename model package to core
---
 .../org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java    |  4 ++--
 .../james/jmap/rfc8621/contract/EmailQueryMethodContract.scala |  2 +-
 .../james/jmap/rfc8621/contract/EmailSetMethodContract.scala   |  2 +-
 .../org/apache/james/jmap/{model => core}/Capabilities.scala   |  4 ++--
 .../org/apache/james/jmap/{model => core}/Capability.scala     | 10 +++++-----
 .../main/scala/org/apache/james/jmap/{model => core}/Id.scala  |  2 +-
 .../org/apache/james/jmap/{model => core}/Invocation.scala     |  4 ++--
 .../james/jmap/{model => core}/JmapRfc8621Configuration.scala  |  4 ++--
 .../org/apache/james/jmap/{model => core}/ProblemDetails.scala |  6 +++---
 .../org/apache/james/jmap/{model => core}/Properties.scala     |  2 +-
 .../scala/org/apache/james/jmap/{model => core}/Query.scala    |  2 +-
 .../james/jmap/{model => core}/RequestLevelErrorType.scala     |  4 ++--
 .../org/apache/james/jmap/{model => core}/RequestObject.scala  |  6 +++---
 .../org/apache/james/jmap/{model => core}/ResponseObject.scala |  4 ++--
 .../scala/org/apache/james/jmap/{model => core}/Session.scala  |  8 ++++----
 .../scala/org/apache/james/jmap/{model => core}/SetError.scala |  4 ++--
 .../org/apache/james/jmap/{model => core}/StatusCode.scala     |  2 +-
 .../scala/org/apache/james/jmap/{model => core}/UTCDate.scala  |  4 ++--
 .../org/apache/james/jmap/{model => core}/UnsignedInt.scala    |  2 +-
 .../jmap/{model => core}/JmapRfc8621ConfigurationTest.scala    |  6 +++---
 .../org/apache/james/jmap/{model => core}/KeywordTest.scala    |  4 +++-
 .../james/jmap/{model => core}/KeywordsCombinerTest.scala      |  6 ++++--
 .../org/apache/james/jmap/{model => core}/KeywordsTest.scala   |  9 +++++----
 .../james/jmap/{model => core}/MailboxValidationTest.scala     |  4 ++--
 .../org/apache/james/jmap/{model => core}/UTCDateTest.scala    |  2 +-
 25 files changed, 56 insertions(+), 51 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 f2c599b..08b9328 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
@@ -20,7 +20,7 @@
 package org.apache.james.jmap.rfc8621;
 
 
-import static org.apache.james.jmap.model.JmapRfc8621Configuration.LOCALHOST_CONFIGURATION;
+import static org.apache.james.jmap.core.JmapRfc8621Configuration.LOCALHOST_CONFIGURATION;
 
 import java.io.FileNotFoundException;
 
@@ -28,6 +28,7 @@ import org.apache.commons.configuration2.Configuration;
 import org.apache.commons.configuration2.ex.ConfigurationException;
 import org.apache.james.jmap.JMAPRoutesHandler;
 import org.apache.james.jmap.Version;
+import org.apache.james.jmap.core.JmapRfc8621Configuration;
 import org.apache.james.jmap.http.Authenticator;
 import org.apache.james.jmap.http.BasicAuthenticationStrategy;
 import org.apache.james.jmap.http.rfc8621.InjectionKeys;
@@ -44,7 +45,6 @@ import org.apache.james.jmap.method.SystemZoneIdProvider;
 import org.apache.james.jmap.method.VacationResponseGetMethod;
 import org.apache.james.jmap.method.VacationResponseSetMethod;
 import org.apache.james.jmap.method.ZoneIdProvider;
-import org.apache.james.jmap.model.JmapRfc8621Configuration;
 import org.apache.james.jmap.routes.DownloadRoutes;
 import org.apache.james.jmap.routes.JMAPApiRoutes;
 import org.apache.james.jmap.routes.SessionRoutes;
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/EmailQueryMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
index de4683f..9007184 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
@@ -37,8 +37,8 @@ import net.javacrumbs.jsonunit.core.Option.IGNORING_ARRAY_ORDER
 import net.javacrumbs.jsonunit.core.internal.Options
 import org.apache.http.HttpStatus.SC_OK
 import org.apache.james.GuiceJamesServer
+import org.apache.james.jmap.core.UTCDate
 import org.apache.james.jmap.http.UserCredential
-import org.apache.james.jmap.model.UTCDate
 import org.apache.james.jmap.rfc8621.contract.Fixture.{ACCEPT_RFC8621_VERSION_HEADER, ANDRE, ANDRE_PASSWORD, BOB, BOB_PASSWORD, DOMAIN, authScheme, baseRequestSpecBuilder}
 import org.apache.james.mailbox.FlagsBuilder
 import org.apache.james.mailbox.MessageManager.AppendCommand
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/EmailSetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSetMethodContract.scala
index 025bd7a..4fa93d0 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSetMethodContract.scala
@@ -31,9 +31,9 @@ import javax.mail.Flags
 import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
 import org.apache.http.HttpStatus.SC_OK
 import org.apache.james.GuiceJamesServer
+import org.apache.james.jmap.core.UTCDate
 import org.apache.james.jmap.draft.{JmapGuiceProbe, MessageIdProbe}
 import org.apache.james.jmap.http.UserCredential
-import org.apache.james.jmap.model.UTCDate
 import org.apache.james.jmap.rfc8621.contract.Fixture.{ACCEPT_RFC8621_VERSION_HEADER, ACCOUNT_ID, ANDRE, ANDRE_PASSWORD, BOB, BOB_PASSWORD, DOMAIN, authScheme, baseRequestSpecBuilder}
 import org.apache.james.mailbox.FlagsBuilder
 import org.apache.james.mailbox.MessageManager.AppendCommand
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capabilities.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capabilities.scala
similarity index 93%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capabilities.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capabilities.scala
index fc96632..8b74337 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capabilities.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capabilities.scala
@@ -16,10 +16,10 @@
  * specific language governing permissions and limitations      *
  * under the License.                                           *
  ****************************************************************/
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import eu.timepit.refined.auto._
-import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JAMES_QUOTA, JAMES_SHARES, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE}
+import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JAMES_QUOTA, JAMES_SHARES, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE}
 
 object DefaultCapabilities {
   def coreCapability(maxUploadSize: MaxSizeUpload) = CoreCapability(
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capability.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capability.scala
similarity index 93%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capability.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capability.scala
index 4f1202d..1cebf30 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capability.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capability.scala
@@ -17,17 +17,17 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import eu.timepit.refined
 import eu.timepit.refined.api.Refined
 import eu.timepit.refined.auto._
 import eu.timepit.refined.collection.NonEmpty
 import eu.timepit.refined.string.Uri
-import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JAMES_QUOTA, JAMES_SHARES, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE}
-import org.apache.james.jmap.model.CoreCapabilityProperties.CollationAlgorithm
-import org.apache.james.jmap.model.MailCapability.EmailQuerySortOption
-import org.apache.james.jmap.model.UnsignedInt.{UnsignedInt, UnsignedIntConstraint}
+import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JAMES_QUOTA, JAMES_SHARES, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE}
+import org.apache.james.jmap.core.CoreCapabilityProperties.CollationAlgorithm
+import org.apache.james.jmap.core.MailCapability.EmailQuerySortOption
+import org.apache.james.jmap.core.UnsignedInt.{UnsignedInt, UnsignedIntConstraint}
 import org.apache.james.util.Size
 
 import scala.util.{Failure, Success, Try}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Id.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Id.scala
similarity index 97%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Id.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Id.scala
index 0dc7167..60fc3d7 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Id.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Id.scala
@@ -17,7 +17,7 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import eu.timepit.refined
 import eu.timepit.refined.api.Refined
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Invocation.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Invocation.scala
similarity index 96%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Invocation.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Invocation.scala
index 8faf272..9d2f631 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Invocation.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Invocation.scala
@@ -16,11 +16,11 @@
  * specific language governing permissions and limitations      *
  * under the License.                                           *
  ****************************************************************/
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import eu.timepit.refined.auto._
 import eu.timepit.refined.types.string.NonEmptyString
-import org.apache.james.jmap.model.Invocation.{Arguments, MethodCallId, MethodName}
+import org.apache.james.jmap.core.Invocation.{Arguments, MethodCallId, MethodName}
 import play.api.libs.json._
 
 case class Invocation(methodName: MethodName, arguments: Arguments, methodCallId: MethodCallId)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/JmapRfc8621Configuration.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/JmapRfc8621Configuration.scala
similarity index 95%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/JmapRfc8621Configuration.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/JmapRfc8621Configuration.scala
index 5897623..9ce796a 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/JmapRfc8621Configuration.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/JmapRfc8621Configuration.scala
@@ -17,12 +17,12 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import java.net.URL
 
 import org.apache.commons.configuration2.Configuration
-import org.apache.james.jmap.model.JmapRfc8621Configuration.UPLOAD_LIMIT_30_MB
+import org.apache.james.jmap.core.JmapRfc8621Configuration.UPLOAD_LIMIT_30_MB
 import org.apache.james.util.Size
 
 object JmapRfc8621Configuration {
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/ProblemDetails.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/ProblemDetails.scala
similarity index 92%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/ProblemDetails.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/ProblemDetails.scala
index 4e8df2d..4d864d9 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/ProblemDetails.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/ProblemDetails.scala
@@ -16,12 +16,12 @@
  * specific language governing permissions and limitations      *
  * under the License.                                           *
  ****************************************************************/
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import eu.timepit.refined.auto._
 import org.apache.http.HttpStatus.SC_BAD_REQUEST
-import org.apache.james.jmap.model.RequestLevelErrorType.ErrorTypeIdentifier
-import org.apache.james.jmap.model.StatusCode.ErrorStatus
+import org.apache.james.jmap.core.RequestLevelErrorType.ErrorTypeIdentifier
+import org.apache.james.jmap.core.StatusCode.ErrorStatus
 
 /**
  * Problem Details for HTTP APIs within the JMAP context
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Properties.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Properties.scala
similarity index 98%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Properties.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Properties.scala
index 12d749b..bfbe18d 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Properties.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Properties.scala
@@ -17,7 +17,7 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import eu.timepit.refined.collection.NonEmpty
 import eu.timepit.refined.refineV
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Query.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Query.scala
similarity index 98%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Query.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Query.scala
index a2f6e07..3e746d8 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Query.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Query.scala
@@ -17,7 +17,7 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import com.google.common.hash.Hashing
 import eu.timepit.refined.api.Refined
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/RequestLevelErrorType.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/RequestLevelErrorType.scala
similarity index 97%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/RequestLevelErrorType.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/RequestLevelErrorType.scala
index 5013a73..9fc0e43 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/RequestLevelErrorType.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/RequestLevelErrorType.scala
@@ -16,11 +16,11 @@
  * specific language governing permissions and limitations      *
  * under the License.                                           *
  ****************************************************************/
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import eu.timepit.refined.api.Refined
-import eu.timepit.refined.string.Uri
 import eu.timepit.refined.auto._
+import eu.timepit.refined.string.Uri
 
 object RequestLevelErrorType {
   type ErrorTypeIdentifier = String Refined Uri
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/RequestObject.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/RequestObject.scala
similarity index 92%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/RequestObject.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/RequestObject.scala
index 3f40af6..c612a7e 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/RequestObject.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/RequestObject.scala
@@ -17,10 +17,10 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
-import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.model.Id.Id
+import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
+import org.apache.james.jmap.core.Id.Id
 
 final case class ClientId(value: Id) {
   def referencesPreviousCreationId: Boolean = value.value.startsWith("#")
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/ResponseObject.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/ResponseObject.scala
similarity index 94%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/ResponseObject.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/ResponseObject.scala
index f36f512..349e692 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/ResponseObject.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/ResponseObject.scala
@@ -17,10 +17,10 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import eu.timepit.refined.auto._
-import org.apache.james.jmap.model.State.State
+import org.apache.james.jmap.core.State.State
 
 case class ResponseObject(sessionState: State, methodResponses: Seq[Invocation])
 
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/core/Session.scala
similarity index 94%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Session.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Session.scala
index c76c5b3..00d6708 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/core/Session.scala
@@ -17,7 +17,7 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import java.net.URL
 import java.nio.charset.StandardCharsets
@@ -28,9 +28,9 @@ import eu.timepit.refined.auto._
 import eu.timepit.refined.collection.NonEmpty
 import eu.timepit.refined.refineV
 import org.apache.james.core.Username
-import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.model.Id.Id
-import org.apache.james.jmap.model.State.{INSTANCE, State}
+import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
+import org.apache.james.jmap.core.Id.Id
+import org.apache.james.jmap.core.State.{INSTANCE, State}
 
 case class IsPersonal(value: Boolean)
 case class IsReadOnly(value: Boolean)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/SetError.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/SetError.scala
similarity index 95%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/SetError.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/SetError.scala
index dc23bcc..5e13ca5 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/SetError.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/SetError.scala
@@ -17,12 +17,12 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import eu.timepit.refined.api.Refined
 import eu.timepit.refined.auto._
 import eu.timepit.refined.collection.NonEmpty
-import org.apache.james.jmap.model.SetError.{SetErrorDescription, SetErrorType}
+import org.apache.james.jmap.core.SetError.{SetErrorDescription, SetErrorType}
 
 object SetError {
   type SetErrorType = String Refined NonEmpty
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/StatusCode.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/StatusCode.scala
similarity index 97%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/StatusCode.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/StatusCode.scala
index 4de45e7..64661d5 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/StatusCode.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/StatusCode.scala
@@ -17,7 +17,7 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import eu.timepit.refined.api.Refined
 import eu.timepit.refined.numeric.Interval.Closed
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/UTCDate.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/UTCDate.scala
similarity index 94%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/UTCDate.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/UTCDate.scala
index 3acfb1c..7c62977 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/UTCDate.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/UTCDate.scala
@@ -17,12 +17,12 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import java.time.{ZoneId, ZonedDateTime}
 import java.util.Date
 
-import org.apache.james.jmap.model.UTCDate.UTC_ZONE_ID
+import org.apache.james.jmap.core.UTCDate.UTC_ZONE_ID
 
 object UTCDate {
   private val UTC_ZONE_ID: ZoneId = ZoneId.of("UTC")
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/core/UnsignedInt.scala
similarity index 98%
rename from server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/UnsignedInt.scala
rename to server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/UnsignedInt.scala
index 606b631..cabfb11 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/core/UnsignedInt.scala
@@ -17,7 +17,7 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import eu.timepit.refined
 import eu.timepit.refined.api.Refined
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/JmapRfc8621ConfigurationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/JmapRfc8621ConfigurationTest.scala
similarity index 93%
rename from server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/JmapRfc8621ConfigurationTest.scala
rename to server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/JmapRfc8621ConfigurationTest.scala
index 5d82b91..387f610 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/JmapRfc8621ConfigurationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/JmapRfc8621ConfigurationTest.scala
@@ -17,13 +17,13 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import java.net.URL
 
 import org.apache.commons.configuration2.{Configuration, PropertiesConfiguration}
-import org.apache.james.jmap.model.JmapRfc8621Configuration.URL_PREFIX_PROPERTIES
-import org.apache.james.jmap.model.JmapRfc8621ConfigurationTest.{emptyConfiguration, providedConfiguration}
+import org.apache.james.jmap.core.JmapRfc8621Configuration.URL_PREFIX_PROPERTIES
+import org.apache.james.jmap.core.JmapRfc8621ConfigurationTest.{emptyConfiguration, providedConfiguration}
 import org.scalatest.matchers.must.Matchers
 import org.scalatest.wordspec.AnyWordSpec
 
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/KeywordTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordTest.scala
similarity index 98%
rename from server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/KeywordTest.scala
rename to server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordTest.scala
index 96d5d93..d548bad 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/KeywordTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordTest.scala
@@ -17,14 +17,16 @@
  * under the License.                                           *
  * **************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import javax.mail.Flags
 import nl.jqno.equalsverifier.EqualsVerifier
 import org.apache.commons.lang3.StringUtils
+import org.apache.james.jmap.mail.Keyword
 import org.assertj.core.api.Assertions.{assertThat, assertThatCode}
 import org.scalatest.matchers.must.Matchers
 import org.scalatest.wordspec.AnyWordSpec
+
 import scala.util.Failure
 
 object KeywordTest {
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/KeywordsCombinerTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordsCombinerTest.scala
similarity index 96%
rename from server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/KeywordsCombinerTest.scala
rename to server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordsCombinerTest.scala
index 59d3df9..94f0b8b 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/KeywordsCombinerTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordsCombinerTest.scala
@@ -17,13 +17,15 @@
  * under the License.                                           *
  * **************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
-import org.apache.james.jmap.model.KeywordsFactory.LENIENT_KEYWORDS_FACTORY
+import org.apache.james.jmap.mail.KeywordsFactory.LENIENT_KEYWORDS_FACTORY
+import org.apache.james.jmap.mail.{Keywords, KeywordsCombiner}
 import org.apache.james.util.CommutativityChecker
 import org.assertj.core.api.Assertions.assertThat
 import org.scalatest.matchers.must.Matchers
 import org.scalatest.wordspec.AnyWordSpec
+
 import scala.jdk.CollectionConverters._
 
 class KeywordsCombinerTest extends AnyWordSpec with Matchers {
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/KeywordsTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordsTest.scala
similarity index 97%
rename from server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/KeywordsTest.scala
rename to server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordsTest.scala
index 5d28d36..0708919 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/KeywordsTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/KeywordsTest.scala
@@ -17,14 +17,15 @@
  * under the License.                                           *
  * **************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
-import org.assertj.core.api.Assertions.assertThat
 import javax.mail.Flags
 import javax.mail.Flags.Flag
-import org.apache.james.mailbox.FlagsBuilder
 import nl.jqno.equalsverifier.EqualsVerifier
-import org.apache.james.jmap.model.KeywordsFactory.{LENIENT_KEYWORDS_FACTORY, STRICT_KEYWORDS_FACTORY}
+import org.apache.james.jmap.mail.Keywords
+import org.apache.james.jmap.mail.KeywordsFactory.{LENIENT_KEYWORDS_FACTORY, STRICT_KEYWORDS_FACTORY}
+import org.apache.james.mailbox.FlagsBuilder
+import org.assertj.core.api.Assertions.assertThat
 import org.scalatest.matchers.must.Matchers
 import org.scalatest.wordspec.AnyWordSpec
 
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/core/MailboxValidationTest.scala
similarity index 97%
rename from server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/MailboxValidationTest.scala
rename to server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/MailboxValidationTest.scala
index dc54d01..69ff7d0 100644
--- 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/core/MailboxValidationTest.scala
@@ -17,13 +17,13 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import eu.timepit.refined.auto._
 import org.apache.james.core.Username
+import org.apache.james.jmap.core.UnsignedInt.UnsignedInt
 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.mailbox.model.MailboxPath
 import org.scalatest.matchers.must.Matchers
 import org.scalatest.wordspec.AnyWordSpec
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/UTCDateTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/UTCDateTest.scala
similarity index 98%
rename from server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/UTCDateTest.scala
rename to server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/UTCDateTest.scala
index f1ffe03..6060a6b 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/UTCDateTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/UTCDateTest.scala
@@ -17,7 +17,7 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.model
+package org.apache.james.jmap.core
 
 import java.time.ZonedDateTime
 


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


[james-project] 11/11: JAMES-2366 Demonstrate forward with local copy works with SMTP

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

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

commit b94374f4a640472e3e0d4acf1d3fc70c83d76c5c
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Oct 30 15:49:27 2020 +0700

    JAMES-2366 Demonstrate forward with local copy works with SMTP
---
 .../webadmin-integration-test-common/pom.xml       |  4 +++
 .../integration/ForwardIntegrationTest.java        | 38 ++++++++++++++++++++++
 2 files changed, 42 insertions(+)

diff --git a/server/protocols/webadmin-integration-test/webadmin-integration-test-common/pom.xml b/server/protocols/webadmin-integration-test/webadmin-integration-test-common/pom.xml
index 36f1dfe..ad7cc02 100644
--- a/server/protocols/webadmin-integration-test/webadmin-integration-test-common/pom.xml
+++ b/server/protocols/webadmin-integration-test/webadmin-integration-test-common/pom.xml
@@ -81,6 +81,10 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-guice-smtp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-guice-webadmin</artifactId>
         </dependency>
         <dependency>
diff --git a/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/ForwardIntegrationTest.java b/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/ForwardIntegrationTest.java
index f0d2be3..f656f8f 100644
--- a/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/ForwardIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/ForwardIntegrationTest.java
@@ -46,6 +46,7 @@ import org.apache.james.GuiceJamesServer;
 import org.apache.james.jmap.AccessToken;
 import org.apache.james.jmap.draft.JmapGuiceProbe;
 import org.apache.james.junit.categories.BasicFeature;
+import org.apache.james.modules.protocols.SmtpGuiceProbe;
 import org.apache.james.probe.DataProbe;
 import org.apache.james.util.Port;
 import org.apache.james.utils.DataProbeImpl;
@@ -199,6 +200,43 @@ public abstract class ForwardIntegrationTest {
     }
 
     @Test
+    void smtpMessageShouldBeForwardedWhenBaseRecipientWhenInDestination(GuiceJamesServer jmapServer) throws Exception {
+        webAdminApi.put(String.format("/address/forwards/%s/targets/%s", ALICE.asString(), BOB.asString()));
+        webAdminApi.put(String.format("/address/forwards/%s/targets/%s", ALICE.asString(), ALICE.asString()));
+
+        messageSender
+            .connect("127.0.0.1", jmapServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
+            .sendMessage("cedric@other.com", ALICE.asString());
+
+        AccessToken bobAccessToken = authenticateJamesUser(baseUri(jmapPort), BOB, BOB_PASSWORD);
+        calmlyAwait
+            .pollDelay(Duration.FIVE_HUNDRED_MILLISECONDS)
+            .atMost(30, TimeUnit.SECONDS).until(() -> isAnyMessageFoundInRecipientsMailboxes(bobAccessToken));
+        given()
+            .header("Authorization", bobAccessToken.asString())
+            .body("[[\"getMessageList\", {}, \"#0\"]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .log().ifValidationFails()
+            .statusCode(200)
+            .body(NAME, equalTo("messageList"))
+            .body(ARGUMENTS + ".messageIds", hasSize(1));
+
+        AccessToken aliceAccessToken = authenticateJamesUser(baseUri(jmapPort), ALICE, ALICE_PASSWORD);
+        given()
+            .header("Authorization", aliceAccessToken.asString())
+            .body("[[\"getMessageList\", {}, \"#0\"]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .log().ifValidationFails()
+            .statusCode(200)
+            .body(NAME, equalTo("messageList"))
+            .body(ARGUMENTS + ".messageIds", hasSize(1));
+    }
+
+    @Test
     void recursiveForwardShouldWork() {
         webAdminApi.put(String.format("/address/forwards/%s/targets/%s", ALICE.asString(), CEDRIC.asString()));
         webAdminApi.put(String.format("/address/forwards/%s/targets/%s", CEDRIC.asString(), BOB.asString()));


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